Sokety a C/C++: MTU a IP fragmentace

18. 8. 2003
Doba čtení: 6 minut

Sdílet

Dnes se podíváme na fragmentaci a MTU. Vysvětlíme si pojmy a blíže se seznámíme s atributy IP hlavičky, které se týkají fragmentace. Znalosti z tohoto článku využijeme v příštím díle, kdy vytvoříme program pro měření MTU.

MTU

MTU (Maximum transfer unit) je hodnota udávající maximální velikost přenosové jednotky na úrovni vrstvy síťového rozhraní (nejnižší vrstvy) komunikačního modelu TCP/IP . IP pakety jsou přenášeny mezi počítači, které nemusejí být bezprostředně propojeny. Tím pádem nastává situace, že mezi odesílatelem a příjemcem IP paketu se může nalézat několik počítačů. IP paket projde mezi odesílatelem a příjemcem přes mnoho počítačů a tím pádem přes mnoho sítí LAN. IP pakety jsou vkládány do rámců protokolu nižší vrstvy. Problém nastává v situaci, kdy IP paket je větší než maximální možná velikost rámce protokolu nižší vrstvy. A právě hodnota maximální možné velikosti rámce nižší vrstvy je označována jako maximální velikost přenosové jednotky (MTU). V lokální síti není možné přenášet IP datagram větší než MTU dané sítě.

Fragmentace IP paketu

Protokol IP umožňuje rozdělení velkého IP paketu na menší celky (fragmenty). Každý fragment je samostatný IP paket – má svou (novou) IP hlavičku. Každý fragment potom putuje k cíli samostatně, nezávisle na ostatních. U příjemce jsou jednotlivé fragmenty poskládány a je z nich sestaven původní celek – IP paket. Každý fragment je samostatný IP paket, z čehož vyplývá, že může být podle potřeby opět rozdělen na další fragmenty.

Při rozdělení IP paketu na fragmenty mají všechny fragmenty stejný identifikátor (atribut id IP hlavičky. Viz článek Sokety a C/C++ – struktura IP a UDP). Právě podle shodného identifikátoru pozná příjemce fragmenty, které společně tvoří celek. Každý fragment má rovněž ve své hlavičce údaj o své poloze v původním IP paketu (atribut frag_off IP hlavičky). Příjemce zachytí všechny IP pakety se stejným identifikátorem a pomocí hodnoty atributu frag_off každého fragmentu sestaví původní IP paket. IP pakety z fragmentů může skládat pouze příjemce. Žádný jiný počítač již nesmí fragmenty skládat do jednoho celku. Není totiž zaručeno, že všechny IP pakety půjdou stejnou cestou. Proto by mohl počítač, který by se pokusil složit IP paket z fragmentů mít problém, kdyby nějaký fragment k němu vůbec nedorazil. Jediná jistota je, že u příjemce se všechny fragmenty setkají.

Jestliže putuje po síti jeden IP paket, existuje určitá pravděpodobnost, že dojde k jeho ztrátě. Jestliže se tento IP paket rozdělí na fragmenty, zvýší se pravděpodobnost ztráty paketu. Protože ztráta jednoho jediného fragmentu znamená ztrátu celého původního IP paketu. Na to je dobré pamatovat, používáme-li nad IP protokolem nějaký protokol bez detekce doručení (UDP). Čím větší UDP datagram pošleme, tím více se zvyšuje pravděpodobnost, že dojde při fragmentaci k jeho ztrátě (nedoručení).

Fragmentaci lze zakázat nastavením bitu DF (Don't fragment) . Jestliže je fragmentace zakázána a při tom je nutná, bude IP paket zahozen (nemůže být poslán dále). Odesílatel bude o zahození informován ICMP paketem typu 3, kódu 4.

Problém, který jsem dříve zmínil (MTU < velikost IP paketu), se řeší právě fragmentací, kdy dojde k rozdělení původního IP paketu na menší celky, které se již vejdou do rámců protokolu nižší vrstvy. Protože se fragmentací zvyšuje pravděpodobnost ztráty IP paketu, je dobré vždy zvážit, zda má smysl posílat větší IP paket, než je MTU. Zvláště například velký UDP datagram.

ICMP paket typu 3, kódu 4

Paket signalizuje situaci, kdy dojde k zahození IP paketu, protože jej není možné fragmentovat, ale je potřeba ho přenést po LAN, která má MTU nižší, než je velikost IP paketu. ICMP paket typu 3, kódu 4 je velice podobný jako paket typu 3, kódu 3 (Nedosažitelný UDP port), o kterém jsme již mluvili v článku Sokety a C/C++ – Tělo ICMP paketů. Proměnná část ICMP hlavičky (poslední 4 byte) je nevyužitá a za ní následuje prvních 64 bytů zahozeného ICMP paketu. Nevyužité 4 byte ve volitelné části ICMP hlavičky může přece jen něco někdy obsahovat. Poslední dva byte mohou obsahovat právě onu problematickou hodnotu MTU. Není dobré se ale spoléhat, že tyto dva byte mají smysluplnou hodnotu.

Přijatá data
Typicky 20 bytů IP hlavička přijatého IP paketu. Paket v sobě nese ICMP paket typu 3, kódu 4.
8 bytů ICMP hlavička typu 3 kódu 4. Ve části ICMP hlavičky, která je závislá na typu jsou buď nuly, nebo druhý dvou byte může obsahovat MTU.
ICMP hlavička
  • 1 byte Typ
  • 1 byte Kód
  • 2 byte Kontrolní součet
  • 2 byte Nepoužito – nastaveno na 0
  • 2 byte Nepoužito – nastaveno na 0, nebo má hodnotu MTU
Typicky 20 bytů IP hlavička zahozeného IP paketu. Paket v sobě nesl ICMP žádost o ECHO. Odesílatelem paketu jsme byli my.
Maximálně 64 bytů Tělo zahozeného IP paketu. V našem případě IP paket ve svém těle nesl ICMP paket ECHO žádost.
  • 8 bytů Hlavička ECHO žádosti, kterou jsme odeslali. (Obsahuje identifikátor žádosti a pořadové číslo žádosti.)
  • ? bytů Tělo ECHO žádosti – my posíláme každou žádost s různou velikostí. Velikost bude pokaždé jiná, maximálně ale 56 bytů. (56 = 64 – 8, kde 8 je délka ICMP hlavičky.)

Program

My vytvoříme program, který bude postupně posílat ICMP pakety různé velikosti, které ale ve své IP hlavičce budou mít zakázanou fragmentaci. Podle toho, jak se budou vracet ECHO odpovědi, případně ICMP pakety typu 3 kódu 4, budeme měnit velikost odesílaných dat. Tak pomocí metody půlení intervalu zjistíme nejmenší MTU (nejmenší z maximálních přenosových jednotek) mezi námi a zadaným počítačem.

Popis algoritmu

  1. Nastav maximální délku bufferu na 64536 (maximální délka IP paketu i s hlavičkou).
  2. Nastav minimální délku bufferu na 28 (délka IP hlavičky a ICMP hlavičky).
  3. Vytvoř žádost o ECHO délky (minimální délka bufferu + maximální délka bufferu) / 2
  4. Pošli vytvořenou ECHO žádost
  5. Jestliže se nepodařilo odeslat data, nastav maximální délku bufferu na velikost ECHO žádosti a jdi k bodu 3.
  6. Přijmi ICMP paket
  7. Opakuj bod 6, dokud přijatý paket není ECHO odpověď na odeslanou žádost nebo není paket typu 3, kódu 4.
  8. Je-li ICMP paket ECHO odpověď, nastav minimální velikost bufferu na velikost ECHO žádosti.
  9. Je-li ICMP paket typu 3 a kódu 4, nastav maximální velikost bufferu na hodnotu ECHO žádosti.
  10. Jestliže nepřišel v časovém intervalu ICMP paket, nastav maximální velikost bufferu na hodnotu ECHO žádosti.
  11. Opakuj body 3 až 10, dokud není maximální a minimální velikost bufferu shodná.
  12. Minimální (i maximální) velikost bufferu udává hledané MTU.

Implementace

Vytvoříme soket typu RAW s použitým protokolem ICMP. Nastavíme volbu soketu IP_HDRINCL úrovně SOL_IP. Viz článek Sokety a C/C++ – volby soketů. Volbu jsme použili například v článku Sokety a C/C++ – Raw soket. Tím nastavíme, že součástí bufferu k odeslání bude také hlavička IP protokolu. V atributech IP záhlaví nastavíme příznak DF na hodnotu 1, zakážeme fragmentaci. Poté budeme přijímat ICMP pakety a sledovat příchozí ICMP pakety tak, jak je popsáno v „Popisu algoritmu“.

Problémy

Jednotlivé operační systémy se zde opět dosti liší. Hlavní rozdíl je v tom, pokud chceme odeslat IP paket se zakázanou fragmentací, který nemůže kvůli nízkému MTU vůbec opustit počítač. V takovém případě v Linuxu funkcesendto vrací –1 a proměnnou errno nastaví na hodnotu makra EMSGSIZE. V MS Windows Ž ale proběhne vše v „pořádku“. Tedy funkce sendto nevrátí hodnotu marka SOCKET_ERROR. Ale pochopitelně paket počítač vůbec neopustí. Podle mě by zde mělo dojít k selhání sendto, protože paket vůbec neopustil počítač. Ztrátu paketu poznám až při vypršení času pro příjem ECHO odpovědi.

Další podstatný rozdíl spočívá v doplňování atributů v IP hlavičce operačním systémem. Jak MS Windows&reg, tak i Linux v případě, že zadáme jako identifikátor paketu 0, doplní sami nějakou správnou hodnotu identifikátoru před odesláním paketu. Tím pádem jde taktéž zadat 0 jako kontrolní součet IP paketu. V takovém případě bude vždy kontrolní součet dopočítán. V linuxu lze nastavit i zdrojovou IP adresu (IP adresu odesílatele) na 0. Operační systém ji doplní. V MS WindowsŽ tomu tak není. Proto se ohledně nastavování IP adresy odesílatele liší ukázkové příklady ke stažení v MS WindowsŽ a v Linuxu.

ict ve školství 24

Chyba linuxového jádra

Zajímavé je si povšimnout chyby linuxového jádra, která se ale v našem případě neprojeví. Jestliže je nastavená volba soketu IP_HDRINCL, nebude paket fragmentován. Ani v případě, že příznak DF nebude nastaven (bude 0). Můžete se podívat na manuálovou stránku k pojmu RAW v sekci 7, odstavec BUG.

Článek i s příkladem by byl bohužel příliš velký (asi 21,5 Kb). Proto jsem jej rozdělil na dvě části. V příštím díle předvedu zdrojový text programu, který bude měřit MTU mezi našim a vybraným počítačem.