第 5 章 · 网络层:IP 与路由

我家里到 Cloudflare 的真实路径,用 tracepath example.com 抓的(输出删减,网段隐了几位):

 1:  网关(我家路由器)              3.7 毫秒
 2:  无应答
 3:  无应答
 4:  无应答
 5:  骨干网某跳                    33.2 毫秒
 6:  无应答
 7:  骨干网某跳                    58.4 毫秒
10:  2400:cb00:71:2::4           212.7 毫秒   不对称  9

延迟从 3.7 ms 爬到 212 ms,中间几跳干脆摆烂不回话。这张「半张残缺的地图」是本章最好的引子:网络层负责在任意两台主机之间找路、把包一站站送过去,但它的承诺只有四个字,尽力而为。路怎么找、包怎么编址、为什么有的跳不回话,这一章全讲。

5.1 IP 地址:32 位怎么切

IPv4 地址 32 位,习惯写成四段点分十进制,比如 192.168.1.130。它的结构是一个约定:前缀是网络号(你住在哪个小区),后缀是主机号(小区里第几户)。切分位置用前缀长度表示,/24 意思是前 24 位是网络号,配合子网掩码 255.255.255.0 是同一个意思的两种写法。

拿 /26 做一道完整的手算题,这道题会做的考试白送分,不会做的路由都配不对:192.168.1.130/26 属于哪个子网?

前缀 26 位,主机位剩 6 位,掩码是 255.255.255.192。最后一个字节的块大小为 256 − 192 = 64,所以子网边界落在 0、64、128、192。130 落在 128 到 191 之间,答案是:

子网:      192.168.1.128/26
可用主机:  192.168.1.129 ~ 192.168.1.190  (2^6 − 2 = 62 台)
广播地址:  192.168.1.191

减 2 是因为主机位全 0 是网络地址本身,全 1 是广播地址,都不能分给机器。这套「块大小、边界、范围」的手感值得练熟,后面 NAT、防火墙规则全都建立在它上面。

IPv4 只有约 43 亿个地址,早就分完了,今天的互联网靠两根拐棍活着:CIDR 取消 A/B/C 类的固定边界、按需分配前缀,加上路由聚合把碎成渣的小网络重新捏成大块,路由表才没爆;NAT 让一坨私有地址(10/8、172.16/12、192.168/16)躲在少数公网 IP 后面,见 5.5 节。

5.2 路由表:每台机器都在找下一跳

一个包到达每台机器(包括你自己的笔记本)后,机器都做同一件事:查路由表,决定下一跳给谁。我笔记本上的真实输出(虚拟网卡的行略去):

$ ip route
default via 10.99.188.18 dev wlo1 proto dhcp metric 600
10.99.188.0/24 dev wlo1 proto kernel scope link src 10.99.188.123 metric 600

两行就够讲清楚机制。第一行是默认路由:default 即 0.0.0.0/0,匹配一切,兜底用,意思是「其他规则都不认识的目的,统统交给网关 10.99.188.18」。第二行是内核根据本机网卡配置自动生成的直连路由:去往 10.99.188.0/24 的不用出网关,直接从 wlo1 这块网卡发出去。去外网走第一行,访问同网段走第二行,你的机器每天都在这两类规则之间分拣。

匹配规则只有一条:最长前缀匹配。目的地同时命中多条路由时,选前缀最长的(最具体的)那条。这条规则是 CIDR 能玩聚合的前提:运营商可以把下游几十个网络聚合成一个大前缀对外广播,流量到了边界再被更长前缀的明细路由接走。没有最长前缀匹配,聚合过的地址就再也拆不开了。

5.3 路由协议:RIP 与 OSPF,以及「坏消息传得慢」

静态路由手写只适合玩具网络,真实网络的路由表靠路由协议自动维护,两大流派:

维度距离向量(RIP)链路状态(OSPF)
每台路由器知道什么到各网络的距离表全网拓扑图
和谁交换只跟邻居泛洪给全域
算法Bellman-Ford 迭代收敛Dijkstra 本地最短路
收敛慢快
度量跳数,上限 15带宽/代价可自定义
适用小网络大型自治系统内部

距离向量的哲学是「听邻居的传闻」:我到 B 网络 3 跳,你到我 1 跳,那你去 B 就 4 跳。传闻的问题在坏消息上,经典故障叫计数到无穷。设 A—B—C 三台路由器串成线,C 那头挂了:

  1. B 与 C 直连,B 立刻发现,把「到 C:不可达」记下;
  2. 但 A 的表还留着旧消息「到 C 距离 2,经 B」,周期广播时告诉了 B;
  3. B 一听,A 到 C 只要 2?那我经 A 就是 3 跳,于是把「不可达」改成「3 跳」;
  4. A 看到 B 变成 3,更新自己「4 跳」…… 两台机器互相抬价,一路数到 RIP 的上限 16(即无穷)才承认 C 没了。

明明坏消息一步就能到,却绕着圈子走了十几轮,这就是「坏消息传得慢」。修补手段有水平分割(从哪个接口学来的路由,不再从那个接口广播回去)、毒性逆转(学来的路由以 16 跳广播回去,明示此路不通)等,但 RIP 的天花板摆在那,大网络都用链路状态:每台路由器把邻居关系泛洪给全域,人人手里有同一张完整地图,自己跑 Dijkstra,收敛又快又可控。

5.4 ICMP:网络层的体检科

ICMP 挂在 IP 旁边,专管报错和诊断,不运应用数据。两个最重要的工具都是它撑的。

ping 发 Echo Request、等 Echo Reply,你大概用过,但输出里的信息值得读细。我机器上的真实输出:

64 字节,来自 2606:4700:10::ac42:93f3: icmp_seq=1 ttl=54 时间=192 毫秒
64 字节,来自 2606:4700:10::ac42:93f3: icmp_seq=2 ttl=54 时间=180 毫秒
已发送 3 个包,已接收 2 个包,33.3333% packet loss
rtt min/avg/max/mdev = 179.8/186.0/192.1/6.1 ms

ttl=54 有故事:Linux 发包的 TTL 初值是 64,每过一个路由器减 1,到 0 就地丢弃并回信。54 说明对面是 Linux 系的设备,且路上正好过了 10 跳。TTL 这个字段的存在本身就是为了防路由环路:万一路由表配出环,包在里面转圈,靠 TTL 自杀,免得永远占着带宽。

traceroute(或 tracepath)把 TTL 变成探路工具:先发 TTL=1 的包,第一跳路由器减到 0,回一个 ICMP Time Exceeded,于是你知道了第一跳的地址;再发 TTL=2 的,得到第二跳;如此递增,把沿途每一跳都钓出来。5 章开头那张残缺地图里的大片「无应答」,通常是中间路由器对 ICMP 限速或禁用,只说明它不回话,路照走,包照转。

5.5 NAT:一根拐棍的原理

NAT(网络地址转换)让一片私有地址共享一个公网 IP 出门,是 IPv4 耗尽后的续命核心。转换发生在路由器上,靠一张表:

转换表(NAT 路由器上):
内网                      公网                      对端
192.168.1.103:51776  ↔  203.0.113.5:40001   ↔  93.184.216.34:80
192.168.1.104:51800  ↔  203.0.113.5:40002   ↔  93.184.216.34:80

出包:把源地址端口换成公网侧的,在表里记一笔;回包:按目的端口查表换回去。端口在这里是必需品,光换 IP 区分不开回程,两台内网主机同时访问同一个网站,靠 40001 和 40002 这两个公网端口才能各回各家。

NAT 附赠一个副作用:外网无法主动连进来(表里没有的映射,包进来直接被扔),内网拓扑对外不可见,多少算个免费的防火墙。代价也不小,端到端原则被破坏,两台都在 NAT 后面的机器想直连(P2P 下载、联机游戏)就得打洞,聊天和语音应用为此写了一堆补丁代码。

IPv6 的思路是釜底抽薪:128 位地址,人人都有公网地址,理论上不再需要 NAT;SLAAC 让设备听路由器广播一个前缀、自己生成接口标识,不用 DHCP 就能拿到地址;首部校验和被删掉(链路层和传输层都有校验,IP 层那点收益不值得每个路由器重算一遍);分片也只允许源主机做,路由器从分片苦差里解脱。

5.6 排障笔记

几个真实会撞上的坑,附我自己的误判:

  • MTU 黑洞。帮人排查过一台「网页死活打不开,但 ping 全通」的机器:TCP 握手成功,TLS ClientHello(小包)也发出去了,然后死一般寂静。真相是链路 MTU 比以太网标准小(PPPoE 场景 1500 会缩到 1492),而路径上的设备没做协商,超过 MTU 的大包(比如一两千字节的 ServerHello)被悄悄丢弃。特征非常好认:小包通、大包死,ping 大尺寸包(ping -s 1472)立刻复现。
  • 掩码配错,同网段变「跨网段」。两台机器一台 /24 一台 /25,各自以为对方不在自己子网里,包全发去网关,网关一懵,直接不通。遇到「明明一个交换机下却 ping 不通」,先对一遍掩码。
  • ping 不通当作网络断。ICMP 被防火墙禁掉、被限速,在公网上极其常见。ping 死了先试 TCP 端口(curl -v 或 nc),别急着拔网线。
  • 默认路由缺失。同网段秒通,外网全断,九成是没配网关或者 DHCP 没拿到 default 那一行。

练习

  1. 手算:10.1.7.35/22 属于哪个子网?可用主机范围和广播地址各是什么?
  2. 手算:把 192.168.1.0/24 划成 4 个等大的子网,每个的前缀、范围、可用主机数分别是什么?
  3. 思考:5.2 节的路由表里,如果再加一条 10.0.0.0/8 via 10.99.188.99,那么去往 10.99.188.50 的包走哪条?去往 10.200.0.1 的呢?
  4. 思考:tracepath 输出里「无应答」的跳,如何区分「路由器不回话」和「路真断了」?(提示:看后续跳的延迟)