主题
读懂 packet trace
前面六章反复用到 show trace,但一直没系统讲过怎么读它。 这一篇把 VPP 的排错工具箱一次性讲清楚 —— 它是 VPP 相比内核转发最大的优势之一。
为什么它这么有用
内核里做 NAT,你能拿到的观测手段大概是 conntrack -L 加 tcpdump。 包在协议栈里经历了什么,基本靠猜。
VPP 的转发逻辑是一张显式的图(回顾第 0 章), show trace 会把每个包经过的每个节点、以及节点内部发生了什么逐行打出来。
text
→ af-packet-input 收包
→ ethernet-input 解以太网头
→ ip4-input 解 IP 头
→ nat-pre-in2out 判定要做 NAT
→ nat44-ed-in2out 查会话表
→ nat44-ed-in2out-slowpath 没查到,新建会话并完成转换
→ ip4-lookup 用改过的地址重新查路由
→ ip4-rewrite 改写链路层头
→ host-eth2-output 发出去这是一份逐跳的"包的自述"。
基本用法:三条命令
bash
# 1. 清掉旧记录
vppctl clear trace
# 2. 布下抓取(从哪个输入节点开始,抓多少个包)
vppctl trace add af-packet-input 20
# 3. 制造流量后查看
vppctl show trace输入节点要选对
trace add 的第一个参数是输入节点,不是任意节点。用错了什么都抓不到。
| 收包方式 | 输入节点 |
|---|---|
| AF_PACKET(本教程) | af-packet-input |
| DPDK | dpdk-input |
| AF_XDP | af-xdp-input |
| memif | memif-input |
| 虚拟网卡 | virtio-input / vmxnet3-input |
不确定用哪个?show interface rx-placement 会告诉你:
text
Thread 0 (vpp_main):
node af-packet-input:
host-eth1 queue 0 (polling)抓包数量要给够
trace add af-packet-input 20 里的 20 是总数,不是每个接口 20 个。 而且 ARP/ND、回包都算数 —— 一次 ping 三个包,加上 ARP 和回包, 实际可能要十几个槽位。给少了会看不到关键的那一个。
读一条完整的 trace
下面是从实验里抓的一个首包,逐段拆解:
text
00:08:10:962150: ethernet-input
IP4: aa:c1:ab:c3:66:ff -> aa:c1:ab:27:cc:ec时间戳 + 节点名,然后是这个节点看到的内容。这里是链路层地址。
text
00:08:10:962158: ip4-input
UDP: 10.10.0.11 -> 203.0.113.12
tos 0x00, ttl 64, length 31, checksum 0xe87e
UDP: 52515 -> 9001
length 11, checksum 0x5700原始的五元组和校验和。 这是 NAT 改写之前的样子。
text
00:08:10:962168: nat-pre-in2out
in2out next_index 2 arc_next_index 10分流节点:判定这个包要走 in2out 分支。看到 nat-pre-out2in 就说明走的是回程分支。
text
00:08:10:962170: nat44-ed-in2out
NAT44_IN2OUT_ED_FAST_PATH: sw_if_index 1, next index 3
search key local 10.10.0.11:52515 remote 203.0.113.12:9001 proto UDP fib 0search key 这一行价值极高 —— 它显示 VPP 用什么去查会话表。 排查"为什么会话匹配不上"时,把这个 key 和 show nat44 sessions 里的会话逐字段比对, 差异一目了然。
text
00:08:10:962174: nat44-ed-in2out-slowpath
NAT44_IN2OUT_ED_SLOW_PATH: translation result 'success' via i2of
i2of match: saddr 10.10.0.11 sport 52515 daddr 203.0.113.12 dport 9001
rewrite: saddr 203.0.113.1 sport 52515 daddr 203.0.113.12 dport 9001
o2if match: saddr 203.0.113.12 sport 9001 daddr 203.0.113.1 dport 52515
rewrite: saddr 203.0.113.12 daddr 10.10.0.11 dport 52515出现 slowpath 说明这是首包,正在新建会话。 i2of / o2if 是刚建好的来回两个方向的改写规则 —— 和 会话表里看到的完全一致。
text
00:08:10:962189: ip4-lookup
UDP: 203.0.113.1 -> 203.0.113.12
checksum 0xb691
UDP: 52515 -> 9001
checksum 0x2513改写后的样子。 和前面 ip4-input 那段并排看,就知道 NAT 动了哪几个字段。
只看节点序列
包的内容有时候不重要,你只想知道"它走了哪条路"。过滤一下:
bash
vppctl show trace | grep -E '^Packet |^[0-9]{2}:[0-9]{2}:[0-9]{2}:[0-9]{6}: 'text
Packet 1
00:00:01:123456: af-packet-input
00:00:01:123460: ethernet-input
00:00:01:123465: ip4-input
00:00:01:123470: nat-pre-in2out
00:00:01:123472: nat44-ed-in2out
00:00:01:123475: nat44-ed-in2out-slowpath
00:00:01:123489: ip4-lookup
00:00:01:123491: ip4-rewrite
00:00:01:123494: host-eth2-output这个视图特别适合回答两类问题:
包走到哪一步就没了? 序列在哪个节点戛然而止,问题就在那儿。
快路径还是慢路径? 有没有 -slowpath 那一跳。 第 1 章用这个方法对比过 ping -c 3 的三个包 —— 首包有 slowpath,后续包没有。
trace filter:流量大时只看关心的
生产环境上 trace add 一开,几十个包瞬间抓满,全是无关流量。 trace filter 可以只保留经过特定节点的包:
bash
vppctl trace filter include nat44-ed-in2out 10
vppctl trace add af-packet-input 100
# ... 制造流量 ...
vppctl show trace
vppctl trace filter none # 用完记得关掉include <节点> <数量> 表示只保留经过该节点的包。也可以用 exclude 反过来排除。
这在排查"某类流量不通"时很有用
比如只想看走 out2in 分支的包:
bash
vppctl trace filter include nat44-ed-out2in 20这样 ARP、内网互访之类的噪声就被滤掉了。
pcap trace:把包存成文件
show trace 是文本,看不了字节级细节,也没法给别人。 pcap trace 直接存成标准 pcap 文件:
bash
vppctl pcap trace rx tx max 1000 intfc host-eth2 file nat.pcap
# ... 制造流量 ...
vppctl pcap trace off文件落在容器里的 /tmp/nat.pcap,拷出来用 Wireshark 打开。
最有价值的用法:抓被丢弃的包
bash
vppctl pcap trace drop max 100 file drops.pcap只抓被丢弃的包。 这在排查"某些包莫名其妙消失"时几乎是唯一的手段 —— show trace 要你提前知道该抓什么,pcap trace drop 则是守株待兔。
实测效果:
text
Write 2 packets to /tmp/drops.pcap, and stop capture...bash
ssh chenzhong@192.168.49.182 \
'docker exec clab-natbase-nat ls -la /tmp/drops.pcap'text
-rw-r--r-- 1 root root 332 Sep 4 03:21 /tmp/drops.pcappcap dispatch trace:两者结合
bash
vppctl pcap dispatch trace on max 1000 file dispatch.pcap buffer-trace af-packet-input 100这会把包内容 + 它经过的节点序列一起存进 pcap。 用 Wireshark 打开能看到每个包的 VPP 图路径 —— 相当于 show trace 的可视化版本。 排查复杂问题时值得一试。
show runtime:节点级的性能视图
trace 看单个包,show runtime 看整体:
bash
vppctl clear runtime # 先清零
# ... 跑一段流量 ...
vppctl show runtimetext
vector rates in 1.0608e5, out 1.0608e5, drop 0.0000e0, punt 0.0000e0
Name State Calls Vectors Packet-Clocks Vectors/Call
af-packet-input polling 455100 38595 3.25e3 .08
ip4-input active 36699 254342 3.62e2 6.93
nat44-ed-in2out active 36531 200006 6.15e2 5.47
nat44-ed-in2out-slowpath active 1 1 3.21e4 1.00四个关键列:
| 列 | 含义 | 怎么用 |
|---|---|---|
Calls | 这个节点被调用了多少次 | polling 模式下输入节点会有天文数字,是空转 |
Vectors | 处理了多少个包 | 和 Calls 一起决定批处理效率 |
Packet-Clocks | 每个包消耗的 CPU 时钟 | 找性能瓶颈就看这一列 |
Vectors/Call | 平均每次调用处理几个包 | VPP 性能的核心指标,见性能那一篇 |
上面这组数据里藏着一个重要事实:
text
nat44-ed-in2out 615 clocks/包 ← 快路径
nat44-ed-in2out-slowpath 32100 clocks/包 ← 慢路径,贵 52 倍标准诊断流程
把这些工具串起来,形成一套固定动作:
第 2 步(看计数器)最关键,因为大部分 NAT 故障都有明确的计数器签名。 下一篇专门讲这些签名。
动手练一遍
bash
./labctl reset base
./labctl apply base 11
./labctl verify base 11这个实验会故意制造四种典型故障,每种都完整走一遍诊断流程。
小结
| 工具 | 用途 |
|---|---|
trace add <输入节点> <数量> | 抓逐跳路径,排查"包走到哪没了" |
trace filter include/exclude | 流量大时只保留关心的包 |
pcap trace rx tx intfc X file Y | 存成 pcap,用 Wireshark 看字节 |
pcap trace drop | 只抓被丢弃的包,守株待兔 |
pcap dispatch trace | 包内容 + 节点路径一起存 |
show runtime | 节点级性能视图,找瓶颈看 Packet-Clocks |
show errors | 错误计数器,诊断第一站 |
trace 里最该盯的三行:search key(查表用的键)、 i2of/o2if(改写规则)、以及 节点序列(走没走 slowpath、在哪断的)。
下一篇:典型故障速查。