1. 为什么网络层是整个协议栈里最“拧巴”的一层干了这么多年网络相关的活我越来越觉得网络层是整个协议栈里最拧巴的一层。它夹在链路层和传输层中间上不着天下不着地却要扛起“把数据从地球这头送到那头”的活儿。你打开任何一个抓包工具看到IP头那二十个字节密密麻麻的字段第一反应多半是“这都什么跟什么”。但真把IP协议吃透了你会发现后面学TCP、学路由、学抓包排障全都顺了。这篇东西我想聊的就是IP协议本身——网络层里那个负责寻址和转发的核心协议。它解决的核心问题很朴素一台机器怎么把数据包送到另一台机器哪怕中间隔着无数个路由器、无数条链路。适合谁看刚学计算机网络被IP头字段绕晕的学生、做后端开发想搞明白“为什么我的服务连不上”的工程师、以及需要抓包定位网络问题的运维同学。我会从设计思路讲到字段细节再讲到实操抓包和排障尽量把那些教科书上一笔带过、但实际干活时天天踩的坑说清楚。先给个整体印象IP协议是无连接、不可靠、尽力而为的。这三个词不是贬义是设计哲学。它不保证送达、不保证顺序、不保证不重复把可靠性这些活儿全甩给了上层的TCP。为什么这么设计因为网络层要面对的是异构的链路——有的链路可靠有的不可靠有的快有的慢如果网络层自己搞一套重传确认机制复杂度会爆炸而且和底层链路的能力重复。所以IP只做一件事尽最大努力把包往前送送不到就拉倒。理解了这一点后面所有字段的设计你都能想通。2. IP协议的整体设计与思路拆解2.1 无连接与尽力而为把复杂度留给上层我见过太多人一开始不理解“不可靠”为什么反而是优点。这里打个生活化的比方IP协议就像一个只管把信投进邮筒的邮差他不负责挂号、不负责回执、不负责丢了重寄。你要保证信一定送到那是你自己的事上层TCP来干。这么设计的好处是邮差跑得飞快不用为每封信停下来等确认。整个互联网能撑起今天的规模很大程度上就是因为网络层足够“傻”——傻到每个路由器只需要看目的地址查表转发不需要维护每条连接的状。这个思路带来的直接后果是IP包在网络里是独立旅行的。同一个TCP连接的两个包可能走完全不同的路径到达目的地甚至后发的先到。这就是所谓的“数据报”模型。你在抓包时会看到序列号乱序别慌那是正常的TCP会帮你排好。反过来如果你在写一个基于UDP的应用就得自己处理乱序和丢包因为IP和UDP都不管这些。2.2 IPv4与IPv6的取舍为什么地址从32位跳到128位IPv4用32位表示地址理论上43亿个地址。当年设计的时候觉得够用了结果互联网爆炸式增长地址早就分完了。于是有了NAT网络地址转换这种“打补丁”的方案让一个公网地址后面挂一堆内网设备。NAT确实续了命但它破坏了端到端连通性——外面的机器没法主动连到你内网的设备P2P、某些实时通信场景都得靠打洞来绕。这就是地址不够用的代价。IPv6直接上128位地址空间大到可以给地球上每粒沙子都分一个。但IPv6的推广慢得出奇原因不在技术在生态——路由器、防火墙、应用、运维习惯全得跟着改。我实际接触的项目里纯IPv6环境还是少数大多数是双栈或者NAT后面跑IPv4。所以学IP协议IPv4的字段必须吃透IPv6的头部结构也得了解因为迟早要迁移。IPv6的头部比IPv4简洁得多去掉了校验和、去掉了分片字段分片交给源端做固定40字节扩展功能靠扩展头实现。这个设计思路值得单独拎出来说把不常用的功能从主头里挪出去让转发路径上的路由器处理更快。2.3 分片与MTU一个绕不开的坑IP协议里最容易出问题的地方我个人认为是分片。链路层有个MTU最大传输单元以太网默认1500字节。一个IP包如果比MTU大要么在IP层分片要么让上层别发这么大。分片的麻烦在于只要分片中的一个丢了整个原始包就得重传而且重组只在目的端做中间路由器不负责重组。更坑的是很多防火墙和NAT设备对分片包处理得很随意导致“小包能通、大包不通”的经典故障。所以现代实践里大家更倾向于路径MTU发现发送方先把包设成“不分片”标志如果中间某段链路MTU不够路由器会回一个ICMP“需要分片”的消息告诉发送方该把包改小。这个机制很优雅但前提是ICMP消息不能被防火墙拦掉。我踩过的坑就是某些网络环境把ICMP全禁了结果路径MTU发现失效大包一直发不出去表现为“能ping通但传不了文件”。这个后面排障章节会细说。3. IPv4头部字段逐个拆解与实操要点3.1 二十字节固定头每个字段都在解决一个具体问题IPv4头部固定20字节选项字段可有可无。我习惯按“这个字段是为了解决什么问题”来记比死记位置强。下面这张表是我自己整理的理解版把字段和作用对应起来字段长度解决什么问题实操关注点版本4位区分IPv4/IPv6抓包时先看这个值4就是v4首部长度4位头部有多长含选项单位是4字节值5表示20字节服务类型8位优先级和QoS标记DSCP和ECNQoS调优才用总长度16位整个IP包多长单位字节最大65535标识16位分片时标记同一原始包分片重组的依据标志3位是否允许分片、是否还有分片DF位是路径MTU发现的关键片偏移13位分片在原始包中的位置单位8字节生存时间8位防止包无限循环每过一个路由器减1减到0丢弃协议8位上层是什么协议6是TCP17是UDP1是ICMP首部校验和16位校验头部有没有出错只校验头不校验数据源地址32位谁发的排障时重点看目的地址32位发给谁排障时重点看这里我想重点说几个容易被忽略的。首部长度这个字段单位是4字节最小值520字节最大值1560字节。为什么要有它因为选项字段长度可变接收方得知道数据从哪开始。总长度是16位最大65535字节但实际受MTU限制一个包很少超过1500。标识字段在分片时特别重要同一个原始包的所有分片标识相同接收方靠它把分片拼回去。3.2 TTL不只是防环还是排障利器TTL生存时间这个字段名字有迷惑性它其实不是时间是跳数。每经过一个路由器减1减到0就丢弃并回一个ICMP超时消息。设计初衷是防止路由环路导致包永远转圈。但实际干活时TTL是特别好用的排障工具——traceroute就是靠它工作的先发TTL1的包第一个路由器减到0回超时你就知道了第一跳的地址再发TTL2知道第二跳……一路把路径摸出来。我实际排查跨网问题时经常先跑一遍traceroute看路径在哪断的。如果某一跳之后全是星号说明那个位置之后不通或者不回ICMP。注意有些网络会屏蔽ICMP导致traceroute结果不准这时候得换TCP模式的探测。TTL的默认值也能透露信息Linux一般64Windows一般128某些网络设备255。抓包看到TTL异常小可能是经过了NAT或者代理也可能是被中间设备改过。3.3 协议号与校验和两个小而关键的字段协议号告诉接收方把数据交给谁。6是TCP17是UDP1是ICMP2是IGMP47是GRE50是ESP。这个字段在抓包过滤时特别有用比如ip proto 6只抓TCP。我见过有人写防火墙规则时把协议号搞错结果规则不生效排查半天。首部校验和只校验IP头部不校验数据。为什么因为数据部分的校验交给上层TCP/UDP了网络层不重复劳动。而且每过一个路由器TTL要减1校验和得重算如果连数据一起校验路由器负担太重。这个设计体现了“各层只干自己该干的”原则。计算方法是把头部按16位分组做反码求和再取反抓包工具会自动帮你验但自己写底层程序时得注意。提示IPv6直接取消了首部校验和因为链路层和传输层已经有校验网络层再校验一次被认为是冗余。这个变化说明协议设计也在不断做减法。4. 实操抓一个IP包把字段一个个对上去4.1 用抓包工具看真实IP头光看字段表没感觉得抓个真包。我用的是常见的抓包工具随便ping一下某个地址抓到的ICMP包外面就裹着IP头。下面是一段典型的IPv4头部十六进制我按字段拆给你看45 00 00 54 00 01 00 00 40 01 8c 3a c0 a8 01 02 c0 a8 01 01逐段解读45是版本4加首部长度520字节00是服务类型00 54是总长度84字节00 01是标识00 00是标志加片偏移DF0MF0偏移040是TTL6401是协议号1ICMP8c 3a是校验和后面c0 a8 01 02是源地址192.168.1.2c0 a8 01 01是目的地址192.168.1.1。对着这个真实包前面那些字段一下就活了。我建议每个学IP协议的人都亲手抓一次、手动拆一次比看十遍书管用。抓包时注意如果你在NAT后面看到的源地址可能是内网地址出去后被改成公网地址这就是NAT在改IP头。4.2 构造一个带选项的IP包选项字段平时很少用但某些场景必须用比如记录路由、时间戳、安全选项。选项让首部长度超过5最大到1560字节。构造带选项的包首部长度字段要相应改大校验和也要重算。我实际用过的场景是调试某些老设备需要带特定选项才能通。操作上用原始套接字自己拼头部时记得选项长度要补齐到4字节的倍数否则解析会错位。这个细节教科书很少提但自己写底层代码时一定会遇到。4.3 观察分片把MTU调小逼出分片想直观理解分片可以把某块网卡的MTU临时调小比如设成576然后发一个大包抓包就能看到分片。每个分片的标识相同片偏移不同最后一个分片的MF位为0表示结束。你会看到第一个分片带着完整的传输层头部后续分片只有数据。重组时接收方按标识归组、按偏移排序。这个实验我强烈建议做一次做完你就明白为什么“分片丢一个全重传”了——因为缺任何一片都拼不出完整数据。注意调MTU是临时实验手段做完记得改回来否则会影响正常通信。生产环境改MTU要谨慎可能导致路径上某些设备处理异常。5. 常见问题与排查技巧实录5.1 能ping通但传不了大文件路径MTU的锅这是我最常遇到的IP层故障没有之一。现象很典型小包ping默认几十字节通大文件传不动或者卡死。根因是路径上某段链路MTU比发送方以为的小而发送方又设了DF位不允许分片包被路由器丢弃同时ICMP“需要分片”消息被防火墙拦了发送方一直不知道要改小。排查思路先用不同大小的包试探找到临界值再检查中间设备是否放行ICMP类型3代码4的消息。解决要么放行ICMP要么在应用层或系统层把MSS/MTU调小。5.2 TTL过期导致的“神秘断连”有次遇到一个跨多个网络段的连接短连接正常长连接跑一会儿就断。抓包发现包在某个节点后消失TTL减到0被丢了。原因是路径跳数超过了发送方设的初始TTL。默认64一般够用但如果路径特别绕或者有环路就会耗尽。排查时看抓包里的TTL值如果接近0基本就是这个问题。临时把初始TTL调大能验证但根治得查路由为什么绕。5.3 地址冲突与ARP干扰IP层排障绕不开地址问题。同一网段两个设备配了相同IP表现是时通时不通抓包能看到两个不同MAC回同一个IP的ARP。这种问题在虚拟化环境特别常见克隆虚拟机没改IP就会撞。排查用ARP扫描看有没有重复响应。另外IP地址配错网段、子网掩码写错也会导致“本地通、跨网不通”这类问题先查本机IP配置再抓包。现象可能原因排查动作小包通大包不通路径MTU问题试探包大小查ICMP放行连接跑一会儿断TTL耗尽抓包看TTL查路由跳数时通时不通IP地址冲突ARP扫描查重复跨网段不通掩码或网关错查本机路由表和配置抓包看到重复包路由环路traceroute看路径5.4 抓包时IP头校验和错误有时候抓包工具报IP头校验和错误先别慌。如果是在发送方本机抓的可能是网卡做了校验和卸载checksum offload抓到的包校验和还没算好这是正常的。如果是在中间或接收方抓到错误那才是真有问题可能是链路干扰或设备故障。这个坑我踩过当时以为网络坏了查半天发现是卸载机制。判断方法换个位置抓或者关掉网卡卸载再抓对比。6. 从IP协议延伸出去的几个实用认知6.1 IP地址的分类与CIDR别再死记A/B/C类早期IP地址分A/B/C类现在基本被CIDR无类别域间路由取代。CIDR用“地址/前缀长度”表示比如192.168.1.0/24表示前24位是网络号。为什么改因为分类地址浪费严重一个B类地址给一个中等规模网络用不完也退不掉。CIDR让前缀长度可以任意路由聚合也更灵活。实际配网络时算网段、算可用主机数都靠前缀长度/24有256个地址去掉网络地址和广播地址剩254个可用。这个计算必须熟练配错掩码是低级但高频的错误。6.2 NAT对IP协议的影响NAT在IP层改地址和端口让多个内网设备共享一个公网地址。它解决了地址短缺但破坏了IP的端到端模型。表现就是外部无法主动连内网设备某些协议比如带地址信息的应用层协议经过NAT会出问题。理解NAT的关键是知道它在哪改出方向改源地址入方向改目的地址靠连接跟踪表维护映射。排障时如果发现“内网互访正常、访问外网异常”先怀疑NAT配置。6.3 IPv6头部简化带来的转发加速IPv6固定40字节头去掉了校验和、分片字段和选项改用扩展头。路由器转发时不用算校验和、不用处理分片速度更快。扩展头链式设计让不常用的功能不拖累主路径。这个设计思路对我们做系统优化也有启发把高频路径做薄低频功能外挂。实际迁移时注意IPv6没有广播用组播和任播替代邻居发现协议也换了这些变化会影响原有的网络配置习惯。7. 我个人在实际操作中的几点体会学IP协议这件事我最大的体会是别把它当孤立的知识点背要当成一个“为了解决特定问题而做的工程取舍”来理解。每个字段背后都是一个具体约束TTL为了防环校验和为了检错分片为了适配不同MTUDF位为了路径发现。你把这些约束想通了字段自然就记住了。另外抓包是学IP协议最好的老师。我建议你找个安静的环境自己发各种包、抓各种包把头部拆开对着看。看多了你扫一眼十六进制就能反应出这是TCP还是UDP、TTL大概多少、有没有分片。这种手感是看书看不来的。最后分享一个小技巧排障时养成“先看三层、再看四层”的习惯。很多“应用连不上”的问题根子在三层——地址配错、路由不通、MTU不对、TTL耗尽。先用ping和traceroute确认三层通不通再往上查端口和协议能省掉大量瞎猜的时间。IP协议看着枯燥但它是整个网络的地基地基打牢了上面盖什么都稳。