内核实现
tcpdump在抓取网络包时,实际它会创建一个socket,并且设置网卡为混杂模式,用于接收网络链路上所有地址的包。
发送方向:
dev_queue_xmit->dev_queue_xmit_nit:
static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
{
......
//检测到有sniffer抓包的钩子就会执行发送报文的抓包
struct list_head *ptype_list = &ptype_all;
list_for_each_entry_rcu(ptype, ptype_list, list) {
if (pt_prev) {
deliver_skb(skb2, pt_prev, skb->dev);
pt_prev = ptype;
continue;
}
......
}
}
接收方向:
netif_receive_skb-->__netif_receive_skb_core:
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
......
//在这里处理sniffer相关的skb接收处理,也就是tcpdump抓包的位置
list_for_each_entry_rcu(ptype, &ptype_all, list) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
......
}
抓包数据异常原因
从上面介绍的内核实现来看,tcpdump抓取的数据分别是驱动接收处理的最后,以及驱动发送处理的开始。因此tcpdump抓取的数据可能不是最终在物理链路上发出去的和接收到的包。
使用tcpdump的抓取的包,然后利用wireshark进行分析那么可能会发现一些报错,主要会有两种异常现象:
- 包长度超过MTU
- 包的checksum不正确
主要原因在于:
1.硬件支持TSO、GRO特性
2.硬件支持checksum offload特性
在支持TSO和checksum offload的网卡上,skb数据在进入驱动层之前是不进行分片处理和完整的checksum计算的,而tcpdump抓取的包恰恰是在此处,而这并不代表实际链路上的包会发生错误。