Nicht unbedingt Perl, Syntax der Aufgabe entsprechend jedoch ähnlich.
Das Rechnen mit IP-Adressen ist kein Hexenwerk. Schritt für Schritt erklärt dieser Artikel, welche Berechnungen notwendig sind und wie sich daraus einzelne Funktionen ableiten lassen. Alle Funktionen und Codebeispiele sind in der Programmiersprache c dargestellt, damit kann eine komplette Library aufgebaut werden, siehe dazu auch Hinweise am Ende dieser Seite. Grundsätzlich geht es hier auch nur um das Internet Protokoll (IP) in der Version 4 (ipv4).
Jede IP-Adresse, Netzmaske oder Broadcastadresse ist eine integer Zahl mit 32 Bit. Somit ist eine solche Adresse auf einer 32-Bit-Architektur gerade noch abbildbar. Ein:
printf("%u\n", 0xffffFFFF);
gibt diese größtmögliche Zahl aus: 4,294,967,295 (der Übersicht halber die Tausender mit Komma getrennt). Wichtig ist der Platzhalter %u in der printf-Anweisung, %u heißt unsigned integer und das bedeutet, dass sich die 32 Bit nur im positiven Bereich abspielen. Wird dieser Sachverhalt nicht beachtet, läge der Wertebereich bei plusminus 2,147,483,647 und es gäbe nur noch 31 Bit für den positiven Bereich. Merke: Alle Variablen für die numerischen Werte für IP-Adressen, Netzadressen und Broadcastadressen sind als
unsigned long ipaddr, netaddr, bcaddr; oder unsigned int ipaddr, netaddr, bcaddr;
zu deklarieren! Noch ein Wort zu 0xffffFFFF: Das ist ein 32-Bit-integer in hexadezimaler Schreibweise. Ein 'F' steht für vier Bit (Dezimalzahl 15), acht 'F' ergeben somit 32 Bit. Die Schreibweise ist case unsensitive, vier kleine und vier große 'F' sind also leicht überschaubar im Programmcode.
Bekanntlich schreibt niemand gerne große Zahlen, deswegen hat sich für die Darstellung von IP-Adressen die sogenannte Oktettenschreibweise durchgesetzt. Da sind 32 Bit einfach nur aufgeteilt in vier Oktetten mit jeweils acht Bit und mit einem Punkt getrennt aufgeschrieben:
Die IP-Adresse mit dem numerischen Wert 2 hoch 31 bitweise dargestellt: 10000000.00000000.00000000.00000000 Betrachten wir die erste Oktette allein für sich: 10000000 Die ergibt nach dezimal umgerechnet 2 hoch 7 128 Und so sieht die Dezimal- (Oktetten) Schreibweise aus: 128.0.0.0
Zwischen (2 hoch 31) und (2 hoch 7) liegen also 31 - 7 = 24 Bit. Gedanklich haben wir soeben die Zahl 128 um 24 Bit nach links geschoben.
Mit diesem Verständnis kommen wir kurz und knapp zu den Formeln der Umrechnung von Oktetten in die numerische Adresse und umgekehrt.
/*************************************************************************/ // rechnet ip (4 Oktetten einzeln übergeben) nach numerisch // und gibt ipnum zurück int ip_to_num(int a, int b, int c, int d){ unsigned long ipnum; // IP numerisch <= 4294967295; ipnum = (a << 24) + (b << 16) + (c << 8) + d; return ipnum; } /*************************************************************************/
Die erste Oktette wird um 24 Bit (32 minus 8 Bit) nach links geschoben, die zweite Oktette um 16 Bit und die dritte Oktette um acht Bit. Die vierte Oktette wird einfach nur dazuaddiert. Daraus ergibt sich der numerische Wert eine IP-Adresse. Zum Prüfen: die Adresse 255.255.255.255 ergibt 2 hoch 32 (0xFFFFFFFF).
Zum Verschieben der Bits nach links oder nach rechts, gibt es in c den sogenannten Shift-Operator. Das sind die beiden Angel-Brackets (spitze Klammern), die entweder nach links (Left-Shift) oder nach rechts (Right-Shift) zeigen.
Bisher haben wir gesehen, wie mit Left-Shift die einzelnen Oktetten in eine 32-Bit-Zahl hineingeschoben werden um den numerischen Wert dieser Zahl zu ermitteln.
Andersherum, mit Right-Shift also, können die einzelnen Oktetten aus einer numerischen IP-Adresse auch wieder "herausgezogen" werden, dazu wird nach dem Shift-Operator eine 8-Bit-Maske über das Bitmuster gelegt:
/*************************************************************************/ // rechnet aus numerischer IP die 4 Oktetten zurück // legt die 4 Oktetten auf eine Array, was per Pointer übergeben wurde void num_to_okt(int * ar, unsigned long ipnum){ ar[0] = (ipnum >> 24) & 0xff; // 11111111.0000... >> 00000000.00000000.00000000.11111111 ar[1] = (ipnum >> 16) & 0xff; // 00000000.1100... >> 00000000.00000000.00000000.11000000 ar[2] = (ipnum >> 8) & 0xff; // usw. ar[3] = ipnum & 0xff; } /*************************************************************************/
Der Operator & vergleicht die ge-shiftete Oktette mit der Maske 0xff, eine Maske mit genau acht Bit. Infolge dieser logischen (bitweisen) UND-Verknüpfung wird die einzelne Oktette als Ganzzahl ermittelt.
Ein paar Worte seien noch gesagt zu diesem Thema. Anders als in Perl, kann eine Unterfunktion in c lediglich einen einzigen Wert und diesen auch nur nach einem speziellen Datentyp zurückgeben. Am einfachsten ist die Rückgabe eines Zahlenwertes vom Datentyp int oder float (Ganzzahl oder Fließkommazahl). Dementsprechend sind die Unterfunktionen deklariert wie int funktion... oder float funktionsname.... Eine Funktion, die nichts zurückgibt, ist mit dem Schlüsselwort void deklariert.
Die Rückgabe von mehr als einem Wert ist programmiertechnisch auch nicht unbedingt notwendig, für diese Fälle bietet c die Möglichkeit an, mit Pointern zu arbeiten, Pointer sind Zeiger auf Adressbereiche.
Im vorliegenden Artikel werden einige Funktionen, so wie obenstehend, beschrieben, die mit diesen Pointern arbeiten. Die Funktion num_to_okt beispielsweise bekommt einen Zeiger übergeben, der auf den Adressbereich eines Arrays mit integer-Werten zeigt. In der Funktion selbst wird dieses Array mit Werten (Oktetten) gefüllt. Der Zeiger auf das Array ar wird mit einen '*' gekennzeichnet.
Innerhalb der runden Klammern einer Funktions-Deklaration können weitere Parameter übergeben werden. Diese Parameter sind vom Datentyp her namentlich benannt und innerhalb der Sub-Funktion direkt ansprechbar wie z.B. die Variable ipnum obenstehend oder die einzelnen Oktetten a,b,c,d in der anderen Funktion.
Die übergabe eines Pointers auf ein Array aus der main() Funktion an eine Unterfunktion ist gar nicht mal so schwer. Je nach Datentyp wird das Array deklariert:
int zahlen[10]; // ein Array für 10 Zahlen vom Datentyp integer char zeichen[20]; // ein Array für 20 beliebige Zeichen wie "a", "B" oder "." usw.
Gaanz wichtig ist die Initialisierung von Variablen in c, im Fall der Arrays passiert das in dem Moment, wo die Länge innnerhalb der Klammern [] vorgegeben wird, in diesem Moment wird im Hauptspeicher des Rechners der Speicherplatz mit der entsprechenden Größe reserviert.
Mit dem Aufruf einer Funktion like fx(zahlen) oder fy(zeichen) werden diesen Funktionen praktisch Zeiger auf Adressbereiche übergeben (Array zahlen oder Array zeichen). Die Funktionen selbst sind wie folgt deklariert:
void fx(int zahlen[]); void fy(char zeichen[]); oder in einer etwas anderen Schreibweise void fx(int * zahlen); void fy(char * zeichen);
Betrachte mit mir die Funktion:
/*************************************************************************/ // legt den IP-String auf das übergebene Array void ipdots(char * ips, unsigned long ipnum){ int okt[4]; num_to_okt(okt, ipnum); sprintf(ips, "%d.%d.%d.%d", okt[0], okt[1], okt[2], okt[3]); } /*************************************************************************/
Aufgerufen wird diese Funktion wie folgt:
int main(){ char ipstring[20]; // Naja, mit 20 Zeichen reichlich deklariert ipdots(ipstring, 0xffffFFFF); // erwarte auf ipstring die Zeichenkette 255.255.255.255 printf("%s\n", ipstring); // 255.255.255.255 return 0; }
Die Funktion ipdots() nimmt bekommt einen Zeiger auf die Adresse des Ergebnis-Arrays und eine Nummer übergeben. Der IP-String wird berechnet und auf das Ergebnis-Array gelegt, das geschieht mit der Funktion sprintf(). sprintf() formatiert die einzelnen Zahlen der Oktetten um als eine Zeichenkette.
Die ersten vier Bits bestimmen die Default Class einer IP-Adresse. Es gilt für die ersten vier Bits:
0 class A 0-127 10 class B 128-191 110 class C 192-223 111 class D 224-239 1111 class E ab 240
Zum Berechnen der Default-Class einer IP-Adresse wird also lediglich die erste Oktette benötigt und von dieser Oktette wiederum nur die ersten vier Bit. Um an diese vier Bit heranzukommen, wird der Shift-Operator angewandt. Obenstehende Bitmuster für die einzelnen Netzklassen A, B, C, D, E können in Zahlen ausgedrückt werden:
0 0 10 2 110 6 111 7 1111 0xF
Untenstehend die Funktion zur Berechnung der Default-Class.
/*************************************************************************/ // ermittelt die Default Class aus der ersten Oktette // 0 class A 0-127 // 10 class B 128-191 // 110 class C 192-223 // 111 class D 224-239 // 1111 class E ab 240 void default_class(char buffer[], int okt){ if(okt >> 7 == 0) { strcpy(buffer, "A");} else if( okt >> 4 == 0xF){ strcpy(buffer, "E (Reserviert)");} else if( okt >> 5 == 7) { strcpy(buffer, "D (Multicast)");} else if( okt >> 5 == 6) { strcpy(buffer, "C");} else if( okt >> 6 == 2) { strcpy(buffer, "B");} } /*************************************************************************/
Bevor die Funktion strcpy() benutzt werden kann, wird mit der Prä-Prozessor-Anweisung:
#include <string.h>
die Library string.h geladen. strcpy() kopiert die sich ergebende Zeichenkette (z.B. "D (Multicast)") auf den buffer, welcher von der aufrufenden Funktion als Pointer übergeben wurde.
Zur eindeutigen Bestimmung einer IP-Adresse ist neben dieser auch eine Netzmaske anzugeben. Gebräuchlich sind Angaben wie diese:
192.168.0.0 255.255.0.0 oder 192.168.0.0/16
Die Maskenlänge wird von links her gezählt, im Beispiel 255.255.0.0 haben wir 255 in der ersten Oktette, das sind acht Bit. Dazu kommen acht Bit aus der zweiten Oktette, womit sich eine Maskenlänge von 16 Bit ergibt, siehe Schreibweise weiter unten.
Für weitere Berechnungen zu einer bestimmten IP-Adresse muss die Maskenlänge (0..31) in einen numerischen Wert umgerechnet werden.
/*************************************************************************/ // maskenlänge zu einer uint 32 bit Zahl umrechnen // masklen 0..32 !!! int masklen_to_num(int masklen){ if(masklen == 0) return 0; else return(0xffffFFFF <<(32 - masklen)); } /*************************************************************************/
Ein paar Worte auch hierzu. Eine Maskenlänge von 0 ist klar, das sieht bitmäßig so aus:
00000000.00000000.00000000.00000000
Eine Maskenlänge von 16 sieht folgerichtig so aus (16 Bit v.l.n.r):
11111111.11111111.00000000.00000000
Obenstehende Funktion schiebt praktisch von rechts her eine bestimmte Anzahl Nullen (32 minus Maskenlänge) in die 32-Bit-Zahl 0xffffFFFF womit sich aus der Maskenlänge der Numerische Wert der Netzmaske ergibt.
Etwas trickreicher ist die Umrechnung einer 32-Bit-Zahl zur Maskenlänge. Hierbei ist nämlich genau zu prüfen, ob die Bits aufeinanderfolgend sind.
Zum Verständnis: Die Netzmaske 255.255.0.0 ergibt lückenlos aneinandergereihte Bits 11111111.11111111.00000000.00000000 woraus sich eine gültige Maske ergibt. Eine Netzmaske 255.0.128.0 hingegen, ergibt 11111111.00000000.10000000.00000000 ein Bitmuster mit NICHT aufeinanderfolgenden Bits, somit ist diese Netzmaske nicht zulässig!!! /*************************************************************************/ // numerische Maske (32 unsigned int) nach Maskenl. 0..32 umrechnen // prueft die Gueltigkeit der sich ergebenden Maske int num_to_masklen(unsigned long n){ int bit; int masklen = 0; int merk; int changes = 0; // Beim Durchgehen durch die Bits darf es max nur einen Wechsel geben int i = 0; for(i = 0; i < 32; i++){ bit = (n >> i) & 1; if(i == 0){ merk = bit; } if(bit != merk){ // Wechsel changes++; merk = bit; } if(bit) masklen++; } if(changes <= 1){ return masklen ;} else{ return -1; } // Maske ist nicht gueltig } /*************************************************************************/
Die gezeigte Funktion schiebt per Shift jedes einzelne Bit an die Position 2 hoch 0 also nach ganz rechts. Hier kommen entweder nur Nullen oder Einsen an. Eine Netzmaske ist nur dann gültig, wenn es maximal einen Wechsel von 0 auf 1 gegeben hat. Sofern es keinen Wechsel gab, liegt eine Maskenlänge von 32 Bit vor.
Siehe auch die Tabelle weiter unten, sie zeigt den Zusammenhang zwischen gültigen Netzmasken in Oktettenschreibweise und Maskenlänge.
Zur Berechnung beider Adressen wird eine IP-Adresse und die Netz-Maske benötigt. Sofern beide Werte als integer-Zahlen vorliegen, ergeben sich Netz- sowie Broadcastadresse aufgrund einer einfachen binären Verknüpfung:
/*************************************************************************/ // gibt die Netzadresse numerisch zurück (ipnum, maskenum) int get_netaddr(unsigned long ipnum, unsigned long masknum){ return(ipnum & masknum); } /*************************************************************************/ // gibt die Broadcastadresse numerisch zurück (ipnum, maskenum) int get_bcaddr(unsigned long ipnum, unsigned long masknum){ return(ipnum |~ masknum); } /*************************************************************************/
Die Operatoren & und | sind bitweises AND und bitweises OR. Der Operator ~ dreht alle Bits von 0 auf 1 und umgekehrt (Komplement).
Der gesamte Bereich der IP-Adressen geht von 0.0.0.0 (Netzadresse) bis 255.255.255.255 (Broadcastadresse) was sich aus einer Masklenlänge von 0 ergibt. Das sind, wie bisher dargelegt wurde, 4,294,967,295 Hosts. Für Geräte (PC) stehen jedoch die Broadcastadresse und die Netzadresse nicht zur Verfügung, das bedeutet, dass bei jeder Netzberechnung zwei Adressen abgezogen werden müssen. Es ergibt sich folgende Aufstellung:
Netzmaske | Maskenlaenge/Bit | Anzahl moeglicher Hosts |
---|---|---|
255.255.255.255 | 32 | 1 (Single Host) |
255.255.255.254 | 31 | 0 (nicht benutzbar) |
255.255.255.252 | 30 | 2 |
255.255.255.248 | 29 | 6 |
255.255.255.240 | 28 | 14 |
255.255.255.224 | 27 | 30 |
255.255.255.192 | 26 | 62 |
255.255.255.128 | 25 | 126 |
255.255.255.0 | 24 | 254 |
255.255.254.0 | 23 | 510 |
255.255.252.0 | 22 | 1022 |
255.255.248.0 | 21 | 2046 |
255.255.240.0 | 20 | 4094 |
255.255.224.0 | 19 | 8190 |
255.255.192.0 | 18 | 16382 |
255.255.128.0 | 17 | 32766 |
255.255.0.0 | 16 | 65534 |
255.254.0.0 | 15 | 131070 |
255.252.0.0 | 14 | 262142 |
255.248.0.0 | 13 | 524286 |
255.240.0.0 | 12 | 1048574 |
255.224.0.0 | 11 | 2097150 |
255.192.0.0 | 10 | 4194302 |
255.128.0.0 | 9 | 8388606 |
255.0.0.0 | 8 | 16777214 |
254.0.0.0 | 7 | 33554430 |
252.0.0.0 | 6 | 67108862 |
248.0.0.0 | 5 | 134217726 |
240.0.0.0 | 4 | 268435454 |
224.0.0.0 | 3 | 536870910 |
192.0.0.0 | 2 | 1073741822 |
128.0.0.0 | 1 | 2147483646 |
0.0.0.0 | 0 | 4294967293 |
Die Tabelle von unten nach oben gelesen zeigt, dass jede Verlängerung der Maske um ein Bit das vorliegende Netz durch zwei teilt. Teilungen durch ungerade Zahlen sind nicht zulässig, ebenso Teilungen durch krumme Werte, die sich aus log2(teilungsfaktor) ergeben. Siehe dazu auch eine spezielle Funktion weiter unten.
Die erste Teilung des gesamten IP-Ranges ergibt:
Basis Netz mit Maskenlaenge 0 und 4294967293 Hosts Netzadresse Broadcastadresse 0.0.0.0 255.255.255.255 2 Subnetze mit Maskenlaenge 1 und 2147483646 Hosts Netzadresse Broadcastadresse 0.0.0.0 127.255.255.255 128.0.0.0 255.255.255.255
Die Netzadresse ist also die erste IP-Adresse in einem Netz und die Broadcastadresse ist die letzte IP-Adresse.
Bisher betrachteten wir die Eigenschaften eines IP-Netzes, wie Netzadresse, Broadcastadresse usw. Um dies zu berechnen, schrieben wir einige Funktionen, sogenannte Elementarfunktionen. Die Programmiersprache c erlaubt es, verschiedene Datentypen in einem sogenannten struct zusammenzufassen. Ein struct ist wie eine Karteikarte, ein Steckbrief sozusagen.
Ein Steckbrief für ein Netzwerk könnte beispeilsweise so aussehen:
/*************************************************************************/ /* Struktur für ein Netz */ struct netx{ unsigned long ipaddr; // IP-Adresse numerisch int masklen; // 0..32 unsigned long netmask; // Netzmaske numerisch unsigned long netaddr; // Netzadresse numerisch unsigned long bcaddr; // Broadcastadresse numerisch char netmasks[20]; // Netzmaske als String like 255.0.0.0 char ipaddrs[20]; // IP-Adresse als String like 172.31.11.12 char netaddrs[20]; // Netzadresse als String like 0.0.0.0 char bcaddrs[20]; // Broadcastadresse als String like 255.255.255.255 char defaultclass[20]; // Default Class A, B, C, D, E }; /*************************************************************************/
In einem solchen struct sind praktisch mehrere Datentypen miteinander vereint. Zur Verwendung des structs wird dieses noch vor der main()-Funktion deklariert. Dazu kommt eine Funktion, welche das struct mit Daten füllt, diese Funktion bekommt einen Pointer auf das struct übergeben, die Oktetten und die Maskenlänge:
/*************************************************************************/ // füllt das struct netx mit Werten // benutzt dazu die weiter oben beschriebenen Elementarfunktionen void fillnet(struct netx *p, int o1, int o2, int o3, int o4, int mlen){ char b[20]; // buffer p->ipaddr = ip_to_num(o1,o2,o3,o4); p->masklen = mlen; p->netmask = masklen_to_num(mlen); p->netaddr = get_netaddr(p->ipaddr, p->netmask); p->bcaddr = get_bcaddr(p->ipaddr, p->netmask); ipdots(b, p->netmask); strcpy(p->netmasks, b); ipdots(b, p->ipaddr); strcpy(p->ipaddrs, b); ipdots(b, p->netaddr); strcpy(p->netaddrs, b); ipdots(b, p->bcaddr); strcpy(p->bcaddrs, b); default_class(b, o1); strcpy(p->defaultclass, b); } /*************************************************************************/ int main(){ struct netx *p; // Ein Pointer auf das struct; // Speicher anfordern für den pointer (Initialisierung) p = (struct netx *)malloc(sizeof(struct netx)); // Nun kann eine Funktion mit dem Pointer im Argument aufgerufen werden fillnet(p, 192, 168, 0, 0, 24); // Pointer, oktette1..oktette4, maskenlänge // danach kann mit net.* auf jeden Wert des structs zugegriffen werden, Beispiele: printf("IP: %s\n", p->ipaddrs); printf("Netzadresse: %s\n", p->netaddrs); printf("Broadcastadresse: %s\n", p->bcaddrs); return 0; }
Sofern Netze in Subnetze geteilt werden sollen, ist zu beachten, dass der Teiler eine ganzzahlige Potenz von 2 ist. In der c-Library math.h gibt es zwar die Funktion log2(), diese liefert jedoch eine Zahl vom Datentyp float, was weitere Prüfungen erfordert. Untenstehende Funktion tut unabhängig von der math.h und ermittelt den Logarithmus gleich als einen Integer. Im Fehlfall gibt die Funktion -1 zurück. Beachte: Der Logarithmus 1 zur Basis 2 ist 0.
/*************************************************************************/ // Funktion prüft, ob die Zahl eine ganzzahlige Potenz von 2 ist // Im Fehlfall wird -1 zurückgegeben int logint2(unsigned long n){ int i = 0; int bit = 0; int einser = 0; int pos = 0; for(i = 0; i < 32; i++){ bit = (n >> i) & 1; if(bit){ pos = i; einser++; } } if(einser > 1){ return -1;} else{ return pos; } } /*************************************************************************/ int main(){ printf("%d\n", logint2(16)); // 4 return 0; } /*************************************************************************/
Die nächste hier vorgestellte Funktion legt das Bitmuster einer 32-Bit-Zahl auf ein Array mit integer-Werten. Diese Funktion leistet gute Dienste zur Ermittelung der Common-Bits mehrerer IP-Adressen, beispielsweise zur Berechnen einer Route-Summary:
Gegeben sind die Adressen 192.168.0.0 192.168.128.0 Gesucht ist die Anzahl der führenden Bits, die beide Adressen gmeinsam haben.
Das Beispiel ist noch überschaubar, nur die ersten beiden Oktetten sind gleich, das ergibt 16 Bit. Somit ergibt sich die Route-Summary wie folgt:
192.168.0.0/16
/*************************************************************************/ // bitmuster einer Zahl auf ein array legen void bitmuster(int * ar, unsigned long n){ int i = 0; for(i = 0; i < 32; i++){ ar[i] = (n >> i) & 1; } } /*************************************************************************/ int main(){ int i; int bits[33]; // hier werden die Bits abgelegt bitmuster(bits, 255); // nur ein Beispiel // umgekehrt ausgeben for(i = 31; i > -1; i--){ printf("%d", bits[i]); } printf("\n"); // 00000000000000000000000011111111 return 0; } /*************************************************************************/
Um an die einzelnen Oktetten und die Maskenlänge einer IP-Adresse like 172.31.11.12/24 zu kommen, wird dieser String gesplittet. Bewährt hat sich dazu diese Funktion:
/*************************************************************************/ // splittet nach "." und "/" // nice to use for 172.31.32.0/24 void split(int * ar, char buffer[]){ char * pch; int i = 0; pch = strtok(buffer, "./"); while (pch != NULL){ // printf("%s\n", pch); ar[i] = atoi(pch); pch = strtok (NULL, "./"); i++; } } // Achtung strok() zerlegt den buffer im Speicherbereich!!! // Nach Verlassen dieser Funktion ist buffer[] nicht mehr im Original!!! /*************************************************************************/ // Die Verwendung obenstehender Funktion split() int main(){ int okt[10]; // Array mit Integer-Werten für die Oktetten und Maskenl. char buffer[20]; // hier liegt die IP-Adresse als Zeichenkette strcpy(buffer, "172.31.32.0/24"); // IP auf den buffer kopieren split(okt, buffer); // Die Oktetten liegen nun auf okt[0] bis okt[3] // Die Maskenlänge liegt auf okt[4] return 0; } /*************************************************************************/
Wichtig: Liegt der IP-String als eine Benutzereingabe vor, sind bereits vor der Anwendung von split() Prüfungen notwendig:
/*************************************************************************/ // ein IP-String wie 255.255.255.255/30 darf nicht länger als 18 sein // muss genau 3 dots und einen slash enthalten // darf ausser dots und slashes nur Ziffern enthalten int valips(char buffer[]){ if(strlen(buffer) > 18) return 0; int dots = 0; // dots int slhs = 0; // slashes int muell = 0; // nicht dot, nicht slash, nicht ziffer int i; for(i = 0; i < strlen(buffer); i++){ if( (int)buffer[i] == 46) dots++; if( (int)buffer[i] == 47) slhs++; if( (int)buffer[i] < 46 || (int)buffer[i] > 57 ) muell++; } if( dots != 3 ) return 0; else if( slhs != 1 ) return 0; else if( muell > 0 ) return 0; else return 1; } /*************************************************************************/
Nur wenn die Prüfung der Zeichenkette keinen Fehler ergibt, kann diese an split() übergeben werden. Die Sinnfälligkeit der einzelnen Oktetten und die Sinnfälligkeit der Maskenlänge ist später gesondert zu prüfen.
Die IP-Adressen müssen numerisch in einem Array vorliegen. Untenstehende Funktion sortiert aufsteigend nach einem eigenen, von mir am 22.4.2008 entwickelten Algorithmus. Dabei wird ein Zahlen-Array mit bspw. 10 Zahlen lediglich nur fünfmal durchlaufen, danach ist es sortiert. Ein Array mit 11 Zahlen wird sechsmal durchlaufen. Das macht diese Sortierfunktion sehr performant.
Funktionsweise: Bei jedem Durchlauf wird das Minimum UND das Maximum ermittelt, sowie die Positionen dieser Werte im Array. Das Minimum tauscht dann seinen Platz mit der Startposition und dem dortigen Wert. Das Maximum tauscht den Platz mit der Stopposition. Das ist der Gewinn dieses Algorithmus gegenüber einem Selection Sort, bei dem lediglich das Minimum verschoben wird.
// keine Rekursion, Funktion muss iterativ aufgerufen werden! void xSort(int * p, int start, int stop){ unsigned long min = 0xffffFFFF; // GAZ, Größte Anzunehmende Zahl unsigned int unsigned long max = 0; int posmin; int posmax; int i; for(i = start; i < stop; i++){ if( p[i] < min ) { min = p[i]; posmin = i; } if( p[i] > max ) { max = p[i]; posmax = i; } } // nun die Zahlen auf den richtigen Positionen ablegen // merke wert auf p[start] unsigned long wsta = p[start]; p[start] = min; p[posmin] = wsta; // posmax ist posmin, wenn max auf start stand if(posmax == start){posmax = posmin;} // merke wert auf p[stop-1] unsigned long wsto = p[stop-1]; p[stop-1] = max; p[posmax] = wsto; } /*************************************************************************/ // Beispiel Funktionsaufruf int main(){ int i; int zn[] = {1,2,3,4,5}; int size = sizeof(zn)/sizeof(int); printf("zn hat %d Zahlen\n", size); // iteration über xSort for(i = 0; i < size - i; i++) xSort(zn, i, size - i); // Ausgabe for(i = 0; i < size; i++) printf ("%d\n", zn[i]); return 0; }
Zweckmäßig werden alle Funktionen in einer Library, z.B. ip-lib.c zusammengefasst, hierin wird auch das struct deklariert. Diese Library kann als ASCII-Datei mit einer Präprozessor-Anweisung eingebunden werden:
#include "ip-lib.c"
Darüber hinaus ist es möglich, die Library zu kompilieren und später beim Übersetzen des Programmcodes zu linken:
Die Library vorkompilieren: gcc -c -Wall ip-lib.c Das erzeugt die Datei ip-lib.o Als vorkompilierte Object-Datei wie folgt linken gcc a.c -o a.exe ip-lib.o
Mit dem Vorkompilieren werden auch mögliche Fehler im Code frühzeitig erkannt.
Alle hier gezeigten Formeln zur Berechnung von IP-Adressen sind, unter Beachtung der Syntax auch in Perl funktional. Perl kennt Bit-Operatoren genauso wie Referenzen auf Arrays. Da Perl keine Datentypen kennt, ergeben sich sogar einige Vereinfachungen, aber Vorsicht: Pragma strict und -w sind ein absolutes Muss! Der Vollständigkeit halber ein Beispiel in Perl:
#!/usr/bin/perl -w use strict; my @z = (); # Array fuer die Oktetten # Uebergebe Array-Referenz und eine grosse Zahl num_to_okt(\@z, 0xFFFFFFFF); print join(".", @z), "\n"; # 255.255.255.255 sub num_to_okt{ my $ar = shift; # Die Referenz auf das Array mit den Oktetten my $ipnum = shift; # Die IP-Adresse numerisch $$ar[0] = ($ipnum >> 24) & 0xff; $$ar[1] = ($ipnum >> 16) & 0xff; $$ar[2] = ($ipnum >> 8) & 0xff; $$ar[3] = $ipnum & 0xff; }
Datenschutzerklärung: Diese Seite dient rein privaten Zwecken. Auf den für diese Domäne installierten Seiten werden grundsätzlich keine personenbezogenen Daten erhoben. Das Loggen der Zugriffe mit Ihrer Remote Adresse erfolgt beim Provider soweit das technisch erforderlich ist. sos@rolfrost.de und wenn Sie möchten daß mein Prepaid nicht verfällt dürfen Sie mich auch gerne anrufen 01625 26 40 76.