Packet capturing s libpcap (1)

Packet capturing s libpcap (1)
05.07.2009 17:45 | Články | Oliver Kindernay
Zložité infranštruktúry dnešných sietí jednoznačne potrebujú nástroje na analýzu sieťovej prevádzky a diagnostiku problémov. Tieto nástroje, ktoré zachytávajú a analyzujú sieťovú komunikáciu sa označujú ako sniffery, network monitory, packet analyzery. V tomto miniseriáli sa vás pokúsim zoznámiť s knižnicou priamo určenou na programovanie snifferov - libpcap. Seriál si nekladie za cieľ poskytnúť vyčerpávajúci manuál, ale snaží sa zrozumiteľnou formou vysvetliť prácu s touto knižnicou.

Toto v žiadnom prípade nie sú všetky možnosti ktoré libpcap poskytuje. Dalšie skúmanie je len na vás, odkazujem vás hlavne na zdrojové kódy nástroja tcpdump.

libpcap

libpcap, ako už bolo spomenuté, nám umožnuje jednoducho a efektívne vo vlastných programoch zachytávať sieťovú komunikáciu. Veľa známych programov je na tejto knižnici postavených, spomeniem napr. tcpdump, wireshark, snort. Ak ho ešte v systéme nainštalovaný nemáte (máte zlý systém ;)), stiahnuť si ho môžete z www.tcpdump.org alebo použite balíčkovací systém vašej distribúcie.

Bližší pohľad na packet capturing

Packet capturing(sniffing), teda zachytávanie packetov, je činnosť, pri ktorej zachytávame a zbierame dáta ktoré cestujú po sieti. Od príchodu dát k sieťovej karte po odovzdanie dát do uživatelského priestoru - to prebieha približne takto: sieťová karta obdrží packet, skontroluje, či je adresovaný jej a ak hej vyvolá IRQ (interrupt request - spôsob, akým komunikuje hardware so softwarom). IRQ dalej obslúži príslušný ovládač karty ktorý spracuje dáta a uloží ich do zásobníka nazývaného "networking stack". Odtiaľ už je spracovávaný jednotlivými aplikáciami, pre ktoré sú dáta určené (v skutočnosti je to zložitejšie ale pre naše potreby to stačí takto). Pri packet capturingu, to prebieha rovnako, až na to, že dáta nie sú len uložené v "networking stacku", ale putujú aj do "packet filtru" - časti kernelu, ktorá umožnuje packet capturing. Kdežto aplikácia obrdží väčšinou dáta v už spracovanej forme, z packet filtru budeme dáta ťahať nespracované, raw. To znamená, že každú vrstvu si musíme spracovať sami. Pre lepšiu predstavu si to môžeme nakresliť do grafu:

graf

Promiskuitný mód

Promiskuitný mód je vlastnosť sieťovej karty, ktorá nám umožnuje zachytávať a sledovať aj tie packety, ktoré niesú určené pre ňu samotnú.

Struktúra aplikácie

Pri každej aplikácií využívajúcej libpcap musíme splniť niekoľko krokov, ktoré sú popísané nižšie.
  • Získať názov rozhrania, na ktorom budeme sniffovať.
  • Inciliazovať pcap. Predáme mu názov rozhrania, môžme špecifikovať aj iné možnosti ako promiskutný mód karty atd. Pcap vytvorí tzv. sniffing session, s ktorou budeme následne manipulovať pomocou "popisovačov" (descriptorov).
  • Zvoliť a aplikovať filter. Krok pozostáva z troch častí, vytvoriť filter, "skompilovať" ho a aplikovať na popisovač. Skompilovať sa tu nemyslí ako preklad kódu do strojových inštrukcií ako tradične, jedná sa terminológiu libpcap. Tento bod je samozrejme nepovinný, filter používať nemusíme.
  • Začať samotný sniffing. Opäť môžeme špecifikovať parametre ako napr. počet zachytených packetov.
  • Zavrieť session.

Základné operácie

Poznámka: Všetky konštanty, funkcie, datové typy sú definované v hlavičkovom súbore pcap.h

Získanie názvu sieťového rozrhania

Prvým krokom teda je získať nejaké sieťové rozhranie, nad ktorým budeme operovať. Môžeme požiadať užívateľa, aby ho zadal sám, ale väčšinou budeme používať tzv. "default device". To, ktoré to bude závisí od operačného systému. Na GNU/Linux je to väčšinou eth0. Získať názov defaultného rozhrania nám pomôže funkcia 'pcap_lookupdev'. Jej prototyp je nasledujúci
char *pcap_lookupdev(char *errbuf)
Vracia názov defaultného rozhrania a ako argument príjma ukazateľ na pole char o veľkosti PCAP_ERRBUF_SIZE, do ktorého následne uloží chybovú správu (ak nastala chyba). V prípade chyby vracia NULL. Poznámka: Tzv. errbuf príjmajú ako argument mnohé funkcie.

Program, ktorý len získa a vypíše názov defaultného rozhrania by teda vyzeral takto:
	#include <pcap.h>
	#include <stdio.h>
	#include <unistd.h>

	int main(int argc, char **argv) {
		
		char errbuff[PCAP_ERRBUF_SIZE];
		char *device;

		device = pcap_lookupdev(errbuff);
		if(device == NULL) {
			fprintf(stderr, "%s\n", errbuff);
			_exit(-1);
		}
		printf("Default device: %s\n", device);
		return 0;
	}
	
Treba pripomenúť, že treba prilinkovať libpcap. Pri gcc len zadáte parameter -lpcap. Samotný pcap obsahuje viac funkcií pre tento účel. Napr 'pcap_findalldevs'. Cieľom článku ale nie je zachádzať až do podrobností. Ak vás to zaujme, všetky potrebné informácie nájdete na manuálovej stránke pcap a v zdrojových kódoch tcpdumpu.

Poznámka: Tento, aj všetky programy uvedené neskôr je potrebné spúštať z právami užívateľa root.

Otvorenie sniffing session

Tento bod je prekvapivo jednoduchý. Stačí zavolať funkciu pcap_open_live ktorá má nasledujúci prototyp:
 pcap_t *pcap_open_live(const char *device, int snaplen,
               int promisc, int to_ms, char *errbuf)
	
Prvý parameter je jasný - rozhranie. Druhý udáva maximálnu veľkosť zachytených dát v bytoch. Nízka hodnota môže byť užitočná hlavne ak nemáme záujem o dáta ktoré packet nesie ale len o hlavičky protokolov. Naopak, ak nechceme prísť ani o jediný byt, je dobré nastaviť hodnotu na 65535 čo je bežná hodnota MTU vo väčšine sietí. Tretí argument ako už jeho názov vypovedá určuje, či bude karta v promiskuitnom móde, alebo nie. Ak je hodnota true (akákoľvek nenulová) bude rozhranie uvedené do promiskuitného módu. Stvrtý paramter udáva timeout v milisekundách. Timeout určuje, koľko bude kernel čakať kým nám poskytne nejaké dáta. Medzitým ich samozrejme bude hromadiť. Kvôli platformovým rozdielom je lepšie túto hodnotu nenastavovať na nulu. Môžeme sa napr. inšpirovať tcpdumpom a nastaviť ju na 1000. Posledný argument je, nám známy, errbuf. Funkcia vracia pri úspechu ukazateľ na popisovač, ktorý je typu pcap_t. Opäť libpcap poskytuje viacero možností ale základ je najdôležitejší a ten je táto funkcia.

Zachytávanie packetov

Zatiaľ čo pri veľmi jednododuchej aplikácií, akú si napíšeme nakonci si vystačíme s funkciou 'pcap_next', pri zložitejších už je oveľa vhodnejšia funkcia 'pcap_loop'. 'pcap_loop' si predstavíme v druhej časti teraz je čas na 'pcap_next'. Má nasledujúci prototyp:
	const u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h);
	
Funkcia zachytí len jeden packet a následne na dáta v ňom vráti ukazateľ typu const u_char. Ako prvý argument príjma získaný popisovač. Druhý argument je ukazateľ na štruktúru 'pcap_pkthdr', ktorá vyzerá nasledujúco:
	struct pcap_pkthdr {
        	struct timeval ts;      /* time stamp */
       		bpf_u_int32 caplen;     /* length of portion present */
       		bpf_u_int32 len;        /* length this packet (off wire) */
	};
	
Položka 'ts' obsahuje časový údaj o tom, kedy bol packet zachytený, 'caplen' udáva počet bytov, ktoré sú nám zneho k dispozícií (max je druhý parameter 'pcap_open_live'), tretí udáva skutočnú veľkosť packetu. Kedže analýzou zachytených dát sa budeme zaoberať až v druhej časti, nebudem to tu teraz rozoberať. Za zmienku stojí, že 'pcap_next' len volá 'pcap_dispatch'. Už bolo uvádzané, že takto do detailu zachádzať nechcem, možno niečo pridám do druhej časti.

Trochu praxe

Doteraz to boli len teoretické "žvásty", je čas niečo naprogramovať. Napíšeme si jednoduchý program, ktorý zachytí jeden packet a zobrazí o ňom informácie. Môžeme síce dať pcap_next do cyklu a sniffovat donekonečna ale tento spôsob je vrcholne nespoľahlivý a je lepšie použiť funkciu 'pcap_loop', ktorú si predstavíme nabudúce. Ku kódu niet čo vysvetlovať, hádam len že funkcia 'pcap_close' (neočakávane) zatvára popisovač.
#include <stdio.h>
#include <unistd.h>
#include <pcap.h>

#define SNAPLEN 65535

int main(int argc, char **argv) {

        char errbuff[PCAP_ERRBUF_SIZE];
        char *device;
        const u_char *packet;   //zatial nepotrebne
        pcap_t *sd;
        struct pcap_pkthdr packet_header;

        device = pcap_lookupdev(errbuff);
        if(device == NULL) {
                fprintf(stderr, "%s\n", errbuff);
                _exit(1);
        }

        sd = pcap_open_live(device, SNAPLEN, 0, 1000, errbuff);
        if(sd == NULL) {
                fprintf(stderr, "%s\n", errbuff);
                _exit(1);
        }

        packet = pcap_next(sd, &packet_header);

        printf("Packet!\nTime: %sLength: %d bytes\n", ctime((const time_t *)&(packet_header.ts.tv_sec)), packet_header.len);
	pcap_close(sd);
        return 0;
}

	

Záver

Čo bude ďalej

Ked teraz viete, čo sa dá spraviť s tromi funkciami, zaiste ste plný očakávaní na druhý diel. Aby som mierne uspokojil vašu zvedavosť, nakukneme do neho. Ako prvé si vysvetlíme, ako filtrovať zachytenú komunikáciu. Potom sa vrhneme na už spomínanú 'pcap_loop' a naučíme sa analyzovať zachytené dáta. Výsledkom bude už trochu pokročilejší sniffer, ktorý bude prezentovať všetko, čo sa (dúfam) naučíme.

Úplný záver

Možno vás obsah článku uspokojil, možno nie. Snažil som splniť očakávanie a vedzte, že tento miniseriál nieje príručkou k libpcap a nemá zmysel aby sme si vysvetľovali metódy a postupy, ktoré nikdy nepoužijete. Postupy popísané v tomto a nasledujúcom článku vám budú bohato stačiť na plnohodnotnú prácu s libpcap.
    • kontrolna otazka 06.07.2009 | 20:13
      martin   Návštevník
      pekny clanok ;-)

      kontrolna otazka: nie je potrebne mat na prepnutie karty do promiskuitneho modu podporu v ovladaci ?
      minimalne u wifi kariet to je potrebne ...

      dakujem za ozrejmenie ;-)
      • Re: kontrolna otazka 06.07.2009 | 20:49
        Avatar Oliver Kindernay Slackware  Používateľ
        No kedže každá komunikácia s hocijakou kartou prebieha prostredníctvom jej ovládača povedal by som, že treba. 100 % vám to ale povedať neviem :).
      • Re: kontrolna otazka 07.07.2009 | 12:39
        jm   Návštevník
        uprimne povedane nepoznam sietovku ktora "nepodporuje" promiskuitny mod. a vzhladom k tomu ze na fungovanie akejkolvek karty je potrebny modul (resp. na spristupnenie jej funkcii OS) tak rozhodne ta podpora byt musi.

        A pri wifi kartach nejde o prepnutie do promiskuitneho modu (to je eth vrstva) ale o prepnutie do monitor modu (co je skor radio vrstva).
        • Re: kontrolna otazka 08.07.2009 | 13:56
          kormidelnik   Návštevník
          eth vrstva, radio vrstva :)

          http://sk.wikipedia.org/wiki/OSI_model
          • Re: kontrolna otazka 08.07.2009 | 16:34
            jm   Návštevník
            poznam osi model. ako by si inak laicky vysvetlil rozdiel medzi promiskuitnym modom sietovej karty a monitor modom wifi karty?