路由就是「一张表」,表里写着「去哪个网段、交给谁」。 每个包出门前都要查一次,查的规则只有一条:前缀越长越优先。
第四章把 IP 地址讲完了,这一章讲「地址有了,路怎么找」。 5.1 说过「IP 只负责送」,这一节开始拆开「送」这个动作。
一、「路由」到底是个什么动作
先去掉玄学:一台主机或路由器收到包,看一眼目的地址,然后从路由表里找一条最合适的记录, 按记录上写的「下一跳」发出去。 就这么三步——查表、选一条、发出去。 注意是「下一跳」而不是「终点」: 路由表里写的不是「最终怎么走完整条路」,而是「这一步交给谁」。 这种「一跳一跳接力」的做法叫逐跳转发(hop by hop)——每一台设备只对自己这一跳负责。 这个设计的好处是「换一段不影响全段」: 中间某条链路断了,只要相邻那两台改一下表,后面的设备什么都不知道。 代价是「谁也说不清完整路径」——所以 traceroute 才要一个一个去问。
二、路由表里到底存什么
最小的路由表只要两列:「目的网段」和「下一跳」。 但实际会多几列,每一列都有用:
| 列 | 作用 | 没有会怎样 |
|---|---|---|
| 目的网段 | 匹配用 | 不知道往哪查 |
| 掩码 | 决定匹配多长 | 没法做最长前缀匹配 |
| 下一跳 | 交给谁 | 不知道往哪发 |
| 出口接口 | 从哪块网卡发 | 知道交给谁也不知道走哪条线 |
| 度量 | 多条都匹配时比优劣 | 并列时只能瞎选 |
第一列和第二列合起来才是「一个网段」:没有掩码,192.168.3.0 到底是 /24 还是 /16 就说不清。 这也是为什么 7.4.3 讲的 RIPv1 不带掩码会出大问题——它让「匹配多长」这件事没法准确表达。 第四列常被忽略:「下一跳」和「出口」是两件事。 点对点链路上,下一跳就是对面那台,出口也好定; 但在广播网段上,「交给谁」和「从哪块网卡发」必须分开写。
三、为什么按「网段」而不是按「主机」
如果按主机存路由,全世界的路由表会大到无法维护:光 IPv4 就有 42.9 亿个地址。 按网段存,同一条链路后面的所有主机共享一条记录——这就是「聚合」的价值。 算一下差距:
| 存法 | 条目数 | 能不能维护 |
|---|---|---|
| 按主机 | 约 42.9 亿 | 不可能 |
| 按 /24 网段 | 约 1678 万 | 勉强 |
| 按实际在用的前缀 | 约 95 万 | 可以 |
第三行是现实:全球 BGP 路由表大约一百万条,因为大家都把能合的网段合起来报。 一个运营商握着几万个 /24,可以合成几百条大前缀报出去—— 这一步叫「路由聚合」,是让互联网还能运转的关键工程手段。
四、最长前缀匹配:规则只有一条
一个目的地址可能同时匹配好几条路由:8.8.8.8 既在 0.0.0.0/0 里,也可能在 8.8.0.0/16 里。 这时候听谁的? 规则是「掩码最长的那条优先」——这叫最长前缀匹配。 道理很直白:前缀越长,说明这条记录描述的范围越精确。 /0 表示「所有我不知道的」,/32 表示「就这一台」。 精确的当然该赢过笼统的。 这条规则带来一个反直觉的结果:默认路由(/0)永远排在最后,所以它叫「默认」—— 不是「优先」,是「兜底」。 这也解释了为什么「配了默认网关却上不了网」时,要先去查「是不是有条更长的路由把流量引偏了」。
五、主机也要有路由表
很多人以为「路由是路由器的事」,其实每台电脑都有自己的路由表,而且规则一模一样。 它至少要有两条:一条「本网段直连」(同网段的人直接找,不用经谁)、 一条「默认路由」(其他所有流量交给网关)。 少了第一条,同网段的打印机都连不上;少了第二条,外网全断。 所以「能 ping 通网关但上不了网」和「连网关都 ping 不通」是两类故障: 前者多半是默认路由或 DNS 的问题,后者是链路或本网段路由的问题。 这个判断顺序比「重装驱动」有用得多。
六、动手 Demo:本机路由表里到底有什么
用 route print -4 把本机的 IPv4 路由表读出来,然后拿三个目标地址实际查一遍。
重点看「同网段」和「跨网段」这两条记录的区别。
import re, ipaddress, subprocess
raw = subprocess.run('route print -4', shell=True,
capture_output=True).stdout.decode('gbk', 'ignore')
rows, started = [], False
for ln in raw.splitlines():
if '活动路由' in ln:
started = True
continue
if not started:
continue
p = re.split(r'\s{2,}', ln.strip())
if len(p) == 5 and re.match(r'^\d+\.\d+\.\d+\.\d+$', p[0]):
rows.append(p)
cand = [(ipaddress.IPv4Network('%s/%s' % (r[0], r[1]), strict=False),
r[2], r[3]) for r in rows]
for target in ('192.168.3.7', '192.168.5.7', '8.8.8.8'):
ip = ipaddress.IPv4Address(target)
hit = max([c for c in cand if ip in c[0]],
key=lambda c: c[0].prefixlen)
print(target, '->', hit[0], 'via', hit[1])
实跑输出:
--------------------------------------------------------------------
本机路由表里到底有什么
--------------------------------------------------------------------
**「路由」这个词听起来高深,其实就是一张表。**
**每个包出门之前,都要先在这张表里查一次「该往哪走」。**
网络目标 掩码 网关 接口 跃点
-------------------------------------------------------------------------------------
`0.0.0.0` `0.0.0.0` 192.168.3.1 `192.168.3.152` 50
`127.0.0.0` `255.0.0.0` 在链路上 `127.0.0.1` 331
`127.0.0.1` `255.255.255.255` 在链路上 `127.0.0.1` 331
`127.255.255.255` `255.255.255.255` 在链路上 `127.0.0.1` 331
`192.168.3.0` `255.255.255.0` 在链路上 `192.168.3.152` 306
`192.168.3.152` `255.255.255.255` 在链路上 `192.168.3.152` 306
`192.168.3.255` `255.255.255.255` 在链路上 `192.168.3.152` 306
`224.0.0.0` `240.0.0.0` 在链路上 `127.0.0.1` 331
`224.0.0.0` `240.0.0.0` 在链路上 `192.168.3.152` 306
`255.255.255.255` `255.255.255.255` 在链路上 `127.0.0.1` 331
`255.255.255.255` `255.255.255.255` 在链路上 `192.168.3.152` 306
**一共 11 条活动路由**:
**只有第一条是真的「往外走」**,**其余都是「本机网段」和「特殊地址」自动加的。**
**第一行 `0.0.0.0/0` 就是默认路由**——**它的意思是「所有我不知道的,都交给 192.168.3.1」。**
**第五条 `192.168.3.0/24` 后面写的是「在链路上」**:
**意思是「这个网段的人我都直接能找到,不用经谁转发」。**
**同一台主机的路由表里,这两条决定了「家里通、外面也通」。**
**规则只有一条:谁的前缀长,就听谁的。**
**这叫「最长前缀匹配」。**
**下面拿三个目标实际查一遍:**
目标 命中的网段 前缀长度 下一跳 出口
------------------------------------------------------------------------------------
`192.168.3.7` `192.168.3.0/24` 24 在链路上 `192.168.3.152`
`192.168.5.7` `0.0.0.0/0` 0 192.168.3.1 `192.168.3.152`
`8.8.8.8` `0.0.0.0/0` 0 192.168.3.1 `192.168.3.152`
**第二行最能说明问题**:
**192.168.5.7 和本机 192.168.3.152 长得很像,但它不在 192.168.3.0/24 里。**
**所以它只匹配上默认路由,被交给了网关。**
**这就是「同网段直发、跨网段找网关」的判断依据**——
**不是什么规则,就是把地址和掩码做了一次按位与。**
本机只有 11 条活动路由,其中只有第一条是「往外走」的。 其余十条里,两条是回环、两条是多播、四条是本机地址的特殊记录。 这些都不是「配出来的」,是系统自动加的——所以「路由表很乱」通常不是故障,是正常现象。 查表结果里第二行最能说明问题: 192.168.5.7 和本机 192.168.3.152 长得很像,但它不在 192.168.3.0/24 里。 所以它只匹配上默认路由,被交给了网关。 这就是「同网段直发、跨网段找网关」的判断依据—— 不是什么规则,就是把地址和掩码做了一次按位与。
30 秒实验
- 算一下:如果把全球 IPv4 地址按 /24 存路由,路由表大概多少条?
- 跑一下
route print -4,数一数你自己机器上有几条「在链路上」的记录。 - 想一下:为什么默认路由的前缀长度是 0,却排在最后?
常见误区
- 以为「路由表存的是完整路径」。 它只存下一跳,整条路径是很多台设备各自的表拼出来的。
- 以为「默认路由优先级最高」。 它是 /0,优先级最低,任何一条更长的前缀都能盖过它。
- 以为「路由只是路由器的事」。 每台主机都有路由表,没配默认网关就上不了网。
自测三问(点开对答案)
1. 路由表最少要有哪两列? —— **目的网段(加掩码)和下一跳。** 再加上出口接口,才知道从哪块网卡发。 2. 最长前缀匹配为什么这么定? —— **前缀越长描述的范围越精确**,所以精确的记录该盖过笼统的记录。 3. 「能 ping 通网关但上不了网」,先查什么? —— **默认路由和 DNS。** 能通网关说明链路和本网段路由没问题,问题在「出网」这一步。下一节看这张表是「人填的」还是「自己长出来的」。
- ☐ (本节完,下一节 7.1.2 静态路由与动态路由)