IPv4: Analiza ruchu IP
 

Popatrzmy jak praktycznie zachowuje się i działa protokółu IPv4. Wykorzystamy dwa różne systemy operacyjne, aby pokazać, że pomimo drobnych różnic w domyślnej implementacji i zachowaniu na nich protokołu IP, urządzenia takie mogą się bez problemów ze sobą komunikować.


Praktyczna analiza ruchu IPv4 

Do weryfikacji wysyłanego ruchu będziemy posługiwać się analizatorem pakietów sieciowych tcpdump, a polecenie ping posłuży nam do generowanie ruchu IP. Opcja "-s" polecenia ping umożliwia określenie wielkości pola danych zapytania ICMP (w poniższym przykładzie 1472 oktety), a opcja "-c" wskazanie ilości wysyłanych zapytań (poniżej wysyłamy tylko 1 zapytanie ICMP).

HOST-1# ping -s 1472 -c 1 10.8.100.1
PING 10.8.100.1 (10.8.100.1) 1472(1500) bytes of data.
1480 bytes from 10.8.100.1: icmp_req=1 ttl=255 time=1.54 ms

--- 10.8.100.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.546/1.546/1.546/0.000 ms
HOST-1#  

Skutkiem użyciu wyżej opisanego polecenia wysłaliśmy do sieci jeden pakiet IP, którego zawartość możemy zobaczyć poniżej. Pomarańczowym kolorem oznaczony został fragment pokazujący zawartość nagłówka protokołu IP.

HOST-1# tcpdump -vn -i eth0 dst host 10.8.100.1 and icmp
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
18:11:53.273660 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 1500) 
    10.8.100.51 > 10.8.100.1: ICMP echo request, id 31284, seq 1, length 1480
HOST-1# 

Powyżej widać, że pakiet został wysłany z adresu IP 10.8.100.51, do adresu IP 10.8.100.1 (10.8.100.51 > 10.8.100.1). Pole DSCP posiada domyślną wartość (tos 0x0). Początkowa wartość pola TTL została ustawiona na 64 (ttl 64). Nie doszło do fragmentacji, gdyż do sieci został wysłany tylko jeden pakiet z ID zero (id 0), przesunięciem fragmentacji wynoszącym zero (offset 0) i nie ma on ustawionego bitu MF w polu flag (flags [DF]). Widać też, że system operacyjny GNU/Linux Debian 6.0 (HOST-1) z którego został przeprowadzony test, oznaczył pakiet bitem DF (flags [DF]) i nie ponumerował go (id 0). Skoro pakiet i tak nie będzie mógł podlegać fragmentacji (flags [DF]), to jego ID i tak nie będzie potrzebne. ID pakietu jest niezbędne podczas procedury składania pakietu w całość. Identyfikuje ono kolejne fragmenty, jakie się na niego składają.

Wydajmy to samo polecenie w innym systemie operacyjnym - UNIX/Mac OS X 10.10 (HOST-2). 

HOST-2# ping -c 1 -s 1472 10.0.1.1 
PING 10.0.1.1 (10.0.1.1): 1472 data bytes
1480 bytes from 10.0.1.1: icmp_seq=0 ttl=255 time=4.517 ms

--- 10.0.1.1 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 4.517/4.517/4.517/0.000 ms
HOST-2# 

W analizatorze pakietów sieciowych możemy zobaczyć, że tym razem pakiety domyślnie nie mają ustawionych żadnych flag (flags [none]) i są numerowane przy użyciu ID (id 39589). 

HOST-2# tcpdump -vn -i en0 dst host 10.0.1.1 and icmp
tcpdump: listening on en0, link-type EN10MB (Ethernet), capture size 65535 bytes
11:46:10.217711 IP (tos 0x0, ttl 64, id 39589, offset 0, flags [none], proto ICMP (1), length 1500)
    10.0.1.5 > 10.0.1.1: ICMP echo request, id 16925, seq 0, length 1480
HOST-2#

Wymusimy teraz ustawienie bitu DF za pomocą opcji "-D" polecenia ping i zweryfikujmy ponownie wartość ID. 

HOST-2# ping -D -c 1 -s 1472 10.0.1.1 
PING 10.0.1.1 (10.0.1.1): 1472 data bytes
1480 bytes from 10.0.1.1: icmp_seq=0 ttl=255 time=4.016 ms

--- 10.0.1.1 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 4.016/4.016/4.016/0.000 ms
HOST-2#

Widać poniżej, że system ten ustawia wartość ID (id 44014), nawet kiedy ustawiony jest bit DF. 

HOST-2# tcpdump -vn -i en0 dst host 10.0.1.1 and icmp
12:24:42.754510 IP (tos 0x0, ttl 64, id 44014, offset 0, flags [DF], proto ICMP (1), length 1500)
    10.0.1.5 > 10.0.1.1: ICMP echo request, id 38430, seq 0, length 1480
HOST-2#

Wróćmy do poprzedniego systemu i sprawdźmy jak zachowa się, kiedy każemy mu zdjąć flagę DF (parameter "-M dont" polecenia ping). 

HOST-1# ping -c 1 -s 1472 10.8.100.1 -M dont
PING 10.8.100.1 (10.8.100.1) 1472(1500) bytes of data.
1480 bytes from 10.8.100.1: icmp_req=1 ttl=255 time=1.07 ms

--- 10.8.100.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.078/1.078/1.078/0.000 ms
HOST-1#

Pakiet IP może teraz podlegać fragmentacji, stąd jest numerowany przez system przy użyciu pola ID (id 18332).

HOST-1# tcpdump -vn -i eth0 dst host 10.8.100.1 and icmp
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
13:24:33.972131 IP (tos 0x0, ttl 64, id 18332, offset 0, flags [none], proto ICMP (1), length 1500)
10.8.100.51 > 10.8.100.1: ICMP echo request, id 17651, seq 1, length 1480
HOST-1#

Z tego co powyżej pokazaliśmy wynika, że pewne domyślne aspekty zachowania stosu TCP/IP zależne są od używanego systemu operacyjnego. Da się je modyfikować, niemniej nie jest to potrzebne, aby systemy te mogły się ze sobą komunikować. Warto zaznaczyć, że dotyczy to tylko pewnych aspektów. Niektóre muszą być obsługiwane tak samo, a w przypadku niektórych to tylko kwestia naszych gustów i wymagań.

Powyższa przypadłość pozwala na podstawie ruchu IP w przybliżeniu odgadnąć używany system operacyjny.

Wróćmy do analizy naszego ruchu. Widać, że w pakiecie IP przenoszone są dane protokołu ICMP - pole Protocol ma wartość 1 (proto ICMP (1)). Wielkość całego pakietu IP razem z nagłówkiem ma 1500 oktetów (length 1500). Kazaliśmy poleceniu ping wysłać w komunikacie ICMP 1472 oktety (opcja "-s"). Skąd zatem wzięło się 1500 oktetów? Do naszych danych (1472 oktetów), w procesie enkapsulacji zostało dodane 8 oktetów nagłówka protokołu ICMP i 20 oktetów nagłówka IP, co sumarycznie daje 1500 oktetów.

Warto zauważyć, że 1500 oktetów jest domyślną wartością MTU na interfejsach używanych tutaj systemów operacyjnych. Wartość MTU możemy sprawdzić poleceniem ifconfig - została ona poniżej oznaczona kolorem pomarańczowym. 

Wynik polecenia ifconfig dla systemu GNU/Linux Debian 6.0. 

HOST-1# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:0c:29:c2:2e:e0  
          inet addr:10.8.100.51  Bcast:10.8.100.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fec2:2ee0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:38949378 errors:0 dropped:0 overruns:0 frame:0
          TX packets:33219434 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:5255633072 (4.8 GiB)  TX bytes:4832872295 (4.5 GiB)
HOST-1# 

Wynik polecenia ifconfig dla systemu UNIX/Mac OS X 10.10. 

HOST-2# ifconfig en0
en0: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
ether 28:cf:e9:15:25:a9 
inet6 fe80::2acf:e9ff:fe15:25a9%en0 prefixlen 64 scopeid 0x4 
inet 10.0.1.5 netmask 0xffffff00 broadcast 10.0.1.255
nd6 options=1<PERFORMNUD>
media: autoselect
status: active
HOST-2# 

Pole określające długość pakietu IP ma 16 bitów, stąd maksymalna jego wielkość nie powinna przekroczyć 65,535 oktetów. Jeżeli odejmiemy od tej wartości wielkość nagłówka ICMP (8 oktetów) i nagłówka IP (20 oktetów), to da nam to wartość 65,507. Oznacza to, że system nie powinien przyjąć datagramu ICMP o wielkości 65,508 oktetów. Zobaczmy co się stanie, kiedy każemy mu go wysłać.

Wynik dla systemu GNU/Linux Debian 6.0:

HOST-1# ping -s 65508 -c 1 10.8.100.1
WARNING: packet size 65508 is too large. Maximum is 65507
PING 10.8.100.1 (10.8.100.1) 65508(65536) bytes of data.
ping: local error: Message too long, mtu=1500

--- 10.8.100.1 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms
HOST-1#

Wynik dla systemu UNIX/Mac OS X 10.10:

HOST-2# ping -c 1 -s 65508 10.0.1.1 
ping: packet size too large: 65508 > 65507
HOST-2# 

W zależności od systemu dostaliśmy błąd lub informację, że datagram ICMP może maksymalnie przenosić 65,507 oktetów. Sprawdźmy, czy jeśli zmniejszymy pole danych o 1 oktet, to uda się go wysłać.

HOST-2# ping -c 1 -s 65507 10.0.1.1 
PING 10.0.1.1 (10.0.1.1): 65507 data bytes
65515 bytes from 10.0.1.1: icmp_seq=0 ttl=255 time=122.034 ms

--- 10.0.1.1 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 122.034/122.034/122.034/0.000 ms
HOST-2# 

Udało się. Przy czym należy pamiętać, że doszło tutaj do fragmentacji - długość pakietu przekroczyła wartość MTU. Jak widać było wyżej, MTU używanych interfejsów ma wartość 1500. Zobaczmy co się stanie, jeśli spróbujemy przekroczyć tę wartość o 1 oktet. Poniżej, każemy poleceniu ping wysłać 1473 oktetów danych, co po dodaniu nagłówka ICMP (8 oktetów) i IP (20 oktetów) da 1501 oktetów.

HOST-1# ping -s 1473 -c 1 10.8.100.1
PING 10.8.100.1 (10.8.100.1) 1473(1501) bytes of data.
1481 bytes from 10.8.100.1: icmp_req=1 ttl=255 time=1.69 ms

--- 10.8.100.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.693/1.693/1.693/0.000 ms
HOST-1# 

Niżej można zobaczyć, że na skutek fragmentacji do sieci wysłane zostały tak naprawdę dwa pakiety IP. 

HOST-1# tcpdump -vn -i eth0 dst host 10.8.100.1 and icmp
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
15:52:11.419501 IP (tos 0x0, ttl 64, id 18330, offset 0, flags [+], proto ICMP (1), length 1500)
    10.8.100.51 > 10.8.100.1: ICMP echo request, id 17888, seq 1, length 1480
15:52:11.419513 IP (tos 0x0, ttl 64, id 18330, offset 1480, flags [none], proto ICMP (1), length 21)
    10.8.100.51 > 10.8.100.1: icmp
HOST-1#  

Mają one tą samą wartość ID (id 18330). Oznacza to, iż są to dwa fragmenty składające się na jeden pakiet. Pierwszy ma ustawione przesunięcie na zero (offset 0) oraz bit MF (flags [+]), co oznacza iż jest on pierwszym fragmentem pakietu IP o ID 18330 (id 18330). Drugi, w polu przesunięcia ma wartość 1480 (offset 1480) i brak bitu MF (flags [none]), co oznacza iż jest ostatnim fragmentem składającym się na pakiet o ID 18330 (id 18330). Skąd w przesunięciu wartość 1480 (offset 1480)? Kiedy od długości 1500 oktetów pierwszego pakietu (length 1500) odejmiemy długość nagłówka IP (20 oktetów), to otrzymamy wartość 1480. Jest to ilość oktetów danych z punktu widzenia protokołu IP, jaka została wysłana w pierwszym fragmencie. Przesunięcie wskazuje, że kolejne oktety danych, jakie przenoszone są w drugim fragmencie należy zacząć umieszczać za 1480 oktetem bufora. Drugi fragment przenosi 21 oktetów (length 21). Sam nagłówek protokołu IP to 20 oktetów, co oznacza, że w polu danych przenoszony jest tylko 1 oktet.

Warto również zwrócić uwagę na to, że fragmentacja realizowana jest na poziomie protokołu IP, stąd nagłówek przenoszonego w środku pola danych protokołu znajduje się tylko w pierwszym fragmencie pakietu. Dla protokołu IP, cały datagram ICMP jest tylko danymi. Jako, że nagłówek ICMP znajduje się w jego pierwszych oktetach, to będzie on widoczny tylko w pierwszym fragmencie. Widać to w otrzymanym przez nas wyniku. Tylko w pierwszym fragmencie znajduje się informacja o typie datagramu ICMP i jego opcjach. Na podstawie drugiego wiemy tylko tyle, że są to jakieś dane ICMP - nic więcej (informację tą mamy z nagłówka IP (proto ICMP (1)).

Fragmenty IP są kłopotliwe do obsługi przez systemy filtrujące ruch. Zwykle filtrowanie realizowane jest w oparciu o nagłówki protokołów, jakie transportuje w sobie IP (np. porty UDP lub TCP). Niestety, proces fragmentacji sprawia, iż znajdują się one tylko w pierwszym fragmencie pakietu.

Należy też pamiętać, że protokół IP jest protokołem zawodnym, stąd jeżeli jeden z fragmentów pakietu IP zostanie zgubiony, to trzeba retransmitować cały pakiet IP na nowo - wszystkie jego fragmenty. Protokół IP nie zareaguje w żaden sposób na fakt nie dotarcia pojedynczego fragmentu. Retransmisję musi zainicjować coś powyżej protokołu IP, co nie ma pojęcia o fragmentacji, jaka miała miejsce w sieci, stąd wszystkie fragmenty zostaną wysłane na nowo. Zatem fragmentacja zwiększa prawdopodobieństwo potrzeby retransmisji pakietów, co może skutkować zmniejszeniem prędkości transmitowanych danych. Oczywiście, fragmenty pakietu mogą podróżować różnymi drogami i dotrzeć w różnej kolejności. Stąd odbiorca powinien przez pewien okres czasu przetrzymywać wszystkie odebrane fragmenty pakietu z nadzieją, że brakujący fragment jeszcze dotrze. Czas ten zależny jest od konkretnej implementacji systemu operacyjnego i nie powinien być zbyt długi. Wynika to z tego, że istnieje potencjalna możliwość wykonania ataku polegającego na przepełnieniu buforów przeznaczonych na obsługę fragmentacji u odbiorcy.

Na koniec warto przypomnieć, że fragmentację w IPv4 może przeprowadzić nadawca lub każdy urządzenie na drodze pakietu. Proces złożenia pakietu IPv4 w całość wykonywany jest dopiero u odbiorcy.

Fragmentacja jest w pewnych aspektach problematyczna, stąd nie jest pożądana i warto jej unikać. O technikach, które jej zapobiegają będzie trochę w innym artykule. Dla pakietów IPv4 wymagana jest obsługa MTU o wielkości minimum 576 oktetów. Przy czym, na obydwóch wyżej używanych systemach fragmentacja na poziomie protokołu IP jest domyślnie realizowana nawet jeżeli MTU wyjściowego interfejsu jest mniejsze, niż wartość wymagana przez standard.


Zapraszamy do kontaktu drogą mailową This email address is being protected from spambots. You need JavaScript enabled to view it. lub telefonicznie +48 797 004 932.