pcap 库怎么获取抓到包的网卡信息¶
pcap 的基本使用¶
pcap 包处理函数的原型一般是这样的:
void packet_handler(u_char *args, const struct pcap_pkthdr *pkthdr, const u_char *p);
第三个参数指向一个 buffer,这个 buffer 里是网络包的数据。第二个参数 pcap_pkthdr 中只有下面这 3 个字段,记录跟这个网络包相关的一些元数据,然而并没有网卡相关的信息。
struct pcap_pkthdr {
struct timeval ts; // 时间戳
bpf_u_int32 caplen; // 抓到的包的长度,可能截断了
bpf_u_int32 len; // 包的实际长度
}
那 tcpdump -i any -e
(底层用的 pcap 库)看到的网卡信息等是怎么来的呢。
DLT_LINUX_SLL2 模式¶
翻 tcpdump 和 pcap 库的代码,发现在网卡是 any
的情况下,可以通过 pcap_set_datalink(handle, DLT_LINUX_SLL2)
这个调用让 pcap 库切换到 DLT_LINUX_SLL2 模式去,这个模式下,第二个参数返回不变,第三个参数网络包中的 以太网包头 会被一个 伪包头 替换掉,这个 伪包头 里包含了网卡号,包的类型流向等一些额外信息。
完整的使用代码如下:
void packet_handler(u_char *args, const struct pcap_pkthdr *pkthdr, const u_char *p) {
// 网络包的一开始是 伪包头
const struct sll2_header *sllp = (const struct sll2_header *)p;
uint32_t if_index = ntohl(sllp->sll2_if_index);
char ifname[IF_NAMESIZE];
if (!if_indextoname(if_index, ifname))
strncpy(ifname, "?", 2);
// 后面开始是 IP 包头,以太网包头被前面的伪包头替换掉了
p += SLL2_HDR_LEN;
len = pkthdr->len - SLL2_HDR_LEN;
printf("Nic: %s, Packet Length: %d %s\n", ifname, len);
...
}
int main() {
...
// 需要是 any 网卡的时候,设置 DLT_LINUX_SLL2 才会生效
handle = pcap_open_live("any", 65536, 1, 1000, errbuf);
pcap_set_datalink(handle, DLT_LINUX_SLL2);
pcap_loop(handle, 0, packet_handler, NULL);
...
}
如果编译的时候报 DLT_LINUX_SLL2
找不到,需要升级 pcap 库。
sll2_header
完整的字段如下:
struct sll2_header {
// ether type 802.3/802.1Q/...
uint16_t sll2_protocol;
uint16_t sll2_reserved_mbz;
// 网卡号
uint32_t sll2_if_index;
uint16_t sll2_hatype;
// 包类型,可以是 HOST/BROADCAST/MULTICAST/OTHERHOST/OUTGOING
uint8_t sll2_pkttype;
uint8_t sll2_halen;
// 网卡 MAC 地址
uint8_t sll2_addr[SLL_ADDRLEN];
};
还是用 tcpdump 来看一看这个里面可以打印出的一些信息, tcpdump -i any -e -XX
可以看到包的网卡、流向等信息,注意下面 16 进制打印出来的前 20 个字节就是这个 sll2_header
了,并不是实际的以太网包头。
# tcpdump -i any -e -XX
tcpdump: data link type LINUX_SLL2 👈 运行在 SLL2 模式下
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
07:37:36.139372 eth0 In ifindex 2 48:57:02:f4:e2:92 (oui Unknown) ethertype IPv4 (0x0800), length 72: bogon.59104 > arch.10022: Flags [.], ack 474860, win 1156, options [nop,nop,TS val 1170175875 ecr 1805243877], length 0
0x0000: 0800 0000 0000 0002 0001 0006 4857 02f4 ............HW..
0x0010: e292 0000 4540 0034 0000 4000 3e06 0dd9 ....E@.4..@.>...
... 👆 从这里开始是 IP 包头
更多细节可以参见 tcpdump 项目中 sll2 相关的打印代码: https://github.com/the-tcpdump-group/tcpdump/blob/master/print-sll.c