linux - 如何使用 libpcap 实现 tcpdump -i interface arp

标签 linux libpcap arp

我想在我的 ubuntu 上执行命令 tcpdump -i eth0 arp 来观察接口(interface) eth0 上的 arp 数据包。我用的是libpcap,但是函数pcap_next_ex的返回值一直是0。同时配合tcpdump -i eth0 arp,可以观察到arp包。

/*
 *  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;
}

最佳答案

无需深入了解您的代码,我就能看出一个主要问题:

在您使用 pcap_open_live() 时,您没有设置混杂模式:第三个参数应该是非零的。如果 ARP 请求不是针对您的接口(interface) IP,如果没有混杂模式,pcap 将看不到它。 tcpdump 会,除非使用 --no-promiscuous-mode 特别告知不要这样做,否则请使用 promisc(因此需要 CAP_NET_ADMIN特权,您将通过 sudo 获得,您的程序也需要它)。

旁注:

1/泄漏:您可能想在 pcap_setfilter() 之后使用 pcap_freecode() 释放过滤器。

2/我假设你已经阅读了这里的官方教程:

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

...如果不是这种情况,您最好先这样做。我引用:

A note about promiscuous vs. non-promiscuous sniffing: The two techniques are very different in style. In standard, non-promiscuous sniffing, a host is sniffing only traffic that is directly related to it. Only traffic to, from, or routed through the host will be picked up by the sniffer. Promiscuous mode, on the other hand, sniffs all traffic on the wire. In a non-switched environment, this could be all network traffic. [... more stuff on promisc vs non-promisc]

编辑:

实际上,与我在生产级别(内部和客户)运行了 +1 年的代码相比,更深入地查看您的代码,我可以看到更多可能出错的地方:

  • 您永远不会调用 pcap_create()
  • 永远不要调用pcap_set_promisc(),我们已经讨论过了
  • 你永远不会调用pcap_activate(),这可能是这里的核心问题

...pcap 对于首先获取 pcap_t 句柄然后对其进行操作的操作顺序非常敏感。

目前,我能给你的最好建议是:否则这将是你我之间的实时调试 session :

1/阅读并使用官方教程中的代码进行调整:

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

这是强制性的。

2/FWIW,我的 - 绝对有效 - 操作顺序是这样的:

  • pcap_lookupnet()
  • pcap_create()
  • pcap_set_promisc()
  • pcap_set_snaplen(),您可能需要也可能不需要
  • pcap_set_buffer_size(),您可能需要也可能不需要
  • pcap_activate() 附注:非常重要:首先激活,然后从 PCAP_SETNONBLOCK(3PCAP) 设置非阻塞:首次使用 pcap_activate() 激活或使用 pcap_open_live() 打开时,捕获句柄未处于 非阻塞模式'';需要调用 pcap_set-nonblock() 才能将其置于非阻塞模式。

...然后,因为我不使用发臭的阻塞/超时阻塞,忙循环:

  • pcap_setnonblock()
  • pcap_get_selectable_fd()

...然后并且只有那时: - pcap_compile() - 随后是 pcap_setfilter() - 然后正如我提到的 pcap_freecode() - 然后是我从 pcap_get_selectable_fd()pcap_dispatch() 的文件'des' 上的 select() 或系列,但这是另一个主题。

pcap 是一个可以追溯到 80 年代的旧 API,它确实非常非常敏感。但不要气馁!太棒了 - 一旦你做对了。

关于linux - 如何使用 libpcap 实现 tcpdump -i interface arp,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35809730/

相关文章:

android - 使用 ndk 工具链为 android 编译的 libpcap 静态链接错误

c# - 通过 IPv6 获取远程 MAC 地址

linux - 如何添加多个值?

linux - 如何从 shell 脚本中的命令行参数中检索值

linux - 我如何在 bash 中 ping 多个主机(通过 dns 名称并解析它的 ip 地址)?

linux - 使用 HTTPTunnel (htc/hts) 连接防火墙内的 SSH 服务器?

buffer - FreeBSD:有关 NIC 环形缓冲区、mbuf 和 bpf 缓冲区的问题

c - 在 OS X 中使用 C 语言的 libpcap 时出现问题

linux - 如何向套接字提供目标MAC地址

winsock - 可以使用Winsock构建和发送ARP报文,或者接收ARP报文吗?如何?