1. 从一个抓包现场说起为什么IP分片值得单独拎出来讲很多人第一次用Wireshark抓包注意力全在TCP三次握手、HTTP请求响应这些上层剧情上IP层往往被当成一个透明的信封扫一眼源目地址就翻过去了。直到某天你在排查一个大包发不出去、小包一切正常的诡异故障或者发现抓到的报文里冒出一堆标记着Fragmented IP protocol的条目才会意识到IP分片这个机制有多重要。IP分片IP Fragmentation是IPv4协议里一个非常基础但又极易被忽视的机制。简单说当一个IP数据报的长度超过了出口链路的MTU最大传输单元路由器或发送端就必须把这个大包切成若干个小片分别发送到了目的地再重新拼装起来。这个切和拼的过程就是分片与重组。它解决的核心问题是不同链路的MTU不一样一个1500字节的包到了MTU只有1400的链路上就过不去了必须拆。这篇文章要聊的就是怎么用Wireshark把IP报文的分片与不分片行为看得清清楚楚。我会从抓包环境的搭建讲起拆解IP头部里跟分片相关的几个关键字段演示分片报文在Wireshark里长什么样、怎么过滤、怎么判断重组是否完整最后重点分享几个我在实际排查中踩过的坑。适合已经会用Wireshark做基本抓包、但对IP层细节还不够熟的运维和开发同学也适合正在准备网络相关认证、需要把协议字段吃透的朋友。需要提前说明的是分片这个话题绕不开MTU、MSS、DF标志位这几个概念它们之间是联动的我会在讲Wireshark操作的同时把这些关系理清楚不然你抓到了包也看不懂它为什么长那样。2. 抓包前的准备环境、工具与关键参数设定2.1 抓包点的选择决定了你能看到什么抓包位置这件事很多人不当回事随手就在自己电脑上开抓结果发现根本抓不到分片报文。原因很简单分片通常发生在路径中间的某台设备上或者发生在发送端因为本地网卡MTU限制而主动分片时。如果你抓包的位置在分片发生点之后、重组点之前你才能看到分片如果抓包点在重组点之后你看到的就是已经拼好的完整报文。我一般会这样选抓包点排查发送端主动分片直接在发送端主机上抓看它发出的包是否被切成了多片。排查中间设备分片需要在分片设备的上下游分别抓对比进出报文的大小和分片标志。验证重组行为在接收端主机上抓看是否收到了所有分片以及是否成功重组。在Wireshark里抓包接口的选择也很关键。有线网卡和无线网卡的MTU往往不同虚拟网卡比如各类虚拟化平台创建的网卡的MTU又可能是另一个值。你可以在Wireshark的捕获选项里看到每个接口的详细信息也可以直接在系统里用命令查当前接口的MTU。Linux下查MTUip link show # 或者针对某个接口 ip link show eth0Windows下查MTUnetsh interface ipv4 show subinterfaces这两个命令输出的MTU值就是你判断这个包会不会被分片的第一手依据。记住一个粗略的判断IP报文总长度含IP头超过出口MTU且DF标志位为0就会被分片。2.2 Wireshark的关键配置项默认配置下的Wireshark其实已经能抓到分片报文但有几个设置我建议你调整一下不然分析起来会很别扭。第一个是重组选项。Wireshark默认会尝试重组IPv4分片这在大多数时候是好事但如果你想看清楚每一个分片的原始样子就得把这个功能关掉或者调整。路径在编辑→首选项→Protocols→IPv4里面有个Reassemble fragmented IPv4 datagrams选项。勾选时Wireshark会把分片拼起来显示取消勾选则每个分片单独显示。我的习惯是排查阶段先取消勾选看清楚分片结构确认没问题后再勾选上验证重组结果。第二个是列显示的自定义。默认的Wireshark列里没有分片相关字段你得手动加。右键列标题→列首选项可以添加这几列列名称字段类型字段名作用Identification自定义ip.id同一数据报的所有分片ID相同Flags自定义ip.flags看DF、MF标志位Fragment Offset自定义ip.frag_offset分片在原始数据报中的位置Total Length自定义ip.len每个分片的总长度加上这几列之后分片报文在列表里就一目了然了同一个Identification值的多个包就是同一个原始数据报被切出来的片。第三个是捕获过滤器。如果你只想抓特定主机的分片流量可以在捕获前就设好过滤器减少噪音。比如只抓某台主机的流量host 192.0.2.10注意这是捕获过滤器BPF语法跟显示过滤器Wireshark语法不是一回事别搞混了。捕获过滤器在抓包开始前生效能减少抓到的数据量显示过滤器在抓包后生效只影响显示。2.3 制造分片流量的几种实用方法光等着自然产生的分片流量太被动了排查时我们通常需要主动制造分片来验证。这里分享几种我常用的方法。方法一用ping命令配合大包和DF标志。这是最经典的手段。ping默认发的包很小不会分片。你可以指定包大小并且控制DF标志# Linux下发送一个3000字节的包不允许分片 ping -M do -s 3000 192.0.2.20 # 允许分片默认行为不加-M do ping -s 3000 192.0.2.20-M do表示禁止分片DF1如果包超过MTU发送端会直接报错Message too long这正好可以用来探测路径MTU。不加这个参数包就会被分片发出去你在Wireshark里就能看到分片报文。方法二用大UDP包。UDP没有MSS协商机制发送端想发多大就发多大超过MTU自然就分片了。可以用一些工具发大UDP包或者自己写个简单的脚本。方法三调整本地MTU。把发送端的网卡MTU临时调小比如从1500调到1000然后发一个正常大小的包它就会被分片。这个方法在Linux下很方便sudo ip link set dev eth0 mtu 1000 # 测试完记得改回来 sudo ip link set dev eth0 mtu 1500提示调整MTU属于影响网络连通性的操作建议在测试环境或自己的实验机上做别在生产服务器上随手改。这几种方法各有适用场景。ping适合快速验证和探测大UDP包适合模拟真实应用的分片行为调MTU适合在受控环境下观察分片全过程。我一般会先用ping确认分片机制正常工作再用实际应用的流量来复现问题。3. IP头部里的分片密码字段逐个拆解3.1 三个决定命运的字段Identification、Flags、Fragment Offset要读懂分片必须先把IP头部里跟分片相关的三个字段吃透。它们在IP头里是挨着的一共占4个字节32位Wireshark会把它们展开显示。Identification标识16位这是同一个原始数据报的所有分片的身份证号。发送端每发出一个数据报就分配一个ID如果这个数据报被分片了所有分片共用这个ID。接收端就是靠这个ID把属于同一个原始数据报的分片归拢到一起的。注意这个ID只有16位也就是说同时最多有65536个不同的ID在流转高速网络下ID会回绕所以重组时还要结合源目地址和协议号来唯一确定一个数据报。Flags标志3位这3位里实际用到2位。第0位保留必须为0。第1位是DFDont Fragment不分片。DF1表示这个包不允许被分片如果它超过了链路MTU路由器会丢弃它并返回一个ICMP差错报文类型3代码4需要分片但设置了DF。DF0表示允许分片。第2位是MFMore Fragments还有分片。MF1表示后面还有分片MF0表示这是最后一个分片或者这个包根本没被分片。Fragment Offset分片偏移13位表示当前分片的数据部分在原始数据报数据部分中的位置单位是8字节。为什么是8字节因为13位最多表示8191如果按字节算只能表示8KB太小了按8字节为单位最大能表示65528字节足够覆盖IP数据报的最大长度65535字节。这也意味着每个分片的数据部分长度必须是8的倍数除了最后一个分片。这三个字段的组合逻辑是这样的场景DFMFFragment Offset说明完整未分片包0或100正常报文分片的第一个片010起始片分片的中间片01非0中间片分片的最后一片00非0结束片不允许分片的大包100超MTU会被丢弃看这张表就能明白判断一个包是不是分片关键看MF和Fragment Offset只要MF1或者Fragment Offset≠0它就是分片。3.2 Wireshark里这些字段长什么样在Wireshark的报文详情面板里展开Internet Protocol Version 4这一层你能看到这样的结构以某个分片为例Internet Protocol Version 4, Src: 192.0.2.10, Dst: 192.0.2.20 0100 .... Version: 4 .... 0101 Header Length: 20 bytes (5) Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) Total Length: 1500 Identification: 0x1a2b (6699) Flags: 0x2000, More Fragments 0... .... .... .... Reserved bit: Not set .0.. .... .... .... Dont fragment: Not set ..1. .... .... .... More fragments: Set Fragment offset: 0 Time to live: 64 Protocol: UDP (17) Header checksum: 0x.... Source Address: 192.0.2.10 Destination Address: 192.0.2.20这里Flags显示为0x2000二进制是0010 0000 0000 0000第2位MF为1DF为0。Fragment offset为0说明这是第一个分片。Total Length是1500说明这个分片本身有1500字节含20字节IP头数据部分1480字节。再看第二个分片它的Identification应该还是0x1a2b但Fragment offset会变成1851480/8185MF可能还是1如果还有后续分片或者0如果是最后一片。3.3 分片与MSS、MTU的联动关系这里必须把MTU和MSS的关系讲清楚不然你会对为什么分片大小是那个数感到困惑。**MTUMaximum Transmission Unit**是链路层能承载的最大数据帧 payload以太网默认1500字节。这个1500字节要装下IP头通常20字节带选项可能更多加上IP的数据部分。所以IP数据报最大能到1500字节不含以太网帧头尾。**MSSMaximum Segment Size**是TCP层的概念表示TCP报文段里数据部分的最大长度。TCP建立连接时会协商MSS通常MSS MTU - IP头 - TCP头 1500 - 20 - 20 1460字节。TCP不会让单个报文段超过MSS所以正常情况下TCP流量不会触发IP分片。那什么时候会分片两种情况UDP或ICMP等没有MSS协商的协议发送端可能发出超过MTU的包触发分片。路径MTU变小比如中间某段链路MTU只有1400而发送端按1500发的包到了那里就得由路由器分片如果DF0。理解了这个联动关系你就能预判一个1500字节MTU链路上UDP包如果数据部分超过1480字节1500-20就会被分片。分片时第一个片的数据部分通常是1480字节8的倍数后续片按同样规则切分最后一片可能不足1480。注意分片偏移以8字节为单位所以每个非最后分片的数据长度必须是8的倍数。1480正好是8的倍数1480/8185这是巧合也是设计使然——MTU减去IP头后通常都是8的倍数。4. 实操用Wireshark完整观察一次分片与重组4.1 制造并捕获分片流量我们来走一遍完整流程。假设有两台实验机A是发送端192.0.2.10B是接收端192.0.2.20中间是以太网MTU都是1500。第一步在B上启动Wireshark抓包捕获过滤器设为host 192.0.2.10。第二步在A上发送一个大UDP包。我用一个简单的Python脚本来发这样可控性最强import socket # 发送一个4000字节的UDP数据报 data bA * 4000 sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(data, (192.0.2.20, 9999)) sock.close()4000字节的数据加上UDP头8字节和IP头20字节总共4028字节远超1500的MTU必然分片。第三步回到B的Wireshark停止抓包你应该能看到类似这样的报文列表No. Time Source Destination Protocol Length Info 1 0.000000 192.0.2.10 192.0.2.20 UDP 1514 Source port: 54321 Destination port: 9999 2 0.000012 192.0.2.10 192.0.2.20 IPv4 1514 Fragmented IP protocol (protoUDP 17, off1480, ID0x1a2b) 3 0.000025 192.0.2.10 192.0.2.20 IPv4 1102 Fragmented IP protocol (protoUDP 17, off2960, ID0x1a2b)注意看第一个包Wireshark识别为UDP因为它是第一个分片包含了UDP头Wireshark能解析出来。第二、三个包被标记为Fragmented IP protocol因为它们是后续分片不含UDP头Wireshark无法直接解析成UDP。三个包的Length分别是1514、1514、1102。1514是1500IP总长14以太网帧头。第一个分片IP总长1500数据部分1480第二个分片IP总长1500数据部分1480第三个分片IP总长10881102-14数据部分1068。1480148010684028正好等于原始数据报总长4000数据8 UDP头20 IP头4028。等等这里要仔细算原始IP数据报的数据部分是400084008字节加上IP头20字节总长4028。分片时每个分片都有自己的IP头20字节所以分片1IP总长1500数据部分1480分片2IP总长1500数据部分1480分片3IP总长1068数据部分10481480148010484008正好是原始数据部分。对上了。4.2 用显示过滤器精准定位分片抓到一堆包之后怎么快速筛出分片Wireshark提供了几个好用的显示过滤器。筛出所有分片报文ip.flags.mf 1 || ip.frag_offset 0这个过滤器的逻辑是MF1的包还有后续分片或者偏移量大于0的包不是第一个片都是分片。筛出某个特定数据报的所有分片ip.id 0x1a2b把0x1a2b换成你实际看到的Identification值就能把同一个原始数据报的所有分片筛出来。筛出设置了DF标志的包ip.flags.df 1这个在排查为什么大包被丢弃时特别有用因为DF1的大包超MTU会被丢。筛出被分片的UDP流量udp ip.flags.mf 1注意这个只能筛出第一个分片因为只有第一个分片能被解析成UDP后续分片得用ip.id关联。我通常的操作流程是先用ip.flags.mf 1 || ip.frag_offset 0把所有分片捞出来看看有没有异常的分片行为然后针对某个可疑的ID用ip.id xxx深入看它的所有分片。4.3 验证重组结果Wireshark默认开启IPv4重组所以当你选中第一个分片时详情面板底部会出现一个[Reassembled IPv4 in frame: X]的链接点它能跳到重组后的完整报文。反过来选中最后一个分片也会有[Reassembled IPv4 in frame: Y]指向重组结果。如果你想确认重组是否完整可以看第一个分片的详情里有没有Reassembly error之类的提示。常见的重组问题包括分片丢失某个中间分片没抓到Wireshark会提示Reassembly error, fragment missing。分片重叠不同分片的偏移量有重叠这在某些攻击场景里会出现Wireshark会标记。超时重组有超时限制通常30秒或60秒超时未收齐所有分片重组失败。我一般会这样验证先关掉重组选项数一数分片数量算一算偏移量是否连续再打开重组选项看Wireshark能否成功拼出完整报文。两步都通过说明分片和重组都正常。4.4 对比不分片的情况为了对比我们把DF标志设上再发一次大包。在Linux下用pingping -M do -s 3000 192.0.2.20因为3000283028字节超过MTU 1500且DF1这个包会被直接丢弃ping会报错ping: local error: message too long, mtu1500同时在Wireshark里你抓不到任何发往192.0.2.20的大包因为它在发送端就被拦下了。如果是在中间路由器上因为DF1被丢你可能会抓到路由器返回的ICMP差错报文类型3代码4。这个对比很关键DF1时超MTU的包要么在发送端报错要么在中间被丢并返回ICMPDF0时超MTU的包会被分片发送。理解了这一点你就能解释很多大包不通小包通的故障了。5. 常见问题与排查技巧实录5.1 抓不到分片报文怎么办这是新手最常遇到的问题。抓不到分片通常有这几个原因原因一抓包点不对。分片发生在A点你在B点抓而B点在重组点之后自然看不到分片。解决办法是往上游找抓包点或者直接在发送端抓。原因二流量根本没触发分片。如果应用发的是小包或者TCP流量受MSS限制根本不会分片。解决办法是主动制造大包流量用前面讲的ping或UDP方法。原因三网卡卸载功能干扰。现代网卡普遍支持TSOTCP Segmentation Offload、LROLarge Receive Offload等卸载功能这些功能会把分片/重组的工作交给网卡硬件做导致Wireshark在软件层抓到的包和实际链路上的包不一致。你可能抓到的是没分片的大包但实际链路上是分片的。解决办法是临时关闭这些卸载功能# Linux下关闭TSO和GSO sudo ethtool -K eth0 tso off gso off gro off关闭后再抓就能看到真实的分片行为了。这个坑我踩过不止一次尤其是在虚拟化环境里网卡卸载行为更复杂。原因四捕获过滤器太严。检查你的捕获过滤器是不是把分片流量过滤掉了。比如你只抓了某个端口但分片的后续片不含端口信息因为不含传输层头可能被过滤掉。这种情况建议放宽捕获过滤器或者干脆不设捕获过滤器用显示过滤器来筛。5.2 分片重组失败的排查思路重组失败通常表现为接收端应用收到不完整的数据或者Wireshark提示重组错误。排查思路如下第一步确认分片是否收齐。用ip.id xxx筛出所有分片看偏移量是否连续、有没有缺片。如果缺片往上游找丢包点。第二步检查是否有重叠分片。重叠分片可能是攻击行为也可能是设备bug。Wireshark会标记重叠你可以看详情里的提示。第三步检查重组超时。如果分片到达间隔太长超过了重组超时时间重组会失败。这种情况在高延迟链路上可能出现需要调整重组超时参数在接收端系统里调。第四步检查MTU和分片大小是否匹配。有时候分片大小设置不当比如某个分片超过了下一跳的MTU会导致二次分片或丢包。我把常见问题整理成一张速查表现象可能原因排查方法解决方向抓不到分片抓包点不对/网卡卸载/过滤器太严换抓包点、关卸载、放宽过滤器调整抓包策略重组失败分片丢失/重叠/超时用ip.id筛分片、看偏移量找丢包点、调超时大包不通小包通DF1且超MTU看是否有ICMP类型3代码4调MTU或改DF分片大小异常MTU配置不一致对比各接口MTU统一MTU配置性能下降分片重组消耗CPU看系统分片重组统计启用路径MTU发现5.3 几个容易踩的坑和独家心得坑一把IPv4分片和IPv6分片混为一谈。IPv6的分片机制和IPv4完全不同。IPv6里分片只能由发送端做中间路由器不允许分片而且IPv6用扩展头Fragment Header来承载分片信息不是主头里的字段。如果你在IPv6环境里用IPv4的过滤语法会一无所获。IPv6的分片过滤要用ipv6.fraghdr相关字段。坑二忽略Identification的回绕。前面说过Identification只有16位高速网络下会回绕。如果你用ip.id xxx筛分片在流量大的时候可能筛出不同数据报的分片因为ID重复了。这时候要结合源目地址和时间戳来区分。坑三以为所有分片都能被Wireshark解析成上层协议。只有第一个分片含传输层头后续分片Wireshark只能识别到IP层。所以你不能用udp.port 9999来筛后续分片得用ip.id关联。坑四在NAT环境下分片行为会变。NAT设备在转换地址时可能需要修改端口号而端口号只在第一个分片里。有些NAT设备会因此重组再分片有些则要求所有分片都经过它。这会导致分片行为和你预期的不一样。排查NAT环境的分片问题最好在NAT设备两侧都抓包对比。心得一善用Follow UDP Stream。虽然分片的后续片不含UDP头但Wireshark重组后你可以对重组后的报文用Follow UDP Stream看到完整的应用层数据。这个在分析分片应用流量时很好用。心得二用tshark做批量分析。如果你要分析大量抓包文件里的分片情况Wireshark的GUI就不够用了用tshark命令行更高效# 统计分片报文数量 tshark -r capture.pcap -Y ip.flags.mf 1 || ip.frag_offset 0 -T fields -e ip.id | sort | uniq -c这个命令能列出每个Identification对应的分片数量快速发现异常。心得三关注ICMP差错报文。当DF1的包超MTU被丢时路由器会返回ICMP类型3代码4的报文里面携带了下一跳的MTU值。这个报文是路径MTU发现PMTUD的基础。如果你在抓包里看到这类ICMP说明路径上有MTU不匹配的问题可以据此调整发送端的MSS或MTU。6. 从抓包到定位一个完整的排查案例复盘6.1 故障现象与初步抓包某次排查一个文件上传偶尔失败的问题。现象是小文件上传正常大文件上传到一半就卡住重试有时能成功有时不能。应用层日志只显示连接超时没有更多信息。我在客户端和服务端分别抓包。客户端抓包显示上传大文件时TCP报文段大小正常不超过MSS 1460没有IP分片。但服务端抓包显示客户端发来的某些TCP报文段被分片了而且分片的后续片有丢失。这就矛盾了客户端说它没分片服务端说收到了分片。问题出在中间路径上。6.2 逐层分析与定位我在路径中间的几台设备上分别抓包发现其中一段链路的MTU是1400而不是1500。客户端按1500的MTU发送的包到了这段链路因为DF0被路由器分片了。分片本身没问题但这段链路质量不好分片的后续片偶尔丢失导致服务端重组失败TCP报文段不完整触发重传重传又可能再次分片再次丢失最终超时。定位到问题后解决方案有两个方向一是让客户端感知路径MTU主动按1400发送二是在那段链路上启用路径MTU发现让客户端通过ICMP差错报文得知MTU变小。6.3 验证与经验总结我们最终在客户端启用了PMTUD并调整了TCP的MSS上限。重新测试大文件上传稳定了。验证方法是在客户端抓包确认发出的包不超过1400在服务端抓包确认不再收到分片。这个案例给我的经验是分片问题往往不是分片本身的问题而是MTU不一致或路径质量问题。抓包时不要只盯着分片报文看要结合ICMP差错报文、TCP重传、链路质量一起分析。另外DF0虽然允许分片但在质量不好的链路上分片反而增加了丢包概率片越多丢任意一片就全废所以能不分片就不分片尽量让发送端按路径MTU发送。我个人在实际操作中的体会是Wireshark抓分片这件事工具本身不难难的是理解分片背后的MTU、MSS、DF、PMTUD这一整套联动机制。你把这几个概念的关系理清了再看抓包结果很多现象就顺理成章了。最后再分享一个小技巧如果你怀疑某个设备在偷偷分片可以在它两侧同时抓包对比同一个数据报的Identification和分片标志一眼就能看出它有没有动手脚。这个内容后续还可以扩展到IPv6分片、隧道环境下的分片比如各种封装协议导致的有效MTU变小那些场景的分片行为又有一套不同的规则值得单独再写一篇。
阅读完成 · 觉得有帮助?