跳到主要内容
AZ Tools

把 IP 地址与子网讲清楚

子网划分素有繁琐之名,但它其实只是一个想法的一贯应用:地址是一个数字,前缀说明其中前多少位是固定的。想通这一点,主机数量、地址段重叠与聚合就都能顺理成章地推出来。本文讲清这套算法、具有特殊含义的地址段,以及直觉失效的地方。

地址是数字,前缀是边界

一个 IPv4 地址就是一个 32 位数字。我们熟悉的点分写法,只是把这个数字拆成四个字节给人看:192.168.1.10 就是 3232235786。子网掩码做的全部事情,就是决定在哪里切开这个数字——前面的位标识网络,后面的位标识其中的主机。

CIDR 记法直接写出这个切点。`/24` 表示前 24 位是网络,留 8 位给主机;`/26` 固定 26 位、留 6 位。掩码 255.255.255.0 与后缀 `/24` 是同一句话的两种写法,而更值得用来思考的是前缀长度:它是一个计数,于是比较与包含判断都变成了纯粹的位运算。

读一个前缀:共有多少地址,可用多少

IPv4 中长度为 *n* 的前缀包含 2^(32−n) 个地址。`/24` 是 256 个,`/26` 是 64 个,`/30` 是 4 个。在普通的广播子网里,第一个地址是网络标识、最后一个是广播地址,所以可用主机数要少两个:分别是 254、62 和 2。最后这个数字,正是点对点链路传统上用 `/30` 的原因。

边界并非随意:前缀总是从自身大小的整数倍处开始。`/26` 位于 .0、.64、.128 和 .192,中间没有别的——192.168.1.100/26 实际描述的是从 192.168.1.64 开始的那一块。两个前缀要么完全嵌套,要么毫不重叠,正因如此路由表和防火墙规则才是可处理的。

有特殊含义的地址段

IPv4 有三段保留给私有用途、不会在公网上被路由:10.0.0.0/8、172.16.0.0/12(注意:不是整个 172.x)和 192.168.0.0/16。此外 127.0.0.0/8 是环回,169.254.0.0/16 是链路本地——DHCP 失败时机器给自己分配的地址——而 100.64.0.0/10 是运营商级 NAT,这也是设备可能报告一个「公网」地址却依然不可达的原因。

知道这些不只是冷知识。日志里全是 10.x,说明客户端地址是在代理之后记录的;出现 169.254 地址,说明网络根本没有起来;而因为「172 不是私有的」就把 172.20.x 当作公网,是一个真实且反复出现的错误。凡涉及白名单,请核对地址段边界,而不是第一个八位组。

IPv6 改变了算法

IPv6 地址是 128 位,写作八组四位十六进制数,其中连续为零的一段可以折叠成 `::`——每个地址只能折叠一次,否则展开就有歧义。组内前导零可以省略,所以同一个地址有多种合法写法,文本比较毫无用处。比较或去重之前请先规范化。

惯例也不同。单条链路的标准大小是 `/64`,于是「有多少台主机」这个问题基本失去了意义;没有广播地址,也没有需要扣除的网络/广播这一对。前缀依旧是嵌套的位区间,因此聚合的做法与 IPv4 完全一样——只是数字大得多。

排序、聚合与比较

永远不要把地址当字符串排序。按字典序,10.0.0.9 排在 10.0.0.100 之后,9.0.0.1 排在 10.0.0.1 之后,任何这样排出来的报表都在悄悄出错。请按数值排序——只有这样相邻的地址块才会挨在一起,而这是一切聚合的第一步。

聚合本身是拆分的逆运算:两个长度相同、除最后一位外所有位都相同的前缀,可以合并成一个短一位的前缀。反复这样做,能把散乱的白名单缩成寥寥几块,在规则条数上限和查找时间上都是实打实的收益。但要记住,任意的「起始—结束」区间未必正好是一个前缀:要精确覆盖 10.0.0.5–10.0.0.20 需要好几块,而把它凑成一个整齐的 `/27`,就等于放行了你并不打算放行的地址。

  • `/24` = 256 个地址,可用 254 个;`/26` = 64 个,可用 62 个;`/30` = 4 个,可用 2 个。
  • 前缀从自身大小的整数倍开始——192.168.1.100/26 就是 .64 那一块。
  • 私有:10.0.0.0/8、172.16.0.0/12、192.168.0.0/16。链路本地:169.254.0.0/16。CGNAT:100.64.0.0/10。
  • 比较地址请用数值,绝不要用文本。

相关工具