Files
cs-note/hhs/NETWORK/03-网络层/02-CIDR与子网划分.md
T
2026-05-27 23:01:37 +08:00

294 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [计算机网络, CIDR, 子网划分, IP计算]
create time: 2026-05-18 00:00
---
# CIDR 与子网划分
## 概述
CIDR(Classless Inter-Domain Routing,无类别域间路由)是现代 IP 寻址的核心机制。它摒弃了 A/B/C 类地址的固定边界,允许任意长度的前缀——让网络管理员可以按需精确分配 IP 空间。
> [!QUESTION] 为什么需要子网划分?
> 想象一个有 5000 台主机的公司,如果全放在 /12 大网络里:每秒数千个 ARP 广播、巨大的 MAC 表、单点故障影响面极大。子网划分将大问题拆小,既缩小了广播域,又加强了安全性隔离。
## CIDR 表示法
```
192.168.1.0/24 ← /24 表示前 24 位是网络号
```
即 `192.168.1.0` + 子网掩码 `255.255.255.0`
### 常见子网对照表
| CIDR | 子网掩码 | 主机数 (usable) | 典型用途 |
|------|---------|-----------------|---------|
| /30 | 255.255.255.252 | 2 | 点对点链路 |
| /29 | 255.255.255.248 | 6 | 小型办公室 |
| /28 | 255.255.255.240 | 14 | VLAN 极小 |
| /27 | 255.255.255.224 | 30 | 小组 VLAN |
| /26 | 255.255.255.192 | 62 | 小型部门 |
| /25 | 255.255.255.128 | 126 | 中型部门 |
| /24 | 255.255.255.0 | 254 | **最常用**,标准办公室 VLAN |
| /23 | 255.255.254.0 | 510 | 大型部门 |
| /22 | 255.255.252.0 | 1022 | 数据中心租户 |
| /21 | 255.255.248.0 | 2046 | 大规模部署 |
| /16 | 255.255.0.0 | 65534 | 大型企业内网 |
> [!tip] 快速计算可用主机数
> $N_{usable} = 2^h - 2$,其中 $h$ 是主机位数量($h = 32 - \text{prefix}$)
> - `-2` 因为网络地址和广播地址不可用
> - 对于 /31(点对点链路),RFC 3021 允许使用 2 个地址(无网络/广播位)
## CIDR vs 分类地址:为什么需要 CIDR?
CIDR(1993 年,RFC 1518/1519)诞生之前,IP 地址按 A/B/C 三类硬性划分,带来两个致命问题:
| 对比项 | 分类地址(Classful) | CIDR(Classless) |
|--------|-------------------|-----------------|
| 前缀长度 | A=/8, B=/16, C=/24 固定三档 | 任意 /0 ~ /32 |
| 分配灵活性 | 差——中等规模网络要么浪费 B 类(/16 多余),要么 C 类(/24)不够 | 精确按需分配 |
| 路由聚合 | 不支持,每个网络单独一条路由 | 支持超网聚合,压缩路由表 |
| 路由表规模 | 随网络数线性增长 | 聚合后显著缩小 |
> [!QUESTION] 分类地址到底浪费有多严重?
> 一个需要 2000 台主机的机构,在分类体系下只能申请 B 类地址(/16,65534 个 IP),实际使用率仅 **3%**——剩下 63000+ 个地址被白白锁死。CIDR 只需分配 /21(2046 个 IP),利用率直接拉满。
## 子网划分的核心算法
### 示例:从 /24 划分为 4 个子网
原始:`192.168.1.0/24`(254 台主机)
目标:分成 4 个子网 → 需借 2 位主机位 → `/26`
```
原始 /24 的网络位:
192.168.1.0 = 11000000.10101000.00000001.00000000
借 2 位给子网:
192.168.1.XX = XX 是子网位(00, 01, 10, 11)
四个子网:
Subnet 0: 192.168.1.0/26 — 范围: .0 ~ .63 — 可用: .1 ~ .62 — Bcast: .63
Subnet 1: 192.168.1.64/26 — 范围: .64 ~ .127 — 可用: .65 ~ .126 — Bcast: .127
Subnet 2: 192.168.1.128/26 — 范围: .128~.191 — 可用: .129~.190 — Bcast: .191
Subnet 3: 192.168.1.192/26 — 范围: .192~.255 — 可用: .193~.254 — Bcast: .255
```
```mermaid
flowchart LR
S0["192.168.1.0/26<br/>.1-.62"] -->|"PCs, Servers"| SW0["Switch-A"]
S1["192.168.1.64/26<br/>.65-.126"] -->|"IP Phones"| SW1["Switch-B"]
S2["192.168.1.128/26<br/>.129-.190"] -->|"Guest WiFi"| AP1["AP-C"]
S3["192.168.1.192/26<br/>.193-.254"] -->|"IoT Devices"| GW1["Gateway .1"]
style S0 fill:#DDA0DD,color:#000
style S1 fill:#FFD700,color:#000
style S2 fill:#98FB98,color:#000
style S3 fill:#B0C4DE,color:#000
```
### VLSM(可变长子网掩码)
不同子网可以用不同长度的掩码——这就是**可变长子网掩码**(VLSM)。在 CIDR 出现之前,同一网络内所有子网必须等长,造成大量地址浪费。VLSM 允许按需"量体裁衣"。
场景:公司申请了 `192.168.0.0/22`(1022 个可用 IP),4 个部门需求如下:
| 部门 | 主机数 | 需要 ≥ | 分配掩码 | 可用 IP |
|------|--------|-------|---------|--------|
| 研发部 | 200 | 202 | /24 | 254 |
| 市场部 | 50 | 52 | /26 | 62 |
| 行政部 | 10 | 12 | /28 | 14 |
| 互联链路 | 2 | 2 | /30 | 2 |
**分配原则:从大到小,依次切割**(先给需求最大的部门分配,避免碎片化):
```
总地址池: 192.168.0.0/22(含 .0.0 ~ .3.255,共 1024 地址)
① 研发部 → 192.168.0.0/24 用掉 256 地址(.0.0 ~ .0.255)
剩余: 192.168.1.0 ~ 192.168.3.255
② 市场部 → 192.168.1.0/26 用掉 64 地址(.1.0 ~ .1.63)
剩余: 192.168.1.64 ~ 192.168.3.255
③ 行政部 → 192.168.1.64/28 用掉 16 地址(.1.64 ~ .1.79)
剩余: 192.168.1.80 ~ 192.168.3.255
④ 互联链 → 192.168.1.80/30 用掉 4 地址(.1.80 ~ .1.83)
剩余: 192.168.1.84 ~ 192.168.3.255(留给未来扩展)
```
> [!tip] VLSM 规划要点
> - **先大后小**:优先满足最大子网需求,剩余空间再切分,避免碎片
> - **注意边界对齐**:每个子网起始地址必须是其掩码块大小的整数倍(例如 /26 块大小 64,起址必须是 64 的倍数)
> - 实际操作中常用 Excel 或 `ipcalc`/`sipcalc` 工具辅助规划
## 实用计算工具
### Linux ipcalc
```bash
$ ipcalc 192.168.10.50/26
Network: 192.168.10.0/26
Netmask: 255.255.255.192 = 26
Wildcard: 0.0.0.63
Broadcast: 192.168.10.63
HostMin: 192.168.10.1
HostMax: 192.168.10.62
Hosts/Net: 62
# Python 中内置 ipaddress 模块
$ python3 -c "import ipaddress; print(list(ipaddress.ip_network('192.168.10.0/26').hosts()))"
[IPv4Address('192.168.10.1'), ..., IPv4Address('192.168.10.62')]
```
### 快速手算方法
| 步骤 | 操作 |
|------|------|
| 1 | 将 IP 地址与子网掩码都转为二进制 |
| 2 | **网络地址** = IP **按位 AND** 掩码(两列都为 1 才写 1) |
| 3 | **广播地址** = 网络地址的主机位全部置 1 |
| 4 | **可用范围** = 网络地址+1 ~ 广播地址-1 |
> [!example] 动手练:求 `172.16.50.100/18` 的网络地址、广播地址、可用范围
>
> **步骤 1** — 写出第三字节的二进制(/18 意味着前两个字节 + 第三字节前 2 位是网络位):
> ```
> IP 第三字节: 50 = 00110010
> 掩码第三字节: 192 = 11000000 ← /18 对应 255.255.192.0
> ```
>
> **步骤 2** — 按位 AND:
> ```
> IP: 00110010
> Mask: 11000000
> AND: 00000000 → 网络地址第三字节 = 0
> 网络地址 = 172.16.0.0
> ```
>
> **步骤 3** — 主机位全 1(第三字节后 6 位 + 第四字节 8 位 = 14 位主机位):
> ```
> 00111111.11111111 = 63.255
> 广播地址 = 172.16.63.255
> ```
>
> **步骤 4** — 可用范围:
> `172.16.0.1 ~ 172.16.63.254`,共 $2^{14} - 2 = 16382$ 个可用 IP
## 私有地址空间(RFC 1918)
以下地址段在公网不可路由,专供内部网络使用:
| 地址段 | 数量 | 默认掩码 | 适用场景 |
|--------|------|---------|---------|
| `10.0.0.0 – 10.255.255.255` | 16,777,216 | /8 | 大型企业、运营商 |
| `172.16.0.0 – 172.31.255.255` | 1,048,576 | /12 | 中型企业 |
| `192.168.0.0 – 192.168.255.255` | 65,536 | /16 | 家庭/小型办公室 |
> [!tip] 如何选择私有地址段?
> - 单机房、<1000 台主机 → `192.168.0.0/16` 够用
> - 多机房、分布式 → `10.0.0.0/8` 留有余量
> - Docker 默认使用 `172.17.0.0/16`
## 特殊地址段速查
除了 RFC 1918 私有地址,还有几个常考的特殊地址段:
| 地址段 | 名称 | 用途 |
|--------|------|------|
| `127.0.0.0/8` | 环回地址(Loopback) | 本机测试,最常用 `127.0.0.1` |
| `169.254.0.0/16` | 链路本地地址(Link-local) | DHCP 失败时自动分配(APIPA) |
| `224.0.0.0/4` | 组播地址(Multicast) | OSPF 用 `224.0.0.5`/`224.0.0.6` |
| `0.0.0.0/8` | 本网络 | 表示「当前网络」,路由表中代表默认路由 |
> [!QUESTION] 为什么 `127.0.0.1` 能 Ping 通自己?
> 发送到 127.x.x.x 的数据包不会离开主机——操作系统内核的网络栈直接将它**环回**到传输层,不经过任何物理网卡。这也是它作为服务健康检查首选地址的原因。
## 最长前缀匹配(Longest Prefix Match)
CIDR 引入了一个关键的路由查找规则:当一个目标 IP 匹配多条路由时,**前缀越长(掩码越精确)的路由优先**。
```
路由表:
192.168.0.0/16 → 下一跳 A
192.168.1.0/24 → 下一跳 B
192.168.1.0/26 → 下一跳 C
目标 IP: 192.168.1.50
匹配 /16 ✅ /24 ✅ /26 ✅
最长前缀 → /26 → 选择下一跳 C
```
> [!tip] 面试高频题
> 最长前缀匹配是路由器硬件(TCAM)的核心算法。理解它,就理解了为什么 CIDR 能让路由表既精简又灵活——聚合路由用短前缀覆盖大范围,精确路由用长前缀处理例外。
## 超网聚合(Supernetting / CIDR Aggregation)
将多个连续的小网合并为一个更大的网:
```
原始: 192.168.1.0/24, 192.168.2.0/24, 192.168.3.0/24
= 11000000.10101000.00000001.0/24
+ 11000000.10101000.00000010.0/24
+ 11000000.10101000.00000011.0/24
共同前缀: 11000000.10101000.000000XX → /22
聚合后: 192.168.0.0/22 包含 1024 个 IP
(注意:实际只用了其中 3 个 /24 块,浪费了 192.168.0.0/24 这 1 个)
```
路由聚合减少了全球 BGP 路由表的大小。没有 CIDR 聚合,BGP 路由表会膨胀到百万级别。
## Go 中的子网判断
```go
package main
import (
"fmt"
"net"
)
func main() {
ip := net.ParseIP("192.168.1.50")
_, cidr, _ := net.ParseCIDR("192.168.1.0/26")
// 判断 IP 是否在子网内
if cidr.Contains(ip) {
fmt.Println("IP 在子网范围内 ✅")
}
// 计算广播地址(网络地址 | ^子网掩码)
broadcast := make(net.IP, len(cidr.IP))
for i := range cidr.IP {
broadcast[i] = cidr.IP[i] | ^cidr.Mask[i]
}
// 遍历所有可用 IP(跳过网络地址和广播地址)
for ip := cidr.IP.Mask(cidr.Mask); cidr.Contains(ip); inc(ip) {
if ip.Equal(cidr.IP) || ip.Equal(broadcast) {
continue
}
fmt.Println(ip) // 处理每个可用 IP
}
}
// IP 地址自增(逐字节进位)
func inc(ip net.IP) {
for i := len(ip) - 1; i >= 0; i-- {
ip[i]++
if ip[i] > 0 { break }
}
}
```
## 关联笔记
- [[hhs/NETWORK/03-网络层/01-IPv4首部与分段重组]] — IP 地址在 IPv4 包中的位置
- [[hhs/NETWORK/03-网络层/03-IPv6地址与扩展头部]] — IPv6 同样使用 CIDR 表示法
- [[hhs/NETWORK/03-网络层/08-NAT原理与应用]] — NAT 通常映射到私有地址空间