我想实现命令tcpdump -i eth0 arp
来观察我的ubuntu上接口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;
}
答案 0 :(得分:1)
如果您的代码没有太深入,我可以看到一个主要问题:
在使用pcap_open_live()
时,不设置混杂模式:第三个参数应为非零。如果ARP请求不是针对您的接口IP,则pcap将不会在没有混杂模式的情况下看到它。 tcpdump
除非明确告知不要使用--no-promiscuous-mode
这样做,否则请使用promisc(因此需要CAP_NET_ADMIN
权限,您可以sudo
获得该权限,你的程序也需要。)
旁注:
1 /泄漏:您可能希望在pcap_freecode()
之后使用pcap_setfilter()
释放过滤器。
2 /我假设你已经在这里阅读官方tuto:
http://www.tcpdump.org/pcap.html
......如果情况并非如此,那么建议您先这样做。我引用:
关于混杂与非混杂嗅探的说明:两者 技术风格迥然不同。在标准,非滥交 嗅探,主机只嗅探与之直接相关的流量 它。仅挑选来自主机的流量,来自主机或通过主机路由 由嗅探器。另一方面,混杂模式嗅探所有 电线上的交通。在非交换环境中,这可能就是全部 网络流量。 [...更多关于promisc vs non-promisc的内容]
编辑:
实际上,与我在生产级别(内部和客户)运行+1年的代码相比,我更深入地了解您的代码,我可以看到更多可能出错的代码:
pcap_create()
pcap_set_promisc()
,我们已经谈过此事pcap_activate()
,这可能是此处的核心问题... pcap 非常对于首先获取pcap_t句柄的操作的顺序顺序敏感,然后对其进行操作。
目前,我能给你的最佳建议 - 否则这将是你和我之间的实时调试会议,是:
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()打开时,捕获句柄不在{ {1}}非阻塞&#39;&#39;模式。...然后,因为我没有使用带有超时的臭味阻塞/阻塞,繁忙的循环:
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()
- 然后文件上的select()或系列&#39; des&#39;我从pcap_freecode()
转到pcap_get_selectable_fd()
,但这是另一个主题。
pcap_dispatch()
是一个旧的API,从80年代开始,它真的非常 非常敏感。但不要气馁!这很棒 - 一旦你做对了。
答案 1 :(得分: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
,当在小端机器(例如PC)上运行时,它几乎总是评估为“假”,因为该值看起来像0x0008,而不是0x0800, ARP数据包 - ARP类型是big-endian,因此它们在小端机器上看起来是字节交换的。这意味着它将评估为0,并且字节交换0给出零,因此if
条件将评估为“false”,并且不会调用打印代码。
如果你解决了这个问题,你仍然会有很多超时,除非有一个泛滥的ARP数据包,但至少你会得到偶尔打印出来的ARP数据包。 (我建议在超时时不打印任何内容;基于pcap的程序执行实时捕获应该会发生超时,并且不应该将它们报告为异常事件。)