Hlavní navigace

Sokety a C/C++: explicitní směrování

Radim Dostál

Dnes se podíváme na volitelnou položku IP hlavičky "explicitní směrování". Jedná se o možnost, jak zadat v IP hlavičce cestu, kterou má IP paket putovat. V článku je okomentovaný zdrojový text, v němž odešleme ECHO žádost, která bude mít v IP hlavičce nastavenou položku explicitní směrování.

Explicitní směrování je volitelná položka IP záhlaví, pomocí níž můžeme určit směrovače, kterými bude IP paket putovat. Již dříve jsme se setkali s buňkami na IP adresy ve volitelné části IP hlavičky. Například jsme v článku Sokety a C++ – zaznamenávání směrovačů obdrželi IP adresy směrovačů, kterými IP paket prošel. Odeslali jsme paket (IP paket nesoucí ICMP paket – žádost o ECHO), který měl buňky prázdné. V odpovědi byly buňky zaplněné.

Volba „explicitní směrování“ nám umožní zadat do každé buňky (maximálně jich bude 9) jednu IP adresu. IP paket od adresáta k příjemci poputuje právě přes počítače s danými IP adresami. A to i bez ohledu na fakt, že směrovače by, na základě hodnot ze směrovací tabulky, bez volby „explicitní směrování“ poslaly paket jinudy.

Volba „explicitní směrování“ nepřikazuje, aby IP paket šel pouze po zadaných IP adresách. Mezi určenými počítači může být libovolné množství jiných počítačů, přes které IP paket půjde.

Princip explicitního směrování

Jak se podaří „obejít“ nastavení směrovacích tabulek? Princip spočívá v tom, že IP paket vlastně nemá po cestě k cíli vyplněnou jako IP adresu cíl, ale postupně všechny IP adresy zadané ve volitelné části IP hlavičky typu „explicitní směrování“.

Představme si, že chceme IP paket poslat na počítač s IP adresou C a chceme, aby prošel počítači A a B. IP paket bude mít ve volitelné části IP hlavičky uloženy adresy A a B. Adresát bude C. V prvním kroku (ještě na počítači odesílatele – zajistí operační systém) se vymění adresa C za A a naopak. Tedy adresát bude A. Adresa C bude dočasně uchována ve volitelné části IP hlavičky. IP paket takto opustí počítač. Adresát IP paketu bude A. Tím zajistíme, že IP paket půjde na počítač A bez ohledu na to, že by třeba nebýt volby „explicitní směrování“ šel od odesílatele do C jinudy. IP paket dorazí do A, kde se opět přehodí A a C (IP paket se uvede do původního stavu) a poté se stejným způsobem přehodí B a C. Paket má tedy adresáta B, adresa C je dočasně umístěna ve volitelné části IP hlavičky. Díky tohoto nastavení IP paket putuje k B. A takhle se paket mění a postupně obchází všechny zadané IP adresy. Takhle lze „ošidit“ směrování. Volitelná část IP paketu obsahuje atribut „ukazatel“ (posunutí). Jedná se o třetí byte, který udává posunutí „aktuální“ buňky s IP adresou. Aktuální buňka je buňka, jejíž obsah bude při nejbližší příležitosti vyměněn za adresáta. Každý směrovač tedy kromě výměny adres, o které jsem se zmínil, také zvýší hodnotu ukazatele o 4. Díky toho každý směrovač pozná, se kterou adresou v seznamu má pracovat.

Formát „explicitní směrování“

Tabulka č. 494
Velikost Název makra udávajícího posunutí Popis
1 byte IPOPT_OPTVAL Udává typ volby. Volba „explicitní směrování“ má hodnotu 131 (makro IPOPT_LSRR).
1 byte IPOPT_OLEN Udává velikost volitelné části IP hlavičky.
1 byte IPOPT_OFFSET Ukazatel (posunutí) na aktuální buňku s IP adresou. Na začátku bude mít hodnotu 4.
? (4 * počet buněk)  – Jednotlivé čtyřbytové buňky s IP adresami.

Pokud tabulku srovnáte s formátem položky zaznamenávej směrovače, zjistíte, že jsou úplně stejné. Liší se jen v hodnotách.

Selhání explicitního směrování

Je zřejmé, že explicitní směrování znamená určitý bezpečnostní problém. Směrovače většinou IP paket obsahující tuto volbu zahodí (z důvodu bezpečnosti). Jestliže dojde k zahození IP paketu, měl by zahazující směrovač informovat odesílatele IP paketem typu 3 (nedoručitelný IP paket), kódu 5 (explicitní směrování selhalo). Někdy ale směrovač zahazující paket neodešle ani signalizační ICMP paket odesílateli.

Velice špatně se bude hledat síť, kde IP paket s nastavenou položkou „explicitní směrování“ skutečně dojde k cíli (nebude zahozen). Dnes je zakazování explicitního směrování naprosto běžné. Ale kdo hledá, ten najde.

ICMP paket typu 3, kódu 5

Jedná se o ICMP paket signalizující selhání explicitního směrování. Jeho formát je v podstatě stejný jako formát ICMP paketu typu 3, kódu 3, o kterém jsme již mluvili. První čtyři byty jsou typ, kód a kontrolní součet. Další čtyři byty jsou nuly. Poté následuje IP hlavička zahozeného paketu a za ní teoreticky maximálně prvních 64 bytů dat z těla zahozeného IP paketu.

Příklad

Příklad opět odešle ICMP paket žádost o ECHO a bude očekávat ECHO odpověď. ICMP paket bude vložen do IP paketu s volitelnou položkou IP záhlaví typu explicitní směrování. Program bude také přijímat ICMP paket typu 3, kódu 5 (explicitní směrování selhalo). Program bude jako parametry příkazové řádky očekávat seznam IP adres.

Formality

Vložíme hlavičkové soubory, začneme funkcí main, deklarujeme proměnné, ošetříme parametry příkazové řádky, získáme informace z DNS o posledním parametru a připravíme strukturu sockaddr_in pro odeslání dat.

#include <iostream>
#include <netinet/in.h>
#include <sys/socket.h>

#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netinet/ip.h>
#include <netinet/udp.h>

#include <netinet/ip_icmp.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define MAX 65536

using namespace std;

unsigned short checksum(unsigned char *addr, int count);

int main(int argc, char *argv[])
{
  socklen_t size;
  hostent *host = NULL;
  icmphdr icmp, *icmpRecv;
  iphdr *ipRecv;
  int sock, lenght;
  sockaddr_in sendSockAddr;
  fd_set mySet;
  timeval tv;
  unsigned short int pid = getpid();
  char buffer[MAX];
  char ipOptions[MAX_IPOPTLEN];
  bool recv = false;

  if ((argc > 11) || (argc < 3))
  {
    cerr << "Syntaxe:\n\t"
        << argv[0] << "seznam adres 2 - 10 adres"
        << endl;
    return -1;
  }
  if ((host = gethostbyname(argv[argc - 1])) == NULL)
  {
    cerr << "Spatna cílova adresa" << endl;
    return -1;
  }
  if ((sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
  {
    cerr << "Nelze vytvorit soket" << endl;
    return -1;
  }
  sendSockAddr.sin_family = AF_INET;
  sendSockAddr.sin_port = 0;
  memcpy(&(sendSockAddr.sin_addr),
    host->h_addr, host->h_length);

Vyplnění ICMP hlavičky

Vytvoříme hlavičku ICMP žádosti o ECHO.

  icmp.type = ICMP_ECHO;
  icmp.code = 0;
  icmp.un.echo.id = pid;
  icmp.checksum = 0;
  icmp.un.echo.sequence = 1;
  icmp.checksum =
    checksum((unsigned char *)&icmp, sizeof(icmphdr));

Volitelné položky IP záhlaví

Typ položky

Nejprve buffer vynulujeme a poté nastavíme první byt na hodnotu 131 (makro IPOPT_LSRR).

  memset(ipOptions, 0, MAX_IPOPTLEN);
  ipOptions[IPOPT_OPTVAL] = IPOPT_LSRR;

Velikost položky

Velikost položky jsou tři byty (úvodní část) + počet adres * 4 bytů. Počet adres je počet parametrů příkazové řádky – 1. Velikost se zapisuje na druhý byte.

  ipOptions[IPOPT_OLEN] = 3 + (argc - 1) * 4;

Posunutí k první adrese

Nastavíme posunutí k první adrese. Posunutí se zapisuje na třetí byte. Bude mít hodnotu 4.

  ipOptions[IPOPT_OFFSET] = IPOPT_MINOFF;

Vyplnění IP adres

Nyní postupně zjistíme informace z DNS o všech IP adresách, které byly předány jako parametr příkazové řádky. Budeme při tom měnit třetí byte našeho bufferu. Na závěr nesmíme zapomenout jej opět nastavit tak, aby ukazoval na první adresu.

  for (int i = 1; i < argc; i++,
    ipOptions[IPOPT_OFFSET] += 4)
  {
    if ((host = gethostbyname(argv[i])) == NULL)
    {
      cerr << "Spatna mezi adresa" << endl;
      close(sock);
      return -1;
    }
    memcpy((void *)&ipOptions[ipOptions[IPOPT_OFFSET] - 1],
        (void *)host->h_addr, host->h_length);
  }
  ipOptions[IPOPT_OFFSET]  = 4;

Nastavení volitelné části IP hlavičky.

Nastavíme náš buffer jako volitelnou část IP hlavičky.

  if (setsockopt(sock, SOL_IP, IP_OPTIONS,
    ipOptions, ipOptions[IPOPT_OLEN]) == -1)
  {
    cout << "Nelze nastavit explicitni smerovani" << endl;
    close(sock);
    return -1;
  }

Odeslání dat

  if ((sendto(sock, (char *)&icmp, sizeof(icmphdr), 0,
    (sockaddr *)&sendSockAddr, sizeof(sockaddr)) == -1))
  {
    cerr << "Problem s odeslanim dat" << endl;
    close(sock);
    return -1;
  }

Příjem dat

  do
  {
    FD_ZERO(&mySet);
    FD_SET(sock, &mySet);
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    if (select(sock + 1, &mySet, NULL, NULL, &tv) < 0)
    {
      cerr << "Selhal select" << endl;
      break;
    }
    if (FD_ISSET(sock, &mySet))
    {
      size = sizeof(sendSockAddr);
      if ((lenght = recvfrom(sock, buffer, MAX, 0,
        (sockaddr *)&sendSockAddr, &size)) == -1)
      {
            close(sock);
            return -1;
      }
      cout << "Prijato " << lenght << " bytu ze "
        << inet_ntoa((in_addr)sendSockAddr.sin_addr)
        << endl;

Nastavení ukazatelů

Přečetli jsme příchozí data, která jsou uložena v bufferu jménem buffer. V datech je IP hlavička, která může obsahovat volitelné části. Za IP hlavičkou je ICMP hlavička.

      ipRecv = (iphdr *)buffer;
      icmpRecv = (icmphdr *) (buffer + ipRecv->ihl * 4);

Kontrola velikosti bufferu

Nez začneme číst data, na která se odkazujeme přes nastavené ukazatele, měli bychom zkontrolovat, zda je buffer dostatečně velký – zda nebudeme něco číst z oblasti mimo příchozí buffer.

      if (ipRecv->ihl * 4 + sizeof(icmphdr)
        > (unsigned int)lenght)
      {
            continue;
      }

Odpověď na naši žádost

Zkontrolujeme, zda se nejedná o ECHO odpověď na naši ECHO žádost.

      if ((icmpRecv->type == ICMP_ECHOREPLY)
        && (icmpRecv->code == 0)
        && (icmpRecv->un.echo.id == pid)
        && (icmpRecv->un.echo.sequence == 1))
      {
          cout << "Jedna se o odpoved"
            << " na mou zadost (neuveritelne)"
            << endl;
          recv = true;
      }

ICMP paket typu 3, kódu 5

Zkontrolujeme, jestli náhodou nejsme informováni o zahození našeho ICMP paketu z důvodu selhání explicitního směrování. O zahození paketu z důvodu selhání explicitního směrování jsme informováni ICMP paketem typu 3 (makro ICMP_DEST_UNRE­ACH, kódu 5 (makro ICMP_SR_FAILED).

       if ((icmpRecv->type == ICMP_DEST_UNREACH)
        && (icmpRecv->code == ICMP_SR_FAILED))
      {

Kontrola těla ICMP paketu

ICMP paket typu 3, kódu 5 obsahuje v druhé polovině své hlavičky 4 byte nastavené na 0 a za ICMP hlavičkou následuje IP záhlaví + prvních maximálně 64 bytů zahozeného paketu. Nejprve zkontrolujeme, zda buffer je alespoň tak velký, abychom pomocí ukazatelů nečetli mimo přijatá data.

            if ((unsigned int)lenght <
                ipRecv->ihl * 4 + 2 * sizeof(icmphdr) +
                sizeof(iphdr) + ipOptions[IPOPT_OLEN])
            {

Nastavíme ukazatele na začátek zahozeného IP paketu. Hlavička zahozeného IP paketu začíná ihned za hlavičkou ICMP paketu typu 3, kódu 5. Za IP hlavičkou zahozeného paketu (může mít volitelné položky) by měla následovat námi vyplněná ICMP hlavička ECHO žádosti).

             ipRecv = (iphdr *)
                ((char *)icmpRecv + sizeof(icmphdr));
             icmpRecv = (icmphdr *)
                (((char *)ipRecv) + ipRecv->ihl * 4);

Zkontrolujeme, zda se opravdu jedná o naši ICMP žádost.

              if ( (icmpRecv->type == ICMP_ECHO)
                && (icmpRecv->code == 0)
                && (icmpRecv->un.echo.id == pid)
                && (icmpRecv->un.echo.sequence == 1))
              {
                     cout << "Je to ICMP paket signalizujici"
                         << " zahozeni naseho ECHO pozadavku"
                         << " - explicitni smerovani selhalo"
                         << endl;
                     recv = true;
              }
            }
      }

Konec programu

Ukončíme všechny závorky, uzavřeme soket a ukončíme program.

    else
    {
      cerr << "Nic neprislo" << endl;
      break;
    }
  } while (!recv);
  close(sock);
  return 0;
}

Příklady ke stažení

Tabulka č. 495
Soubor OS
lin29.tgz Linux
win29.zip MS WindowsŽ

Striktní směrování

Existuje ještě přísnější předpis na cestu, kterou má IP paket projít. Jedná se o volbu striktní směrování. Rozdíl je v tom, že mezi jednotlivými počítači, jejichž IP adresy zadáme do volitelné části IP hlavičky nesmí, být žádný další počítač. Princip je stejný jako u explicitního směrování (mám na mysli ony výměny IP adres), jen rozdíl je v tom, že u striktního směrování nesmí IP paket projít žádným jiným počítačem, než tím, který je právě nastaven v seznamu jako další. Takže počítač, jehož adresa je zadána jako další v pořadí, musí být vzdálen maximálně jeden „skok“ – musí být bezprostředním sousedem.

U striktního směrování je stejný formát jako u explicitního směrování. Jen typ není 131, ale 137 (makro IPOPT_SSRR).

Našli jste v článku chybu?

25. 9. 2003 14:56

Radim Dostál (neregistrovaný)

Implicitní směrování mělo sloužit pro ladění a diagnostiku sítě. Dá se zjistit, jestli existuje nějaká cesta, jaká je propustnost a podobně.
Dnes, kdy se skoro všude implicitní směrování zakazuje, již nemá moc velký význam.


23. 9. 2003 12:50

standus (neregistrovaný)

Ma to nejake prakticke vyuziti ???

Vitalia.cz: 7 druhů hotových těst na vánoční cukroví

7 druhů hotových těst na vánoční cukroví

Vitalia.cz: Taky věříte na pravidlo 5 sekund?

Taky věříte na pravidlo 5 sekund?

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č?

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

Přehledná titulka, průvodci, responzivita

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

Vitalia.cz: Proč vás každý zubař posílá na dentální hygienu

Proč vás každý zubař posílá na dentální hygienu

Podnikatel.cz: Zavře krám u #EET Malá pokladna a Teeta?

Zavře krám u #EET Malá pokladna a Teeta?

Lupa.cz: Teletext je „internetem hipsterů“

Teletext je „internetem hipsterů“

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Vitalia.cz: Spor o mortadelu: podle Lidlu falšovaná nebyla

Spor o mortadelu: podle Lidlu falšovaná nebyla

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

1. den EET? Problémy s pokladnami

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

120na80.cz: Na ucho teplý, nebo studený obklad?

Na ucho teplý, nebo studený obklad?

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

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

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

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu

DigiZone.cz: Rádio Šlágr má licenci pro digi vysílání

Rádio Šlágr má licenci pro digi vysílání