CIDR 技术:路由汇总与超网怎么算
CIDR 技术用在路由上,就是把多个连续的网段合并成一条前缀更短的路由,减少路由条目、隔离网络变化。算法的核心是把网段写成二进制,取共同的前缀位。能否精确汇总,取决于合起来的地址数是不是 2 的幂、起点是否对齐、中间有没有缺口。
CIDR 技术在路由层面的用途,是把多个连续的网段合并成一条前缀更短的路由,这个动作叫路由汇总,也叫路由聚合;汇总出来的那个大块,在跨过 A、B、C 类边界时被称为超网。例如 192.168.32.0/24 到 192.168.39.0/24 这 8 个网段,可以汇总成一条 192.168.32.0/21。计算方法是把网段写成二进制,取它们从左往右完全相同的那几位,相同位数就是汇总后的前缀长度。汇总能否“精确”,也就是不多吸进不属于你的地址,取决于三个条件:合起来的地址数是 2 的幂、起点对齐到汇总块的大小、中间没有缺口。下面给出算法、例题、失败原因和设备上的配置方法。
路由汇总、超网、CIDR 聚合是什么关系
这三个词经常混用,说的是同一类操作,只是侧重点不同:
| 说法 | 英文 | 侧重点 | 例子 |
|---|---|---|---|
| 路由汇总 | Route Summarization | 在路由协议或路由表里,用一条短前缀代替多条长前缀 | OSPF 区域边界把 32 条 /24 汇总成一条 /19 |
| 路由聚合、CIDR 聚合 | Route Aggregation | 与汇总同义,BGP 里的叫法,命令就叫 aggregate | 运营商把客户的小块聚合成自己的大前缀对外宣告 |
| 超网 | Supernetting | 汇总后的前缀比有类掩码还短,跨过了 A、B、C 类边界 | 4 个 C 类 192.168.32.0/24 至 192.168.35.0/24 合成 /22 |
区别只在超网:它强调“比有类边界更短”。把 10.1.0.0/24 到 10.1.255.0/24 汇总成 10.1.0.0/16 是路由汇总,但 /16 仍比 A 类的 /8 长,严格说不叫超网;把 4 个 C 类合成 /22 才是超网。在 CIDR 普及之前,路由器按地址类别处理路由,比有类掩码还短的前缀无法表达,CIDR 允许任意前缀长度,超网才成为可能。今天的设备都按无类方式工作,这个区分主要出现在教材和考试里,工作中说“汇总”即可。
汇总带来的好处很具体:上游设备的路由表变小;内部某个网段增删或闪断,只要汇总路由还在,上游感知不到,路由协议不会因此在全网重新计算;ACL、防火墙策略也可以按汇总块写一条规则。
汇总算法:四步求出汇总路由
以 192.168.32.0/24 到 192.168.39.0/24 共 8 个网段为例:
- 找出变化的那一节。8 个网段的前两节都是 192.168,第三节从 32 变到 39,第四节都是 0,只需要看第三节。
- 把这一节写成二进制。只写最小值和最大值就够了,中间的值一定夹在两者之间:
32 = 0010 0000
39 = 0010 0111
^^^^ ^ 前 5 位相同,后 3 位不同
- 数相同的位数,加上前面固定的节。前两节固定 16 位,第三节相同 5 位,汇总前缀长度是 16 + 5 = 21。
- 把不同的位清零,得到起始地址。第三节保留前 5 位 00100,后 3 位清零,还是 32,汇总路由为 192.168.32.0/21,掩码 255.255.248.0。
最后做一次核对:/21 包含 2 的 11 次方,即 2,048 个地址,也就是 8 个 /24,正好是第三节 32 到 39,和要汇总的网段一一对应,这是一个精确汇总。
如果网段大小相同,还有一个更快的判断方法:数量 N 是 2 的幂,并且起始的那一节能被 N 整除,就能精确汇总,前缀长度减少 log₂N 位。上例 N = 8,32 ÷ 8 = 4 整除,/24 减 3 位得 /21。两个条件任一不满足,就只能多吸进地址或拆成多条,后面的例题会遇到。
四道例题:从整齐到不整齐
例题一:前缀长度不同的网段能汇总吗? 一个机房有 10.40.8.0/23、10.40.10.0/24、10.40.11.0/25、10.40.11.128/25 四个网段。长度不同不影响算法,把最小起点和最大终点拿出来比:最小是 10.40.8.0,最大终点是 10.40.11.255,第三节 8 = 0000 1000,11 = 0000 1011,前 6 位相同,汇总为 10.40.8.0/22。再核对地址数:512 + 256 + 128 + 128 = 1,024,与 /22 的大小相等,精确汇总。
例题二:三个网段怎么汇总? 172.16.4.0/24、172.16.5.0/24、172.16.6.0/24。第三节 4 = 0000 0100,6 = 0000 0110,前 6 位相同,能覆盖它们的最小前缀是 172.16.4.0/22。但 /22 有 4 个 /24,多出来的 172.16.7.0/24 不属于你。两种处理:一是宣告两条精确路由 172.16.4.0/23 和 172.16.6.0/24;二是宣告 /22,前提是 172.16.7.0/24 确定没有分配给别人,并在本设备上配丢弃路由。
例题三:数量是 2 的幂,为什么还是不行? 172.16.6.0/24 到 172.16.9.0/24,正好 4 个。第三节 6 = 0000 0110,9 = 0000 1001,只有前 4 位相同,覆盖它们的最小前缀是 172.16.0.0/20,一口气吸进 16 个 /24,其中 12 个不属于你。原因是起点 6 不能被 4 整除,这 4 个网段横跨了 172.16.4.0/22 和 172.16.8.0/22 两个块的边界。精确的写法是两条 /23:172.16.6.0/23 和 172.16.8.0/23。
例题四:第四节上的汇总。 4 条互联链路 10.255.0.0/30、10.255.0.4/30、10.255.0.8/30、10.255.0.12/30。变化发生在第四节,0 = 0000 0000,最大终点 15 = 0000 1111,前 4 位相同,24 + 4 = 28,汇总为 10.255.0.0/28。N = 4,起点 0 能被 4 整除,/30 减 2 位,结果一致。
手算容易在边界上出错,可以用 Python 自带的 ipaddress 模块核对:下面的函数求出能覆盖全部网段的最小汇总前缀,并列出汇总后会多吸进哪些不属于你的块,列表为空就是精确汇总。
import ipaddress as ip
def summarize(nets):
nets = [ip.ip_network(n) for n in nets]
lo = min(int(n.network_address) for n in nets)
hi = max(int(n.broadcast_address) for n in nets)
plen = 32 - (lo ^ hi).bit_length() # 起点和终点相同的高位个数
sup = ip.ip_network((lo, plen), strict=False)
extra = [sup]
for n in nets: # 从汇总块里扣掉自己的网段
out = []
for b in extra:
if b.subnet_of(n):
continue
out.extend(b.address_exclude(n) if n.subnet_of(b) else [b])
extra = out
return sup, sorted(extra)
print(summarize(["172.16.6.0/24", "172.16.7.0/24", "172.16.8.0/24", "172.16.9.0/24"]))
# 172.16.0.0/20,多吸进 172.16.0.0/22、172.16.4.0/23、172.16.10.0/23、172.16.12.0/22
print(summarize(["10.40.8.0/23", "10.40.10.0/24", "10.40.11.0/25", "10.40.11.128/25"]))
# 10.40.8.0/22,多吸进的列表为空
汇总失败的常见原因
“汇总失败”有两种表现:算出来的汇总块比实际网段大,把别人的地址也包了进来;或者汇总配上去之后,部分流量走错、丢包。原因归纳如下:
| 原因 | 例子 | 后果 | 处理方法 |
|---|---|---|---|
| 数量不是 2 的幂 | 3 个、6 个连续 /24 | 汇总块里有空位 | 拆成多条精确汇总;或确认空位未分配后整块宣告并配 Null0 |
| 起点没对齐 | 第三节从 6 到 9 | 汇总块成倍扩大 | 按对齐边界拆成多条;新规划时让起点对齐 |
| 中间有缺口 | 8 到 15 里有一个 /24 分给了别的机房 | 两处都宣告覆盖同一段的路由 | 缺口所在位置宣告更长的明细,靠最长前缀匹配分流;或者回收重新规划 |
| 网段分散在不同出口 | 10.40.8.0/23 在机房 A,10.40.10.0/23 在机房 B | 任一边宣告 /22 都会把另一边的流量引过来 | 各自只汇总本地拥有的部分 |
| 汇总点选错 | 在只连了一部分网段的设备上配汇总 | 这台设备成为整段地址的入口,收到不属于它的流量 | 在所有明细都汇聚到的那台设备上汇总 |
| 汇总后的前缀仍然太长 | 两个 /26 合成 /25 后向公网宣告 | 多数运营商不接收长于 /24 的 IPv4 前缀,宣告被上游过滤 | 公网宣告至少凑成 /24,具体以上游运营商的要求为准 |
第三、四行最隐蔽:两台设备宣告了重叠的前缀,平时靠最长前缀匹配各走各的,一旦某处的明细路由消失,流量就会被另一处的汇总路由吸走。所以规划地址时,同一个汇总块只放在一个位置,宁可多留空块,也不要把一个块拆给两个机房。
汇总路由要配一条指向 Null0 的丢弃路由
汇总块里有未使用的地址时,必须防止环路。设想汇聚路由器 DC-R1 向核心宣告 10.40.8.0/22,自己的默认路由指向核心,而 10.40.11.0/24 还没有启用。一个发往 10.40.11.5 的包会这样走:
- 核心查表,命中 10.40.8.0/22,交给 DC-R1;
- DC-R1 没有 10.40.11.0/24 的明细,只能命中默认路由,又交回核心;
- 核心再交给 DC-R1,来回往返,直到 TTL 减到 0 才被丢弃。
TTL 为 64 的包会在这条链路上来回约 32 次,扫描器对整段地址做探测时,这种流量会被放大几十倍占用互联带宽。解决方法是在汇总设备上加一条指向 Null0 的汇总路由:
ip route 10.40.8.0 255.255.252.0 Null0
Null0 是一个虚拟接口,送进去的包直接丢弃。根据最长前缀匹配,10.40.8.0/23、10.40.10.0/24 这些明细路由比 /22 更长,仍然正常转发;只有没有明细的地址才落到这条 /22 上被丢弃,不会再回到默认路由。OSPF、EIGRP、BGP 在设备上做汇总时,思科 IOS 默认会自动生成这条丢弃路由,静态汇总要手工添加。
在思科、华为设备上怎么配汇总
汇总配置在哪里,取决于路由从哪里来:
! 1. 静态宣告:本地丢弃路由 + 通过 BGP 宣告
ip route 10.40.8.0 255.255.252.0 Null0
router bgp 65001
network 10.40.8.0 mask 255.255.252.0
!
! 2. OSPF:在 ABR 上把区域 1 的明细汇总后发往骨干区域
router ospf 1
area 1 range 10.40.8.0 255.255.252.0
!
! 3. OSPF:在 ASBR 上汇总重分布进来的外部路由
router ospf 1
summary-address 10.40.8.0 255.255.252.0
!
! 4. EIGRP:在出接口上汇总,100 是 AS 号
interface GigabitEthernet0/0/1
ip summary-address eigrp 100 10.40.8.0 255.255.252.0
!
! 5. BGP:表中至少有一条明细时生成聚合,summary-only 抑制明细不再宣告
router bgp 65001
aggregate-address 10.40.8.0 255.255.252.0 summary-only
几个要点:
area range只在 ABR 上、只对该区域内部的路由生效;对重分布进来的路由要用summary-address。两者写错位置时命令能输入,但不起作用。- BGP 的
network ... mask要求路由表里有完全一致的前缀才会宣告,这也是第一种写法要先加 Null0 静态路由的原因。 aggregate-address不加summary-only时,汇总和明细会一起宣告出去,上游路由表并没有变小。- 配好后用
show ip route 10.40.8.0 255.255.252.0 longer-prefixes查看汇总路由和它下面的全部明细;OSPF、EIGRP 的汇总会显示为is a summary ... Null0,BGP 被抑制的明细在show ip bgp里带有s标记。
华为 VRP 的对应写法:
| 场景 | 思科 IOS | 华为 VRP |
|---|---|---|
| 丢弃路由 | ip route 10.40.8.0 255.255.252.0 Null0 |
ip route-static 10.40.8.0 22 NULL0 |
| OSPF 区域间汇总 | area 1 range ...(OSPF 进程下) |
abr-summary 10.40.8.0 255.255.252.0(区域视图下) |
| OSPF 外部路由汇总 | summary-address ... |
asbr-summary 10.40.8.0 255.255.252.0(OSPF 视图下) |
| BGP 聚合并抑制明细 | aggregate-address ... summary-only |
aggregate 10.40.8.0 22 detail-suppressed |
| 查看结果 | show ip route ... longer-prefixes |
display ip routing-table 10.40.8.0 22 longer-match |
华为设备做 OSPF 或 BGP 汇总时,是否自动生成指向 NULL0 的黑洞路由随协议和软件版本而定,配置后用 display ip routing-table 10.40.8.0 22 检查,没有就手工加上第一行的静态路由。具体参数随软件版本略有差异,以设备的命令手册为准。
在 IPAM 里怎么落地
汇总能不能做干净,在分配地址的那一刻就决定了:一个 /22 只要有一个 /24 分到了别的机房,这个 /22 就再也无法完整汇总。所以地址台账要按汇总块来组织,而不是按“下一个空闲的 /24”来分。
Toplink DCIM 的 IP 地址管理以 CIDR 格式录入地址段,自动算出起止地址和掩码,大段可以自动拆分为多个 /24 分别管理;每个段记录所属机房、VLAN 和网关设备。按机房录入汇总块后,哪个 /24 在哪个机房、哪些还空着一目了然,例题二里那种“空位到底有没有分给别人”的问题,查一下段的归属就能回答。暂时不用的段可以设为锁定,自动分配时不会被占用,给将来的扩容和汇总留出完整的块。
常见问题
思科的 auto-summary 是什么,要不要关?
auto-summary 是 RIP 和 EIGRP 在有类边界上自动汇总的功能,例如把 10.40.8.0/22 自动汇总成 10.0.0.0/8。同一个有类网络的子网分散在不同位置时,它会让两边都宣告同一条 /8,造成流量走错。EIGRP 从 IOS 15.0(1)M 起默认关闭;RIPv2 在不少 IOS 版本里默认开启,用 show ip protocols 可以确认当前状态。今天的网络应当执行 no auto-summary 关闭它,改为手工汇总。
IPv6 也能这样汇总吗?
能,算法相同,只是按十六进制换算二进制。例如 2001:db8:10::/48 到 2001:db8:13::/48 四个块,第三组 0x10 到 0x13 的前 14 位相同,加上前两组的 32 位,汇总为 2001:db8:10::/46。IPv6 地址空间大,规划时按 2 的幂对齐预留,汇总通常比 IPv4 简单。
汇总后某个子网断了,上游还会把流量送过来吗?
会。只要汇总范围内还有任何一个明细路由存在,汇总路由就一直宣告,上游看不到明细的变化,发往断开子网的流量仍会送到汇总设备,再被 Null0 丢弃。这是汇总隔离故障的代价:需要靠监控发现明细路由的消失,而不是靠上游路由表。