Hlavní navigace

Sokety a C/C++: přijímání ICMP paketů

Radim Dostál 21. 7. 2003

V minulém dílu jsme se seznámili se strukturou a formátem ICMP paketu. Dnes si uděláme první jednoduchý příklad používající ICMP protokol. Bude se jednat o program monitorující všechny ICMP pakety přicházející na počítač.

Každý program, který na počítači běží, může přijímat všechny ICMP pakety. Programu stačí, aby vytvořil soket typu SOCK_RAW používající protokol IPPROTO_ICMP (samozřejmě musí mít k tomu přístupová – administrátorská – práva).

Použití protokolu ICMP v programu

Už jsem vše nastínil v minulém dílu. Chceme-li odeslat data (což v dnešním dílu ještě dělat nebudeme), musí odesílací buffer obsahovat řádně vyplněnou hlavičku ICMP protokolu. Za ní následují data, jestliže nějaká jsou. Při odesílání paketu nevkládáme hlavičku IP do odesílaného bufferu. Když ICMP paket přijímáme, v přijímajícím bufferu je IP hlavička i ICMP hlavička. Za ní teprve následují data, jsou-li nějaká.

Malá poznámka k WinSock

Celý seriál používám (píšu pro něj příklady) prostředí DEV-CPP založené na překladači MinGW. S překladačem MinGW jsou dodávány hlavičkové soubory, ve kterých bohužel není vše, co by v nich být mělo. Již v článku Sokety a C/C++ – Raw soket jsem narazil na problém, že nebyla k dispozici struktura reprezentující IP a UDP hlavičky. S protokolem ICMP tomu není jinak. Stejně jako tenkrát jsem i nyní pro WinSock vytvořil hlavičkový soubor, který je ke stažení jako součást příkladu pro MS WindowsŽ na konci článku. 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.

Příklad – přijímač ICMP paketů

Příklad je vlastně velice jednoduchý. Vytvoříme soket typu SOCK_RAW používající protokol IPPROTO_ICMP. Poté v cyklu voláme funkcirecvfrom, která čte příchozí ICMP pakety. Z každého ICMP paketu získám nějaké ty informace a vypíšu je. Přijmu tolik ICMP paketů, kolik si uživatel přál.

Formality

Nejprve provedeme náležité formality. Vložíme potřebné hlavičkové soubory, začneme funkci main, deklarujeme potřebné proměnné a ošetříme parametry příkazové řádky.

#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>

#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <unistd.h>

#define BUFSIZE 1024

using namespace std;

int main(int argc, char *argv[])
{
  hostent *host;
  iphdr *ip;
  icmphdr *icmp;
  int sock;
  unsigned int count;
  sockaddr_in sender;
  char buffer[BUFSIZE];
  int bufferLenght;
  socklen_t lenght;
  char *stringIP, *unknown = "?";

  if (argc != 2)
  {
    printf
      ("Správná syntaxe:\n\t%s\tpočet sledovaných paketů\n",
      argv[0]);
    return -1;
  }
  count = atoi(argv[1]);

Vytvoření soketu

Vytvoříme soket typu raw (hodnota makra SOCK_RAW) s použitím protokolu ICMP (hodnota makra IPPROTO_ICMP).

  if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
  {
    perror("Inicializace soketů");
    return -1;
  }

Příjem ICMP paketů

Postupně v cyklu budeme přijímat ICMP pakety. V bufferu, který přijmeme, je obsažena hlavička IP protokolu, hlavička ICMP protokolu a data, která ICMP paket přenáší.

for(unsigned int p = 0; p < count; p++)
{
 lenght = sizeof(sockaddr_in);
 if ((bufferLenght =
    recvfrom(sock, buffer, BUFSIZE, 0, (sockaddr *)&sender, &lenght))
    == -1)
 {
   printf("Příjem dat: %u\n", 0);
   close(sock);
   return -1;
 }

Získáme potřebné informace z jednotlivých hlaviček

První v bufferu je hlavička IP protokolu. Ukazatel na strukturu s atributy IP hlavičky bude ukazovat na začátek bufferu. Hned za IP hlavičkou je ICMP hlavička. Velikost IP hlavičky zjistíme pomocí atributu ihl. Tím zjistíme také polohu ICMP hlavičky v bufferu. Poté připravíme textovou reprezentaci adresy odesílatele a doménové jméno odesílatele. Všechny informace vypíšeme na standardní výstup.

ip = (iphdr*)buffer;
icmp = (icmphdr *) (buffer + ip->ihl * 4);
stringIP = strdup(inet_ntoa(sender.sin_addr));
host = gethostbyaddr((char *)&sender.sin_addr, 4, AF_INET);
printf(
  "\nPřišel ICMP paket velikosti %d bytů\nOdesílatel: %s (%s)\n"
  "TTL: %d\nTyp ICMP: %d\nKód ICMP: %d\n",
  bufferLenght, stringIP, (host == NULL? unknown : host->h_name),
  (int)ip->ttl, icmp->type, icmp->code);
free(stringIP);

Rozdělení podle typu paketu

Jestliže byl doručen nějaký typ paketu, kterého si chceme podrobněji všímat, vypíšeme o něm více informaci. Informace jsou obsaženy v proměnné části hlavičky ICMP paketu. Pomocí soketového API je celá věc implementována prostřednictvím unie jazyka C. Unie ve struktuře icmphdr se jmenuje un. Některé ICMP pakety obsahují za ICMP hlavičkou nějaká data. O těch si ale povíme až příště.

switch (icmp->type)
{
  /* Tady podle jednotlivých typů vypisuji
    podrobné informace. Viz příklad ke stažení.*/
}

Konec programu

Zavřeme soket a ukončíme program.

  }
  close(sock);
  return 0;
}

Příklady ke stažení

Tabulka č. 457
Soubor OS
lin20.tgz Linux
win20.zip MS WindowsŽ

Příklady ke stažení lze podle potřeby samozřejmě upravit. Nerozeznají mnoho typů ICMP paketů a také by mohly vypisovat více informací.

Několikrát jsem se zmínil, že protokol ICMP může přenášet také nějaká data (nejen svou hlavičku s informacemi). Nejedná se ale o nějaká data protokolu vyšší vrstvy. Jedná se o data, která nějakým způsobem doplňují informace v hlavičce ICMP paketu. Na tuto situaci se podíváme příště.

Našli jste v článku chybu?

28. 7. 2003 9:02

Radim Dostál (neregistrovaný)

Děkuji za upozornění. Od příštího dílu už nebudu zaměňovat AF_INET (Address Family) s PF_INET (Protocol Family).

23. 7. 2003 17:15

bln (neregistrovaný)

jen pro upresneni socket se ve vasem pripade vola jako socket(PF_INET.... ne AF_INET, vim ze je to zatim jedno protoze obsahuji stejnou hodnotu, ale pak se to vsichni nauci blbe a jednou se budou divit...

Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Podnikatel.cz: Vládu obejde, kvůli EET rovnou do sněmovny

Vládu obejde, kvůli EET rovnou do sněmovny

DigiZone.cz: Česká televize mění schéma ČT :D

Česká televize mění schéma ČT :D

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Root.cz: Vypadl Google a rozbilo se toho hodně

Vypadl Google a rozbilo se toho hodně

Lupa.cz: Babiš: E-shopů se EET možná nebude týkat

Babiš: E-shopů se EET možná nebude týkat

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Lupa.cz: Není sleva jako sleva. Jak obchodům nenaletět?

Není sleva jako sleva. Jak obchodům nenaletět?

Podnikatel.cz: 1. den EET? Problémy s pokladnami

1. den EET? Problémy s pokladnami

Podnikatel.cz: Na poslední chvíli šokuje vyjímkami v EET

Na poslední chvíli šokuje vyjímkami v EET

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

DigiZone.cz: NG natáčí v Praze seriál o Einsteinovi

NG natáčí v Praze seriál o Einsteinovi

Vitalia.cz: Baletky propagují zdravotní superpostel

Baletky propagují zdravotní superpostel

Podnikatel.cz: EET zvládneme, budou horší zákony

EET zvládneme, budou horší zákony

DigiZone.cz: ČT má dalšího zástupce v EBU

ČT má dalšího zástupce v EBU

Lupa.cz: Avast po spojení s AVG propustí 700 lidí

Avast po spojení s AVG propustí 700 lidí

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu

Podnikatel.cz: Víme první výsledky doby odezvy #EET

Víme první výsledky doby odezvy #EET

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET