Sokety a C/C++: Struktura IP a UDP

30. 6. 2003
Doba čtení: 7 minut

Sdílet

Dnes si ukážeme strukturu IP paketu a UDP datagramu. Tím získáme teoretický základ pro takzvané raw sokety. Informace z tohoto článku využijeme v příštím dílu, kdy pomocí raw soketu odešleme UDP datagram. Tento článek je čistě teoretický. Nenaleznete v něm žádný ukázkový zdrojový text.

Doposud jsme v rámci seriálu používali sokety pro přenos dat způsobem, který po nás nevyžadoval cokoliv vědět o principech počítačových sítí. Stačilo znát pár funkcí, umět vybrat vhodný protokol (TCP nebo UDP), a bylo možné posílat a přijímat data. Dnes se podíváme trochu „pod pokličku“.

Vrstvy komunikačního modelu TCP/IP

Tabulka č. 442
Aplikační vrstva
Transportní vrstva
Internetová vrstva
Síťová

Vrstvy komunikačního modelu ISO OSI

Tabulka č. 443
Aplikační vrstva
Prezentační vrstva
Relační vrstva
Transportní vrstva
Síťová vrstva
Linková vrstva
Fyzická vrstva

Model ISO OSI je dán standardizační komisí a jedná se vlastně o standard. Malý problém je ale v tom, že internet (založený na modelu TCP/IP) vznikl trochu dříve než norma ISO OSI. V podstatě to ale nevadí, oba modely se totiž nevylučují. Ve skutečnosti spolu splývají. ISO OSI vlastně jen upřesňuje TCP/IP. Fyzická a linková vrstva ISO OSI v podstatě odpovídají síťové vrstvě modelu TCP/IP. Síťová vrstva ISO OSI odpovídá internetové vrstvě modelu TCP/IP. Transportní vrstvy jsou stejné a vrchní tři vrstvy ISO OSI odpovídají aplikační vrstvě modelu TCP/IP.

Nebudu se rozepisovat, jaké jsou úlohy a role jednotlivých vrstev. Tato problematika je podrobně vysvětlena v úvodu snad každé knihy o počítačových sítí. Snad jen je dobré uvést příklady protokolů. V modelu TCP/IP:

  • Aplikační – HTTP, FTP a podobně
  • Transportní – TCP, UDP
  • Internetový – IP
  • Síťový – například Ethernet a jiné

Když jsme v úvodu seriálu vytvářeli TCP klienta a TCP server, aplikační protokol byl vlastně definován velice jednoduše. Na textový řetězec „Ahoj“ odpovídal server „Nazdar“. Používat pro něco takového pojem „protokol aplikační vrstvy“ je asi trochu silné slovo. My si ale na tomto příkladě ukážeme, v jaké formě data putovala po síti.

Přenos dat v modelu TCP/IP

Data „vzniknou“ na nejvyšší vrstvě, tedy aplikační. V našem případě se jedná o textový řetězec „Ahoj“. Tedy krátkou posloupnost bytů. Ta je předána protokolu transportní vrstvy, v našem případě se jednalo o TCP. Žádná vrstva se nezabývá obsahem dat, které jí jsou předána z vrstvy vyšší. V transportní vrstvě je vytvořená odpovídající hlavička TCP protokolu, za kterou jsou vložená data, která byla předána z vyšší vrstvy. Tím došlo k vytvoření TCP segmentu (hlavička TCP a data z vyšší vrstvy). TCP segment je předán nižší (internetové) vrstvě. Zde se na TCP segment pohlíží jako na data. Je vytvořena hlavička IP protokolu a za ní je přidán TCP segment. Celému bloku dat se říká IP paket. Tato data jsou předána síťové vrstvě. Například na síťové vrstvě je používán Ethernet, potom bude vytvořen ethernetový rámec. Budou vytvořena ethernetové záhlaví a ethernetové zápatí a mezi ně bude vložen IP paket. A data mohou vyrazit na cestu. U příjemce jdou data od nejnižší vrstvy nahoru a na každé vrstvě jim je „odseknuta“ hlavička. Až nakonec aplikační vrstva obdrží řetězec „Ahoj“.

Samozřejmě, že celá problematika je trochu složitější, například mohou vzniknout situace, že data předávána vyšší vrstvou nižší se nemusejí vejít do bloku dat nižší vrstvy. Také IP paket se může po cestě při průchodu různými lokálními sítěmi „obalovat“ do různých síťových protokolů. A podobně. Pro představu to ale stačí.

Tabulka č. 444
Záhlaví ethernetového rámce Hlavička IP paketu Hlavička TCP segmentu Přenášená data (Řetězec „Ahoj“) Zápatí ethernetového rámce

Jak je tedy vidět, použití soketů typu SOCK_STREAM nebo SOCK_DGRAM před námi skrylo mnoho podrobností. Existuje ale typ soketu, jmenuje se SOCK_RAW, který nám umožní (kromě síťové vrstvy) mít plně pod kontrolou tvorbu všech hlaviček. Slovo „raw“ lze přeložit jako syrový nebo hrubý. My si příště předvedeme syrový soket na příkladu, kde pomocí něj odešleme UDP datagram. Jako název protokolu použijeme IPPROTO_RAW – 3. parametr funkce socket. Nejedná se vlastně o protokol ve smyslu třeba TCP nebo UDP. Hodnota makraIPPROTO_RAW sděluje operačnímu systému, že chceme vyplňovat hlavičku IP a hlavičku protokolu transportní vrstvy (v našem případě se bude jednat o UDP).

Se síťovou vrstvou pomocí soketů pracovat nelze. Takže kombinací maker SOCK_RAW jakožto typu soketu a IPPROTO_RAW jakožto názvu protokolu se vlastně dostaneme „nejníže“, jak jen to v soketech jde.

Než začneme s psaním samotného příkladu (v příštím dílu), podíváme se na význam jednotlivých položek v hlavičkách protokolů IP a UDP.

Malá poznámka k WinSock

Celý seriál používám (příklady píšu) pro prostředí DEV-CPP založené na překladači MinGW. Vůbec to nedokážu pochopit (nebo to možná jen nedokážu najít :-) ), ale MinGW nemá v hlavičkových souborech, které jsou s ním dodávány, deklarovány struktury potřebné pro práci s IP, TCP a UDP záhlavím. Také tam nejsou deklarována některá makra. Je vidět, že MinGw má hodně co dohánět. Aby šel níže napsaný příklad použít i v DEV-CPP, vytvořil jsem potřebné struktury, které jsou na konci článku ke stažení. Pokud používáte jiný překladač, budete nejspíše mít potřebné struktury a makra definované v jeho hlavičkových souborech. Poté můžete použít buďto mé struktury, anebo raději „originální“, dodávané s překladačem. V takovém případě ale musíte počítat s tím, že se názvy struktur nebo atributů budou asi trochu lišit. Já pojmenoval struktury i atributy struktur stejně, jako jsou pojmenovány v Linuxu.

Hlavička IP protokolu

Tabulka č. 445
Velikost (bity) Název atributu Název položky Popis
4 version Verze IP Verze IP protokolu. Budeme zadávat vždy 4 – časem možná i 6 :-). Ale hlavička IP verze 6 vypadá jinak než hlavička IP verze 4. Atribut verze společně s následujícím atributem tvoří první byte hlavičky IP.
4 ihl Délka záhlaví (počet 4 bytů záhlaví) Velikost záhlaví / 4. Záhlaví IP může obsahovat nepovinné položky. Jeho velikost proto není předem dána. Jestliže nebude velikost IP záhlaví dělitelná čtyřmi, musí být IP hlavička doplněna nulami. Povinné položky IP hlavičky mají celkem 20 bytů. Proto je nejmenší možná hodnota 5. My nebudeme používat nepovinné položky IP hlavičky. Společně s předchozím atributem tvoří první byte hlavičky IP.
8 tos Typ služby Typ služby. Nepoužívá se. Budeme nastavovat na 0.
16 tot_len Celková velikost IP paketu Velikost IP hlavičky a dat IP paketu
16 id Identifikátor datagramu Jednoznačný identifikátor datagramu. Číslo přiděluje OS. Jestliže se IP paket nevejde do síťového rámce, bude rozdělen na menší IP pakety – fragmentace. Každý fragment je platným IP paketem. Všechny fragmenty, na které je IP paket rozdělen, mají stejné identifikační číslo. Na základě identifikačního čísla jsou u příjemce IP fragmenty spojovány v původní celek.
3 frag_off Příznaky 3 bity příznaků IP paketu.
  1. vždy nula
  2. DF – Do not fragment – je-li nastaven na 1, paket nebude nikdy fragmentován. Bude raději zahozen.
  3. MF – More fragments – je-li nastaven na 1, pak IP paket není posledním fragmentem původního IP paketu.
Příznaky jsou umístěny společně s následujícím atributem v jednom dvou-byte.
13 frag_off Offset fragmentu Posunutí (offset) fragmentu v původním IP paketu.
8 ttl Doba životnosti paketu (Time To Live) Číslo udávající, kolika počítači (směrovači) ještě může paket projít, než bude zahozen. O TTL jsme si povídali v minulém dílu.
8 protocol Protokol vyšší vrstvy Protokol transportní vrstvy. Udává, jaká data (jaký typ dat) paket přenáší.
16 check Kontrolní součet Kontrolní součet vypočítaný POUZE z hlavičky IP paketu. Funkci pro výpočet kontrolního součtu si „půjčíme“ z RFC1071 dokumentu.
32 saddr IP adresa odesílatele.  
32 daddr IP adresa příjemce.  

Struktura popisující hlavičku IP protokolu (pouze povinné části) se jmenuje iphdr. Je deklarována v hlavičkovém souboru netinet/ip.h.

Hlavička UDP protokolu

Tabulka č. 446
Velikost (bity) Název atributu Název položky Popis
16 source Zdrojový port Číslo zdrojového UDP portu – UDP port odesílatele.
16 dest Cílový port Číslo UDP portu příjemce
16 len Velikost UDP datagramu Velikost (hlavička + data)
16 check Kontrolní součet Kontrolní součet je (měl by být) počítán z hlavičky i z dat. My ale bude dosazovat 0. Podle mě se nic nestane.

Název struktury s UDP hlavičkou je udphdr. Je deklarována v hlavičkovém souboru netinet/udp.h

Raw soket

V příštím dílu jako ukázku vytvoříme raw soket, pomocí kterého odešleme UDP datagram.

bitcoin_skoleni

sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);

V kombinaci parametrů SOCK_RAW a IPPROTO_RAW budeme muset vyplnit hlavičku IP paketu i UDP datagramu. V dalších dílech potom budeme pracovat s ICMP protokolem. Jako poslední parametr funkce socket budeme dosazovat IPPROTO_ICMP. V takovém případě nám OS sám vyplní hlavičku IP datagramu.

Potřebné deklarace pro MinGW – budeme je potřebovat v příštím dílu.

Seriál: Sokety a C/C++