
搞网络排障这些年我见过太多人一抓包就犯晕满屏十六进制不知道从哪看起。其实IPV4报头就是把网络层所有“控制信息”打包的地方寻址、分片、防环、协议识别全靠它。很多看似玄乎的问题——UDP大包发不出去、某些路径MTU不对、traceroute在某跳停顿——只要把报头字段读明白三分钟就能定位。这篇文章不想当RFC的搬运工而是想从一个天天和报文打交道的人的角度把IPV4报头的20字节默认结构彻底讲透并结合抓包、排障、MTU这些实际场景告诉你每个字段到底是怎么用的。无论你是刚入行的运维、在备考网络认证还是写网络程序的老会被分片坑到的开发者这篇都应该能帮你少走很多弯路。1. 从一次ping不通说起IPV4报头为什么值得啃透1.1 一个真实场景抓包前两眼一抹黑有一回远程处理一个跨三层访问故障客户的业务系统在服务器A客户端在另一个网段。路由协议都正常ping网关也通但访问业务端口就是不行。客户催得急我让现场同事在服务器上抓包得到的反馈是“有请求进来也有回包出去”。这就很有意思了请求到了回包也发了为什么客户端没收到后来我让同事把抓包文件发过来一层层看下去才找到线索。服务器发出的回包里IPV4报头的TTL字段已经接近耗尽数据包在返回路径上被某个设备丢弃而且这个设备没有回ICMP超时消息。也就是说业务方眼里“一切正常”的数据包实际上在半路已经死了。这个问题的定位靠的就是IPV4报头里一个很小的字段——TTL。这个经历让我养成了一个习惯遇到网络问题先看报头再谈其他。IPV4报头虽然只有20字节起步但它浓缩了网络层几乎所有的决策信息。你不会看它抓包就是看天书你会看它很多故障就是一眼的事。1.2 IPV4报头在网络协议栈里的位置先简单对齐一下位置。网络分层里IP层处在二层之上、四层之下。链路层负责在同一个物理链路内传帧传输层的TCP/UDP负责端到端的可靠传输和端口区分而网络层的IP负责的是“从源地址到目的地址”这一整条路径上的寻址和转发。IPV4报头就是这个名字里“报头”二字的实体——它像信封上的信息发件人地址、收件人地址、邮资戳记、限时要求、防拆标记。而TCP/UDP报头更像是信纸里的开头落款和正文内容。你检查一封信投递问题得先看信封写得对不对再考虑信纸内容有没有问题。抓包分析也一样先剥开IPV4报头基本能确定“这封信该往哪走、还能走多远、是不是被拆成了几封寄出”。1.3 这篇文章适合谁、能解决什么问题如果你正在做网络运维、技术支持或者备考网络类认证IPV4报头是绕不过去的硬知识。但我要强调的是它不只是考试内容。写网络程序的开发同事也会遇到明明UDP包发出了对方怎么收不到多半和分片、DF标志有关。这些都不难难的是你不知道报头里哪个字段在起作用。这篇文章我会把20字节的默认报头逐字段讲透解释每个字段为什么这么设计然后带你实际抓一次包亲手把二进制字段“翻译”成人话。接着重点讲分片、MTU黑洞、TTL超时这些实战价值最高的细节。最后用一张表告诉你排障时到底优先看哪几个字段。2. 20字节默认报头逐字段拆解设计背后的道理先放一张完整结构表后面逐个展开字段长度作用Version4位IP版本号IPv4固定为4IHL4位报头长度单位4字节默认5TOS8位服务类型现代主要用于DSCP/ECNTotal Length16位IP报文总长度报头数据Identification16位标识符分片重组用Flags3位DF/MF等标志Fragment Offset13位片偏移单位8字节TTL8位生存时间每跳减1Protocol8位上层协议号Header Checksum16位报头校验和Source Address32位源IP地址Destination Address32位目的IP地址Options0~40字节可选字段现代网络中基本不用2.1 版本号与首部长度最开始的4位4位IPV4报头第一个字节通常写成0x45。这个字节拆开来看高4位是版本号低4位是IHL。0x45等于二进制0100 0101前4位0100就是IPv4的版本号4后4位0101等于5表示报头长度是5个32位字也就是20字节。为什么要用4位来表示“单位是4字节”的报头长度因为IP层做转发处理时希望所有字段都按照32位对齐这样CPU读取多个字节时效率最高。IHL最大能表示15所以报头长度最大是15乘以4等于60字节也就是默认20字节加上最多40字节的选项字段。很多初学者最容易忽略的是IHL字段的“可变性”。正常抓包看到的IPV4报文IHL基本都等于5。一旦遇到带选项的报文IHL变大路由器就需要额外计算报头长度处理性能会下降。这也是后面要说的“选项字段为什么被嫌弃”的根本原因之一。2.2 服务类型与总长度从“质量控制”到DSCP/ECN服务类型TOS字段一共8位早期设计者想用它来表达“这个包是否重要”比如延迟敏感、高吞吐、高可靠性之类的诉求。但实际用起来后发现运营商和大型网络需要更精细的QoS分类于是后来把前6位定义成了DSCP差分服务编码点后2位定义成了ECN显式拥塞通知。DSCP在语音、视频业务里非常常见。你可以把DSCP理解成“快递单上的服务等级标贴”EF标记通常给语音AF41这种给视频普通流量就是BE也就是0。路由器看到不同的DSCP会放进不同的队列决定先转发谁。ECN是后2位当路由器发生拥塞时不再直接丢包而是把ECN位标记为“拥塞遇到过”让接收端通知发送端降速。这种机制在TCP里能明显改善拥塞时的吞吐。实际抓包时在Wireshark里看到的DSCP字段值就是从这8位里拆出来的。总长度字段是16位单位是字节最大能表示65535字节。它表示整个IP报文报头载荷的总大小。需要注意IP总长度最大值是65535但数据链路层MTU通常只有1500左右真正能跑出65535字节的IP报文非常少见除非是多层链路内部传递或使用了巨型帧。你在Wireshark里看到一个IP报文的总长度是60、74、1514之类的数值而不是65535这个是正常的。2.3 标识、标志、片偏移分片三件套是怎么配合的这是IPV4报头里最需要整体理解的一组字段。16位的标识符、3位的标志字段、13位的片偏移字段它们共同配合完成IP分片和重组。为什么需要分片因为每种数据链路层技术都有MTU限制。以太网是1500字节如果上层要发一个3000字节的UDP报文IP层就必须把它拆成若干个“小报文”分别发送。拆出来的每个分片都有相同的标识符——16位Identification。接收端看到相同标识符的多个分片就知道它们属于同一个原始报文。3位Flags的具体含义是第一位保留必须为0第二位DFDont Fragment禁止分片第三位MFMore Fragments后面还有分片。DF置1时如果报文超过路径MTU路由器不会拆包而是直接丢包并回一个ICMP错误。MF用来标记是不是最后一个分片除了最后一片前面的分片MF都等于1最后一片MF等于0表示到此结束。片偏移字段是13位但它标记偏移量的单位不是字节而是8字节。为什么设计成8字节因为13位最大只能表示8192如果单位是1字节最大只能覆盖到8192字节的报文远不够65535。把单位定为8字节之后可表示范围扩大到65528字节刚好覆盖IP报文的最大长度。这个设计不是随意的而是为了精度和覆盖率之间取了一个折中。2.4 TTL、协议号、校验和三个容易被低估的字段TTLTime To Live8位设计初衷是“生存时间”早期用秒作为单位实际使用中简化成了“跳数”。每经过一台路由器TTL就减1减到0还没送到目的地路由器就丢弃这个包并返回一个ICMP超时报文给源端。这个机制解决了路由环路问题——没有TTL的话一个被错误路由反复转发的包会在网络里永远打转。协议号字段8位它告诉你IP报头后面跟的是哪种上层协议。常见值1是ICMP6是TCP17是UDP47是GRE50和51分别对应ESP和AH。很多新手把“协议号”和“端口号”混在一起其实它们在不同层IP报头里只有协议号TCP头里才有端口号。抓包想过滤“TCP流量”在Wireshark里可以写tcp底层其实就是根据协议号6来识别的。报头校验和16位它校验的是“IP报头本身”不校验后面的数据。为什么数据部分不用IP层管因为TCP和UDP层都有自己的校验和IP层再做一遍就重复了。而且路由器转发时TTL每次都会变校验和必须跟着重算如果IP校验和连数据一起算每台路由器还要反复读取整个报文性能代价太大。所以范围只限报头这是转发速度和安全性的折中。2.5 源地址、目的地址与选项字段灵活性的代价源地址和目的地址各32位这个没什么好说的就是IPv4地址本体。正常情况下每个IP报文都必须有这两个字段没有它们路由器根本无法工作。选项字段则有点“历史遗留”的味道。它设计初衷是灵活的可以记录路由路径、填写时间戳、指定某些特殊转发要求。但它有两个硬伤第一报文头会变得不固定路由器处理选项字段时要额外判断和解析严重影响转发性能第二很多选项字段可以被恶意利用比如源路由选项可以让数据包“绕开”网络管理员配置的路径这是安全上绝对不可接受的。所以现在的网络设备大多默认丢弃带选项的包尤其是公网上你几乎看不到带选项的IPV4报文。如果你用Wireshark在现网环境抓到了带Options的包大概率是网络扫描、探测类流量要引起警惕。选项字段加填充字段最多40字节所以IPV4报头默认20字节最大60字节IHL字段在这里起到了“报头有多长”的告知作用。3. 亲手抓一次包把报头字段从二进制里“剥”出来3.1 环境准备用Wireshark抓一个ping包讲完理论一定要上手抓包验证否则记不牢。环境不复杂一台装了Wireshark的电脑就行Windows或Linux都无所谓。Wireshark抓包需要底层驱动Windows上用NpcapLinux上直接用libpcap。打开Wireshark选择“WLAN”或“以太网”这样的接口点击开始捕获。然后在命令行执行一条ping命令比如ping -c 4 223.5.5.5Linux用-c控制次数Windows可以用ping -n 4。ping返回后回到Wireshark在显示过滤栏输入icmp就能看到四条请求和四条应答。双击任意一条ICMP请求包底下会出现一个十六进制对比面板上面是解析后的协议树下面是原始字节。3.2 十六进制里的0x45第一个字节说了两件事在Wireshark的字节面板里你会看到类似这样的开头0000 00 1c 42 ... 08 00 45 00 00 3c ...前面14个字节是以太网帧头从第15个字节开始才是IPV4报头。大多数ping包的第一个IP字节都是0x45。0x45用二进制展开是0100 0101。把8位拆成两组0100就是版本号40101就是IHL等于5。IHL等于5表示报头长度是5个32位字5乘以4等于20字节正好是IPV4默认报头长度。就这么一个字节同时告诉我们两件事这是一个IPv4包报头没有选项字段。再往后看几个字节0x00 0x00通常是TOS和部分DSCP字段接下来是总长度。抓包时如果看到类似00 3c换算成十进制就是60第三个字节数值等于60说明这个IP报文总长度是60字节这是绝大多数不带数据的ICMP echo请求的典型长度。3.3 Wireshark解析面板和原始字节的对应关系在Wireshark的协议树里你展开Internet Protocol Version 4这一行会看到它把每个字段都拆了出来Version: 4 —— 对应第一个字节高4位Header Length: 20 bytes —— 对应第一个字节低4位Total Length: 60 —— 对应第3、4字节Identification: 0xXXXX —— 对应第5、6字节Flags: 0x0 —— 3位标志位Fragment Offset: 0 —— 13位片偏移Time to Live: 64 —— 第9字节Protocol: ICMP (1) —— 第10字节Header Checksum: 0xXXXX —— 第11、12字节Source Address: 你的IP —— 第13到16字节Destination Address: 对端IP —— 第17到20字节你一边看十六进制原码一边看解析面板就能建立极强的对应认知。比如第9字节是0x40也就是十进制的64这就是Linux主机常见的初始TTL。第10字节是0x01代表ICMP协议。第13字节开始是c0 a8对应192.168之类的私网地址看到这种对应关系你以后读二进制报文就不会慌乱。Wireshark还有一个很有用的功能在过滤栏输入ip.version 4会自动把所有IPv4报文标出来用来快速筛选。做协议分析时建议把Wireshark的Preferences里的“Validate the IPv4 checksum if possible”选项打开它会在校验和正确时打上勾。3.4 用Python手算一次报头校验和看校验和正确与否是抓包基本功。IPV4校验和算法不复杂核心步骤是把报头按16位为一组全部做二进制反码求和再对结果取反。为了演示我写个简单的Python函数输入报头字节输出计算出的校验和def ip_checksum(header: bytes) - int: if len(header) % 2: header b\x00 s 0 for i in range(0, len(header), 2): word (header[i] 8) header[i1] s word if s 0xffff0000: # 有进位就回卷 s (s 0xffff) (s 16) return (~s) 0xffff使用的时候需要注意计算发送端的校验和时需要把报头里的校验和字段先置为0再加上整个报头做反码求和。接收端校验时则直接用“包含校验和字段的完整报头”做反码求和结果应该是0xffff如果结果不是0xffff就说明报头在传输中发生了改变。我自己在排查时常用这个脚本验证可疑报文的校验和。但有一个非常关键的实战提醒Wireshark抓到本机发送的包时经常会出现“Header checksum incorrect”的红色标红这未必是链路错误很可能只是网卡开启了checksum offload真正发出的数据包在网卡硬件里才补上正确的校验和。这个话题等会儿在排障章节详细说。4. 分片、DF标志和MTU黑洞报头里最值钱的实战细节4.1 IP为什么要分片MTU与路径MTUMTU是数据链路层能承载的最大帧大小。以太网通常是1500字节也就是说IP报文不能超过1500字节否则链路层放不下。当IP层要发送一个大于出接口MTU的报文时如果IPV4报头里的DF标志为0就可以进行分片。分片在哪发生可能发生在源主机也可能发生在中间的某台路由器。如果一条路径上不同链路MTU不同比如从1500的以太网进入1400的隧道那路由器就必须把这个IP报文拆开。分片后的每个片都是一个独立的IP报文有相同的标识符和不同的片偏移。这里要理清一个概念路径MTUPath MTU是整条源到目的路径上最小的那个MTU。它不是你单台交换机接口上配置的MTU而是整条链路里最细的那截“水管”。只要有一个接口MTU小大包过不去。4.2 DF标志下的“黑洞”问题当PMTUD失效现代操作系统出于安全和效率考虑默认策略是设置DF1也就是“尽量别分片”。那大包怎么发出去靠PMTUDPath MTU Discovery发送端先按较大MTU发包如果路由器觉得包超过下游MTU会向源端回一个ICMP type 3 code 4报文附带“需要分片但我没分因为你的DF置了1”的通知。发送端收到后就把自己的发送MTU调小再继续探测。这个机制正常工作时非常好用但有一个致命漏洞很多网络安全设备为了“防ICMP攻击”会直接丢ICMP type 3 code 4报文。发送端发出去的大包被丢了回收不到通知还会傻傻地重发同样大小的大包直到超时。这个现象就叫“MTU黑洞”或“PMTUD黑洞”。典型表现就是小包能通ping大包不通某些应用打不开。比如网页加载时TCP握手是正常的但一到传输大数据段就卡死。TCP握手包只有几十字节MTU足够数据包可能超过路径MTU一旦DF置1又收不到ICMP通知就会无限重传。4.3 分片包长什么样用偏移量和MF标志看穿分片分片报文在Wireshark里非常好认。假设一个UDP报文的上层载荷有3000字节加上UDP头8字节后IP层要载荷3008字节。以太网MTU是1500去掉20字节IP头每个分片最多能带1480字节的载荷。这个1480刻意选取是因为它刚好是8的倍数分片的偏移量好对齐。这个3008字节的报文会被拆成三个分片第一个分片载荷1480字节Fragment Offset0MF1第二个分片载荷1480字节Fragment Offset185MF1第三个分片载荷48字节Fragment Offset370MF0片偏移为什么是185和370因为偏移单位是8字节1480除以8等于185。第三个分片偏移是370因为前两个分片已经带了2960字节的载荷370乘以8等于2960偏移量指向剩余载荷的起始位置。最后一个分片MF0表示重组到这里就结束了。调试的时候见到Fragment Offset大于0或者MF1的报文就要高度关注分片问题。UDP本身不保证可靠传输IP分片只要丢一片整个原始报文就无法重组接收端会把已经收到的一整组分片全部丢弃。这就是为什么大UDP包在网络上特别容易“送不到”的原因。4.4 一条命令测路径MTU的实操笔记实战中判断路径MTU最直接的办法是用ping带DF标志加大包。Linux和Windows命令略有差异但原理一致Linuxping -M do -s 1472 223.5.5.5Windowsping -f -l 1472 223.5.5.5这个1472不是随便写的。以太网1500字节减去20字节IP头再减去8字节ICMP头得到1472字节。这样设置整个IP报文是1500字节正好等于标准以太网MTU。如果你ping 1472字节的包通了说明路径MTU至少是1500。如果收到类似“Frag needed and DF set”的提示说明路径上有接口的MTU小于1500。我习惯用二分法逐步测先从1472测不通就降到1400再不通降到1300找到刚好能通的最大值再加上28字节就是实际的路径MTU。注意ICMP本身可能会有额外开销复杂网络里隧道封装会降低有效MTU所以这招虽然不是100%精确但排障时非常高效。5. 抓包排障时报头字段到底怎么帮我定位问题5.1 TTL超时与traceroute原理TTL字段最重要的应用就是traceroute。原理不复杂traceroute从源端发出一个TTL1的报文第一台路由器收到后TTL先减1变成0发现自己不能再转发就丢掉报文并返回一个ICMP Time Exceeded报文源端就知道第一跳地址。接下来源端发TTL2的报文第一跳减到1后继续转发第二跳减到0后返回ICMP Time Exceeded这样逐跳递增就能把整条路径都描出来。实际抓包中如果看到连续几个ICMP Time Exceeded就看原始报头里的TTL字段能判断是哪一跳开始出问题的。另外很多系统工具还可以用TTL初始值反推主机类型Linux默认初始TTL64Windows默认128很多网络设备默认255。不过经过NAT或多跳转发后TTL会被重置或递减所以只能作为辅助判断不能当作定论。5.2 校验和报错时别急着骂网卡Wireshark里看到“Header checksum incorrect”红色标红很多人第一反应是“网络有坏包”。但根据我的经验这个红色有相当一部分是假报警。最常见的原因是发送端的网卡开启了硬件校验和卸载Checksum Offload。网卡硬件在报文真正发出去前才把校验和算好填进去而Wireshark是在驱动层抓包的抓到的是还没被硬件填充校验和的版本自然算不对。遇到这种情况可以分两步判断第一步关掉网卡的IP/TCP/UDP校验和卸载功能再重新抓包第二步看接收方向上是否也有大量校验和错误。如果只是本机发送方向报错通常是offload造成的。如果是接收方向也报错再结合底层是否有CRC错误来综合判断是不是链路真的有问题。还有一点要注意IP校验和只覆盖IPV4报头不覆盖后面的数据。TCP/UDP数据部分出错了TCP层或UDP层会有自己的校验和报错。因此看到IP校验和错误不要立刻怀疑数据被篡改它只说明“IP头本身可能有问题”。5.3 协议号决定上层别在IP层找端口很多刚接触抓包的人会在过滤栏里写ip.port 80然后发现没有这个字段。IPV4报头里真的没有端口号。端口号在TCP头或UDP头里IP层只有协议号告诉你上层是谁。协议号和端口号是两个完全不同的维度协议号是“哪类协议”端口号是“这个协议里的哪个服务”。排障时要分层拆解。比如业务方说“TCP 8080端口连不上”你在抓包时应先确认IP层有没有把报文发到正确的目的地址再确认IP报头里的Protocol字段是6TCP最后才去看TCP头里的目的端口是不是8080。如果一开始就往TCP端口上找可能忽略了IP层地址错误导致的问题。我常用的抓包过滤写法是先ip.addr目的IP再tcp.port8080把范围逐步收窄。不要一上来就tcp.port8080那样会漏掉中间环节的异常。5.4 报头选项字段为什么现代网络基本不用它前面提到选项字段会造成报头长度可变影响路由器处理性能。这里把安全因素也说透IP选项里的源路由选项可以指定“这个包必须经过哪几个路由器”如果被恶意利用攻击者可以让报文绕过正常的访问控制路径。所以主流网络安全设备普遍选择丢弃带选项的IP包。另一个影响是IHL不再是5报文头变长后某些只按固定长度读取报头的硬件设备可能处理出错。这在一个高速转发环境里是不可接受的。因此实战中你看到的IPV4报文几乎都是IHL5的20字节标准报头。如果哪天抓到一个IHL大于5的普通业务包我建议你多留个心眼要么是特殊网络探测工具要么是配置异常的设备在发包顺着源地址查一查总会发现点东西。6. 从IPv4到IPv6读懂报头演进才真正懂报文设计6.1 固定20字节换来的硬件转发速度理解了IPV4报头的字段后再看IPv6的设计就顺理成章。IPv4的IHL字段是因为选项字段才存在的选项字段又是为了“灵活性”设计的。但在实际网络里这种灵活性几乎没带来正面收益反而成了性能和安全的包袱。现代路由器转发报文尤其是核心设备动辄每秒处理几百万个包。硬件转发希望每次处理的格式是固定、可预期的。IPv6把报头固定成40字节没有IHL字段没有校验和没有中间分片相关字段路由器转发的动作变得非常机械查路由表改跳数字段重新走。这只是一种“减法”但直接换来了更稳定的转发能力。6.2 IPv6是怎么删掉校验和与分片字段的IPv6报头里没有IPV4的Header Checksum原因是三层以下已经有链路层的FCS校验三层以上有TCP或UDP的校验和再做一次IP头校验属于重复劳动。去掉之后每台路由器都少一次校验和计算这对高带宽设备是实打实的性能收益。分片字段在IPv6里也不再出现在基础报头中而是挪到了扩展头里并且规定只有源主机能做分片中间路由器不允许分片。如果遇到MTU不足的情况路由器直接丢包并向源端报告。这个改动让中间设备不再承担分片计算工作也让报头保持固定长度。IPv4里那套“标识标志片偏移”三件套在IPv6基础报头里彻底看不到了要看分片就得去看分片扩展头。6.3 排障时优先看哪几个字段一张个人经验表我把平时排障时优先关注的字段整理了一下方便你快速对照故障现象优先查看字段判断逻辑数据包发不到目的地源地址、目的地址、TTL地址对不对TTL减到0说明中途丢了大包通、小包通不了DF、MF、Fragment Offset、Total Length看是否分片、DF是否置1、总长度是否超MTU应用层协议识别混乱Protocol确认IP头标的上层协议是否正确怀疑报头损坏Header Checksum校验和是否正确但这要结合硬件卸载判断中间链路丢包TTL和ICMP Time Exceeded定位到具体第几跳这几组字段不要孤立地看很多问题是组合出来的。比如看到DF1、总长度又超过路径MTU同时收不到ICMP type 3 code 4那基本就是MTU黑洞。看到MF1且Fragment Offset有值就说明这是分片包后续还要继续等同一个Identification的其余分片。最后说点个人体会吧。IPV4报头这套结构我在书里背过很多遍真正理解它是在一次一次抓包中完成的。遇到网络问题不要急着重启设备先抓包把报头字段对着十六进制读一遍你会发现所谓“玄学故障”九成都有明确答案。闲下来也可以拿Python写个解析器把每个字段打出来比看文档有用得多。希望这篇长文能帮你把IPV4报头彻底吃透下回抓包时一眼就能看出问题在哪。