Do spisu tresci tematu 8

8.2.1 Protokol komunikacyjny IP



Spis tresci


Wprowadzenie

Protokol Internetu (IP) ma za zadanie zajecie sie trzema waznymi sprawami w problemie przesylu danych sieciami. Sa to :

Moduly zajmujace sie przesylaniem wykorzystuja przesylany z kazdym pakietem danych (ang. datagram) naglowek protokolu IP (ang. IP Header) do przesylania danych pomiedzy stacjami zrodlowa i docelowa. Wiadomosci zawarte w naglowku IP sa rowniez wykorzystywane przy dzieleniu i laczeniu pakietow danych podczas przesylania.

Moduly przesylajace sa niezbedna czescia kazdej jednostki wchodzacej w sklad sieci i z tego powodu sa ustalone pewne reguly odnosnie interpretacji adresow i dzielenia-laczenia pakietow. Moduly takie (w szczegolnosci bramy sieciowe) maja zaimplementowane funkcje pomagajace podjac decyzje o dalszej drodze pakietu.

IP traktuje kazdy pakiet, ktory przetwarza jako calkowicie niezalezny.

IP jest zawodnym protokolem. Nie sprawdza poprawnosci danych. Jedynym kryterium pozwalajacym sprawdzic poprawnosc przesylania jest suma kontrolna naglowka umieszczona w nim (pole check). Jezeli w trakcie transmisji zostal odkryty blad to pakiet jest niszczony przez stacje, ktora wykryla niezgodnosc. W przypadku bledu nie ma zadnych powtorek transmisji i kontroli przeplywu danych.


Budowa naglowka protokolu IP

Deklaracja struktury naglowka w Linuxie jest umieszczona w pliku /include/linux/ip.h . Wyglada ona nastepujaco:

struct iphdr {
        __u8    ihl:4,
                version:4;
        __u8    tos;
        __u16   tot_len;
        __u16   id;
        __u16   frag_off;
        __u8    ttl;
        __u8    protocol;
        __u16   check;
        __u32   saddr;
        __u32   daddr;
};

A oto schemat i wyjasnienia znaczen poszczegolnych pol:

0123
01234567890123456789012345678901

VersionIHLType Of ServiceTotal Length
IdentificationFlagsFragment Offset
Time To LiveProtocolHeader Checksum
Source Address
Destination Address
OPTIONS

 

Version: 4 bity

Pole w strukturze iphdr: version

Wersja formatu naglowka IP. W linuxie zaimplementowano wersje 4, wersja 6 jest w przygotowaniu.

IHL: 4 bity

Pole w strukturze iphdr: ihl

Dlugosc naglowka IP (ang. Internet Header Lenght) wyrazona w 32 bitowych slowach. Wazne jest to, ze minimalna wartosc tego pola to 5.

Type of Service: 8 bitow

Pole w strukturze iphdr: tos
Pole to jest uzywane do przechowywania czysto abstrakcyjnych wartosci majacych podniesc jakosc obslugi pakietu. Niektore sieci moga honorowac ustawione tu bity a pozostale nie. Znaczenie bitow jest nastepujace: Wartosc priorytetu rosnie gdy rosnie wartosc wyznaczona przez bity 0-2.

Total Length: 16 bitow

Pole w strukturze iphdr: tot_len

Calkowita dlugosc pakietu wlacznie z naglowkiem i danymi (w bajtach). 16 bitow pozwala na przesylanie maksymalnie 65,535 bajtow. Takie pakiety praktycznie nie sa przesylane w sieci. Standard IP wymaga, aby wszystkie stacje mogly przetwazac pakiety o dlugosci 576 bajtow. Liczba ta bierze stad, ze dane sa zwykle dzielone na pakiety o dlugosci 512 bajtow, 60 pozostalych to dlugosc maksymalnego naglowka IP i zostaja dodatkowe 4 bajty marginesu dla innych protokolow. Najczesciej spotykana dlugoscia naglowka IP jest 20 bajtow.

Identification: 16 bitow

Pole w strukturze iphdr: id

Wartosc ta jest ustawiana przez nadawce. Ma za zadanie pomoc w identyfikacji fragmentow przy scalaniu pakietow.

Flags: 3 bity

Pole w strukturze iphdr: Pole nie wystepuje w strukturze iphdr, ale 3 pierwsze
bity pola frag_off sa tak walsnie traktowane.

Rozne flagi kontrolne:

Fragment Offset: 13 bitow

Pole w strukturze iphdr: frag_off

To pole okresla, gdzie w oryginalnym pakiecie powinien byc umieszczony dany fragment. Jednostka tutaj jest 8 bajtow (64 bity). Pierwszy fragment ma wartosc ofsetu zero.

Time to Live: 8 bitow

Pole w strukturze iphdr: ttl

To pole okresla maksymalny czas przebywania pakietu w sieci. W momencie gdy to pole osiagnie wartosc zero pakiet danych musi zostac zwrocony. To pole jest modyfikowanie w czasie przesylania komunikatu w sieci. Jednostka sa tu sekundy, ale kazda z przetwarzajacych pakiet stacji ma obowiazek zmniejszyc ja o co najmniej jeden (nawet gdy przetwarzanie zajelo mniej czasu). Dzialanie to ma spowodowac wyeliminowanie pakietow niemozliwych do dostarczenia.

Protocol: 8 bits

Pole w strukturze iphdr: protocol

To pole okresla, ktory protokol zostal uzyty na wyzszym poziomie w przetwarzaniu danych pakietu.

Header Checksum: 16 bitow

Pole w strukturze iphdr: check

Suma kontrolna naglowka danych IP. Poniewaz naglowek moze sie zmieniac, suma jest obliczana w kazdym z miejsc w sieci, do ktorego dotal pakiet.

Source Address: 32 bity

Pole w strukturze iphdr: saddr

Adres nadawcy pakietu.

Source Address: 32 bity

Pole w strukturze iphdr: daddr

Adres odbiorcy pakietu.

Options

Pole w strukturze iphdr: W Linuxie osobna struktura.

Dodatkowe informacje o pakiecie danych. Jest to opcjonalna czesc naglowka protokolu IP. Najczesciej nie jest ona dolaczana do pakietu. W Linuxie reprezentowana jest przez strukture options, zaimplementowana w pliku /include/linux/ip.h.

Obecnie zdefiniowane opcje to:

Adresowanie danych

Adres w Internecie zajmuje 32-bity i sklada sie z identyfikatorow sieci i stacji. Kazda stacja w sieci musi miec jednoznacznie wyznaczony swoj internetowy adres. Kazdy internetowy adres moze miec jedna z trzech postaci przedstawionych na schemacie:

Klasa A

7bitow24bity
0identyfikator sieciidentyfikator stacji

 

Klasa B

14bitow16bitow
10identyfikator sieciidentyfikator stacji

 

Klasa C

21bitow8bitow
110identyfikator sieciidentyfikator stacji

 

Jak latwo zauwazyc adesy klasy A sa uzywane w sieciach zlozonych, w ktorych przypada wiele stacji na pojedyncze sieci, podczas gdy adresy klasy C sa odpowiednie dla sieci, ktore maja stosunkowo malo stacji.

Adresy internetowe sa zazwyczaj zapisywane jako czworki liczb dziesietnych rozdzielonych kropkami. Kazda liczba dziesietna odpowiada jednemu bajtowi 32-bitowego adresu (na przyklad 148.255.4.13).

Identyfikatory sieci sa wyznaczane przez upowazniona do tego instytucje, ale identyfikatory stacji sa wyznaczane indywidualnie przez administratora sieci. Daje to mozliwosc przeznaczenia kilku najbardziej znaczacych bitow identyfikatora stacji na tzw. identyfikatory sieci wewnetrznych. Ta wlasciwosc powoduje pojawienie sie jeszcze jednego poziomu w hierarchii adresow w Internecie:

 


Rozdrabnianie i scalanie danych

Rozdrabnianie danych jest niezbedne gdy sieci lokalne, przez ktore musi przejsc pakiet aby osiagnac cel, nie pozwalaja na przejscie calego pakietu jednorazowo. Z pojeciem tym zwiazana jest wielkosc maksymalnej jednostki przeplywu danych w sieci (ang. Maximum Transmission Unit - MTU). Pakiet przesylany siecia moze miec ustawiona flage DF (ang. Don't Fragment) i wtedy pakiet w zadnym wypadku nie moze zostac podzielony. Jezeli przez to pakiet nie moze byc dostarczony, to powinien zostac zniszczony, a nadawca natychmiast poinformowany o zaistnialej sytuacji. Poniewaz rozdrobnione dane musza zostac scalone przez odbiorce, wartosci jednoznacznie identyfikujace dane sa przekazywane w polu id (ang. Identification). Pozycja pojedynczego fragmentu w calym rozdrobnionym pakiecie danych jest przekazywana w polu frag_off (ang. Fragment Offset). Pierwszy fragment pakietu ma offset 0. Ostatni fragment ma ustawiona flage MF (ang. More Fragments) na 0.

Struktury sluzace do przechowywania fragmentow pakietow sa zdefiniowane w pliku /include/net/ip.h. Sa tam zaimplementowane dwie wazne i proste struktury:

Budowa tych struktur wyglada nastepujaco:

struct ipfrag
{
        int             offset;         /* offset fragmentu w pakiecie    */  
        int             end;            /* ostatni bajt danych w pakiecie */
        int             len;            /* dlugosc tego fragmentu         */
        struct sk_buff  *skb;           /* oryginalnie odebrany fragment  */
        unsigned char   *ptr;           /* oryginalny fragment danych     */
        struct ipfrag   *next;          /* wskazniki polaczeniowe         */
        struct ipfrag   *prev;
};

struct ipq
{
        unsigned char   *mac;           /* wskaznik na naglowek sprzetowy */
        struct iphdr    *iph;           /* wskaznik na naglowek IP        */
        int             len;            /* calkowita dlugosc 
                                           rozdrobnionego pakietu         */
        short           ihlen;          /* dlugosc naglowka IP            */
        short           maclen;         /* dlugosc naglowka sprzetowego   */
        struct timer_list timer;        /* ile czasu pozostalo do
                                           przeterminowania fragmentow    */
        struct ipfrag   *fragments;     /* lista polaczonych fragmentow   */
        struct ipq      *next;          /* wskazniki polaczeniowe         */
        struct ipq      *prev;
        struct device   *dev;           /* Urzadzenie do wysylania 
					   informacji o bledzie           */
};

Funkcje obslugujace fragmentacje sa zaimplementowane w pliku /net/ipv4/ip_fragment.c . Jest tam tez zadeklarowany globalny wskaznik na wszystkie kolejki fragmentow - ipqueue.

Organizacja przechowywania pakietow w Linuxie wyglada nastepujaco:

Opis funkcji uzywanych do operacji na fragmentach

Funkcja ip_frag_create() :

DEFINICJA: static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr)

WYNIK: NULL w przypadku braku pamieci
       wskaznik na zaalokowany fragment gdy wszystko poszlo dobrze

Allokuje pamiec i wypelnia strukture ipfrag przechowujaca fragment pakietu.

Funkcja ip_find() :

DEFINICJA: static struct ipq *ip_find(struct iphdr *iph)

WYNIK: wskaznik na odpowiednia kolejke (jezeli istnieje)
       NULL w przypadku bledu   

Przechodzi po kolejce ipqueue i znajduje strukture ipq, ktora ma ta sama wartosc w polach id, protocol, saddr, daddr co naglowek przekazany jako parametr wywolania. Jezeli nie ma takiej kolejki to zwraca NULL.

Funkcja ip_free() :

DEFINICJA: static void ip_free(struct ipq *qp)

WYNIK: nie ma 

Usuwa i niszczy kolejke ipq przekazana w parametrze. To jest wywolywane w przypadku przetworzenia i scalenia pakietu, ale takze gdy uplynal "czas zycia" pakietu.

Funkcja ip_expire() :

DEFINICJA: static void ip_expire(unsigned long arg)

WYNIK: nie ma 

Wywolywana gdy pakiet sie "przeterminowal". Usuwana jest odpowiednia struktura ipq i wysylany komunikat za pomoca protokolu bledu ICMP.

Funkcja ip_create() :

DEFINICJA: static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev);

WYNIK: NULL w przypadku braku pamieci
       wskaznik na stworzona strukture ipq gdy wszystko bylo w porzadku

Tworzy nowa kolejke ipq dla nowego pakietu danych.

Funkcja ip_done() :

DEFINICJA: static int ip_done(struct ipq *qp)

WYNIK: 1 gdy wszystkie fragmenty sa juz w kolejce
       0 wpp. 

Sprawdza, czy sa juz wszystkie fragmenty pakietu w kolejce ipq.

Funkcja ip_glue() :

DEFINICJA: static struct sk_buff *ip_glue(struct ipq *qp)

WYNIK: wskaznik na bufor ze sklejonym pakietem
       NULL gdy doszlo do bledu 

Funkcja skleja wszystkie fragmenty umieszczone we wskazanej kolejce ipq w jeden pakiet danych i usuwa kolejke.

Funkcja ip_defrag() :

DEFINICJA:  struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev)

WYNIK: NULL w przypadku bledu i gdy pakiet zostal umieszczony w kolejce
       wskaznik na bufor ze sklejonymi fragmentami gdy mozna bylo je skleic 

Funkcja przetwarza przekazany fragment. Jezeli byl to pierwszy fragment pakietu, to tworzona jest nowa kolejka ipq. Jezeli taka kolejka juz istnieje, to dodajemy fragment do listy. Jezeli lista jest pelna, to sklejamy wszystko w jeden pakiet i zwracamy wskaznik na niego, wpp. zwracamy NULL.

Funkcja ip_fragment():

DEFINICJA: void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)

WYNIK:  nie ma

Funkcja jest wywolywana gdy trzeba podzielic pakiet na fragmenty. Funkcja w petli while dzieli pakiet i wysyla opakowane fragmenty.


Interfejsy komunikacyjne

Przetwarzanie pakietow na wejsciu.

Interfejs wejsciowy protokolu IP jest zdefiniowany w pliku /net/ipv4/ip_input.c .

Funkcja ip_rcv():

DEFINICJA: int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 
WYNIK: 0 gdy wyszystko w porzadku 
       <0 gdy zostal wykryty blad
Jest to funkcja wywolywana przez handler interfejsu sprzetowego sieci i standardowo obsluguje wszystkie nadchodzace pakiety.

Jako parametry wejsciowe funkcja otrzymuje min. wskaznik na bufor z danymi pakietu. Z tego parametru funkcja wyluskuje sobie wskaznik na naglowek protokolu IP (w bloku deklaracji zmiennych lokalnych).

W pierwszej kolejnosci funkcja wychwytuje i niszczy wyszystkie fragmenty oznaczone jako wersja 6-ta protokolu IP.

Nastepnie sprawdza czy nie ma bledu w naglowku tzn. czy jest to wersja 4 IP, czy dlugosc bloku nie jest mniejsza od zadeklarowanej dlugosci naglowka i czy naglowek jest najmniej dlugosci 5 (uwaga: w 32-bitowych slowach - patrz definicja naglowka). Jezeli zostal wykryty jakis blad, to pakiet jest niszczony.

Rozpatrywane sa takie przypadki: pakiet jest dla naszej stacji, pakiet jest rozglaszany (ang. broadcast), dla grupy do ktorej nalezymy, lub dla innej stacji. Jezeli pakiet jest dla nas, to wykonywane jest sprawdzanie czy dany pakiet jest podzielony i jesli tak to wywolywane jest scalanie (funkcja ip_defrag()). Jezeli scalanie nie bylo potrzebne lub dalo w wyniku poprawny pakiet, to jest on przekazywany do wszystkich handlerow protokolow wyzszej warstwy (czyli np. tcp_rcv() lub udp_rcv()).

Jezeli potrzebne jest dalsze przekazanie pakietu, to generowane sa nowe sumy kontrolne dla naglowka scalonego pakietu i wywolywane jest przekazanie (funkcja ip_forward()).


Zrodla informacji:

  1. Zrodla Linuxa 2.0.0:
  2. W. Richard Stevens: Programowanie zastosowan sieciowych w systemie UNIX. WNT 1995
  3. Postel J.:(red.) Internet Protocol. RFC 791

Pytania (wraz z odpowiedziami)

Jakie struktury danych sa uzyte do reprezentowania pakietu odebranego z sieci i przygotowanego do wyslania w Linuxie?

Rozwiazane jest to za pomoca struktury scisle zwiazanej z implementacja gniazd. Struktura ta nosi nazwe sk_buff . W przypadku odbierania pakietu stukture ta allokuje handler interfejsu sprzetowego, a w przypadku wysylania modul implementacji gniazd. Jest to bardzo zlozona struktura, zawierajaca m.in. wskazniki na naglowki wszystkich protokolow uzytych do przetworzenia danych. Struktury te sa kolejkowane w specjalnych kolejkach zwiazanych z gniazdami. Oczywiscie zdarza sie, ze protokol IP zmuszony jest podzielic pakiet i wtedy rowniez musi zaallokowac struktury sk_buff dla wszystkich fragmentow.


Autor: Sebastian Stefanowski