Files
cs-note/hhs/NETWORK/03-网络层/02-CIDR与子网划分.md
T
2026-05-24 11:42:38 +08:00

183 lines
6.1 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.255.254 | 510 | 大型部门 |
| /22 | 255.255.255.252 | 1022 | 数据中心租户 |
| /21 | 255.255.255.248 | 2046 | 大规模部署 |
| /16 | 255.255.255.0 | 65534 | 大型企业内网 |
> [!tip] 快速计算可用主机数
> $N_{usable} = 2^h - 2$,其中 $h$ 是主机位数量($h = 32 - \text{prefix}$)
> - `-2` 因为网络地址和广播地址不可用
> - 对于 /31(点对点链路),RFC 3021 允许使用 2 个地址(无网络/广播位)
## 子网划分的核心算法
### 示例:从 /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(可变长子网掩码)
不同子网可以用不同长度的掩码——这就是**可变长子掩码**。
场景:公司有 4 个部门,需要不同规模:
- 研发部:200 台 → 需要至少 202 → /24(254 台)
- 市场部:50 台 → 需要至少 52 → /26(62 台)
- 行政部:10 台 → 需要至少 12 → /28(14 台)
- 互联链:2 台 → 需要 2 → /30(2 台)
总计借用:32 - 24 = 8 位主机位
- 研发 /24: 借回 0 位子网位 → 用掉 1 个大块
- 剩余给其他 3 个 /26, /28, /30...
## 实用计算工具
### 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 | 将最后一个 octet 转换为二进制 |
| 2 | 前 prefix%32 位为网络位,其余为主机位 |
| 3 | 网络地址 = 全置 0;广播地址 = 全置 1 |
| 4 | 可用范围 = 网络地址+1 ~ 广播地址-1 |
## 私有地址空间(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`
## 超网聚合(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
(注意:实际只用了其中两个 /24 块,浪费了中间两个)
```
路由聚合减少了全球 BGP 路由表的大小。没有 CIDR 聚合,BGP 路由表会膨胀到百万级别。
## Go 中的子网判断
```go
import "net"
ip := net.ParseIP("192.168.1.50")
_, cidr, _ := net.ParseCIDR("192.168.1.0/26")
if cidr.Contains(ip) {
fmt.Println("IP 在子网范围内 ✅")
}
// 遍历所有可用 IP
for ip := cidr.IP.Mask(cidr.Mask); cidr.Contains(ip); inc(ip) {
if ip.Equal(cidr.IP) || ip.Equal(lastIP) {
continue // 跳过网络地址和广播地址
}
// 处理每个可用 IP
}
func inc(ip net.IP) { for i := len(ip) - 1; i >= 0; i-- {
ip[i]++
if ip[i] > 0 { break }
}}
```
## 关联笔记
- [[hhs/NETWORK/IPv4首部与分段重组]] — IP 地址在 IPv4 包中的位置
- [[hhs/NETWORK/IPv6地址与扩展头部]] — IPv6 同样使用 CIDR 表示法
- [[hhs/NETWORK/NAT原理与应用]] — NAT 通常映射到私有地址空间