CNS/MiNiTHiNS - La version la plus récente de ce document devrait être trouvée sur http://www.minithins.net/ AUTEUR: cns at minithins.net ************************************************************************** TOUTE DISTRIBUTION HORS DU SITE DE MINITHINS (http://www.minithins.net/) EST PROHIBEE SANS L'ACCORD DES AUTEURS DU TEXTE. TOUTE REPRODUCTION ENTIERE OU PARTIELLE SANS L'ACCORD DES AUTEURS DU TEXTE EST FORMELLEMENT INTERDITE. TOUTE UTILISATION FRAUDULEUSE DE CE TEXTE EST FORMELLEMENT INDERDITE AUSSI ************************************************************************** Manuel de référence pour la libpcap // PARTIE 1 pour: * découvrir libpcap * découvrir l'utilitée de libpcap et ses fonctions * découvrir comment utiliser libpcap I) Introduction A une époque ou les réseaux et les dispositifs sont en pleines croissance dues aux avancées technologiques et au développement des différents systèmes d'exploitation, de nouveaux outils font leur apparition pour simplifier le travail des codeurs. L'un d'eux est la libpcap, librairie de capture de paquets, développée par les "Lawrence Berkeley National Laboratories". A l'origine, libpcap fut son apparition lors du développement de tcpdump, qui alors avait besoin d'un certain nombre de fonctions pour la capture des paquets. Libpcap permet la récupération des paquets en utilisant les expressions BPF (Berkeley Packet Filter) au niveau utilisateur, ce qui permet un portage plus aisé entre les différents noyaux (linux, *bsd, ...). Les fonctions de libpcap peuvent se regrouper dans 4 groupes distincts, qui seront aussi généralement appelées dans cet ordre: * Initialisation du réseau: sélection du/des périphériques à filtrer, ouverture des descripteurs * Création des filtres BPF: filtrage des paquets que l'on veut visualiser ou non, en précisant le filtre bpf ou une chaine de caractères qui sera compilé en filtre bpf. * Récupération des paquets filtrés (soit dans une boucle, soit dans une fonction callback qui sera appelée à réception de chaque paquets). * Fermeture des descripteurs, gestion des erreurs et autres Ce document va commenter les différentes fonctions avec de nombreux exemples et ensuite de montrer différentes utilisations de la libpcap dans des cas pratiques. II) Libpcap * Installation de la libpcap Vous pouvez trouver la version la plus récente de la libpcap, (0.6.2) sur le site http://www.tcpdump.org/; Toutefois, ne prennez la -current que si vous voulez développer dessus, car il s'agit de la version de développement et est parfois buggée (quand vous arriverez à la compiler ;). Tout les exemples si dessous utiliseront la libpcap 0.6.2 sur un noyau linux 2.2.19. Toutefois, les tests seront aussi effectués sur un FreeBSD 4.3-STABLE, et les différences constatées seront décrites. mycroft@kiev /tmp> md5sum libpcap-0.6.2.tar.gz a6325b5fe429eba06294ce2db9263a66 libpcap-0.6.2.tar.gz /* utilisateurs BSD: la commande est surement "md5" */ mycroft@kiev /tmp> tar zxf libpcap-0.6.2.tar.gz mycroft@kiev /tmp> ./configure --prefix=/usr [ ... ] mycroft@kiev /tmp> make [ ... ] mycroft@kiev /tmp> su Passwork: root@kiev /tmp> make install [ ... ] root@kiev /tmp> exit mycroft@kiev /tmp> La librairie sera installée: mycroft@kiev /tmp/libpcap-0.6.2> ls -l /usr/lib/libpcap.a -rw-r--r-- 1 root root 124126 Jul 7 19:10 /usr/lib/libpcap.a * Utilisation de libpcap dans un programme Il y a 2 actions importantes et obligatoire pour l'utilisation de la libpcap dans un programme écrit en C : 1) L'inclusion de l'entête pcap.h: #include 2) La compilation par gcc en incluant "-lpcap" $ gcc -o monprogramme monprogramme.c -lpcap $ * Les nouveaux types de variables utilisés par la libpcap (présents dans pcap.h) typedef struct pcap pcap_t; typedef struct pcap_dumper pcap_dumper_t; pcap_t et pcap_dumper_t sont 2 types de descripteurs utilisés par la plupart des fonctions (pour pcap_t) et par pcap_dump_open et pcap_dump_close pour pcap_dumper_t. Ceux ci seront utilisés pour identifier quel filtre on utilise (interface, filtre bpf ...) typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, const u_char *); pcap_handler sera le pointeur vers une fonction qui prendra pour arguments un pointeur vers une chaine de caractères non signés, un pointeur vers une structure pcap_pkthdr et une autre chaine de caractères non signées. Cette fonction ne renverra rien (type void). Les pcap_handlers seront principalement utilisés par pcap_dispatch et pcap_loop qui appelleront cette fonction dès qu'un paquet sera recu. struct pcap_pkthdr { struct timeval ts; /* date de réception du paquet */ bpf_u_int32 caplen; /* taille du paquet capturé */ bpf_u_int32 len; /* taille du paquet sur le réseau */ }; pcap_pkthdr sera utilisé par les fonctions de récupération des paquets pour préciser l'heure et la taille à la réception. Utilisé par pcap_dump et pcap_next (et aussi par les fonctions de callback, voir pcap_handler) struct pcap_stat { u_int ps_recv; /* nombre de paquets recus */ u_int ps_drop; /* nombre de paquets droppés par le noyau */ u_int ps_ifdrop; /* droppés par l'interface XXX (non supporté) */ }; pcap_stat est une structure de statisques remplie à l'appel de pcap_stats(), après un 1er appel de celui si (c'est à dire à partir du second appel, le 1er servant généralement à initialiser la structure.). char errbuf[PCAP_ERRBUF_SIZE]; PCAP_ERRBUF_SIZE est la taille en caractères des erreurs retournées. Vous devriez utiliser cette variable pour vos buffers de récupération d'erreurs. * Les fonctions de la libpcap ** Les fonctions d'initialisations du réseau ------[ char *pcap_lookupdev(char *errbuf) pcap_lookupdev() retourne un pointeur vers une chaine de caractères contenant le périphérique réseau utilisable ensuite par pcap_open_live() ou pcap_lookupnet(). Si une erreur est détectée, NULL est retourné et errbuf (le premier argument) est remplis par le message d'erreur associé. -CODE-[ test-pcap_lookupdev.c #include #include int main(void) { u_char *device; char errbuf[PCAP_ERRBUF_SIZE]; device = pcap_lookupdev(errbuf); if (device == NULL) { printf("Une erreur a été détectée: %s\n", errbuf); } else { printf("Le périphérique %s a été trouvé.\n", device); } return 0; } -CODE-[ fin test-pcap_lookupdev.c -RES--[ test-pcap_lookupdev.c mycroft@kiev ~/dev/docs/pcap/sources> gcc -O2 -Wall -o test-pcap_lookupdev \ > test-pcap_lookupdev.c -lpcap mycroft@kiev ~/dev/docs/pcap/sources> ./test-pcap_lookupdev Le périphérique eth0 a été trouvé. mycroft@kiev ~/dev/docs/pcap/sources> su Password: root@kiev /home/mycroft/dev/docs/pcap/sources> ifconfig eth0 down root@kiev /home/mycroft/dev/docs/pcap/sources> ./test-pcap_lookupdev Une erreur a été détectée: no suitable device found root@kiev /home/mycroft/dev/docs/pcap/sources> -RES--[ fin test-pcap_lookupdev.c Il est à noter que lo (lo0 sur les systèmes BSD), l'interface loopback n'est pas utilisé par libpcap, même si l'on peut l'utiliser en la précisant: u_char *device; device = (u_char*)malloc(16 * sizeof(u_char)); strcpy(device, "lo"); (Et bien entendu, on ne doit pas appeler pcap_lookupdev sinon le buffer est tout simplement perdu). ****************************************************************************** ------[ int pcap_lookupnet(char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf) pcap_lookupnet() est utilisé pour déterminer à partir du périphérique réseau le réseau sur lequel il est et son masque. netp et maskp sont des pointeurs de bpf_u_int32. Si -1 est renvoyé, alors errbuf sera remplis par le message d'erreur adéquat, sinon 0 est renvoyé. pcap_lookupnet() is used to determine the network number and mask associated with the network device device. Both netp and maskp are bpf_u_int32 pointers. A return of -1 indicates an error in which case errbuf is filled in with an appropriate error message. -CODE-[ test-pcap_lookupnet.c #include #include #include int main(void) { u_char *device; char errbuf[PCAP_ERRBUF_SIZE]; bpf_u_int32 netp, maskp; struct in_addr in; device = pcap_lookupdev(errbuf); if (device == NULL) { printf("Une erreur a été détectée: %s\n", errbuf); } else { printf("Le périphérique %s a été trouvé.\n", device); } if (pcap_lookupnet(device, &netp, &maskp, errbuf) == -1) { printf("Une erreur a été détectée: %s\n", errbuf); } else { printf("Périphérique: %s\n", device); in.s_addr = netp; printf("Réseau: %s\n", inet_ntoa(in)); in.s_addr = maskp; printf("Masque: %s\n", inet_ntoa(in)); } return 0; } -CODE-[ fin test-pcap_lookupnet.c -RES--[ test-pcap_lookupnet.c root@kiev /home/mycroft/dev/docs/pcap/sources> gcc -O2 -Wall \ -o test-pcap_lookupnet test-pcap_lookupnet.c -lpcap root@kiev /home/mycroft/dev/docs/pcap/sources> ./test-pcap_lookupnet Le périphérique eth0 a été trouvé. Périphérique: eth0 Réseau: 192.168.0.0 Masque: 255.255.255.224 root@kiev /home/mycroft/dev/docs/pcap/sources> -RES--[ fin test-pcap_lookupnet.c ************************************************************************** ** Les fonctions de préparation des descripteurs ------[ pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) Afin de lire un périphérique avec tel ou tel expression de filtrage BPF, il va être nécessaire d'ouvrir un flux de données pour préciser où lire. Généralement, on va vouloir lire directement l'interface réseau (eth0, lo ... pour les utilisateurs linux, (xl0, lo0 .. pour ceux de BSD). pcap_open_live permet d'ouvrir un de ces périphériques (tous si "any" est transmis via char *device) afin de consulter ce qu'il s'y passe par la suite. pcap_open_live est alors utilisé pour obtenir un descripteur de capture de paquets (de type pcap_t) afin de visualiser les paquets sur le réseau. device est une chaine de caractères comprenant le périphérique à ouvrir (souvent on peut utiliser le résultat de pcap_lookup device). Sur les noyaux 2.2 et suivants de linux, on peut aussi transmettre "all" ou NULL afin de capturer les paquets de toutes les interfaces. snaplen est un entier comprenant la taille maximale en octets des paquets à capturer. promisc spécifie si l'interface doit être en mode promiscuous ou non. (Malgré cela, il est possible que l'interface soit tout de même en promisc. si 0 est transmis. Il faut aussi noter que cela ne marche pas si l'on transmet "any" ou NULL comme device, et donc cela sera ignoré. to_ms spécifie la période d'attente de lecture d'un paquet. ebuf est utilisé pour transmettre les messages d'erreurs ou les avertissements, et quand pcap_open_live retournera NULL à la suite d'une erreur. Malgré cela, pcap_open_live peut aussi fonctionner et un texte peut être transmis dans ebuf comme tel ou tel avertissement. -CODE-[ test-pcap_open_live.c #include #include #include #include int main(void) { u_char *device; pcap_t *desc; char errbuf[PCAP_ERRBUF_SIZE]; /* remise à blanc de errbuf */ bzero(errbuf, PCAP_ERRBUF_SIZE); device = pcap_lookupdev(errbuf); if (device == NULL) { printf("Une erreur a été détectée: %s\n", errbuf); } else { printf("Le périphérique %s a été trouvé.\n", device); } if ((desc = pcap_open_live(device, 1512, 1, 1000, errbuf)) == NULL) { printf("Erreur fatale, pcap_open_live vient d'échouer:\n%s\n", errbuf); exit(1); } printf("descripteur ouvert avec succès ...\n"); if (strlen(errbuf)) printf("Message: %s\n", errbuf); return 0; } -CODE-[ fin test-pcap_open_live.c -RES--[ test-pcap_open_live.c root@kiev /home/mycroft/dev/docs/pcap/sources> gcc -O2 -Wall \ -o test-pcap_lookupdev test-pcap_lookupdev.c -lpcap root@kiev /home/mycroft/dev/docs/pcap/sources> ./test-pcap_lookupdev Le périphérique eth0 a été trouvé. descripteur ouvert avec succès ... root@kiev /home/mycroft/dev/docs/pcap/sources> ifconfig eth0 down root@kiev /home/mycroft/dev/docs/pcap/sources> ./test-pcap_lookupdev Une erreur a été détectée: no suitable device found descripteur ouvert avec succès ... Message: no suitable device found root@kiev /home/mycroft/dev/docs/pcap/sources> -RES--[ fin test-pcap_open_live.c ************************************************************************** ------[ pcap_t *pcap_open_offline(char *fname, char *ebuf) pcap_open_offline permet d'ouvrir un fichier contenant des données compatibles libpcap. Ceux ci sont compatibles avec tcpdump ou tcpslice, et de nombreux sniffeurs utilisant libpcap (snort, CNSsniff ...). Des données peuvent être écrites dans cette sorte de fichier via la fonction pcap_dump. Après l'ouverture, on utilise de la même facon le descripteur attribué par pcap_open_offline que celui utilisé par pcap_open_offline. ************************************************************************** ------[ pcap_t *pcap_open_dead(int linktype, int snaplen) pcap_open_dead est utilisé pour initialiser un descripteur pcap pour utiliser d'autres fonctions de libpcap, comme pour compiler du code BPF. linktype sera un entier avec pour valeur le type de lien (couche 2 du schéma OSI). On trouve: static struct linktype_map { int dlt; int linktype; int len; /* len == 255 si inconnu */ } map[] = { /* * These DLT_* codes have LINKTYPE_* codes with values identical * to the values of the corresponding DLT_* code. */ { DLT_NULL, LINKTYPE_NULL, 4 }, /* also for 100Mb and up */ { DLT_EN10MB, LINKTYPE_ETHERNET, 14 }, /* 3Mb experimental Ethernet */ { DLT_EN3MB, LINKTYPE_EXP_ETHERNET, 14 }, { DLT_AX25, LINKTYPE_AX25, 255 }, { DLT_PRONET, LINKTYPE_PRONET, 255 }, { DLT_CHAOS, LINKTYPE_CHAOS, 255 }, /* DLT_IEEE802 is used for Token Ring */ { DLT_IEEE802, LINKTYPE_TOKEN_RING, 255 }, { DLT_ARCNET, LINKTYPE_ARCNET, 255 }, { DLT_SLIP, LINKTYPE_SLIP, 16 }, { DLT_PPP, LINKTYPE_PPP, 4 }, { DLT_FDDI, LINKTYPE_FDDI, 21 }, /* LLC/SNAP-encapsulated ATM */ { DLT_ATM_RFC1483, LINKTYPE_ATM_RFC1483, 255 }, /* Raw IP */ { DLT_RAW, LINKTYPE_RAW, 16 }, /* BSD/OS SLIP BPF header */ { DLT_SLIP_BSDOS, LINKTYPE_SLIP_BSDOS, 4 }, /* BSD/OS PPP BPF header */ { DLT_PPP_BSDOS, LINKTYPE_PPP_BSDOS, 255 }, /* BSD/OS Cisco HDLC * { DLT_C_HDLC, LINKTYPE_C_HDLC, 255 }, /* Linux ATM Classical IP */ { DLT_ATM_CLIP, LINKTYPE_ATM_CLIP, 255 }, /* NetBSD sync/async serial PPP (or Cisco HDLC) */ { DLT_PPP_SERIAL, LINKTYPE_PPP_HDLC, 255 }, /* IEEE 802.11 wireless */ { DLT_IEEE802_11, LINKTYPE_IEEE802_11, 255 }, /* OpenBSD loopback */ { DLT_LOOP, LINKTYPE_LOOP, 255 }, /* Linux cooked socket capture */ { DLT_LINUX_SLL, LINKTYPE_LINUX_SLL, 255 }, { -1, -1, 255 } }; snaplen est la taille de chaque paquets capturés. ************************************************************************** ------[ void pcap_close(pcap_t *p) pcap_close permet de fermer une connection ouverte par pcap_open_live, pcap_open_dead ou encore pcap_open_offline. Il s'occupera de libérer les ressources matérielles dues à l'ouverture du descripteur. pcap_close prend comme argument le descripteur pcap_t que l'on veut fermer et ne renvoit rien. ************************************************************************** ------[ pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname) pcap_dump_open ouvre un descripteur de dump, qui permet d'ouvrir un fichier pour y entrer des données (écriture). Cela permet de créer un fichier de données libpcap compatible avec tcpdump, snort ou tout autre analyseur/lecteur de paquets utilisant libpcap. pcap_dump_open prend en arguments un descripteur pcap_t, ouvert par pcap_open_live ou pcap_open_offline. fname est un pointeur vers une chaine de caractères contenant le nom du fichier à ouvrir. "-" sera un synonyme pour la sortie standard (stdout, "/dev/stdout"). Si NULL est renvoyé, alors le message d'erreur peut etre récupérer via la fonction pcap_geterr(). Cette fonction doit être utilisé avant tout appel de la fonction pcap_dump. ************************************************************************** ------[ void pcap_dump_close(pcap_dumper_t *p) pcap_dump_close permet de fermer un descripteur pcap_dumper_t ouvert par pcap_dump_open (pris en argument). pcap_dump_close ne renvoit rien. ************************************************************************** ** Les fonctions sur la gestion des filtres BPF Après avoir ouvert les descripteurs de lecture sur les interfaces réseaux il est possible de créer et d'appliquer des filtres afin de ne lire qu'une partie des données qui passent via l'interface/le fichier lu(e). Ces fonctions, au nombre de trois, permet la compilation d'une chaine de caractères pour la création d'un filtre (via la fonction pcap_compile), l'application d'un filtre sur un descripteur pcap_t (par la fonction pcap_setfilter) et finalement on peut libérer les ressources grace à la fonction pcap_freecode. Une structure, appelée bpf_program, contient le filtre à appliquer, est utilisée par ces fonctions. Cette structure est de la forme: (Note: cette structure est donnée à titre de documentation ... elle est générée automatiquement lors de la création du filtre, et cela reste transparent pour le programmer ou pour l'utilisateur.) struct bpf_program { u_int bf_len; struct bpf_insn *bf_insns; }; struct bpf_insn { u_short code; u_char jt; u_char jf; bpf_int32 k; }; En consultant pcap.h, le fichier d'entêtes de pcap, on remarque aussi l'implémentation de 4 fonctions gérant les filtres bpf non documentées dans le manuel de pcap: u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int); int bpf_validate(struct bpf_insn *f, int len); char *bpf_image(struct bpf_insn *, int); void bpf_dump(struct bpf_program *, int); ************************************************************************** ------[ int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask) pcap_compile prend une chaine de caractères et la compile en filtre BPF. Cette chaine de caractères est une expression typée dans un format décrit dans la page de man de tcpdump. Il convertiera des chaines du types "tcp and udp port 53" en filtre BPF ce qui permettera de filtrer que les paquets ou les chaines correspondantes. pcap_compile prend en argument: pcap_t *p, le descripteur du flux à filtrer; struct bpf_program *fp, la structure du filtre (on ne donnera qu'un pointeur vers une structure vide); char *str, la chaine de caractère contenant le filtre à appliquer(l'expression); int optimize, qui si une valeur différente de 0 passe ici optimisera le filtre si possible; bpf_u_int32 netmask, contient le masque du réseau où l'on applique le filtre. pcap_compile renvoit -1 en cas d'erreur; dans ce cas, l'erreur peut être lue grace à la fonction pcap_geterr(). ************************************************************************** ------[ int pcap_compile_nopcap(int snaplen_arg, int linktype_arg, struct bpf_program *program, char *buf, int optimize, bpf_u_int32 mask) -SRCS-[ extrait de gencode.c (libpcap-0.6.2) int pcap_compile_nopcap(int snaplen_arg, int linktype_arg, struct bpf_program *program, char *buf, int optimize, bpf_u_int32 mask) { pcap_t *p; int ret; p = pcap_open_dead(linktype_arg, snaplen_arg); if (p == NULL) return (-1); ret = pcap_compile(p, program, buf, optimize, mask); pcap_close(p); return (ret); } -SRCS-[ fin de l'extrait de gencode.c pcap_compile_nopcap() est similaire à pcap_compile() excepté que l'on n'a plus besoin de passer le descripteur pcap_t, mais seulement les deux arguments snaplen et linktype. La fonction va alors créer elle seule un descripteur pcap grace à l'appel de pcap_open_dead(). Cette fonction est utile pour un usage direct du Berkeley Packet Filter, sans avoir à appeler pcap_open(). Un retour de -1 indique une erreur. (Le texte d'erreur n'est pas accessible; Si vous voulez obtenir un message d'erreur, le plus simple est de reprendre le code de la fonction et non utiliser cette fonction directement.) ************************************************************************** ------[ int pcap_setfilter(pcap_t *p, struct bpf_program *fp) pcap_setfilter() est utilisé pour spécifier un filtre bpf. fp est un pointeur vers la structure du filtre, qui est surement le résultat de l'appel de la fonction pcap_compile(). Il faut aussi transmettre le pointeur du descripteur pcap_t sur lequel appliquer le filtre. pcap_setfilter renvoit -1 en cas d'erreur, dans quel cas une erreur est renvoyé et peut etre récupérée par l'appel de pcap_geterr(). 0 est renvoyé en cas de succès. ************************************************************************** ------[ void pcap_freecode(struct bpf_program *fp) pcap_freecode() est utilisé pour libérer la mémoire pointée vers la structure bpf_program générée par pcap_compile() quand le filtre n'est plus utilisé. La fonction prend en paramètre le filtre, fp. ************************************************************************** ** Les fonctions pour la récupération des captures ------[ int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user) pcap_dispatch() est utilisé pour collecter et réagir à la réception des paquets. pcap_t *p est le descripteur pcap ouvert par pcap_open_*() cnt contient le maximum de paquets à attendre avant de rendre la main. Ce n'est pas le nombre minimum; Quand on lit une capture en direct, seulement un buffer de paquets est lu à ce moment, donc moins que 'cnt' paquets recus seront traités. Une valeur de cnt égale à -1 signifie que tout les paquets recus dans un buffer seront traités, ou tout les paquets contenus dans le fichier d'une session dumpé. callback est utilisé pour spécifier un pointeur pour une fonction qui peut être appelée avec trois arguments: un pointeur sur un u_char qui est celui passé pour pcap_dispatch(), un pointeur sur une structure pcap_pkthdr et un pointeur sur un u_char vers les données du paquets. u_char *user est un pointeur vers les données recues. pcap_dispatch() renvoit le nombre de paquets lus. Si 0 est renvoyé, alors aucun paquet n'a été lu à partir de la capture en direct. (si ils avaient été rejeté par le filtre, ...) Si -1 est renvoyé, cela indique une erreur qui peut etre récupérée par pcap_perror() ou pcap_geterr(). ************************************************************************** ------[ int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) pcap_loop est similaire à pcap_dispatch() sauf qu'il continue de lire les paquets jusqu'à ce que les 'cnt' paquets soient recus et traités (ou qu'une erreur surgisse). Il ne renvoit rien si un time out intervient. Les arguments sont les mêmes que pour pcap_dispatch(). Une valeur négative pour cnt fait boucler pcap_loop à l'infini. ************************************************************************** ------[ void pcap_dump(u_char *user, struct pcap_pkthdr *h, u_char *sp) pcap_dump() écrit un paquet dans le fichier ouvert par pcap_dump_open(). Il faut noter que ses arguments sont identiques que ceux utilisés par la fonction de callback utilisé avec pcap_dispatch() ou pcap_loop(). ************************************************************************** ------[ u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h) pcap_next() lit le prochain paquet (en appelant pcap_dispatch() avec une valeur de cnt de 1) et renvoit un pointeur vers une chaine de caractères non signé contenant les données du paquet. p est un pointeur vers le descripteur pcap_t, et h un pointeur vers la structure pcap_pkthdr rattaché au paquet capturé. ************************************************************************** ** Les autres fonctions, misc, statistiques ... ------[ int pcap_datalink(pcap_t *p) pcap_datalink renvoit le type de lien associé au descripteur (ex: DLT_EN10MB); Les différents types sont donnés à la description de pcap_open_dead(). ------[ int pcap_snapshot(pcap_t *p) pcap_snapshot renvoit la taille de capture des paquets. ------[ int pcap_is_swapped(pcap_t *p) pcap_is_swapped renvoit vrai (true, valeur différent de 0), si le fichier de sauvegarde à un différent ordre de bits que le système actuel. ------[ int pcap_major_version(pcap_t *p) pcap_major_version renvoit vrai si le fichier de sauvegarde actuel a été écrit par une version de libpcap plus récente. ------[ int pcap_minor_version(pcap_t *p) pcap_minor_version renvoit vrai si le fichier de sauvegarde actuel a été écrit par une version de libpcap plus vieux. ------[ int pcap_stats(pcap_t *p, struct pcap_stat *ps) pcap_stats() renvoit 0 et remplit la struct pcap_stat. Les valeurs contenues ensuite dans la structure pcap_stat sont les statistiques depuis le début de la catpure jusqu'à l'appel de pcap_stats. Si une erreur survient, alors -1 est renvoyé et l'erreur peut etre obtenu via pcap_perror() ou pcap_geterr(). ------[ FILE *pcap_file(pcap_t *p) pcap_file() renvoit un flux standard I/O du fichier "savefile", si le "savefile" fut ouvert avec pcap_open_offline(), ou NULL si le périphérique réseau fut ouvert avec un pcap_open_live(). ------[ int pcap_fileno(pcap_t *p) pcap_fileno() renvoit le numéro du descripteur à parti de quelle interface les paquets sont lus, si une interface réseau a été ouverte grace à pcap_open_live(), ou -1 si un "savefile" a été ouvert avec pcap_open_offline(). ------[ void pcap_perror(pcap_t *p, char *prefix) pcap_perror() écrit le texte de la derniere sortie d'erreur d'une fonction de pcap, préfixée par 'prefix'. ------[ char *pcap_geterr(pcap_t *p) pcap_geterr() renvoit le texte d'erreur de la derniere provoquée par une erreur de pcap. ------[ char *pcap_strerror(int error) pcap_strerror() a été crée dans le cas où strerror(1) ne serait pas accessible. ************************************************************************** ANNEXE : CODES SOURCES ----- proggy1.c /* * Ce programme va simplement un fichier de dump et afficher son * contenu. Ce fichier a du être construit par un sniffeur utilisant libpcap * comme tcpdump ou autre ... * */ #include #include #include #include #include void showpacket(struct pcap_pkthdr hdr, const u_char *packet) { struct tm *mytime; int i, j, k; mytime = localtime(&hdr.ts.tv_sec); printf("%.2d:%.2d:%.2d.%.6i", mytime->tm_hour, mytime->tm_min, mytime->tm_sec, (int)hdr.ts.tv_usec); printf(", Len: %i (0x%x)\n",hdr.caplen,hdr.caplen); for (i=0;i<=hdr.caplen/16;i++) { if(i*16 != hdr.caplen) printf("0x%.2x ",i*16); k =(((i+1)*16)>hdr.caplen)?hdr.caplen-(i*16):16; for (j=0;j20) printf("%c",*packet); else printf("."); packet++; } if (i*16 != hdr.caplen) printf("\n"); } printf("\n"); } void Usage(char *prog) { printf("Usage: %s \n",prog); } int main(int argn, char **argv) { pcap_t *desc; const u_char *packet; struct pcap_pkthdr hdr; char errbuf[PCAP_ERRBUF_SIZE]; if (argn != 2) Usage(argv[0]), exit(1); desc = pcap_open_offline(argv[1], errbuf); if (desc == NULL) printf("pcap_open_offline: %s\n", errbuf), exit(2); while ((packet = pcap_next(desc, &hdr))) { showpacket(hdr, packet); } pcap_close(desc); return 0; } ----- fin proggy1.c ************************************************************************** ----- proggy2.c /* * Ce programme ouvre une interface, la lit et dump les paquets * */ #include #include #include #include #include void Usage(char *prog) { printf("Usage: %s \n",prog); } void pcap_dump2(u_char *user, struct pcap_pkthdr *h, u_char *sp) { pcap_dump((u_char*)user, h, sp); } int main(int argn, char **argv) { pcap_t *desc; pcap_dumper_t *dumpdesc; char *device = NULL; char errbuf[PCAP_ERRBUF_SIZE]; if (argn < 2 || argn > 3) Usage(argv[0]), exit(1); /* On va ouvrir un périphérique - soit il a été spécifié, soit il est détecté automatiquement avec pcap_lookupdev */ if (argn == 3) { // un périphérique a été spécifié device = argv[2]; } else { if (!(device = pcap_lookupdev(errbuf))) { printf("Erreur pcap_lookupdev: %s\n", errbuf); exit(1); } } printf("Ouverture de : %s\n", device); /* Ouverture du descripteur pour la lecture */ desc = pcap_open_live(device, 1524, 1, 1000, errbuf); if (desc == NULL) printf("Erreur pcap_open_live: %s\n", errbuf), exit(1); /* Ouverture du fichier de dump pour la sauvegarde */ dumpdesc = pcap_dump_open(desc, argv[1]); /* On passe comme variable 'user' le descripteur vers le fichier de dump ouvert, et on attend les 10 premiers paquets */ pcap_loop(desc, 10, (pcap_handler) pcap_dump2, (u_char*)dumpdesc); printf("Fin du dump des 10 paquets...\n"); pcap_dump_close(dumpdesc); pcap_close(desc); return 0; } ------ fin proggy2.c ************************************************************************** ------ proggy3.c /* * Test d'une expression bpf passée en argument. * */ #include #include #include #include /* * Function from tcpdump (util.c) */ char *takerule(register char **argv) { register char **p; register u_int len = 0; char *buf; char *src, *dst; p = argv; if (*p == 0) return 0; while (*p) len += strlen(*p++) + 1; buf = (char *)malloc(len); if (buf == NULL) { perror("malloc"); printf("it is better to end now.\n"); exit(2); } p = argv; dst = buf; while ((src = *p++) != NULL) { while ((*dst++ = *src++) != '\0') ; dst[-1] = ' '; } dst[-1] = '\0'; return buf; } int main(int argn, char **argv) { char *cmd; bpf_u_int32 localnet; struct bpf_program fcode; pcap_t *desc; desc = pcap_open_dead(DLT_EN10MB, 1524); if (argn < 2) { printf("Usage: %s [la règle à tester]\n", argv[0]); exit(1); } cmd = takerule(&argv[1]); localnet = 0xFFFF0000; if (pcap_compile(desc,&fcode,cmd,1,localnet) == -1) pcap_perror(desc, "pcap_compile"); else printf("pcap_compile vient de compiler l'expression:\n[%s]\n",cmd); pcap_freecode(&fcode); pcap_close(desc); return(0); } ------ fin proggy3.c