![](https://csdnimg.cn/release/download_crawler_static/87212215/bg5.jpg)
bpf_u_int32 flags; /* 接口的参数 */
};
char * pcap_lookupdev(register char * errbuf)
{
pcap_if_t *alldevs;
……
pcap_findalldevs(&alldevs, errbuf);
……
strlcpy(device, alldevs->name,
sizeof(device));
}
打开网络设备
当设备找到后,下一步工作就是打开设备以准备捕获数据包。Libpcap 的包捕获
是建立在具体的操作系统所提供的捕获机制上,而 Linux 系统随着版本的不同,
所支持的捕获机制也有所不同。
2.0 及以前的内核版本使用一个特殊的 socket 类型 SOCK_PACKET,调用形式是
socket(PF_INET, SOCK_PACKET, int protocol),但 Linux 内核开发者明确指出
这种方式已过时。Linux 在 2.2 及以后的版本中提供了一种新的协议簇
PF_PACKET 来实现捕获机制。PF_PACKET 的调用形式为 socket(PF_PACKET, int
socket_type, int protocol),其中 socket 类型可以是 SOCK_RAW 和 SOCK_DGRAM。
SOCK_RAW 类型使得数据包从数据链路层取得后,不做任何修改直接传递给用户
程序,而 SOCK_DRRAM 则要对数据包进行加工(cooked),把数据包的数据链路层
头部去掉,而使用一个通用结构 sockaddr_ll 来保存链路信息。
使用 2.0 版本内核捕获数据包存在多个问题:首先,SOCK_PACKET 方式使用结构
sockaddr_pkt 来保存数据链路层信息,但该结构缺乏包类型信息;其次,如果
参数 MSG_TRUNC 传递给读包函数 recvmsg()、recv()、recvfrom() 等,则函数
返回的数据包长度是实际读到的包数据长度,而不是数据包真正的长度。Libpcap
的开发者在源代码中明确建议不使用 2.0 版本进行捕获。
相对 2.0 版本 SOCK_PACKET 方式,2.2 版本的 PF_PACKET 方式则不存在上述两个
问题。在实际应用中,用户程序显然希望直接得到"原始"的数据包,因此使用
SOCK_RAW 类型最好。但在下面两种情况下,libpcap 不得不使用 SOCK_DGRAM 类
型,从而也必须为数据包合成一个"伪"链路层头部(sockaddr_ll)。
某些类型的设备数据链路层头部不可用:例如 Linux 内核的 PPP 协议实现代码
对 PPP 数据包头部的支持不可靠。