Sunteți pe pagina 1din 10

10. Modalitati de control si supraveghere a traficului. Protocolul ICMP, aplicatii.

Protocolul ICMP este strans integrat in stiva TCP/IP. Mesajele ICMP inglobate in pachete IP sunt livrate ca si mesaje out-of-band si se refera la modul de functionare (sau disfunctionare) a retelei. Evident, folosind IP ca si transport aceste mesaje nu sunt garantate ca ajung la destinatie, deci reteaua nu trebuie sa se bazeze pe ele pentru functionare. De aceea pentru mesajele ICMP pierdute nu se genereaza mesaje ICMP. 10.1. ICMP = Internet Control Message Protocol Cateva din functiile ICMP ar fi: Anuntarea erorilor din retea ca un nod sau o intreaga subretea este de neatins, sau un pachet directat catre un port la care nu asculta nimeni. Anuntarea congestiilor din retea: un router incepe bufferarea pachetelor pe care le primeste mai repede decat este capabil sa le transmita, deci trimite sursei mesaje Source Quench (nu prea multe, pentru a nu creste si cu ele incarcarea retelei!) Asista depanarea retelei de exemplu prin serviciul Echo care doar raspunde la un pachet cerere. Aplicatia ping foloseste acest serviciu marcand timpul pe care l-a parcurs pachetul pe retea si astfel calculand incarcarea retelei. Anunta situatiile de timeout in momentul in care TTL a unui pachet devine zero, este abandonat de catre routerul in care a ajuns, iar acest eveniment este anuntat sursei printr-un mesaj ICMP. Aplicatia traceroute foloseste aceasta: generand pachete cu TTL mic si, supraveghind raspunsurile ICMP sosite de pe parcurs, noteaza ruta parcursa de pachete.

10.2. Cateva mesaje ICMP Destination Unreachable 0 Tip Suma de control Nefolosit Header + 64 octeti din datagrama originala 1 Cod 2 3

Tip: Cod:

3 0 reteaua nu a fost gasita 1 nodul nu a fost gasit 2 protocolul nu exista (sau nu e activ pe destinatie etc.) 3 portul nu a fost gasit (nici un proces nu receptioneaza) 4 fragmentare necesara desi a fost setat DF (dont fragment) 5 rutarea de la sursa a esuat Suma de control: complementul pe 16 biti fata de 1 a sumei mesajului ICMP incepand cu campul tip (campul suma de control e zero in calcul)
71

Headerul IP si primii 64 de octeti ai datagramei originale (va fi folosit de sursa pentru a identifica procesul generator al datagramei cu probleme) Time Exceeded 0 Tip 1 Cod 2 Suma de control 3

Nefolosit Header + 64 octeti din datagrama originala Tip: Cod: 11 0 TTL a fost depasit in timpul tranzitului prin retea (mesaj generat de gateway, router...) 1 timpul de reasamblare a pachetului la destinatie a fost depasit (unul din fragmente s-a pierdut pe parcurs)

Source Quench 0 Tip Suma de control Nefolosit Header + 64 octeti din datagrama originala 1 Cod 2 3

Tip: Cod:

4 0

Poate fi trimis atat de gateway/router de pe parcurs care nu mai pot stoca pachetele sosite prea repede pentru a fi rutate, cat si de destinatar care nu le poate procesa in ritmul in care sosesc. Sursa ar trebui ca raspuns sa reduca rata de transmisie a lor. Acest mesaj este emis de nodul cu probleme cand se apropie de limitele sale, nu dupa ce si-a atins capacitatea maxima si incepe sa abandoneze pachetele (astfel putand abandona chiar mesajul acesta). Echo, Echo response 0 Tip Identificator Date... Tip: 0 Ecou raspuns 8 Ecou Cod: 0 Identificator: ajuta identificarea cererii de ecou cu raspunsul respectiv 1 Cod 2 Suma de control Numar secventa 3

72

Numar de secventa: idem Aceste campuri daca sunt setate de catre emitator, vor fi copiate ca atare in mesajul raspuns. Timestamp, timestamp response 0 Tip Identificator Marca originala Marca receptie Marca raspuns Tip: Cod: 13 Marca de timp 14 Raspuns 0 1 Cod 2 Suma de control Numar secventa 3

Timpul este calculat in milisecunde de la miezul noptii UT (Universal Time) si stocat pe 32 biti. Emitatorul trimite timpul original, cand a format pachetul inainte de a-l pune in coada de emisie. Destinatarul raspunde cu timpul original, caruia ii adauga timpul la care l-a primit si timpul la care trimite mesajul de raspuns.

10.3 Aplicatia Ping Aceasta aplicatie este una din cele mai utile unelte de depanare a retelelor. Numele ii vine de la sonarul submarinelor se trimite o salva scurta si se asculta ecoul ping rezultat. Practic intr-o retea IP salva consta intr-un singur pachet iar raspunsul asteptat este de asemenea un singur pachet. Deoarece astfel se testeaza insasi functionalitatea de baza a retelei IP de a transmite un pachet, putem afla multe lucruri dintr-un simplu ping. Implementarea functiei ping se face utilizand pachetele ICMP de ecou si ecou raspuns. Ce functii indeplineste aceasta aplicatie? Plaseaza numere de secventa in fiecare pachet transmis si analizeaza secventa de pachete receptionata. Astfel putem afla daca avem pachete abandonate, duplicate sau reordonate. Calculeaza suma de control a fiecarui pachet, astfel verificand eventuale distrugeri ale pachetelor Plaseaza o marca de timp in fiecare pachet, care este intoarsa de la destinatie si astfel utilizata pentru a calcula timpul de parcurgere dus-intors (Round Trip Time RTT) Raporteaza nu in ultimul rand si celelalte mesaje ICMP aparute, care altfel ar fi ramas invizibile utilizatorului (de exemplu raportul unui router de destinatie de neatins)

73

10.4 Aplicatia Traceroute Traceroute incearca sa traseze drumul parcurs de pachete pana la destinatie. Altfel spus, determina ruta acestor pachete. Da, intr-adevar, exista in specificatiile IP un mecanism pentru memorarea rutei parcurse de pachet, dar aceste specificatii sunt vag definite, foarte rar implementate si adesea dezactivate din motive de securitate, astfel incat nu ne putem baza in nici un fel pe ele. Traceroute isi face treaba in alt fel, putem sa-i spunem ilegal. Traceroute trimite pachete cu valori ale TTL foarte mici. Sa ne amintim ca TTL (Time To Live) este un camp al headerului IP prevazut sa previna posibilitatea intrarii pachetelor in bucle infinite de rutare. Fiecare router scade cu 1 valoare campului TTL, iar in momentul cand aceasta ajunge zero, pacehtul este considerat expirat si abandonat. In mod normal routerul care face aceasta genereaza un mesaj ICMP de tipul Time Exceeded catre emitatorul pachetului. Folosind deci pachete cu valori ale TTL mici, traceroute forteaza routerele de pe traseu sa ne trimita aceste mesaje ICMP ce vor fi folosite pentru a identifica routerele. Un TTL de 1 va forta primul router sa ne trimita un mesaj, 2 un mesaj de la al doilea router samd. Pachetele trebuie in principiu transmise destinate unui port nefolosit pe destinatar, altfel aplicatia ascultand pe acel port va primi pachete de neinteles pentru ea. Practic intai se transmite un grup de mesaje cu TTL de 1. Ar trebui sa ne raspunda un singur router cu adresa interfetei pe care a primit pachetele originale. Acum aplicatia va folosi DNS pentru aceasta adresa de IP, si va afla numele acestui prim router. Din timpii de parcurgere dus-intors a pachetelor se va calcula un timp mediu, iar eventualele mesaje ICMP primite de la router vor fi si ele afisate (in cazul acesta nu mare lucru, dar in momentul adresarii celui de-al doilea, deja putem primi de la primul un mesaj de retea de neatins). Apoi dupa acest prim stagiu se transmite un grup de pachete cu TTL=2 si tot asa. Rezultatele acestei testari nu sunt perfecte, si iata doar cateva din problemele pe care trebuie sa le stim: - Sa tinem minte ca urmarim ruta mai multor pachete. Chiar daca proiectam aplicatia sa trimita un singur pachet o data, pentru urmatorul router vom trimite un alt pachet. Putem presupune ca toate aceste pachete vor avea aceeasi ruta? Daca cumva o legatura cade in timpul in care noi rulam o sesiune traceroute, iesirea va fi un amestec derutant al celor doua rute, cea veche si cea nou stabilita de routerele de pe parcurs. - Nu vom avea niciodata adresa de IP de iesire din routere, doar cea dinspre noi. Dar iesirea de obicei se poate deduce din adresa ruterului urmator, doar trebuie sa fie in aceeasi retea, nu? - Rutarea poate crea probleme serioase. Este posibil ca un router sa nu aiba calea de intoarcere catre noi corect configurata, astfel raspunsul sau nu va ajunge niciodata la emitator. Sau, intoarcerea poate sa se faca pe o ruta total diferita (iar estimarea timpului de parcurgere sa fie falsa), ba chiar mai mult pornind de la o alta interfata a routerului (astfel vom avea o adresa care de fapt nu a fost catusi de putin implicata in rutarea pachetului) - Implementarile TCP/IP nu suporta intotdeauna complet si corect aceasta optiune ICMP, putem avea sisteme care nu decrementeaza TTL, care transmit si pachetele cu TTL zero, care nu genereaza ICMP Timeout, sau care trimit pachete ICMP cu acelasi TTL cu al mesajului primit (deci trimise cu TTL zero evident nu vor ajunge niciodata la noi).

74

10.5 Exemplu de program: ping In continuare este prezentata o simpla aplicatie de tip ping. Acest ping nu implementeaza decat functia de baza a unui ping: trimite pachete te tip ICMP Echo si verifica primirea ICMP Echo Respnse Functia main() a aplicatiei citeste din linia de comanda adresa IP a destinatiei si incearca sa lege aceasta adresa de socket-ul creat. Observam ca se folosesc socket de tip RAW, deci lucram la nivel foarte jos direct cu pachetele IP. Aceasta este necesar caci mesajele ICMP sunt tratate la acest nivel, protocoalele TCP sau UDP fiind implememntate pe baza lor. Dezavantajul acestei tratari este ca programul trebuie rulat de catre supervizor (sau marcat SUID) pentru a permite un asemenea acces in intimitatea stivei TCP/IP. In continuare avem o bucla infinita in care asteptam raspunsurile de tip echo sosite de la destinatia noastra. Stim ca aceste asteptari cu ajutorul functiei recvfrom() blocheaza programul, deci emisia pachetelor ar fi intr-o situatie neplacuta. Evitarea acestui inconvenient se face simplu, prin implementarea unui handler (o functie atasata) pentru semnalul de alarma SIGALRM, aceasta fiind functia periodic(). Functia periodic() trimite un pachet cu ajutorul ping(), dupa care se programeaza pentru o noua rulare peste o secunda cu ajutorul alarm(). Daca cumva am atins numarul maxim de pachete de transmis, imi programez iesirea peste o secunda. Nu ies imediat pentru ami permite interceptarea eventualului raspuns sosit la ultimul ping transmis. Iesirea din program in mod normal se face la terminarea celor 128 de pachete de transmis, sau la apasarea Ctrl-C, prin functia finish() (inregistrata ca handler al semnalelor de intrerupere). Aceasta functie inchide frumos socket-urile folosite inainte de a afisa statisticile referitoare la rularea programului si a iesi. Functia de emisie pachete de tip echo, ping(), formeaza aceste pachete cu minimul de informatie necesara: un identificator al secventei de emisie in primul rand. Aceasta urmareste sa ma asigure ca mai multe aplicatii ping ruland pe acelasi calculator vor sti sa identifice pachetele ICMP Echo reply proprii. De ce este necesar acest artificiu? Toate aplicatiile ce citesc portul de protocol ICMP vor citi aceleasi pachete (aici este o violare a principiului: un acelasi pachet este distribuit TUTUROR aplicatiilor ce lucreaza cu ICMP, este treaba lor sa le trateze sau sa le ignore), deci este bine sa tratez numai pachetele sosite ca raspunsuri la cererile mele. Acest identificator este chiar ID-ul procesului, evident unic pe aceeasi masina. In pachetele emise in mod normal se trece si suma de control functia in_cksum() aici neimplementata si numarul de secventa, de la 0 la 127. Sosirea pachetelor Echo reply este verificata de functia verific(). Dupa o scurta citire a marimii pachetului IP (care trebuie sa corespunda), se verifica headerul ICMP. Cum am mai spus, aplicatia noastra, ca si alte eventuale aplicatii ICMP, primeste toate pachetele ICMP sosite, deci toate care nu ma intereseaza (pachete ce nu sunt Echo reply sau destinate altei aplicatii cu ID diferit) sunt abandonate pur si simplu. Sosirea unui pachet ping corect este afisata pe ecran. Deoarece este posibila si primirea unor pachete duplicate, se verifica acest lucru intr-o lista de pachete primite si eventual se afiseaza aceasta situatie neobisnuita.

75

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include

<sys/param.h> <sys/socket.h> <sys/file.h> <sys/time.h> <sys/signal.h> <netinet/in.h> <netinet/ip.h> <netinet/ip_icmp.h> <arpa/inet.h> <netdb.h> <unistd.h> <stdlib.h> <string.h> <stdio.h> <ctype.h> <errno.h> /* numar maxim de pachete emise */

#define NUMPACK 128

int ident; /* id proces, folosit la identificarea pachetelor */ int s; /* socket folosit pentru emisie */ struct sockaddr whereto; /* destinatarul ping */ char *target; /* adresa destinatarului */ int packlen; /* lungimea pachetului */ u_char outpack[65468]; /* pachetul trimis */ bool check_received[NUMPACK]; /* verific primirea pachetelor raspuns */ long nreceived; long nrepeats; long ntransmitted; /* numarul pachetelor raspuns primite */ /* numarul repetarilor */ /* nr secv al pachetelor transmise*/

void finish(int ignore) { signal(SIGINT, SIG_IGN); putchar('\n'); fflush(stdout); printf("--- statistica ping %s ---\n", target); printf("%ld pachete transmise, ", ntransmitted); printf("%ld pachete primite, ", nreceived); if (nrepeats) printf("+%ld duplicate, ", nrepeats); if (ntransmitted) if (nreceived > ntransmitted) printf("-- cineva a trimis pachete in plus!"); else

76

printf("%d%% pachete pierdute", (int) (((ntransmitted - nreceived) * 100) / ntransmitted)); putchar('\n'); if (nreceived==0) exit(1); exit(0); } int in_cksum(u_short *addr, int len) { /* aici ar trebui implementata o functie de calcul a sumei de control */ return 0; } void ping(void) { struct icmphdr *icp; int cc; int i; icp = (struct icmphdr *)outpack; icp->type = ICMP_ECHO; icp->code = 0; icp->checksum = 0; icp->un.echo.sequence = ntransmitted++; icp->un.echo.id = ident; /* ID */ cc = packlen + 8; /* sar peste portiunea ICMP */ icp->checksum = in_cksum((u_short *)icp, cc); i = sendto(s, (char *)outpack, cc, 0, &whereto, sizeof(struct sockaddr)); if (i < 0 || i != cc) { if (i < 0) perror("ping: sendto"); printf("ping: trimis %s %d caractere, ret=%d\n", target, cc, i); }

void verific(char *buf, int cc, struct sockaddr_in *from) { struct iphdr *ip; struct icmphdr *icp; int hlen;

77

ip = (struct iphdr *)buf; /* verific header IP */ hlen = ip->ihl << 2; if (cc < packlen + ICMP_MINLEN) { fprintf(stderr, "ping-lab: pachet prea scurt (%d octeti) de la %s\n", cc, inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr)); return; } cc -= hlen; icp = (struct icmphdr *)(buf + hlen); if (icp->type == ICMP_ECHOREPLY) { if (icp->id != ident) return; /* nu e reply la ECHO dat de noi */ ++nreceived; if ( check_received[icp->un.echo.sequence] ) { ++nrepeats; --nreceived; dupflag = TRUE; } else { check_received[icp->un.echo.sequence] = TRUE; dupflag = FALSE; } printf("%d octeti de la %s: secventa=%u", cc, inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr), icp->un.echo.sequence); printf(" ttl=%d", ip->ttl); if ( dupflag ) printf (" DUPLICAT!"); printf ("\n"); } /* daca nu e ECHO reply, nu ma intereseaza */ } void periodic(int ignore) { int waittime; ping(); signal(SIGALRM, catcher); if (ntransmitted < NUMPACK) alarm(1); /* asigur emisia urmatorului peste 1 secunda */ else { signal(SIGALRM, finish); /* peste o secunda termin */

78

alarm(1); } } int main(int argc, char *argv[]) { struct protoent *proto; struct sockaddr_in *to; u_char *packet; ntransmitted = 0; memset(check_received, 0, NUMPACK); if (!(proto = getprotobyname("icmp"))) { fprintf(stderr, "ping: icmp protocol necunoscut\n"); exit(2); } if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { if (errno==EPERM) { fprintf(stderr, "ping-lab: trebuie rulat de root\n"); } else perror("ping-lab: eroare creere socket"); exit(2); } argv++; target = *argv; /* adresa IP a destinatiei */ memset(&whereto, 0, sizeof(struct sockaddr)); to = (struct sockaddr_in *)&whereto; to->sin_family = AF_INET; /* transform adresa in format numeric */ if ( ! inet_aton(target, &to->sin_addr)) { fprintf(stderr, "ping-lab: format gresit al adresei IP: %s\n", target); exit(2); } packlen = 56 + 60 + 76; /* pachetul tipic de 56 octeti de date plus lungimea maxima header IP plus header ICMP */ packet = malloc((u_int)packlen); if (!packet) { fprintf(stderr, "ping-lab: eroare alocare memorie\n"); exit(2); } printf("PING %s: %d octeti de date\n", target, 56);

79

signal(SIGINT, finish); /* asigur iesirea curata */ signal(SIGTERM, finish); signal(SIGQUIT, finish); signal(SIGALRM, periodic); /* asigur emisia periodica a pachetelor */ ident = getpid() & 0xFFFF; /* voi marca pachetele proprii*/ periodic(0); /* pornesc pachetele */ for (;;) { struct sockaddr_in from; register int cc; size_t fromlen; fromlen = sizeof(from); if ((cc = recvfrom(s, (char *)packet, packlen, 0, (struct sockaddr *)&from, &fromlen)) < 0) { /* astept sosirea */ if (errno == EINTR) continue; perror("ping: recvfrom"); continue; } verific((char *)packet, cc, &from); } /* aici nu mai ajung... ies doar cu ctrl-C*/ return 0; }

10.6 Teme Dezvoltati aplicatia ping adaugandu-i functionalitati suplimentare (si necesare): marcarea mesajelor cu timpul de emisie si calcularea timpului de parcurgere a retelei, calcularea si verificarea sumei de control etc. Scrieti in mod analog o aplicatie traceroute.

80