libpcap是一个在Unix和Linux操作系统上捕获网络数据包的库。它提供了一些函数和工具,使用户可以捕获和处理网络流量。libpcap可以用于网络安全、网络监控、网络性能分析等领域。它广泛应用于网络工具和安全软件的开发中,例如Wireshark、Tcpdump等工具都是基于libpcap库实现的。
常用抓包流程:
#include <stdio.h>
#include <pcap.h>
void pcapCallback(u_char *user, const struct pcap_pkthdr *header, const u_char *packet)
{
printf("Received a packet of length %d\n", header->len);
}
// 抓包转发
int main () {
pcap_t * handle;
char *dev = "eth0";
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program filter{};
/* 以混杂模式打开会话 */
handle = pcap_open_live(dev, 65535, 1, 0, errbuf);
pcap_compile(handle, &filter, "TCP or UDP", 0, 0);
pcap_setfilter(handle, &filter);
/* 抓包 */
if (pcap_loop(handle, -1, pcapCallback, nullptr) == -1) {
printf("catch package failed...\n");
pcap_close(handle);
exit(1);
}
return (0);
}
pcap_open_live函数是用于打开网络设备并开始捕获数据包的函数。它的原型如下:
pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *errbuf)
函数参数解释如下:
- device:指定要打开的网络设备,可以是设备名称(如"eth0")或设备的IPv4地址(如"192.168.1.1")。如果为NULL,则函数将打开默认网络接口。
- snaplen:指定要捕获的每个数据包的最大字节数。如果网络接口收到的数据包的长度大于此值,则捕获的数据包将被截断。如果为0,则表示不截断数据包。通常,建议将此值设置为65535。
- promisc:是否启用混杂模式。如果为非零值,则将网络接口设置为混杂模式,可以接收到经过网络接口的所有数据包。如果为零,则将网络接口设置为非混杂模式,只能接收到发送给网络接口的数据包或广播数据包。
- to_ms:指定超时值,以毫秒为单位。如果在指定的超时时间内没有数据包到达,则pcap_next_ex或pcap_dispatch函数将返回0。如果为0,则表示不设置超时值,将一直等待数据包到达。
- errbuf:用于存储错误消息的缓冲区。如果函数调用失败,则该缓冲区将包含错误消息。
pcap_loop函数是一个阻塞函数,它将一直等待数据包的到来,当有数据包到达时,就会调用回调函数进行处理,处理完成后继续等待下一个数据包的到来。
因此,如果没有数据包到达,pcap_loop函数就会一直等待。在默认情况下,pcap_loop函数的超时时间是1秒,也就是说,如果1秒内没有数据包到达,pcap_loop函数就会返回,然后再次等待。如果1分钟内没有数据包到达,pcap_loop函数将会被阻塞1分钟。
按照上面的描述就会有个问题,如果抓取的网络包不是连续的,就会是的pcap_loop进入阻塞模式,如果这个时候有网络数据包过来,触发处理包的回调函数会有约一分钟的延时,对于实时系统或者类似Wireshark、Tcpdump等工具那就能立即反应的系统来说很不实用,因此上述方式并不实用与这种场景。
pcap_create()和pcap_activate()是一种更加灵活的打开捕获设备的方法,相对于直接使用pcap_open_live(),它们提供了更多的控制选项。下面是使用pcap_create()和pcap_activate()来打开捕获设备的示例代码:
#include <pcap.h>
#include <stdio.h>
void pcapCallback(u_char* user, const struct pcap_pkthdr* packet_header, const u_char* packet_content) {
// 处理捕获到的数据包
}
int main() {
char* dev = "eth0"; // 需要捕获数据包的设备名
char errbuf[PCAP_ERRBUF_SIZE]; // 存储错误信息的缓冲区
int snaplen = 65535; // 捕获数据包的长度
int promisc = 1; // 混杂模式
int to_ms = 1000; // 等待捕获的超时时间
// 打开捕获设备
pcap_t* handle = pcap_create(dev, errbuf);
if (handle == nullptr) {
printf("Could not create pcap handle: %s\n", errbuf);
return -1;
}
pcap_set_snaplen(handle, snaplen);
pcap_set_promisc(handle, promisc);
pcap_set_timeout(handle, to_ms);
pcap_compile(handle, &filter, "TCP or UDP", 0, 0);
pcap_setfilter(handle, &filter);
pcap_set_immediate_mode(handle, 1); // 开启immediate模式
if (pcap_activate(handle) != 0) {
printf("Could not activate pcap handle: %s\n", pcap_geterr(handle));
pcap_close(handle);
return -1;
}
// 循环捕获数据包
while (true) {
struct pcap_pkthdr* packet_header;
const u_char* packet_content;
int res = pcap_next_ex(handle, &packet_header, &packet_content);
if (res == 1) {
pcapCallback(nullptr, packet_header, packet_content);
} else if (res == -1) {
printf("Error occurred while capturing packets: %s\n", pcap_geterr(handle));
pcap_close(handle);
return -1;
} else if (res == 0) {
printf("Timeout occurred while waiting for packets\n");
}
}
pcap_close(handle);
return 0;
}
pcap_set_immediate_mode()函数是用来设置PCAP设备的immediate模式,即启用或禁用immediate模式。Immediate模式指的是当有数据包到达时立即通知用户程序,而不是等到收到一定数量的数据包或达到一定时间间隔后再通知用户程序。在某些情况下,immediate模式可以提高数据包捕获的实时性和精度。
该函数的原型为:
int pcap_set_immediate_mode(pcap_t *p, int immediate);
其中,参数p是指向PCAP设备的指针,参数immediate是一个整数值,指示是否启用immediate模式。当immediate为0时,禁用immediate模式;当immediate为非0值时,启用immediate模式。
该函数返回0表示设置成功,返回-1表示设置失败,并将错误信息存储在pcap_t结构体的errbuf成员中。
pcap_next_ex() 函数用于从网络接口上捕获下一个数据包。该函数返回以下值之一:
- 1:成功捕获数据包
- 0:超时
- -1:发生错误
- -2:数据包读取完成
函数原型如下:
int pcap_next_ex(pcap_t* p, struct pcap_pkthdr** pkt_header, const u_char** pkt_data)
参数说明:
p
:指向用pcap_open_live()
或pcap_open_offline()
打开的网络接口或离线捕获文件的pcap_t
指针。pkt_header
:用于存储数据包头信息的指向指针的指针。该函数会在这个指向指针的指针所指向的位置分配一块空间来存储数据包头信息。pkt_data
:用于存储数据包内容的指向指针的指针。该函数会在这个指向指针的指针所指向的位置分配一块空间来存储数据包内容。
调用 pcap_next_ex()
函数之后,如果返回值为 1,则表示成功捕获到一个数据包,数据包的头信息和内容会被存储在 pkt_header
和 pkt_data
中。需要注意的是,pkt_header
和 pkt_data
中存储的内容在下一次调用 pcap_next_ex()
函数之前是不可用的。
如果返回值为 0,则表示在指定的超时时间内没有捕获到数据包。如果超时时间设置为 -1,则该函数会一直等待直到有数据包被捕获。
如果返回值为 -1,则表示发生了错误,可以通过 pcap_geterr()
函数获取错误信息。
如果返回值为 -2,则表示数据包读取完成,即 pcap_close()
函数已经被调用关闭了捕获会话。