在进行网络性能调优或故障排查时,很多时候我们并不缺少工具,而是缺少对工具所“看到”内容的准确理解。首先需要明确一个关键特性:tcpdump 默认在 netif_receive_skb() 之后、协议栈分发前捕获,即链路层入口之后、IP 层解析之前的位置。因此它能让你看到 VLAN 标签、MAC 地址以及未解封装的 GRE/ERSPAN 头部,但 softirq 调度之前的 skb 入队细节,它是无法观测的。
那么在实际操作中如何用好 tcpdump?有几个值得关注的细节:
- 加上
-e参数,MAC 层头部就能显示出来:tcpdump -i eth0 -e -n - 内核的 BPF 过滤器有时会在抓包前就丢弃某些字段,因此建议添加
-B禁用 BPF 编译优化,或者直接使用sudo tcpdump -i eth0 -w dump.pcap抓取完整数据包,再通过 Wireshark 进行分析 - 网卡的 offload 功能也是一个关键变量。如果开启了 GRO 或 LSO,
tcpdump抓取到的可能是合并后的大包,或者未分片的 TCP 段。稳妥的做法是临时关闭这些功能:ethtool -K eth0 gro off tso off
如何使用 perf 跟踪内核函数调用路径
若要真正看清 skb 在内核中经过哪些函数处理,就需要进入内核态。最简单的方式是使用 perf 配合 kprobe 来跟踪系统调用。例如要追踪一个 SYN 包从网卡中断到 tcp_v4_do_rcv() 的完整路径:
首先确认函数符号是否已导出:grep tcp_v4_do_rcv /proc/kallsyms。如果没有输出,说明被 kptr_restrict 遮蔽,可以临时设置 sudo sysctl kernel.kptr_restrict=0。
然后开始跟踪:
sudo perf record -e 'kprobe:tcp_v4_do_rcv' -e 'kprobe:ip_rcv' -e 'kprobe:__netif_receive_skb_core' -g -a
触发连接后按 Ctrl+C,再使用 perf report --call-graph 查看调用栈。值得注意的是,__netif_receive_skb_core 是软中断入口,ip_rcv 是 IP 层起点,tcp_v4_do_rcv 是 TCP 状态机入口——这三者串联起来,基本就是 IPv4 TCP 包在内核中的主干流向。
cat /proc/net/snmp 和 /proc/net/netstat 能揭示哪些流向线索
这两个文件不会告诉你某个包的精确路径,但能暴露协议栈各层的丢包或错误计数,间接反映出流向瓶颈的位置。例如:
InCsumErrors很高,说明 IP 层校验失败,包可能在ip_rcv()阶段就被丢弃TCPSynRetrans高但TCPInCwndReduced很低,问题大概率出在路由或防火墙——SYN 包发送出去后没有收到回应,而不是内核 TCP 处理逻辑出现异常UdpInCsumErrors和UdpIgnoredMulti同时上升,很可能是网卡的多播地址配置错误,导致 UDP 包在udp_rcv()之前就被ip_check_mc()过滤掉了
这两份文件的核心价值在于帮助你排除“包是否到达了某一层”,而不是告诉你“具体走到了哪一行代码”。想用它来定位 skb->dev 或 skb->sk 的变化点,基本上是不现实的。
为什么 iptables TRACE 不适合观察真实流向
iptables -t raw -A PREROUTING -j TRACE 这种方法看起来直观,但实际上它只在 netfilter 框架注册的 hook 点输出日志。而 netfilter 是嵌入在协议栈中的“插件”,并非实际的流向路径。这里有三个容易踩的坑:
- 如果包根本没有进入
PREROUTING(例如被 XDP 程序直接丢弃),那么 TRACE 将完全不产生输出 - 如果启用了
CONFIG_BRIDGE_NETFILTER=y,桥接流量会额外经过nf_bridge_pre_routing(),TRACE 日志的顺序可能被打乱 - TRACE 日志写入
dmesg,在高吞吐环境下极易丢失,而且无法将其与 skb 地址或时间戳进行关联
如果要定位 netfilter 的行为,建议直接使用 perf probe 打点:sudo perf probe 'nf_hook_slow:%return',再结合 skbaddr 参数来查看具体哪个 hook 返回了非零值。
内核网络流向从来不是一条简单的线性流水线,软中断、工作队列、RPS/RFS、XDP、tc、netfilter 这些机制相互交织,形成了多重路径。抓包、perf、proc 统计三者必须交叉验证。单独依赖任何一种工具,都容易将“没看到”误认为“没发生”——这一点,务必牢记。
