Internet Info, s.r.o. Lupa Root Měšec Podnikatel DigiZone Slunečnice Vitalianew Bomba Navrcholu Weblogy Jagg Woko Dobrý web Computer.cz SK: MojeLinky

Hlavní navigace

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

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).

Školení: Úvod do XML schémat

Akademie Root
  • Jiří Kosek vás seznámí se základy schémového jazyka W3C XML Schema!
  • Bez základní znalosti XML schémat se dnes neobejde žádný vývojář moderních aplikací!
  • Porozumíte hotovým schématům, pochopíte jakou strukturu XML připouštějí a a naučíte se provádět ve schématech dílčí úpravy.

Detailní informace o kurzu...

Ohodnoťte jako ve škole: 12345
Průměrná známka 3,07

Přehled názorů

K cemu je to v praxy dobre ??
standus 23. 9. 2003 12:50
└ 
Re: K cemu je to v praxy dobre ??
Radim Dostál 25. 9. 2003 14:56
       

Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.

Zasílat nově přidané příspěvky e-mailem