CIDR 是什么意思:无类域间路由与 /n 斜线写法
CIDR(Classless Inter-Domain Routing,无类域间路由)是 1993 年起取代 A、B、C 类地址的分配与路由方式:网络部分不再固定为 8、16、24 位,而是写在斜线后面。/22 就表示前 22 位固定、后 10 位可变,即一个含 1,024 个地址的块。
CIDR 是什么意思:它是 Classless Inter-Domain Routing 的缩写,中文叫无类域间路由,是今天互联网分配 IP 地址和做路由的基本规则。它做了两件事:一是取消 A、B、C 类对网络位长度的限制,网络部分可以是 0 到 32 之间的任意位数,写在地址后面的斜线里,例如 172.18.20.0/22;二是允许把连续的地址块汇总成一条路由。日常说的“CIDR 写法”“CIDR 地址块”,指的就是这种“起始地址/前缀长度”的表示法,它同时说清了一个块从哪里开始、有多大。
有类地址为什么被 CIDR 取代
CIDR 之前,IPv4 地址按第一个字节分类,网络位只有三种长度:
| 类别 | 首字节范围 | 网络位 | 这一类共有多少个网络 | 每个网络可用主机数 |
|---|---|---|---|---|
| A 类 | 1–126 | 8 位 | 126 | 16,777,214 |
| B 类 | 128–191 | 16 位 | 16,384 | 65,534 |
| C 类 | 192–223 | 24 位 | 2,097,152 | 254 |
问题出在档位太粗。一个需要 2,000 台主机的单位,C 类的 254 个不够用,只能申请 B 类,结果 65,534 个地址只用了约 3%。B 类总共只有 16,384 个,按这种方式发放,很快就不够分了;退而求其次,给这个单位发 8 个 C 类,地址省下了,可每个 C 类都要在互联网路由器上单独占一条路由,路由表随之膨胀。
1993 年发布的 RFC 1519 把当时的困境概括为三点:B 类地址即将耗尽、互联网路由器上的路由表增长过快,超出现有软件和人力能够有效管理的程度、32 位地址空间终将用完。CIDR 针对前两点给出了办法:
- 前缀长度任意:需要 2,000 个地址就给一个 /21(2,048 个地址,2,046 个可用),不必在 254 和 65,534 之间二选一。
- 按层级分配、对外汇总:地址从注册机构分给运营商,再由运营商分给客户。运营商手里成百上千个客户的小块,对外只宣告一个汇总后的大前缀。
RFC 1519 后来被 2006 年的 RFC 4632 取代,规则延续至今。原来的“子网划分”只能在一个有类网络内部往小里切,CIDR 则让块可以比 /8、/16、/24 更大或更小,地址和路由都只看前缀长度,不再看第一个字节。
/n 怎么读:前 n 位固定,剩下的可变
CIDR 写法 地址/n 的意思是:32 位地址里,前 n 位是网络前缀,块内所有地址的这 n 位都相同;后面 32 − n 位可以任意变化。由此直接得到三个数:
- 块大小:2 的(32 − n)次方。/22 是 2^10 = 1,024 个地址。
- 掩码:n 个 1 后面补 0。/22 是 255.255.252.0。
- 起点:起始地址必须让后 32 − n 位全为 0,也就是块大小的整数倍。
n 正好是 8、16、24 时最直观,斜线前面的地址按点对齐,/24 就是前三节固定、第四节从 0 到 255。n 不是 8 的倍数时,有一个字节会被前缀切成两半,这个字节决定了块的边界。前缀在 /17 到 /24 之间,被切开的是第三节:
| 前缀 | 第三节的步长 | 一个 /16 里能分出几个 | 第三节的合法起点 | 块大小 |
|---|---|---|---|---|
| /17 | 128 | 2 | 0、128 | 32,768 |
| /18 | 64 | 4 | 0、64、128、192 | 16,384 |
| /19 | 32 | 8 | 0、32、64……224 | 8,192 |
| /20 | 16 | 16 | 0、16、32……240 | 4,096 |
| /21 | 8 | 32 | 0、8、16……248 | 2,048 |
| /22 | 4 | 64 | 0、4、8……252 | 1,024 |
| /23 | 2 | 128 | 所有偶数 | 512 |
| /24 | 1 | 256 | 0–255 任意 | 256 |
/25 到 /32 的规律完全相同,只是被切开的换成第四节:/25 步长 128,/26 步长 64,一直到 /30 步长 4。任何前缀都能现场推出步长:先求 n 除以 8 的余数 r,步长就是 2 的(8 − r)次方。例如 /22 的 r = 6,步长 2^2 = 4;/27 的 r = 3,第四节步长 2^5 = 32。r 为 0 说明正好按字节对齐,不需要这个公式。
非整字节的例子:/22 和 /23 怎么看
例一:172.18.20.0/22 包含哪些地址。 22 − 16 = 6,第三节的前 6 位属于前缀,后 2 位和第四节的 8 位一起可变:
172.18.20.0/22
第三节 20 = 000101|00 前 6 位固定(16 + 6 = 22)
^^ 这 2 位加上第四节 8 位,共 10 位可变
第三节可取 000101 00、01、10、11,即 20、21、22、23
范围:172.18.20.0 ~ 172.18.23.255,共 2^10 = 1,024 个地址
所以 /22 等于 4 个连续的 /24,第三节从 20 到 23。作为一个子网使用时,网络地址是 172.18.20.0,广播地址是 172.18.23.255,中间 1,022 个都能分配。
例二:172.18.27.130/22 属于哪个块。 看第三节,/22 的步长是 4,27 除以 4 得 6 余 3,块从 6 × 4 = 24 开始,所以它属于 172.18.24.0/22,范围 172.18.24.0 到 172.18.27.255。
例三:172.18.26.0/23。 步长 2,第三节 26 是偶数,合法起点。范围是 172.18.26.0 到 172.18.27.255,共 512 个地址。在这个块里,172.18.26.255 和 172.18.27.0 都是普通的主机地址,可以分给服务器;真正的广播地址只有末尾的 172.18.27.255。
这几个例子对应几种常见的误读:
| 写法 | 常见误读 | 实际含义 |
|---|---|---|
| 172.18.20.0/22 | 只有 172.18.20.x 这一段 | 20、21、22、23 四个 /24 |
| 172.18.22.0/22 | 一个从第三节 22 开始的块 | 22 不是 4 的倍数,不是合法起点;这个地址落在 172.18.20.0/22 里 |
| 172.18.26.0/23 里的 172.18.26.255 | 末位是 255,是广播地址 | 普通可用地址,广播地址是 172.18.27.255 |
| 任意两个相邻的 /24 | 都能合成一个 /23 | 只有第三节以偶数开头的一对才行,26 和 27 可以,27 和 28 不行 |
第三行在实际工作中最容易出事:一些写死了“末位 0 或 255 不能用”的表单校验和老脚本,会把 /23、/22 中间的这类地址误判为非法。分配地址前,按块的边界判断,不要按末位数字判断。
地址块和前缀:一个块只有一种合法写法
前缀和地址块是一回事的两种说法:前缀是块内所有地址共有的那 n 位,地址块是共有这 n 位的全部地址。所以一个合法的块只有一种写法,起点和长度都是唯一的。斜线前的地址如果不是块的起点,也就是主机位不全为 0,那它描述的就不是一个块,很多工具会直接拒绝:
# 主机位不为 0,作为路由目标是非法写法
$ ip route add 172.18.22.0/22 via 10.0.0.1
Error: Invalid prefix for given prefix length.
# Python 同样拒绝,报错的最后一行是:
$ python3 -c "import ipaddress; ipaddress.ip_network('172.18.22.0/22')"
ValueError: 172.18.22.0/22 has host bits set
# 加 strict=False,让它归到所在的块
$ python3 -c "import ipaddress; print(ipaddress.ip_network('172.18.22.0/22', strict=False))"
172.18.20.0/22
但同样的斜线写法配在网卡上就是合法的:ip addr add 172.18.22.15/22 dev eth0 的意思是“本机地址 172.18.22.15,所在块的前缀长度是 22”,系统会自动算出所在的网段 172.18.20.0/22 并加上直连路由。区分方法很简单:路由表、防火墙规则、分配单、IPAM 登记里写的是“块”,斜线前必须是起点;网卡配置里写的是“地址加前缀长度”,斜线前是主机自己的地址。
反过来,一段不按边界对齐的连续地址,不能用一个 CIDR 表示。例如 172.18.21.0 到 172.18.24.255,虽然恰好是 4 个 /24,但起点 21 不是 4 的倍数,凑不成一个 /22,最少要拆成三块:
$ python3 -c "import ipaddress as i; print(*i.summarize_address_range(i.ip_address('172.18.21.0'), i.ip_address('172.18.24.255')))"
172.18.21.0/24 172.18.22.0/23 172.18.24.0/24
拿到“从某某到某某”这种起止范围的分配单时,先用这条命令转换成 CIDR,再录入防火墙或路由配置。
路由器怎么用 CIDR:最长前缀匹配
有了任意长度的前缀,一个目的地址可能同时落在好几条路由里。路由器的规则是最长前缀匹配:能匹配上的路由中,前缀最长、也就是块最小、描述最具体的那条胜出。假设一台路由器上有这四条路由:
| 目的前缀 | 下一跳 | 能匹配 172.18.22.9 吗 |
|---|---|---|
| 0.0.0.0/0 | 10.0.0.1(出口) | 能,/0 匹配一切 |
| 172.18.0.0/16 | 10.0.1.1 | 能 |
| 172.18.20.0/22 | 10.0.2.1 | 能,22 在 20~23 之间 |
| 172.18.22.0/24 | 10.0.3.1 | 能 |
发往 172.18.22.9 的包四条全中,选 /24,交给 10.0.3.1;发往 172.18.21.9 的包匹配不上 /24,选 /22,交给 10.0.2.1;发往 172.18.40.1 的只匹配 /16 和 /0,选 /16;8.8.8.8 只剩默认路由。想知道设备对某个地址实际选了哪条,直接问它:
# Linux:输出实际选中的下一跳和出接口
ip route get 172.18.22.9
# 172.18.22.9 via 10.0.3.1 dev eth0 src 10.0.0.10
华为和 H3C 用 display ip routing-table 172.18.22.9,思科用 show ip route 172.18.22.9,都会显示最终匹配的那条路由。
这条规则是 CIDR 汇总能成立的前提:运营商对外宣告一个大前缀,内部再用更长的前缀把流量分到各个客户,互不冲突。它也解释了机房里的一些常见现象,例如流量清洗服务在攻击期间对外宣告比原路由更长的前缀,就能把这部分地址的流量引到清洗中心;有人在内网配错了一条更具体的静态路由,大段地址里只有那一小块不通。
在 IPAM 里怎么落地
机房的地址台账里,最常见的错误不是不会算,而是写法不统一:有的段写起止地址,有的写 CIDR,有的把 172.18.22.0/22 这种非起点写法直接录进去,几年下来谁也说不清哪些块重叠了。
Toplink DCIM 的 IP 地址管理以 CIDR 为录入格式:填入一个段,系统自动算出开始地址、结束地址和子网掩码,网关可以选段内第一个或最后一个地址;/22、/21 这类大段可以自动拆成多个 /24 分别管理,每个段再记录所在机房、VLAN 和网关设备。IPv6 段同样按 CIDR 录入、按掩码拆分。地址按块登记之后,哪个块分给了谁、还剩多少,在用量比例图上一目了然。
常见问题
CIDR 和 VLSM 有什么区别?
VLSM(变长子网掩码)指在一个网络内部用不同长度的掩码划分子网;CIDR 指分配和路由时不再受 A、B、C 类限制,并允许把多个块汇总成一条路由。前者是内部划分的方法,后者是整个互联网的地址与路由规则,两者经常一起出现。
为什么说 CIDR 让路由表变小了?
连续且对齐的地址块可以汇总成一条前缀更短的路由,例如 4 个相邻的 /24 合成一条 /22。有类时代,同样的地址要用 4 条 C 类路由分别宣告。汇总要求块的数量是 2 的幂、地址连续、起点对齐,不满足时只能拆成多条。
IPv6 也用 CIDR 写法吗?
用,而且 IPv6 从设计之初就没有分类,只有前缀长度,例如 2001:db8:1200::/40。前缀长度范围是 0 到 128,局域网段通常固定用 /64,分配给机房或企业的常见 /48、/56。