3. 网络层核心:IP协议头部结构、分片与重组、TTL与路径MTU发现

网络层是整个协议栈的“交通枢纽”。数据链路层只管同一段链路上的传输,而网络层负责把数据包从源主机送到目标主机——中间可能跨过几十个路由器。IP协议就是干这个的。

我个人习惯把IP协议看作“快递单”。头部就是快递单上的寄件人、收件人、包裹重量、分拣标记。没有这张单子,网络设备根本不知道包裹该往哪送。

3.1 IP头部结构:20字节的硬功夫

标准的IPv4头部固定20字节,不算选项字段。我刚开始学的时候觉得20字节挺大,后来算了一笔账:以太网MTU是1500字节,头部只占1.3%的开销,很划算。

来看核心字段,我挑几个重点说:

字段 长度 作用 我的经验
版本 4 bit IPv4 / IPv6 抓包时先看这个,确认协议族
首部长度 4 bit 头部有多少个32位字 最小值5,表示20字节
总长度 16 bit 整个IP包(头部+数据)的字节数 最大值65535,但实际受MTU限制
标识 16 bit 分片时用于重组 同一个原始包的所有分片标识相同
标志 3 bit DF(不分片)、MF(更多分片) DF=1时,路径MTU发现的基础
片偏移 13 bit 分片在原始包中的偏移量(×8) 单位是8字节,不是1字节
TTL 8 bit 生存时间,每跳减1 默认64或128,traceroute就靠它
协议 8 bit 上层协议类型(TCP=6, UDP=17) 解包时靠这个决定交给谁
首部校验和 16 bit 只校验头部,不校验数据 每跳都要重新计算,因为TTL变了

核心要点:IP头部是“无状态”的。每个包独立处理,路由器不维护连接状态。这跟TCP完全相反——TCP有序列号、确认号、状态机。IP只管“尽力交付”,丢了不重传。

我记得有一次排查丢包问题,抓包发现IP头部校验和一直报错。后来发现是网卡驱动在计算校验和时用了错误的字节序。嗯,这种底层问题最难查。

3.2 分片与重组:大包怎么过小门

每个物理链路都有MTU(最大传输单元)。以太网是1500字节,Wi-Fi通常是1500,但有些隧道链路只有1400甚至1280。如果IP包超过MTU,路由器必须分片。

分片的规则其实不复杂:

  1. 检查大小:IP包总长度 > 出接口MTU?是则分片。
  2. 检查DF标志:DF=1且包太大?直接丢弃,发ICMP“需要分片但DF已设置”。
  3. 切割数据:把原始IP数据部分切成若干片,每片长度是8字节的整数倍(最后一片除外)。
  4. 修改头部:每片复制原始IP头部,修改总长度、标志、片偏移,重新计算校验和。

举个例子。假设一个IP包总长度4000字节(头部20 + 数据3980),要从MTU=1500的链路出去:

  • 每片最大数据量:1500 - 20 = 1480字节
  • 1480 ÷ 8 = 185,正好是8的倍数
  • 分片1:数据0~1479,片偏移0,MF=1
  • 分片2:数据1480~2959,片偏移185,MF=1
  • 分片3:数据2960~3979,片偏移370,MF=0(最后一片)

避坑指南:我曾经在生产环境遇到一个问题——防火墙开启了分片过滤策略,导致某些大包被静默丢弃。应用层表现为文件上传偶尔超时。查了两天才定位到是分片重组超时。后来我建议把应用层消息大小控制在1400字节以内,彻底绕过分片问题。

重组只在目标主机进行。路由器不做重组——它没那个内存,也没那个义务。重组时,接收方用标识字段区分不同原始包,用片偏移排序,等所有分片到齐后拼回原始数据。

这里有个坑:如果某个分片丢了,整个原始包都得丢弃。IP层不会重传,那是TCP的事。所以分片越多,丢包概率越大。你想想看,一个包分成10片,只要丢1片,9片都白传了。

3.3 TTL:防止数据包在网里“鬼打墙”

TTL(Time To Live)最初设计是“生存时间秒数”,但实际实现中每经过一个路由器就减1。为什么?因为路由器处理一个包的时间远小于1秒,按秒算太粗糙了。

TTL的初始值由操作系统决定:

操作系统 默认TTL
Linux 64
Windows 128
macOS / FreeBSD 64
某些网络设备 255

TTL减到0时,路由器丢弃该包,并向源IP发送ICMP超时消息(Type 11, Code 0)。这就是traceroute的原理——先发TTL=1的包,第一个路由器返回超时;再发TTL=2,第二个路由器返回……直到到达目标。

注意:有些网络设备会静默丢弃TTL超时的包,不发送ICMP。这种情况下traceroute会看到星号(* * *)。不是网络不通,是设备配置了“不响应TTL超时”。

我个人习惯在排查路由环路时,先看TTL值。如果抓包发现TTL很小(比如5以下),说明包可能绕了远路或者有环路。有一次客户反馈某个业务间歇性超时,我抓包一看,TTL从64降到了3,再追一跳就归零了。最后发现是BGP路由配置错误,导致流量在三个路由器之间循环。

3.4 路径MTU发现:找到整条路的最窄处

分片虽然能解决问题,但效率低。更好的办法是在发送前就知道整条路径的最小MTU,然后直接把包切成合适的大小。这就是路径MTU发现(Path MTU Discovery, PMTUD)。

PMTUD的工作流程:

  1. 发送方设置IP头部的DF=1(不分片)。
  2. 发送一个大小等于出接口MTU的探测包。
  3. 如果路径上某个路由器发现包太大,丢弃并返回ICMP“需要分片但DF已设置”(Type 3, Code 4),同时附上该链路的MTU值。
  4. 发送方收到ICMP后,减小包大小,重试。
  5. 重复直到包能到达目标,此时的大小就是路径MTU。

实际实现中,发送方通常维护一个PMTU缓存(按目标IP地址),定期刷新。Linux下可以用ip route show查看缓存。

PMTUD的致命弱点:依赖ICMP。如果网络设备过滤了ICMP Type 3 Code 4,发送方永远不知道包被丢了,只会一直重传——这就是“PMTUD黑洞”问题。我在项目中遇到过好几次,表现为某些大文件传输卡死,小文件正常。最后解决方案要么是手动设置接口MTU,要么启用TCP MSS Clamping。

TCP MSS Clamping是啥?简单说,TCP在三次握手时会协商MSS(最大段大小),MSS = 路径MTU - IP头部 - TCP头部。如果中间设备(比如路由器或防火墙)修改了SYN包中的MSS值,就能强制TCP使用更小的段,避免分片。嗯,这是目前最实用的解法。

3.5 知识体系总览

下面这张图把本章的核心逻辑串起来了。从IP头部出发,到分片与重组,再到TTL和PMTUD——它们共同决定了网络层的连接稳定性。

网络层核心知识体系 网络层核心:IP协议知识体系 IP协议(网络层核心) IP头部结构 分片与重组 TTL & PMTUD 20字节固定头部 核心字段解析 校验和计算 MTU限制 分片规则(×8) 重组在目标主机 每跳减1 traceroute原理 环路检测 DF=1探测 ICMP反馈 MSS Clamping 核心原则 IP层无状态,尽力交付 分片降低可靠性 PMTUD依赖ICMP

从这张图能看出来,IP协议的设计哲学是“简单、无状态、尽力而为”。头部结构决定了每个包的自描述能力;分片机制解决了异构网络互通问题;TTL防止了无限循环;PMTUD则试图在发送前就规避分片。这四个点环环相扣,任何一个环节出问题,都会直接影响连接的稳定性。

我的建议:在实际运维中,遇到“能通但慢”的问题,优先检查MTU和分片。用ping -M do -s 1472 target(Linux)测试路径MTU,用tcpdump -i any icmp观察是否有“need to frag”消息。这两步能解决80%的网络层疑难杂症。