首页 / 资讯中心 / 文章详情

Wireshark抓包实战:IP分片与不分片原理、实验与故障排查

Wireshark抓包实战:IP分片与不分片原理、实验与故障排查 ★ FEATURED ARTICLE
1. 从一个抓包现场说起为什么IP分片值得单独拎出来讲很多人第一次用Wireshark抓包注意力全在TCP三次握手、HTTP请求响应这些看得见的协议上IP层往往被当成一个透明的信封扫一眼源目地址就翻过去了。直到某天你在过滤器里敲下ip.flags.mf 1或者ip.frag_offset 0发现抓出来的包长得跟平时不太一样——有的包没有TCP头有的包长度是1480、2960这种非整数有的包在Wireshark里显示成Fragmented IP protocol你才会意识到IP分片这件事平时不显山不露水一旦出问题就是那种ping得通但传不了文件小请求正常大请求超时的诡异故障。这篇内容就是围绕Wireshark捕获IP报文时的分片与不分片这个主题展开的。我会把IP分片的来龙去脉、Wireshark里怎么识别分片、怎么构造分片和不分片的对比实验、以及实际排查中怎么利用这些知识定位问题完整地讲一遍。适合已经会用Wireshark做基本抓包、但对IP层细节还比较模糊的网络运维、后端开发、以及准备网络相关认证的朋友。读完你至少能做到三件事第一看到分片包能立刻判断它属于哪个原始报文第二能主动构造分片和不分片的流量做对比验证第三遇到MTU相关的疑难杂症时知道从哪儿下手。先说一个我自己的经历。早些年排查一个上传大文件偶尔失败的问题应用层日志只报超时没有任何有用信息。抓包之后发现小文件的上传请求都是正常的一个TCP段搞定大文件在传输过程中出现了大量没有TCP头的IP包Wireshark把它们标成Fragmented IP protocol。当时第一反应是这什么鬼后来才明白这就是IP分片而且分片本身不是问题问题是其中某个分片在中间某跳被丢弃了导致整个报文重组失败。从那以后我就养成了一个习惯抓包先看IP层有没有分片这能省掉大量瞎猜的时间。2. IP分片到底是怎么回事原理拆解与关键字段2.1 分片的本质链路层MTU与IP层长度的矛盾要理解分片得先理解一个矛盾。IP报文在网络上传输最终要落到某条物理链路上而每条链路都有一个最大传输单元MTUMaximum Transmission Unit也就是这条链路一次能承载的最大数据帧长度。以太网最常见的MTU是1500字节。而IP协议本身设计上是支持最大65535字节的报文长度的。当一个IP报文的大小超过了出口链路的MTU它就没法作为一个整体发出去这时候就需要分片Fragmentation——把一个大IP报文拆成若干个不超过MTU的小片每片独立封装成帧发送到了目的地再由接收方重组。这里有个容易混淆的点MTU限制的是帧的数据部分不是整个帧。以太网帧头14字节、帧尾FCS 4字节所以1500字节MTU意味着IP报文最大1500字节。而IP报文头本身占20字节不含选项所以TCP/UDP能用的数据部分最多是1500 - 20 1480字节。这就是为什么你经常看到1480这个数字——它是以太网环境下TCP MSS的典型值。分片可以发生在发送端主机也可以发生在中间路由器。发送端主机根据自己出口接口的MTU决定要不要分片中间路由器如果收到一个报文下一跳链路MTU比报文小理论上也可以分片IPv4允许IPv6不允许这是后话。但实际网络中我们更希望由发送端来做因为中间路由器分片会增加它的负担而且分片后的报文如果再有分片丢失重组就失败了。2.2 三个决定性的IP头字段Identification、Flags、Fragment OffsetIP分片能工作全靠IP头里三个字段配合。这三个字段也是你在Wireshark里识别和分析分片的核心抓手。Identification标识16位同一个原始报文的所有分片这个字段的值是相同的。接收方靠它把属于同一个报文的分片归拢到一起。注意这个字段本身不保证唯一不同原始报文可能碰巧用同一个值所以重组时还要结合源IP、目的IP、协议号一起判断。Flags标志3位实际用到两位。**DFDont Fragment不分片**位置1表示这个报文不允许被分片如果它超过了链路MTU路由器会直接丢弃并返回一个ICMP差错需要ICMP不可达消息类型3代码4也就是需要分片但设置了DF位。**MFMore Fragments更多分片**位置1表示后面还有分片置0表示这是最后一个分片。这里有个细节最后一个分片的MF是0但它的Fragment Offset不为0所以判断是不是分片不能只看MF要看MF为1或者Offset大于0。Fragment Offset分片偏移13位表示当前分片的数据在原始报文数据部分中的位置单位是8字节。为什么是8字节因为13位最大能表示8191乘以8等于65528刚好能覆盖IP最大数据部分65515字节65535减去20字节头。用8字节做单位是为了在有限的位数里表达足够大的偏移。这也意味着分片的数据部分长度必须是8的整数倍除了最后一个分片否则偏移没法对齐。把这三个字段串起来看发送端要分片时把原始数据按MTU切成若干块每块不超过MTU - IP头长度且除最后一块外都是8的整数倍。每块加上IP头Identification填同一个值MF除了最后一块都置1Offset按块在原始数据中的位置除以8填写。接收端收到后按Identification分组按Offset排序拼回原始数据。2.3 重组发生在哪里接收端主机的IP层分片的重组只在最终目的主机上进行中间路由器不负责重组。这是IP协议的一个基本设计。原因很简单如果中间路由器要重组它就得缓存所有分片直到收齐这会消耗大量内存而且路径可能不对称分片可能走不同路径到达中间节点根本凑不齐。所以路由器只管转发分片重组是接收端的事。这个设计带来一个实际影响如果任何一个分片在传输中丢失接收端就永远凑不齐整个原始报文被丢弃。而且接收端不会为丢失的分片单独重传重传是上层协议比如TCP的事。这就是为什么分片环境下丢包的代价特别高——丢一个分片等于丢整个报文。TCP如果发现某个段没被确认会重传整个段但如果这个段又被分片又可能再丢形成恶性循环。2.4 IPv4和IPv6在分片上的关键差异虽然这篇主要讲Wireshark抓IP报文但有必要提一下IPv6的差异因为现在很多环境是双栈的。IPv6不允许中间路由器分片分片只能由发送端做。IPv6头里没有Identification、Flags、Fragment Offset这些字段分片信息放在一个独立的**分片扩展头Fragment Extension Header**里。所以你在Wireshark里看IPv6分片结构跟IPv4完全不一样过滤器也要换成ipv6.fraghdr相关的字段。IPv6还要求路径MTU发现必须工作否则发送端不知道该按多大分片。这些差异在实际抓包时如果搞混会浪费很多时间。3. Wireshark里怎么识别和分析IP分片3.1 分片在Wireshark里的显示特征Wireshark对分片的处理很直观。当你抓到一个分片包在协议列它会显示成Fragmented IP protocol而不是常见的TCP或UDP。点开详情IP层下面会多出一个Flags和Fragment offset的展开项。如果是第一个分片Offset为0它通常还带着完整的传输层头TCP/UDP你能看到端口号后续分片Offset大于0就没有传输层头了协议列直接是Fragmented IP protocol因为传输层头只在第一个分片里。这里有个Wireshark的贴心设计它会把属于同一个原始报文的分片自动关联起来。你在第一个分片上右键选Follow或者看Reassembled相关的选项Wireshark能帮你把重组后的完整报文展示出来。不过要注意这个重组是Wireshark自己做的不代表网络里真的重组成功了——如果某个分片没抓到Wireshark也重组不出来它会提示Reassembly error或者干脆只显示已有的分片。3.2 核心过滤器快速筛出分片和不分片的包Wireshark的显示过滤器是分析分片的利器。下面这几个过滤器我几乎每次排查MTU问题都会用到过滤器表达式作用使用场景ip.flags.mf 1筛选所有MF位为1的分片即非最后一片快速定位分片流量ip.frag_offset 0筛选所有偏移大于0的分片即非第一片找后续分片ip.flags.df 1筛选设置了DF位的报文检查哪些流量禁止分片ip.flags.df 0 ip.len 1500筛选可能被分片的大报文定位分片源头ip.id 0x1a2b按Identification筛选特定报文的所有分片追踪单个原始报文icmp.type 3 icmp.code 4筛选需要分片但DF置位的ICMP差错排查PMTUD问题实际用的时候我通常先ip.flags.mf 1 || ip.frag_offset 0把分片全捞出来看看有没有分片流量如果有再按Identification分组看是哪个原始报文在分片。如果怀疑是DF位导致的丢包就ip.flags.df 1配合ICMP差错一起看。3.3 用统计功能看分片全貌光看单个包不够Wireshark的统计功能能帮你从宏观上把握分片情况。Statistics - IPv4 Statistics - All Addresses能看到IP层的整体分布。更实用的是Statistics - Protocol Hierarchy如果分片流量多你会看到Fragmented IP protocol占了一定比例。另外Statistics - Conversations里按IP对话看能发现哪些主机对之间在大量分片。我个人的习惯是抓包先跑一遍Protocol Hierarchy如果Fragmented IP protocol占比超过1%就值得深挖了。正常网络里分片应该是很少的大量分片往往意味着MTU配置有问题或者有应用在发超大UDP包。3.4 一个容易被忽略的点Wireshark的重组设置Wireshark默认会尝试重组IP分片但这个行为可以在Preferences - Protocols - IPv4里调整。有个选项叫Reassemble fragmented IPv4 datagrams默认是勾选的。如果你在做分片相关的实验有时候需要关掉这个重组这样才能看到原始的分片包而不是被Wireshark拼好的完整报文。这个坑我踩过——有次做分片实验怎么抓都只看到完整报文后来才发现是Wireshark默默帮我重组了关掉之后分片才原形毕露。4. 动手实验构造分片与不分片的对比流量光看理论不够得自己动手造分片流量才能把知识变成肌肉记忆。下面这套实验方案我在不同环境里复现过很多次用最少的工具就能跑通。4.1 实验环境与工具准备实验需要两台能互相通信的主机一台当发送端一台当接收端中间最好能经过一台可以抓包的路由器或者用镜像端口。如果条件有限两台主机直连、在其中一台上抓包也行。工具方面Linux环境下用ping、traceroute、nc就够了Windows下用ping的-l和-f参数也能做基础实验。抓包统一用Wireshark。关键是要能控制报文大小和DF位。ping命令天然支持这两个控制-s指定数据部分大小Linux-l指定大小Windows-M do或-f设置DF位。这就够了。4.2 实验一构造一个会被分片的大ICMP报文在Linux发送端执行ping -s 3000 -c 3 接收端IP-s 3000表示ICMP数据部分3000字节加上ICMP头8字节、IP头20字节整个IP报文是3028字节远超1500的MTU必然分片。在接收端或中间节点抓包你会看到类似这样的分片序列分片1IP总长1500MF1Offset0带ICMP头分片2IP总长1500MF1Offset1851480/8185分片3IP总长68MF0Offset3702960/8370注意第三片的总长是68因为3028 - 1480 - 1480 68这是最后一片MF为0。三片的Identification相同。在Wireshark里第一片能看到ICMP类型和序列号后两片看不到协议列显示Fragmented IP protocol。这里有个计算细节值得说清楚为什么第一片数据是1480字节因为MTU 1500减去IP头20字节等于1480。1480能被8整除1480/8185所以Offset是整数。如果MTU不是1500而是别的值比如1400那第一片数据是1380字节1380/8172.5不能整除这时候实际会取1376字节172*8留4字节给下一片。这个向下取整到8的倍数是分片实现里的关键细节很多手写分片代码出错就错在这里。4.3 实验二设置DF位观察不分片时的行为同样的报文大小加上DF位ping -s 3000 -M do -c 3 接收端IP-M do表示DF置1。这时候发送端发现报文超过MTU但又不允许分片会直接报错ping: local error: message too long, mtu1500如果是在中间路由器上遇到MTU更小的链路路由器会丢弃并返回ICMP类型3代码4的差错。在Wireshark里你能抓到这个ICMP差错包里面会带上下一跳的MTU值。这个机制就是**路径MTU发现PMTUD**的基础——发送端先假设一个MTU发DF置位的包如果收到需要分片但DF置位的ICMP就调小MTU重试直到找到路径上最小的MTU。4.4 实验三用UDP构造分片观察传输层头的位置ICMP实验比较干净但实际业务里更多是UDP或TCP分片。用nc发一个大UDP包echo test data | nc -u 接收端IP 端口不过nc不好控制包大小更可控的方式是用Python的socket直接发import socket s socket.socket(socket.AF_INET, socket.SOCK_DGRAM) data bA * 4000 s.sendto(data, (接收端IP, 9999))4000字节的UDP数据加上UDP头8字节、IP头20字节总共4028字节会分成三片。在Wireshark里看第一片带UDP头能看到源目端口后两片没有UDP头。这里有个重要观察点UDP分片后只有第一片有端口信息如果后续分片丢失接收端根本不知道这个报文属于哪个socket只能等重组超时后丢弃。这就是为什么大UDP包在丢包环境下表现特别差。4.5 实验四对比不分片的小包作为对照发一个小于MTU的包ping -s 1000 -c 3 接收端IP1000字节数据加8字节ICMP头加20字节IP头总共1028字节小于1500不会分片。在Wireshark里看就是一个完整的ICMP包MF0Offset0协议列直接显示ICMP。把这个包和实验一的分片包放一起对比分片和不分片的区别一目了然。5. 分片相关的常见故障与排查实录5.1 典型症状小包正常大包失败这是分片问题最经典的表现。ping小包通ping大包不通HTTP小请求正常上传大文件超时DNS小查询正常大响应失败。根本原因通常是路径上某条链路MTU比发送端以为的小而发送端又设置了DF位或者中间设备把分片后的包丢了。排查思路先用ping -s 大值 -M do从发送端测看在哪一跳开始失败。如果发送端直接报message too long说明本地MTU就不够如果能发出去但收不到回应用traceroute配合大包大小逐跳测找到MTU变小的那一跳。Wireshark抓包看有没有ICMP类型3代码4的差错那个差错里会带下一跳MTU。5.2 分片丢失导致的重组失败如果抓包看到分片流量但接收端应用报错要检查是不是有分片丢失。在Wireshark里按Identification分组看每个原始报文的分片是否齐全。比如一个报文应该有三片但只抓到两片那第三片肯定在某个地方丢了。这时候要顺着路径逐跳抓包定位丢在哪一跳。分片丢失的原因很多中间设备缓冲区不足、分片被ACL拦截、分片重组超时、路径上不同分片走了不同路径导致乱序到达超过重组窗口等。我遇到过最隐蔽的一次是某台设备的接口配置了no ip unreachables导致PMTUD需要的ICMP差错被抑制发送端一直用大MTU发DF包全被丢弃而发送端又收不到任何反馈只能超时。5.3 分片与防火墙、NAT的交互问题很多防火墙和NAT设备对分片的处理有特殊逻辑。有些防火墙默认丢弃所有非首片分片因为非首片没有端口信息没法做基于端口的策略这会导致大包全挂。有些NAT设备在分片经过时只对首片做地址转换后续分片因为找不到对应的转换表项而被丢弃。这类问题的典型特征是不分片的小流量完全正常一旦分片就断。排查这类问题关键是抓包看分片在防火墙两侧的形态。如果一侧有分片、另一侧没有或者分片的Identification、地址在两侧不一致基本就能定位到设备。解决办法通常是调整设备的分片处理策略或者从源头避免分片——比如把发送端的MTU调小让报文在发送端就分好片或者干脆让应用层控制包大小。5.4 常见问题速查表症状可能原因排查方法解决方向小包通大包不通路径MTU小于发送端MTUDF位置位ping -M do逐跳测抓ICMP差错调小MTU或启用PMTUD抓包看到分片但应用失败分片丢失或重组超时按Identification检查分片完整性逐跳定位丢包点分片经过防火墙后消失防火墙丢弃非首片防火墙两侧对比抓包调整防火墙分片策略NAT后大包失败NAT只转换首片检查NAT转换表调整NAT分片处理Wireshark看不到分片重组功能开启关闭IPv4重组选项调整Wireshark首选项分片Offset不是8的倍数抓包或解析异常检查IP头解析确认抓包点无篡改5.5 几个实操避坑心得第一抓包点要选对。分片可能在发送端就产生了也可能在中间路由器产生。如果你在接收端抓包看到分片不代表发送端发出来就是分片的。要定位分片产生的位置得在路径上多个点同时抓包对比。第二注意Wireshark的Reassembled提示。Wireshark重组成功不代表网络里重组成功它只是把抓到的分片拼起来了。如果分片不全Wireshark会明确提示重组失败这个提示很有价值别忽略。第三DF位的默认行为因系统而异。Linux默认对TCP设置DF位走PMTUD对UDP不设Windows的默认行为也不完全一样。做实验时如果不显式指定DF结果可能跟预期不符。我建议实验时永远显式指定-M do或-M dont避免被系统默认值干扰。第四分片实验别在生产环境做。大量分片流量可能触发某些设备的防护策略或者影响正常业务。找个隔离的实验环境或者用虚拟化环境搭一套成本很低。6. 从分片看MTU与PMTUD把知识串起来6.1 MTU不匹配是分片问题的总根源说到底分片是MTU不匹配的产物。如果路径上所有链路的MTU都一致且发送端知道这个值它就能在发送前把报文切成合适大小根本不需要分片。问题就出在路径上MTU不一致和发送端不知道路径MTU这两件事上。路径MTU发现PMTUD就是解决这个问题的机制。发送端先按自己接口的MTU发DF置位的包如果路径上某跳MTU更小那跳会返回ICMP类型3代码4带上它的MTU。发送端收到后调小MTU重发直到包能通过。这个过程是动态的每个目的地可能不同。但PMTUD很脆弱。如果ICMP被防火墙拦了发送端收不到反馈就会一直用大MTU发全被丢弃。这就是所谓的PMTUD黑洞。很多大包不通的问题本质就是PMTUD黑洞。解决办法要么是放行必要的ICMP要么是在发送端手动把MTU调到一个保守值比如1400牺牲一点效率换稳定。6.2 TCP MSS与IP分片的关系TCP有个MSSMaximum Segment Size协商机制双方在握手时告诉对方自己能接收的最大TCP段大小。MSS通常等于MTU减去IP头和TCP头以太网下就是1500 - 20 - 20 1460。TCP层按MSS切分数据保证每个TCP段加上IP头后不超过MTU这样IP层就不需要分片了。所以TCP流量正常情况下不应该出现IP分片如果出现了说明MSS协商有问题或者路径MTU比协商时假设的小。这也是为什么排查TCP分片时我会先看握手阶段的MSS值。如果双方协商的MSS是1460但路径MTU只有1400那TCP段就会在IP层被分片。解决办法是调整MSS clamping让中间设备把MSS改小或者发送端直接调小MTU。6.3 一个完整的排查案例复盘最后复盘一个我处理过的案例把前面的知识串起来。某业务反馈上传文件超过100KB就失败小文件正常。抓包发现小文件上传是一个TCP段搞定大文件在传输中出现分片且分片不完整——每个原始报文应该有三片但只抓到两片第三片缺失。进一步在路径中间抓包发现第三片在某一跳之后消失。查那台设备的配置发现它有个基于分片的ACL默认丢弃非首片。调整ACL后问题解决。这个案例的排查链条是症状大文件失败- 抓包发现分片- 检查分片完整性发现缺失- 逐跳定位找到丢弃点- 查配置找到ACL- 调整解决。每一步都用到前面讲的知识识别分片、按Identification分组、逐跳对比、理解设备对分片的处理逻辑。我个人在实际操作中的体会是IP分片这个知识点看文档觉得很简单就那么几个字段但真正到了排查现场能不能快速定位取决于你对分片在哪儿产生、在哪儿重组、中间设备怎么对待它这条链路的理解有多深。Wireshark只是工具它把分片可视化给你看但怎么解读、怎么顺着线索往下挖靠的是对协议行为的理解。建议你找个实验环境把上面那几个实验亲手做一遍尤其是DF位和PMTUD那部分做一次比看十遍文档都管用。后续如果还想深入可以研究一下IPv6的分片扩展头以及分片重组超时对性能的影响这两个方向在实际运维里都挺有价值。
阅读完成 · 觉得有帮助?
咨询建站