Do spisu treści tematu 8

8.2.2 Protokół komunikacyjny IP (2)

Spis treści


8.2.2.1 Interfejs wyjściowy protokołu IP

Interfejs wyjściowy protokołu IP w Linuxie zdefiniowany jest w plikach net/ipv4/ip_output.c oraz (częściowo) net/ipv4/ip_forward.c. Najważniejsze zdefiniowane w nich funkcje to:

Funkcja ip_build_header

DEFINICJA: int ip_build_header(struct sk_buff *skb, __u32 saddr,
__u32 daddr, struct device **dev, int type,
struct options *opt, int len, int tos,
int ttl, struct rtable ** rp)
WYNIK: Długość zbudowanego nagłówka

budująca nagłówek IP dla pakietu IP przechowywanego w przekazywanej strukturze typu sk_buff i dodająca ewentualnie na koniec rekord opcji (woła ip_options_build()),

Funkcja add_to_send_queue

DEFINICJA: static inline void add_to_send_queue(
struct sock * sk, struct sk_buff * skb)
WYNIK: -

przeznaczająca strukturę sk_buff do kolejki pakietów do wysłania związanej z podanym gniazdem sk,

Funkcja ip_queue_xmit

DEFINICJA: void ip_queue_xmit(struct sock *sk,
struct device *dev, struct sk_buff *skb, int free)
WYNIK: -

dodająca podany pakiet (skb) do kolejki do wysłania na dane gniazdo (sk) i uruchamiająca wysyłanie (wywołując dev_queue_xmit())

Funkcja ip_build_xmit

DEFINICJA: int ip_build_xmit(struct sock *sk,
void getfrag (const void *, __u32,
char *, unsigned int, unsigned int),
const void *frag,
unsigned short int length,
__u32 daddr,
__u32 user_saddr,
struct options * opt,
int flags,
int type,
int noblock)
WYNIK: 0 jeśli wszystko poszło dobrze,
kod błędu: ENODEV jeśli nie ma urządzenia,
ENETUNREACH jeśli odbiorca jest niedostępny,
EPERM jeśli pakiet nie może przedostać się przez
zabezpieczenia dostepu (ang. firewall),
EMSGSIZE jeśli pakiet musi być sfragmentowany
lub kod błędu specyficzny dla protokołu wyższego poziomu

Robi to, co wszystkie wcześniejsze funkcje razem: buduje nagłówek, inicjuje sk_buff, wkłada do kolejki i wysyła.

Funkcja ip_forward

DEFINICJA: int ip_forward(struct sk_buff *skb, struct device *dev,
int is_frag, __u32 target_addr)
WYNIK: -1 w przypadku mogących wystąpić błędów (opisanych poniżej)
1 w przypadku sukcesu

Po sprawdzeniu, że pakiet nie jest przeterminowany, odwołuje się do bramy (wywołując ip_rt_route()) aby pobrać adres najbliższego "skoku". Wreszcie sprawdza, czy pakiet nie powinien zostać poddany fragmentacji przed posłaniem go na urządzenie wyjściowe.


8.2.2.2 Adresowanie cz.2

Budowa adresu IP omówiona jest w części 1 tego dokumentu.

Wymienimy tutaj jedynie specjalne przypadki adresów, które były używane do realizacji takich funkcji jak np. rozgłaszanie (ang. broadcasting) - patrz Interfejs wyjściowy protokołu IP - używając następującej notacji, zależnie od klasy adresu:

lub

realizując minus jedynkę (-1) poprzez pole z wszystkimi bitami ustawionymi.

  1. { 0, 0 }
  2. Oznacza "ta stacja w tej sieci". Taki adres jest używany tylko jako adres nadawcy w przypadku pewnych procedur inicjujących, za pomocą których stacje dowiadują się o swoich adresach IP.
  3. { 0, <Numer stacji> }
  4. Oznacza daną stację tej sieci, z zastrzeżeniami jak wyżej.
  5. { -1, -1 }
  6. Ograniczone rozgłaszanie; może być używany tylko jako adres odbiorcy. Datagram zaadresowany w ten sposób zostanie odebrany przez każdą maszynę w macierzystej sieci, ale nie zostanie rozesłany dalej.
  7. { <Numer sieci>, -1 }
  8. Rozgłaszanie skierowane do podanej sieci, również tylko jako adres odbiorcy.
  9. { <Numer sieci>, <Numer podsieci>, -1 }
  10. Rozgłaszanie skierowane do podanej podsieci, tylko jako adres odbiorcy.
  11. { <Numer sieci>, -1, -1 }
  12. Rozgłaszanie skierowane do podanej sieci (w przypadku sieci z podsieciami), tylko jako adres odbiorcy.
  13. { 127, <cokolwiek> }
  14. Adres - pętla wewnątrz naszej stacji. Nie powinien nigdy być wysyłany poza tę stację.

Unikatowe numery sieci przyznawane są administracyjnie.

Należy nadmienić, że początkowo wszystkie adresy IP miały być amorficznymi 32-bitowymi liczbami i właściwie takie arbitralne dzielenie adresu na sekcje sieci, podsieci i stacji jest złamaniem tej reguły; np. przypadek 6 w znacznej ilości sieci nie jest rozpoznawany.


8.2.2.3 Routing - przekazywanie datagramów przez bramy

8.2.2.3.1 Zasady ogólne

Warstwa IP odpowiada za wybranie następnego poprawnego "skoku" dla każdego datagramu, który wysyła. Jeśli odbiorca jest w tej samej sieci, pakiet będzie wysłany bezpośrednio do niego; w przeciwnym przypadku musi być przekazany dalej, do bramy łączącej sieci.

Decyzję, czy adres przeznaczenia leży na przyłączonej do danej stacji sieci, podejmuje się w oparciu o maskę adresów (ang. address mask), sprawdzając zgodność adresów odbiorcy i nadawcy po przefiltrowaniu przez tę maskę. Jeśli wyniki nie są zgodne, należy wybrać bramę, przez którą komunikaty przejdą do docelowej sieci. Ewentualnie, jeśli adres odbiorcy oznacza rozgłaszanie do aktualnej sieci lub podsieci (patrz wyżej), to adres taki jest przekazany do interfejsu sieciowego niższej warstwy.

Aby efektywnie przekazywać pakiety dalej, każda stacja trzyma tzw. route cache - pamięć podręczną adresów bram, do których bezpośrednio można posyłać datagramy o danych adresach przeznaczenia. Każda pozycja w tabeli adresów bram zawerać musi przynajmniej:
Algorytm wykonywany przez stację (zaprojektowany tak, aby główny ciężar wyboru trasy spoczywał na urządzeniu spełniającym funkcję bramy) jest następujący:
  1. Jeśli adres przeznaczenia nie występuje w podręcznej tabeli bram, stacja wybiera domyślną bramę i posyła do niej datagram; adres ten jest też dodawany do route cache;
  2. Jeśli ta brama nie jest [już dłużej] najlepszym punktem "skoku" do celu, przekaże ona datagram do sobie z kolei znanej kolejnej bramy i powiadomi o tym stację nadawcy komunikatem protokołu ICMP - Redirect
  3. Stacja otrzymująca komunikat Redirect uaktualnia w pamięci podręcznej bramę dla danego adresu przeznaczenia, tak, że następne datagramy dla tego adresu pójdą bezpośrednio do tej właśnie bramy, poleconej przez tę poprzednio wywołaną.

Dzięki temu rozwiązaniu, w połączeniu z różnymi mechanizmami wykrywania "martwych bram" (ang. dead gateway detection) i wybierania bram nowych, których w tym miejscu nie omawiamy, protokół IP może spełniać funkcję, do której był oryginalnie zaprojektowany - utrzymania przy życiu sieci, której infrastruktura została w znacznej części zniszczona lub przeciążona.

Dodatkowo, istnieje możliwośc implementowania tras statycznych (ang. static routes) czyli specjalnie ustawionych pozycji w route cache, które nie muszą być uaktualniane komunikatami Redirect. Mogą też one mieć znaczenie jako mechanizm obsługujący pole Type of Service nagłówka IP (patrz budowa nagłówka IP), np. pakiety o wysokim priorytecie mogą iść do szczególnie szybkiej bramy.

Stacja nie będąca fizycznie bramą między dwiema odmiennymi sieciami może również służyć jako brama (tutaj: router) - pośredni "skok" w trasie pakietu - jeśli spełnia następujące warunki:

  1. Zmniejsza wartość pola TTL (Time To Live) i ewentualnie usuwa przeterminowany pakiet
  2. Potrafi generować komunikat protokołu ICMP - Destination Unreachable - jeśli pakiet nie może być przekazany do następnej sieci z powodu np. niemożliwości fragmentacji lub niemożliwości zrealizowania żądanej trasy pakietu (ang. strict source route) w przypadku, gdy następny adres nie znajduje się na przyłączonej sieci.
  3. Nie traktuje każdego pakietu jak wysyłany przez siebie, tzn. zachowuje adres nadawcy w wysyłanych pakietach IP
  4. Jeśli jest na to miejsce w polu opcji nagłówka IP, dopisuje się jako kolejny skok na trasie
  5. Uaktualnia opcję Timestamp w nagłówku IP

Bramy decydujące o dynamicznym wyborze trasy między sieciami działają na zasadzie minimalizacji kosztu przesyłu pakietu, wymieniając między sobą informacje o obciążeniu poszczególnych połączeń. Protokoły używane w takim "równoważeniu" sieci to np.

8.2.2.3.2 Implementacja w Linuxie

Najważniejszą strukturą danych odpowiadającą za wybór trasy pakietu w Linuxie jest lista łączona ip_rt_hash_table, zdefiniowana w net/ipv4/route.c. Aby przyspieszyć operacje na tej liście, która może być dość pokaźnych rozmiarów, jest ona haszowana (domyślnie po kluczu 8-bitowym). Element takiej listy wygląda następująco:

struct rtable 
{
        struct rtable           *rt_next;
        __u32                   rt_dst;
        __u32                   rt_src;
        __u32                   rt_gateway;
        atomic_t                rt_refcnt;
        atomic_t                rt_use;
        unsigned long           rt_window;
        atomic_t                rt_lastuse;
        struct hh_cache         *rt_hh;
        struct device           *rt_dev;
        unsigned short          rt_flags;
        unsigned short          rt_mtu;
        unsigned short          rt_irtt;
        unsigned char           rt_tos;
};

Składowe tej struktury to:

rt_next
Wskaźnik do następnego elementu listy
rt_dst
Adres odbiorcy (stacji lub sieci)
rt_src
Adres nadawcy (używany w przypadku stacji o wielu numerach IP)
rt_gateway
Adres bramy używanej do przesłania pakietu do odbiorcy; adres ten może się zmienić po odebraniu komunikatu ICMP - Redirect
rt_refcnt
Licznik odniesień (wykorzystanie danej pozycji)
rt_use
Czy trasa wykorzystywana?
rt_window
Pole pomocnicze w obsłudze protokołu TCP
rt_lastuse
Wskazuje, kiedy ostatnio ta pozycja była używana
rt_hh
Pole związane z obsługą wysyłania pakietów IP do wielu stacji identyfikowanych tym samym numerem IP (IP Multicasting).
rt_dev
Urządzenie przez które pakiet będzie wysyłany
rt_flags
Miejsce na opcje, zdefiniowane w include/linux/route.h. Ciekawsze z nich to:
  • RTF_GATEWAY oznacza, że odbiorca (rt_dst) jest bramą
  • RTF_REINSTATE powoduje, że trasa zostanie ponownie sprawdzona (i ewentualnie ustawiona inna wartość rt_gateway lub rt_irtt) po zadanym czasie
  • RTF_DYNAMIC oznacza, że trasa może być modyfikowana komunikatem Redirect
  • RTF_MODIFIED oznacza trasę właśnie w taki sposób zmodyfikowaną
rt_mtu
Maksymalna jednostka transmisji (ang. Maximum Transmission Unit) dla sieci na tej trasie
rt_irtt
Czas połączenia na danej trasie (ang. initial round trip time); może być używany w algorytmach dynamicznego wyboru trasy.
rt_tos
Znacznik trasy specjalnie zaprojektowanej do przesyłania pakietów IP o danej zawartości pola nagłówka Type of Service.

Znajdowanie trasy dla pakietu IP realizowane jest za pomocą fukcji ip_build_header (patrz interfejs wyjściowy IP), która wywołuje:

Funkcja ip_rt_route

DEFINICJA: struct rtable * ip_rt_route(__u32 daddr, int local)
WYNIK: pozycja w tabeli tras

Funkcja ta dla podanego adresu odbiorcy zwraca pozycję tablicy rt_ip_hash_table, która będzie użyta do wysłania danego pakietu IP dalej.

Inne funkcje realizujące operacje na tej tabeli zgodnie z podanymi wcześniej zasadami to:

Funkcja rt_redirect

DEFINICJA: static void rt_redirect(__u32 dst, __u32 gw, struct device *dev)
WYNIK: -

dokonująca odpowiednich zmian w tabeli rt_ip_hash_table po otrzymaniu żądania zmiany bramy dla trasy (ICMP Redirect),

Funkcja ip_rt_new

DEFINICJA: int ip_rt_new(struct rtentry *r)
WYNIK: -

dodająca nową pozycję w tablicy rt_ip_hash_table po próbie wysłania do adresu jeszcze w niej nie istniejącego.


8.2.2.4 Błędy i zapytania: Protokół ICMP

Protokół ICMP (Internet Control Message Protocol) jest protokołem pomocniczym współpracującym z IP, opisanym w dokumencie RFC 792. Przekazuje on komunikaty związane z:

Komunikaty obsługi błędów to:

  1. Odbiorca niedostępny (Destination Unreachable), wysyłany w przypadku niemożności dotarcia pakietu do punktu przeznaczenia z powodu np. braku fizycznego połączenia, nie wynegocjowanego protokołu, nieobsługiwanego portu, niewykonanej fragmentacji. Objawia się dla użytkownika komunikatami takimi jak np. "no route to host"
  2. Trasa zmieniona (Redirect), wysyłany przez bramę jeśli jakieś inne połączenie (nie przez tę właśnie bramę) jest najlepszym połączeniem od nadawcy pakietu do odbiorcy.
  3. Przeciążenie bramy (Source Quench), wysyłany przez bramę do nadawcy w przypadku zbyt dużej ilości pakietów do obsłużenia.
  4. Przeterminowanie pakietu (Time Exceeded), wysyłany przez bramę jeśli po zmniejszeniu pola ttl (Time To Live) nagłówka IP jego wartość jest mniejsza od zera
  5. Zły parametr (Parameter Problem), wysyłany przez bramę w przypadku zauważenia niezgodności w nagłówku IP pakietu, takiej jak zła suma kontrolna lub niepoprawna opcja.

Komunikaty zapytań to:

  1. Echo (Echo) - sprawdzający istnieje i poprawne działanie urządzenia o zadanym adresie; musi ono odesłać dokładnie te same dane, jakie dostaje komunikatem Echo. Dla użytkownika dostępny jest poprzez polecenie ping.
  2. Informacja o sieci (Information) - używany w inicjalizacji stacji, służy do dowiadywania się w jakiej sieci znajduje się stacja.
  3. Czas wysłania (Timestamp) - używany do wymiany informacji o ostatnim momencie przetwarzania pakietu IP, która może być potem używana np. do stwierdzenia przeterminowania pakietu lub obliczenia obciążenia trasy.

W Linuxie obsługa protokołu ICMP zaimplementowana jest w pliku net/ipv4/icmp.c. Najważniejszą funkcją jest

Funkcja icmp_send

DEFINICJA: void icmp_send(struct sk_buff *skb_in, int type, int code,
unsigned long info, struct device *dev)
WYNIK: -

jako parametry przyjmująca pakiet IP, którego będzie dotyczył komunikat ICMP, typ i kod komunikatu do wysłania i ewentualnie inne potrzebne informacje (jak zwracany adres, informacje z ping itp.)


8.2.2.5 Bibliografia

  1. Pliki źródłowe Linuxa:
  2. W.Richard Stevens: Programowanie zastosowań sieciowych w systemie Unix. WNT 1995
  3. Dokumenty RFC (Requests For Comments) w Internet Encyclopedia.

Autor: Tomasz Tarchała