TCPDUMP的libpcap库在抓包时使用any接口,不能得到接口的信息。查看随数据包一并得到的pcap_pkthdr结构体,包含的信息有限,以下我们在其中增加一个ifindex字段,表示此数据包的接口信息 (文件pcap/pcap.h)。
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) */
};
PCAP抓包流程
介绍一下pcap库的抓包流程。首先创建PF_PACKET套接口,如果是要抓取any任意接口的数据包,指定类型为SOCK_RAW,抓取原始报文,链接类型设置为DLT_LINUX_SLL。
static int activate_new(pcap_t *handle)
{
int is_any_device = (strcmp(device, "any") == 0);
sock_fd = is_any_device ?
socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)) :
socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
handle->linktype = DLT_LINUX_SLL;
}
接下来设置接收数据包的环形buffer参数。默认情况下libpcap分配2M字节大小的环形buffer:
setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req));
函数pcap_read_linux_mmap_v1负责从环形buffer中读取数据包,交由pcap_handle_packet_mmap函数进行处理(过滤、显示等)。
static int pcap_read_linux_mmap_v1(pcap_t *handle, ...)
{
while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) {
h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER);
if (!h.raw)
break;
ret = pcap_handle_packet_mmap(...);
}
}
PCAP调用接口API
使能pcap实时抓包,device使用any;snaplen使用1600,获取完整的数据包。
pcap_t *pd = pcap_open_live(device, snaplen, 0, 1000, ebuf);
可使用pcap_snapshot函数获取pcap库支持的最小长度snaplen。如果pcap_open_live设置的snaplen小于此值,pcap自动使用最小值:
pcap_snapshot(pd);
设置过滤器,例如此处设置为过滤ICMP报文。
pcap_compile(pd, &fcode, “ICMP”, 1, netmask) < 0);
pcap_setfilter(pd, &fcode) < 0);
开始抓取1000个报文,pcap为每个数据包使用sll_if_print回调函数打印报文信息:
printer = sll_if_print;
pcap_loop(pd, 1000, printer, 0);
printer函数指针为pcap_handler类型,定义如下:
typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, const u_char *);
添加接口信息
要添加接口信息,可通过修改pcap_handler的第二个参数pcap_pkthdr结构体,将接口索引值传给回调函数。增加ifindex字段。
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) */
int ifindex; //增加的接口索引值。
};
最重要的是在pcap库中获取每个数据包的接口索引,在调用callback函数前赋值给pcap_pkthdr的成员ifindex,由抓包流程可知,需要修改函数pcap_handle_packet_mmap。 在此函数中,每个抓取的数据包,在偏移sizeof(struct tpacket_hdr)长度之后,为sockaddr_ll结构信息,其成员sll_ifindex就是需要的接口索引值,赋给ifindex即可。
static int pcap_handle_packet_mmap(pcap_t *handle, pcap_handler callback, ...)
{
sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen);
pcaphdr.ifindex = sll->sll_ifindex;
/* pass the packet to the user */
callback(user, &pcaphdr, bp);
}
这样,就可在sll_if_print函数中打印ifindex的数值了。
完整的修改文件如下,基于libpcap-1.5.3的patch:
diff -burp libpcap-1.5.3-old/pcap/pcap.h libpcap-1.5.3/pcap/pcap.h
--- libpcap-1.5.3-old/pcap/pcap.h 2013-11-08 07:23:22.000000000 +0800
+++ libpcap-1.5.3/pcap/pcap.h 2018-09-18 14:52:06.337276047 +0800
@@ -163,6 +163,8 @@ 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) */
+
+ int ifindex;
};
/*
diff -burp libpcap-1.5.3-old/pcap-linux.c libpcap-1.5.3/pcap-linux.c
--- libpcap-1.5.3-old/pcap-linux.c 2014-01-15 04:40:35.000000000 +0800
+++ libpcap-1.5.3/pcap-linux.c 2018-09-18 14:53:06.837274302 +0800
@@ -1735,6 +1735,7 @@ pcap_read_packet(pcap_t *handle, pcap_ha
pcap_header.caplen = caplen;
pcap_header.len = packet_len;
+ pcap_header.index = from.sll_ifindex;
/*
* Count the packet.
@@ -4219,6 +4220,7 @@ static int pcap_handle_packet_mmap(
pcaphdr.ts.tv_usec = tp_usec;
pcaphdr.caplen = tp_snaplen;
pcaphdr.len = tp_len;
+ pcaphdr.ifindex = sll->sll_ifindex;
/* if required build in place the sll header*/
if (handlep->cooked) {
完。