聚合是把「连续且对齐」的多条路由并成一条,表变小、转发变快。 它有一个前提和一个副作用:前提是地址规划连续,副作用是会宣告不存在的地址。
4.4.1 讲的是「表怎么用」,这一节讲「表怎么变小」。 4.3.7 的 CIDR 是聚合的数学基础,这里讲它在路由上的实际用法。
一、表为什么不能无限长
三个成本同时增长:内存、查表时间、路由更新时传的数据量。
| 成本 | 随条目数怎么变 | 后果 |
|---|---|---|
| 内存 | 线性增加 | 表存不下 |
| 查表时间 | 通常按前缀长度分层 | 转发变慢 |
| 更新流量 | 抖动时反复同步 | 收敛变慢 |
第二行值得多说一句:现代路由器不是「一条条比」,而是用 TCAM 或前缀树一次匹配。 但「一次匹配」也要占硬件表项——表项是硅片上的资源,比内存更贵。 所以聚合在硬件路由器上省的是真金白银。
二、聚合的两个条件
条件一:连续。条件二:对齐。两个都满足才能并。
| 情况 | 能不能并 | 原因 |
|---|---|---|
| 0~7 八个 /24 | 能,并成 /21 | 连续且从 0 起对齐 |
| 0、1、4、5 四个 /24 | 不能 | 中间缺了 2、3 |
| 1~8 八个 /24 | 不能 | 起点 1 不是 8 的倍数 |
第三行那句「起点必须是长度的倍数」是硬约束: 一个 /21 的起点必须能被 8 整除(8 个 /24 为一份), 所以 192.168.1.0 开头的八个 /24 合不成一个 /21,只能退一步,拆成两段分别处理。 这就是「地址规划要从大到小」的真正原因:规划的时候就按块分配,聚合时才会顺理成章。
三、副作用:把不存在的地址也宣告出去
聚合是「把一段整块宣告」,里面可能有没分配的地址。 这些地址的包会被送到聚合点,然后……没有然后。
| 被访问的地址 | 实际状态 | 结果 |
|---|---|---|
| 已分配的 | 有主机 | 正常到达 |
| 未分配的 | 没人 | 走到聚合点后被丢 |
这种「包被吸进去然后消失」的现象叫「黑洞」。
它的症状很迷惑:ping 不通,但不报「网络不可达」,只是超时。
所以排查时要看「是不是聚合宣告得太宽」。
更麻烦的是安全问题:
如果某个 /24 实际在别处,却被你的聚合盖住了,那这段流量就永远到不了真正的主人手里。
所以聚合的范围必须和自己真正拥有的地址严格一致。
四、现实里的聚合
互联网就是一层层聚合起来的:用户 /24 → 运营商 /16 → 骨干 /8。 这也是为什么「骨干路由表只有百万条量级」—— 如果不聚合,按 /24 算要有两千多万条,任何设备都撑不住。 聚合带来的一个结果是「路由是有层次的」: 你在北京访问一个广州的地址,包不会先查「广州在哪儿」,而是先按「这个大块归谁」跳过去, 越走越具体。 这叫「按前缀逐跳收敛」。
五、动手 Demo:八条 /24 合成一条 /21
用 ipaddress.collapse_addresses 真的并一次,再抽掉一条看会怎样。
注意抽掉 192.168.3.0/24 之后,条数从 1 变回 3。
import ipaddress
nets = [ipaddress.ip_network('192.168.%d.0/24' % i)
for i in range(8)]
print(list(ipaddress.collapse_addresses(nets)))
print(sum(n.num_addresses for n in nets))
实跑输出:
--------------------------------------------------------------------
八条 /24 合成一条 /21
--------------------------------------------------------------------
**聚合(超网)是把「连续且对齐」的多条路由并成一条**:**路由表变小,转发变快。**
**先看能不能并**:
聚合前 地址数
--------------------------
192.168.0.0/24 256
192.168.1.0/24 256
192.168.2.0/24 256
192.168.3.0/24 256
192.168.4.0/24 256
192.168.5.0/24 256
192.168.6.0/24 256
192.168.7.0/24 256
**八条合成了 1 条**:
**192.168.0.0/21(2048 个地址)**
**两个数必须相等:2048 = 2048**——
**聚合只是换了写法,没有多也没有少。**
**再看「不连续就并不了」**:**把 192.168.3.0/24 抽掉,剩下的还能并成几条?**
**结果 3 条**:
**192.168.0.0/23**
**192.168.2.0/24**
**192.168.4.0/22**
**抽掉 192.168.3.0/24,条数就从 1 条涨回 3 条**——
**0 和 1 还能并成 /23,4 到 7 还能并成 /22,但 2 就孤零零剩一条。**
**聚合的边界是「对齐」**:**一段地址要能被自己的长度整除,才能整体用一条前缀表示。**
**所以「地址规划」要从大到小、连续分配**——**随手乱给会让路由表永远合不起来。**
2048 等于 2048 这个等式是「聚合不改变覆盖范围」的证明:
并之前是 8 个 256,并之后是 1 个 2048。
抽掉一条之后的结果最能说明「对齐」的含义:
0 和 1 能并成 /23(起点 0 是 2 的倍数),4 到 7 能并成 /22(起点 4 是 4 的倍数),
但 2 孤零零的什么也并不了。
所以「连续性」不是「差不多连着」,是「严格按块对齐」。
collapse_addresses 的行为值得记住:它会「尽可能往上并」,而不是「保持原样」。
所以拿它来检查「我的规划能不能聚合」很方便——并完的条数远小于原来,就说明规划得整齐。
30 秒实验
- 算一下:16 个连续的 /24 能并成几条?掩码是多少?
- 想一下:为什么起点必须是块大小的整数倍?
- 想一下:聚合太宽会带来什么故障?
常见误区
- 以为「只要地址连续就能聚合」。 还要对齐,起点必须是块大小的倍数。
- 以为「聚合越多越好」。 太宽会出黑洞,把不属于自己的地址也吸过来。
- 以为「聚合会改变地址分配」。 只是改写法,覆盖范围一个地址都不多不少。
自测三问(点开对答案)
1. 聚合的两个前提条件是什么? —— **连续、对齐**——地址要连成一片,而且起点必须是块大小的整数倍。 2. 聚合有什么副作用? —— **会把「整块里没分配的地址」也宣告出去**,访问这些地址的包会被吸到聚合点然后丢掉,形成黑洞。 3. 为什么骨干路由表能控制在百万条? —— **因为一层层聚合**,按 /8 到 /16 的粗粒度记,不按 /24 逐条记。下一节换个角度:链路不同,一个包能装多少字节也不同。
- ☐ (本节完,下一节 4.5.1 数据链路不同,MTU则相异)