2016-03-05 7 views
2

Я хочу реализовать команду tcpdump -i eth0 arp для наблюдения за пакетами arp на интерфейсе eth0 на моем ubuntu. Я использую libpcap, но возвращаемое значение функции pcap_next_ex всегда равно 0. С tcpdump -i eth0 arp в то же время он может наблюдать arp-пакеты.Как реализовать tcpdump -i интерфейс arp с libpcap

/* 
* compile(root): gcc test.c -lpcap 
* run   : ./a.out 
* output  : time out 
*     time out 
*     time out 
*     ... 
*/ 
#include <pcap.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 

#define ARP_REQUEST 1 
#define ARP_REPLY 2 

typedef struct arp_hdr_s arp_hdr_t; 
struct arp_hdr_s { 
    u_int16_t  htype; 
    u_int16_t  ptype; 
    u_char   hlen; 
    u_char   plen; 
    u_int16_t  oper; 
    u_char   sha[6]; 
    u_char   spa[4]; 
    u_char   tha[6]; 
    u_char   tpa[4]; 
}; 

#define MAXBYTES2CAPTURE 2048 

int 
main(int argc, char **argv) 
{ 
    char     err_buf[PCAP_ERRBUF_SIZE]; 
    const unsigned char *packet; 
    int      i; 
    int      ret; 
    arp_hdr_t    *arp_header; 
    bpf_u_int32    net_addr; 
    bpf_u_int32    mask; 
    pcap_t     *desrc; 
    struct pcap_pkthdr  *pkthdr; 
    struct bpf_program  filter; 

    net_addr = 0; 
    mask = 0; 
    memset(err_buf, 0, PCAP_ERRBUF_SIZE); 

    desrc = pcap_open_live("eth0", MAXBYTES2CAPTURE, 0, 512, err_buf); 
    if (desrc == NULL) { 
     fprintf(stderr, "error: %s\n", err_buf); 
     exit(-1); 
    } 

    ret = pcap_lookupnet("eth0", &net_addr, &mask, err_buf); 
    if (ret < 0) { 
     fprintf(stderr, "error: %s\n", err_buf); 
     exit(-1); 
    } 

    ret = pcap_compile(desrc, &filter, "arp", 1, mask); 
    if (ret < 0) { 
     fprintf(stderr, "error: %s\n", pcap_geterr(desrc)); 
     exit(-1); 
    } 

    ret = pcap_setfilter(desrc, &filter); 
    if (ret < 0) { 
     fprintf(stderr, "errnor: %s\n", pcap_geterr(desrc)); 
     exit(-1); 
    } 

    while (1) { 
     ret = pcap_next_ex(desrc, &pkthdr, &packet); 
     if (ret == -1) { 
      printf("%s\n", pcap_geterr(desrc)); 
      exit(1); 
     } else if (ret == -2) { 
      printf("no more\n"); 
     } else if (ret == 0) {    // here 
      printf("time out\n"); 
      continue; 
     } 

     arp_header = (arp_hdr_t *)(packet + 14); 
     if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype == 0x0800)) { 
       printf("src IP: "); 
       for (i = 0; i < 4; i++) { 
        printf("%d.", arp_header->spa[i]); 
       } 
       printf("dst IP: "); 
       for (i = 0; i < 4; i++) { 
        printf("%d.", arp_header->tpa[i]); 
       } 
       printf("\n"); 
     } 

    } 

    return 0; 
} 

ответ

0

Это вероятно, будет работать лучше, если бы вы сделали

if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype) == 0x0800) { 

вместо

if (ntohs(arp_header->htype) == 1 && ntohs(arp_header->ptype == 0x0800)) { 

последний Равняется arp_header->type == 0x0800, что, при работе на прямой порядок байтов машины (например, ПК), почти всегда будет оцениваться как «false», потому что значение будет выглядеть как 0x0008, а не 0x0800, в ARP-пакете - типы ARP - это big-endian, поэтому они будут выглядеть байтовыми на малой машине. Это означает, что он будет оценивать до 0, а байтовая замена 0 дает вам нуль, так что условие if будет оцениваться как «ложное», и код печати не будет вызываться.

У вас по-прежнему будет много тайм-аутов, если вы исправите это, если есть наводнения ARP-пакетов, но по крайней мере вы получите распечатанный пакет ARP. (Я бы посоветовал ничего не печатать на тайм-ауте, программы на основе pcap, выполняющие live-захват, должны ожидать, что тайм-ауты должны произойти, и не должны сообщать о них как о необычных событиях.)

+0

Да, это работает! У меня есть 2 вопроса. В приведенном выше коде «eth0» непосредственно задается как первый параметр 'pcap_open_live'. Если я хочу наблюдать «eth0», а не «eth1» «eth2» .. на моем хосте (несколько интерфейсов), как я могу сделать это с помощью 'pcap_lookupdev' или другими способами? Есть ли хороший способ с libpcap для наблюдения за пакетами arp? Я думаю, что моя программа немного сложна и беспорядочна. – hel

+0

Если вы хотите наблюдать «eth0», вы делаете именно то, что должны делать - вы передаете «eth0» в качестве аргумента в 'pcap_open_live()'. Что вы действительно спрашиваете? –

+0

«Есть ли хороший способ с libpcap для наблюдения за пакетами arp? Я думаю, что моя программа немного сложна и беспорядочна». Вы посмотрели файл 'print-arp.c' tcpdump или' пакет-arp.c' Wireshark? Обработка вашей программы ARP-пакетами * намного проще, чем более полный пакетный анализ ARP, выполненный обеими этими программами. И их код для захвата также более сложный. –

1

Не вдаваясь слишком глубоко в коде, я вижу главную проблему:

В вашем использовании pcap_open_live(), вы не установили смешанный режим: третий параметр должен быть отличен от нуля. Если запрос ARP не нацелен на ваш IP-адрес интерфейса, pcap не увидит его без беспорядочного режима. tcpdump делает, если специально не сказал, что не делайте этого, используя --no-promiscuous-mode, используйте promisc (и, следовательно, вам потребуется CAP_NET_ADMIN привилегия, которую вы получите на sudo, что потребует и ваша программа).

Примечание стороны:

1/Leak: вы можете освободить свой фильтр, используя pcap_freecode() после pcap_setfilter().

2/Я предполагаю, что вы читали официальную Tuto здесь:

http://www.tcpdump.org/pcap.html

... если это не так, вы бы также посоветовал сделать это первым. Цитирую:

примечания о беспорядочных против не-беспорядочного фырканья: Два методов очень разные по стилю. В стандартном, не-promiscuous sniffing, хост обнюхивает только трафик, который напрямую связан с . Только трафик на или из маршрута через хост будет выбран сниффером. С другой стороны, режим размножения обнюхивает весь трафик на проводе . В не коммутируемой среде это может быть весь сетевой трафик . [... больше материала на PROMISC против не-PROMISC]

EDIT:

На самом деле, глядя глубже вам код по сравнению с моим кодом, исполняемым на +1 год на уровне производства (как в доме и у заказчика), я могу видеть много больше вещей, которые могут быть ошибочными:

  • Вы никогда не называйте pcap_create()
  • Вы никогда не называйте , мы уже говорили об этом уже
  • Вы никогда не называйте pcap_activate(), это может быть основной проблемой здесь

... PCAP является очень обидчивый о порядке последовательности операций первого получить pcap_t ручку, а затем работать на нем.

На данный момент лучший совет, который я могу дать вам - в противном случае это будет живой сессии отладки между вами и мной, являются:

1/чтения и воспроизведения/настройки с кодом из официального учебника :

http://www.tcpdump.org/pcap.html

Это является обязательным.

2/FWIW, моя - определенно работает - последовательность операций такова:

  • pcap_lookupnet()
  • pcap_create()
  • pcap_set_snaplen(), вы можете или не нужен этот
  • pcap_set_buffer_size(), вам может понадобиться или не нужно это
  • pcap_activate() с примечанием: очень важно: сначала активировать, а затем установить неблокирование из PCAP_SETNONBLOCK (3PCAP): при первом активировании с помощью pcap_activate() или открытии с помощью pcap_open_live() дескриптор захвата не находится в non-blocking mode''; a call to pcap_set-nonblock() is required in order to put it into, не блокируя '' Режим.

... а потом, потому что я не использую вонючей блокировки/блокировки с тайм-аут, занят зацикливание:

  • pcap_setnonblock()
  • pcap_get_selectable_fd()

... тогда и только затем: - pcap_compile() - а затем pcap_setfilter() - а затем, как я уже упоминал, pcap_freecode() - а затем select() или family на file'des 'я получаю от pcap_get_selectable_fd(), до pcap_dispatch(), но это еще одна тема.

pcap старый API, начиная еще в 80-х годах, и его действительно оченьочень обидчивый. Но не унывайте! Это здорово - как только вы это исправите.

+0

Используя 'tcpdump -i eth0 arp' в то же время, Я наблюдаю запрос arp и отвечу в ip интерфейса eth0, но никакие пакеты не обмениваются с моей программой. Поэтому я не знаю, что случилось. Спасибо за ответ! – hel

+0

Результат моей программы указывает, что 'pcap_next_ex' всегда возвращает' 0'. – hel

+0

И вы не 'sudo' ваш tcpdump ??? Если это так, в коде есть что-то еще не так. Попробуй. Тем временем, пожалуйста, установите параметр promisc в свой 'pcap_open_live()' или сделайте его опцией для вашего основного(). 99% времени вы хотите promisc. – jbm