我最早对 CIDR 产生敬畏心,是在一次给分公司做路由汇总的深夜。公司刚接入了一个新办公区,路由表里本来静静躺着几条 /24 的明细路由。我贪省事,想着合并成大网段能少几行配置,就在出口设备上写了一条更大的汇总路由。结果第二天工单群就炸了——好几个本不该被覆盖的网段全跑到这条新链路上来。当时带我的导师只扔过来一句话:你先把无分类编址 CIDR 的边界画清楚再动手。后来我才明白,那晚栽的不是配置,是对“CIDR”的理解。
这篇继续我的计算机网络笔记,第十九篇,专门把 CIDR 从出生背景到计算原理、再到路由聚合和真实排错现场完整捋一遍。如果你也经常被“/23 到底有多大”“为什么 10.10.9.0/23 不能直接当一个网段”“路由器既然有两条匹配路由,凭什么走最长前缀这条”这类问题绕晕,这篇文章应该能帮你把线头一根根理开。
1. 分类编址为什么撑不住了:CIDR 诞生前的困局
1.1 固定分界带来的浪费
现在的年轻人可能很难想象,IPv4 地址当初是按“类”来分配的。32 位地址被切成三类固定长度的网络号加主机号:A 类网络号 8 位,每个网络能容纳约 1677 万台主机;B 类网络号 16 位,每个网络能容纳 65534 台;C 类网络号 24 位,每个网络只能容纳 254 台。
听起来好像还凑合?问题出在分配粒度太粗。一个五百人的公司,按真实需求可能只需要三百个 IP 地址,这在分类编址时代特别尴尬:申请 C 类不够用,申请 B 类又严重浪费。很多单位实际只有几百台机器,却抱着一整个 B 类地址段,剩下六万多个地址从此“消失”在路由表里,没人能够再用。反过来,如果一家公司规模稍大,需要三个 C 类网段,传统路由协议又必须逐条通告这三个 C 类,每一条都独立占一路由项,地址虽然没有浪费多少,路由表却被硬生生撑大了三倍。
这个浪费有多夸张?当时业界做过统计:按分类方式继续分配下去,B 类地址会在几年内被分光。而 C 类虽然还有余量,但越来越多的 C 类被零散分配出去之后,骨干路由器的路由条目开始爆炸式增长。地址耗尽和路由表膨胀两个问题同时逼近,靠原来的“分整类”思路已经彻底走不通。
1.2 路由表爆炸:比地址耗尽更先到来的危机
上世纪九十年代初,骨干网上的路由条目增长速度快到吓人。每多分配一批 C 类地址,核心路由器就要多背一条路由;而互联网规模还在指数级增长。路由器内存不是无限的,CPU 处理路由查找的耗时也在快速上升。可以说,即便 IPv4 地址没有立刻耗尽,这种“按类通告”的方式也会先把路由器内存撑爆。
CIDR 的思路其实很朴素:不再管一个 IP 原本属于 A 类还是 B 类,直接用“IP 地址 + 斜杠 + 前缀长度”来描述网络。前缀长度告诉你这串地址里前多少位是网络部分,剩下的是主机部分。网络规模需要多大,你就用多长前缀,既不按整类硬切,也不浪费多余地址。这套机制在九十年代初的 RFC 里被正式定下来,后来逐步成为互联网地址分配和路由通告的基本方式。
如果你觉得抽象,拿买菜类比就好懂了:分类编址像是只能整头猪一起买,不管你要两斤还是五斤,都至少得扛走一头;CIDR 则是按需切割,要多少切多少。更妙的是,CIDR 还允许把几块“散装”地址拼成一大块向外发布,这就是后面要讲的路由聚合。所以也有人把 CIDR 翻译成“无类域间路由”,核心就是取消了类与类之间的硬边界。
提示:直到今天,互联网上大部分路由依然延续了 CIDR 的“前缀 + 掩码”模型。IPv6 也沿用了这套逻辑,只是地址位长从 32 位变成了 128 位。
2. 无类编址的核心:网络前缀、主机号与斜杠记法
2.1 一个算例彻底搞懂地址范围
CIDR 的正确读法很简单:IP 地址后面加斜杠,斜杠后面跟一个数字,数字表示网络前缀长度。比如 10.10.10.0/23,意思是从 32 位二进制地址的高位开始数,前 23 位是网络部分,后 9 位全部是主机部分。前缀长度等于多少,相当于网络掩码里有多少个连续的 1。所以 /23 的子网掩码展开就是 255.255.254.0。
关键计算来了。主机部分是 9 位,能组合出 2 的 9 次方,也就是 512 个地址。这就是这个地址块的大小。那这个块到底从哪到哪?从 10.10.10.0 开始,主机位从全 0 到全 1,所以地址范围是 10.10.10.0 到 10.10.11.255。很多人第一次算到这里会愣住:怎么还跨到 10.10.11 段去了?
原因在于 /23 已经跨越了第三个八位组的边界。前 23 位里,第三个八位组只被占了前 7 位,最低的 1 位仍然属于主机部分。于是第三段的最后一个二进制位可以自由变化,导致这个地址块既包含 10.10.10.x,也包含 10.10.11.x。换句话说,/23 是把两个 C 类规模的小网段拼在一起的一个“超网”。
再看 /26 的例子。192.168.1.64/26,前缀长度 26,主机位 6 位,地址块大小是 64。范围从 192.168.1.64 到 192.168.1.127。网络地址是 192.168.1.64,广播地址是 192.168.1.127,中间能分配给终端用的就是 192.168.1.65 到 192.168.1.126,一共 62 个可用地址。这个 /26 在云厂商的私有网络划分里非常常见,一个小型业务子网用 /26 刚刚好。
2.2 为什么网络地址和广播地址不能分配
有朋友会问,为什么计算可用主机数时总是要减 2?因为一个地址块里有两个特殊地址:主机位全 0 的那个是网络地址,用来描述“这个网段本身”;主机位全 1 的那个是广播地址,用来给这个网段内所有主机发广播。这两个地址不能被分配给任何一台终端。
用生活例子解释:网络地址相当于一个小区的门牌号 0 号,广播地址相当于小区大喇叭的频道,而 1 号到 N 号才是可以住人的房间。你不可能把门牌号 0 号或广播频道分配给某户住人,否则整个小区通信的基本规则就乱了。
还有一个容易踩坑的边界对齐问题。CIDR 地址块不是随便给一个 IP 加个前缀就算数。地址块起始位置必须是“块大小整数倍”对齐的。比如 10.10.10.0/23 是合法网段,因为它的网络地址经掩码一算还是 10.10.10.0。但你写 10.10.9.0/23 试试?拿掩码 255.255.254.0 去与运算,得到的结果是 10.10.8.0,根本不等于 10.10.9.0。也就是说,10.10.9.0/23 这个写法本身就在描述一个不存在的网段边界,正确的写法应该是 10.10.8.0/23。
这种“边界对齐”的意识非常关键,尤其在做路由汇总和撰写防火墙地址对象的时候,一旦写错,你放通或阻塞的范围就会和你预想的不一致。
3. 最长前缀匹配:数据包在 CIDR 路由表里到底怎么走
3.1 一个选择实验:同一 IP 同时匹配多条路由时
路由器转发数据包,不是拿目标 IP 去和路由表里所有条目做“等于”判断,而是做“包含”判断。一个目标 IP 很可能同时落在好几条路由条目描述的范围内。这时选谁?答案是:谁描述的范围更小、更精确,就选谁。这就是最长前缀匹配原则。
举个例子。路由表里有三条路由:100.100.0.0/16 下一跳是 10.0.0.1,100.100.50.0/24 下一跳是 10.0.0.2,还有一个默认路由 0.0.0.0/0 下一跳是 10.0.0.254。现在有一个数据包要去 100.100.50.6。你拿这个地址去匹配三条路由,结果三条全都匹配上:
- 100.100.50.6 落在 100.100.0.0/16 的范围内,因为 /16 覆盖了从 100.100.0.0 到 100.100.255.255 的整个区域。
- 它也落在 100.100.50.0/24 的范围内,因为 /24 精确覆盖了 100.100.50.0 到 100.100.50.255。
- 默认路由 /0 更是命中一切目标。
最终路由器会选 /24 这条。因为 /24 的前缀长度最长,代表它给出的路径信息最具体。你可以脑补一个快递场景:包裹上写得很清楚杭州西湖区文三路某号,快递员先看到浙江省也匹配,再看到杭州市也匹配,最后看到具体街道直接按最细地址送,不可能按“浙江省”去投递。
3.2 管理距离不是第一优先:容易被忽略的前置条件
很多人配置网络时会有一个模糊印象:静态路由优先级高,所以静态路由一定优先。这个说法在“所有候选路由前缀长度相同”时才成立。如果一条 /16 的静态路由管理距离是 5,而一条 /24 的动态路由管理距离是 200,目标地址落在两者公共范围内,实际转发的仍然是 /24 那条。原因很简单:设备先比前缀长度,前缀一样长的候选才会进入下一轮优先级比较。
这在实际排错里特别重要。我见过有人排查“为什么流量没走静态路由”查了一下午,最后发现根源是旁边自动学习到了一条更长的明细路由。行政上确实静态优先不假,但技术上前缀更长的路由在比较环节就已经赢了。
路由查找的顺序如果写成口诀,就是:先最长匹配,再管理距离,最后才比度量值。你配置策略路由时也要顺着这个逻辑推,不要被“优先级”三个字带偏。
提示:默认路由 0.0.0.0/0 是前缀长度为 0 的特殊路由,任何目标 IP 都能和它匹配,但也是所有候选中最“不精确”的一个,所以总是最后才被考虑。没有更优路由时,它才出场兜底。
4. 路由聚合:用 CIDR 给路由表“瘦身”的实战操作
4.1 聚合条件的数学本质:连续、成块、起点对齐
CIDR 在工程上最有价值的地方,不是帮你省地址,而是帮你把多条路由合并成一条。假设一个分支分部有 8 个 C 类网段:172.16.0.0/24、172.16.1.0/24,一直到 172.16.7.0/24。如果不做任何处理,分部向上级通告 8 条路由,上级路由表就多 8 条条目。如果分部出口设备上配置一条 172.16.0.0/21 的聚合路由,上级只需学一条。
那是不是任意几个网段都能硬并?不是。能聚合的地址段必须满足三个条件:第一,地址范围连续;第二,数量必须是 2 的整数次幂;第三,起始地址必须落在聚合后大地址块的边界上。比如四个连续的 /24 可以并成 /22:172.16.0.0/24、172.16.1.0/24、172.16.2.0/24、172.16.3.0/24,合并结果就是 172.16.0.0/22。
但如果分部只有 172.16.0.0/24 和 172.16.2.0/24 两个网段,你能把这两个网段直接合并吗?不能。因为 /23 这个块要么包含 0 和 1 两个 C 段,要么包含 2 和 3 两个 C 段,中间那一段不是你的。如果强制用 /22 汇总,会把 1 和 3 两个不属于你的网段也包进去,上线之后极可能踩到别人的网络。
真实规划时我习惯先在纸质表格里列一遍网段起止,确认连续性之后再算前缀。有时候少一个网段,就得拆出一条单独的明细路由,反而更安全。
4.2 聚合之后的反直觉风险:黑洞与环路
聚合不是越高越好,它有一个反直觉的风险:聚合范围越大,包含的“未知区域”也越大。假如你在核心交换机上配了 172.16.0.0/21 的汇总路由指向某个出口,但实际网络中 172.16.5.0/24 根本不存在,上层设备一旦收到目标是 172.16.5.x 的流量,就会顺着聚合路由一股脑送到出口链路。设备到达出口后又发现没有更明细路由,只能再次转回核心——造成路由环路。
应对这种问题,业界常用的方案是给聚合网段配一条指向 NULL0 的“黑洞路由”。比如华为风格设备上可以写:
ip route-static 172.16.0.0 255.255.252.0 NULL0这条路由的优先级通常要调高,让它在路由表里“只兜底、不抢跑”。当真实子网的明细路由存在时,流量走明细;当目标是聚合范围内但不存在具体子网时,流量被丢进黑洞,不再回环。这个细节在很多教学文档里不会写,但生产环境里几乎必配。
聚合配置示例也顺手给你一份:
# 在分部出口设备上,把 172.16.0.0/24 ~ 172.16.3.0/24 汇总成一条 /22 ip route-static 172.16.0.0 255.255.252.0 10.0.0.1不同厂商命令语法略有差异,但思路是一致的:聚合条目越靠近出口边界越好,越靠近骨干越好;同时别忘了黑洞兜底。
5. 反向用法:如何根据终端数量反推 CIDR 前缀
5.1 三步反推法:设备数到前缀长度
正着算 CIDR 地址范围是基本功,但日常网络规划更多是反向操作:知道一个房间有多少台设备,要反推出应该分配多大的子网。我的方法很固定,一共三步。
第一步,把设备数量加上 2,留出网络地址和广播地址的位置。比如办公区会规划出 60 台终端、2 台打印机、1 个网关、再加 2 个预留地址,那我们按 65 个算,加 2 得到 67。第二步,找一个刚好大于等于 67 的 2 的整数次幂,这里 67 对应的就是 128。第三步,前缀长度等于 32 减去这个幂次对应的指数。128 是 2 的 7 次方,所以前缀长度是 32 - 7 = 25,也就是 /25。
不过我一直不建议卡得刚刚好。生产环境的设备数预测永远会偏小,今天 60 台,下个月可能就变成 80 台。我通常会在第一步就把数量乘上 1.5 到 2 倍再算。还是上面那个场景,按 100 台预留,加 2 后向上取 128,同样得到 /25。如果原来想给 /26 就有点紧了,后面扩一次网段就要重新规划 VLAN,代价远大于刚开始多给几个地址。
下面这张表可以直接作为规划速查表:
| 需求场景 | 最小推荐前缀 | 地址块大小 | 可用主机数 |
|---|---|---|---|
| 点对点链路传统方案 | /30 | 4 | 2 |
| 极小型子网或主备设备 | /29 | 8 | 6 |
| 小型服务器区 | /28 | 16 | 14 |
| 小型终端区 | /27 | 32 | 30 |
| 常见业务子网 | /26 | 64 | 62 |
| 中大型办公区 | /25 | 128 | 126 |
| 默认常见的 C 类规模 | /24 | 256 | 254 |
5.2 别把 /31 和 /32 用错场景
很多网工一看到 /31 就头大,因为按传统“减 2”的算法,一个 /31 块大小是 2,减 2 之后可用主机数是 0,简直没法用。但 RFC 3021 规定了点对点链路可以特殊对待:/31 不再保留网络地址和广播地址,两个地址都能分配给链路两端。比如两个路由器之间做互联,传统方案用 /30,浪费一半地址;如果两端设备都支持 /31,就可以各配一个 IP,一点也不浪费。
要注意的是,不是所有设备都完整支持 /31。一些老型号的防火墙、路由器交换芯片,可能把 /31 里主机位全 1 的那个地址仍然当作广播来处理,配置后一端通一端不通的现象并不少见。遇到这种情况,别死磕 RFC 标准,直接改回 /30 最省事。
/32 则用于精确描述“单台主机”。它常用于指定某个需要单独放通或单独丢弃的公网 IP,也常用于环回口地址和黑洞拦截。在 Linux 上你可以这么用:
# 把目标指向黑洞,实现精准丢弃 ip route add blackhole 203.0.113.8/32一个 /32 不代表子网里有可用主机,它代表的是一个独立 IP 的路由表现。把防火墙地址对象、安全组规则里那些“只允许某个公网出口访问”的权限写成 /32,是再常见不过的操作。
6. 排错现场:CIDR 配置中最常见的翻车姿势
6.1 子网掩码换算表与“看着像 /24 其实不是”的场景
先给你一张平时最容易用到的换算表,建议收藏:
| CIDR | 子网掩码 | 地址块大小 | 可用主机数 |
|---|---|---|---|
| /20 | 255.255.240.0 | 4096 | 4094 |
| /21 | 255.255.248.0 | 2048 | 2046 |
| /22 | 255.255.252.0 | 1024 | 1022 |
| /23 | 255.255.254.0 | 512 | 510 |
| /24 | 255.255.255.0 | 256 | 254 |
| /25 | 255.255.255.128 | 128 | 126 |
| /26 | 255.255.255.192 | 64 | 62 |
| /27 | 255.255.255.224 | 32 | 30 |
| /28 | 255.255.255.240 | 16 | 14 |
| /29 | 255.255.255.248 | 8 | 6 |
| /30 | 255.255.255.252 | 4 | 2 |
有个高频翻车点,是把 /22 当成了一个稍大点的“C 类网段”。其实 /22 包含 1024 个地址,可用 1022 个。你在防火墙地址对象里写一条 10.10.0.0/22,意味着你同时放通了原来规划的 10.10.0.0/24 到 10.10.3.0/24 四个网段。如果另外三个网段里有不想暴露的测试环境,这就是安全事件了。
6.2 防火墙地址对象和接口地址的 CIDR 写法坑
某次排查一个跨办公区互访问题,发现 VLAN 接口配置的是 192.168.1.1/24,但下面终端自己配了 192.168.2.50 这类地址。从网关接口的角度看,它所在的子网只认识 192.168.1.1 到 192.168.1.254 这些地址。终端发来的 ARP 请求询问“谁知道 192.168.1.1?”,网关应答后,终端把数据发给网关;但网关收到目标为 192.168.2.50 的报文,按 /24 掩码判断这不在自己的直连网段内,于是又把它当成外部流量,路由表里又没有对应的明细路由,自然就丢了。你抓包能看到 ARP 正常,数据也确实到了网关,但转发就是不成功。排查到最后发现,根本不是链路问题,而是接口地址的掩码范围压根没把终端覆盖进去。
防火墙安全组里也常遇到地址对象写错的情况。比如把内网区域地址从 10.20.10.0/24 误写成 10.20.0.0/16,安全策略的放通范围瞬间扩大 256 倍。这种问题不是网络不通,而是“不该通的地方通了”,比不通更隐蔽,也更危险。
再看看 CIDR 写法本身的低级错误。很多人手滑把 192.168.0.0/16 写成 192.168.0.0/16.0,或者把地址写成 192.168.10.0/24 却漏了一位,变成 192.168.10.0/23。后者在设备上不会报错,但它把整个 192.168.11.x 也一起纳入策略范围了。配置之前先把地址按表格算一遍,能省很多事。
提示:IPv6 同样使用 CIDR,只是没有了广播地址概念,所以“减 2”这一步在 IPv6 的普通子网里通常不适用。规划 IPv6 时用 /64、/48 这些前缀,思路完全一致,但别再把“网络地址 + 广播地址”的老观念直接搬过去。
回到开头那次深夜割接。后来我把所有分支网段的边界在模拟器里画了一遍,才发现问题出在自己没把“可聚合条件”和“明细路由优先级”想清楚。CIDR 难的不是公式,而是脑子里有没有那根“边界对齐”的弦。我现在每个新项目的 IP 规划,都会先在表格里把网段起止、掩码、下一跳列出来,对照前缀长度再动手敲命令。建议你也先养成这个习惯,能少熬好几个夜。