VPN 上的 Ping 丢包:先把 Echo 请求分组,再算百分比
六个请求和三个应答并不能得出 50% 丢包的结论。在相除之前,先按源、标识符和序列号对回声请求分组。
一份抓包里有六个 Echo 请求和三个应答,有人却在工单里写下 50% 的丢包率。这个数字对抓包可能成立,对隧道却未必。这六个请求也许属于两个不同的会话,各自有不同的投递约定。先把它们分组再相除,比例就不再是报告里最有趣的部分。
步骤 1:按源和目的拆分数据流
一个 Echo 请求带有源和目的,这两个字段决定了它属于哪个会话。网关生成的探测使用网关的隧道地址,而代表 LAN 主机转发的探测则保留该主机的地址。远端可能对其中一个有返回路由、对另一个没有,于是两组表现出差异,原因与隧道毫无关系。
在开始计数之前按这些字段拆分抓包,并把目的保留为键的一部分。发往不同对端的两条数据流即使源相同也不可比较。
把分组和各自的计数写下来,因为下一步要决定哪些应答属于哪个分组。三个请求却没有匹配应答的分组是发现。三个请求配三个应答的分组是基线。
步骤 2:按标识符和序列号匹配
RFC 792 中的 Echo 格式带有标识符和序列号,并把它们视为把应答与其请求配对的字段。二者缺一不可。单看序列号并不唯一,因为不同的发送方或进程可能在同一抓包窗口内复用同一个值。标识符缩小发送方的范围,序列号缩小位置的范围,正是这一对字段让匹配站得住脚。
应答会反转其请求的地址方向,所以匹配是请求—应答对,而不是两个完全相同的元组。把请求的源与应答的目的比较、把请求的目的与应答的源比较,然后确认标识符和序列号一致。
观察窗口也要保留,因为较长的抓包可能包含对两个字段的正当复用。把窗口裁剪到一个突发,就能消除大部分歧义。这也会让计数对任何读工单的人都可以复现。
tshark -r capture.pcap -Y 'icmp.type == 8' -T fields -e frame.time -e ip.src -e ip.dst -e icmp.ident -e icmp.seq
tshark -r capture.pcap -Y 'icmp.type == 0' -T fields -e frame.time -e ip.src -e ip.dst -e icmp.ident -e icmp.seq步骤 3:在同一边界上比较元组
如果网络地址转换位于抓包点与对端之间,请求和应答中的地址按设计就会不同。 在同一处边界比较元组,或者显式携带映射关系,把差异计入。把转换前的请求和转换后的应答混在一起,会凭空造出线路上从未存在的不匹配。
在开始计数之前先确定边界,并在报告中写明,以便下一位能复现分组方式。客户端一侧的抓包和服务器一侧的抓包是两个不同的边界。各自回答“应答在哪里消失”的不同问题。
被转换的探测分组还会继承转换设备自身的状态,而这可能正是应答始终不来的真实原因。在责怪它之后的路径之前,先检查该状态。
tcpdump -ni any icmp and host 10.90.0.1 -c 20
tshark -r capture.pcap -Y 'icmp' -T fields -e ip.src -e ip.dst -e ip.ttl | head -20步骤 4:说明抓包能证明什么、不能证明什么
观察到的空隙并不能证明远端主机保持沉默。抓包点也许只能看到一个方向,过滤器可能丢弃应答,或者缓冲区在窗口期间发生溢出。每一种都产生同样的缺失和不同的结论。
把抓包位置、过滤表达式和丢弃计数器与计数一并记录,因为没有它们的比例无法审计。正是这三行让数字在事后站得住脚。
然后把观察所确立的内容与它所暗示的内容分开,用两句话而不是一句。第一句陈述计数和边界。第二句陈述假设以及什么能证实它。
当问题是某条路径是否丢弃数据包时,带明确源地址的受控探测比被动统计混合流量更有说服力。从同一台机器、在同一时间、发往同一对端。只改变一个变量,比较就成立。
报告分组与下一处边界
报告有三行:分组定义、每一组内匹配的计数,以及进行比较的边界。这样读者就能看出某一组完整应答、另一组没有应答,从而指向路由或权限,而不是随机丢包。
附上抓包文件和过滤表达式,以便复现分组。没有文件的摘要只是主张,不是结果。
ping -c 5 -I 10.80.0.6 10.90.0.1
ping -c 5 -I 10.30.0.25 10.90.0.1
tshark -r capture.pcap -Y 'icmp.type == 8 || icmp.type == 0' -T fields -e ip.src -e ip.dst -e icmp.ident -e icmp.seq | sort | uniq -c | head当一组显示五个应答、另一组一个也没有时,发现是:一个源有可用的返回路径,另一个没有。这比混合百分比有用得多,它把下一次检查指向返回路由,而不是一个无关的 VPN 实例。