vault backup: 2026-05-27 23:01:37
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
---
|
||||
tags: [计算机网络, IPv4, IP首部, TTL, MTU, 分片]
|
||||
tags: [计算机网络, IPv4, IP首部, TTL, MTU, 分片, ECN, PMTUD, IP Options]
|
||||
create time: 2026-05-17 23:40
|
||||
---
|
||||
|
||||
@@ -12,19 +12,19 @@ IPv4 数据包是互联网的基本传输单元。理解其首部结构,有助
|
||||
## IPv4 首部格式(32-bit fields)
|
||||
|
||||
```
|
||||
Version(4b) │ IHL(4b) │ DSCP/ECN(8b) │ Total Length(16b) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Identification(16b) │DF│ MF │Offset(13b)│
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ TTL(8b) │ Protocol(8b) │ Header Checksum │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Source Address (32b) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Destination Address (32b) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Options (variable) │
|
||||
│ ...padding... │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
Version(4b) │ IHL(4b) │ DSCP(6b) │ ECN(2b) │ Total Length(16b) │
|
||||
├───────────────────────────────────────────────────────────────────────┤
|
||||
│ Identification(16b) │Rsv│DF│MF│ Offset(13b) │
|
||||
├───────────────────────────────────────────────────────────────────────┤
|
||||
│ TTL(8b) │ Protocol(8b) │ Header Checksum(16b) │
|
||||
├───────────────────────────────────────────────────────────────────────┤
|
||||
│ Source Address (32b) │
|
||||
├───────────────────────────────────────────────────────────────────────┤
|
||||
│ Destination Address (32b) │
|
||||
├───────────────────────────────────────────────────────────────────────┤
|
||||
│ Options (variable, 0-40B) │
|
||||
│ ...padding... │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 各字段详解
|
||||
@@ -33,9 +33,11 @@ Version(4b) │ IHL(4b) │ DSCP/ECN(8b) │ Total Length(16b) │
|
||||
|------|------|------|
|
||||
| **Version** | 4 bit | 版本号,IPv4 = `0100` |
|
||||
| **IHL** (Internet Header Length) | 4 bit | 首部长度,单位 4 bytes。**最小值 5**(20 bytes),最大 15(60 bytes) |
|
||||
| **DSCP** (Differentiated Services Code Point) | 6 bit | QoS 优先级标记;前 3 bit 为 ECN(Explicit Congestion Notification)|
|
||||
| **DSCP** (Differentiated Services Code Point) | 6 bit | QoS 优先级标记,替代早期的 ToS 字段前 6 位 |
|
||||
| **ECN** (Explicit Congestion Notification) | 2 bit | 显式拥塞通知。末 2 bit 用于在不丢包的情况下通知端主机网络拥塞(`00`=不支持, `10`/`01`=支持 ECN, `11`=拥塞已经历) |
|
||||
| **Total Length** | 16 bit | 整个 IP 包总长(含首部),最大 65535 字节 |
|
||||
| **Identification** | 16 bit | 唯一标识符。同一原始包的分片共享此 ID |
|
||||
| **Reserved** | 1 bit | 保留位,必须为 0 |
|
||||
| **DF** (Don't Fragment) | 1 bit | 置 1 则禁止分片;路由器不能分片的包如果 DF=1,直接丢弃并返回 ICMP "Fragmentation Needed" |
|
||||
| **MF** (More Fragments) | 1 bit | 置 1 表示后面还有分片;最后一个分片 MF=0 |
|
||||
| **Fragment Offset** | 13 bit | 该分片在原始数据中的偏移量(以 8 字节为单位) |
|
||||
@@ -55,11 +57,46 @@ Version(4b) │ IHL(4b) │ DSCP/ECN(8b) │ Total Length(16b) │
|
||||
受限于链路 MTU: 通常 1500B Payload → 最大常用包 1520 bytes
|
||||
```
|
||||
|
||||
> [!tip] 为什么 IHL 最大值是 15?
|
||||
> IHL 以 4 字节为单位,最大值 15 × 4 = 60 bytes。去掉固定首部 20 bytes,Options 最多 **40 bytes**。这就是为什么 Options 字段上限是 40B。
|
||||
|
||||
### Options 字段(可选,0-40 bytes)
|
||||
|
||||
Options 是 IPv4 首部中最容易被忽略的部分,但在网络诊断和特殊场景中依然重要:
|
||||
|
||||
| 选项类型 | 说明 | 典型用途 |
|
||||
|---------|------|---------|
|
||||
| **Record Route** | 记录包经过的每一跳路由器 IP | 路由追踪(类似 traceroute) |
|
||||
| **Timestamp** | 每个路由器记录到达时间戳 | 测量延迟、时钟同步分析 |
|
||||
| **Strict Source Route** | 指定完整路径,逐跳严格匹配 | 诊断路由问题(现代网络几乎禁用) |
|
||||
| **Loose Source Route** | 指定必须经过的中间节点,其余自由路由 | 绕路测试 |
|
||||
|
||||
> [!warning] 安全注意
|
||||
> 现代路由器和防火墙通常**丢弃包含 Options 的 IP 包**,因为 Source Route 曾被用于 IP 欺骗攻击。这也是为什么正常流量中你几乎看不到 Options。
|
||||
|
||||
### Header Checksum 逐跳重算
|
||||
|
||||
与 L2 CRC 和 TCP/UDP Checksum 不同,IP Header Checksum **每经过一个路由器都要重新计算**——因为 TTL 会递减。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["路由器收到包"] --> B["TTL -= 1"]
|
||||
B --> C["重新计算 Checksum"]
|
||||
C --> D["转发到下一跳"]
|
||||
|
||||
style A fill:#87CEEB,color:#000
|
||||
style D fill:#98FB98,color:#000
|
||||
```
|
||||
|
||||
> [!question] 为什么 IP Checksum 只校验首部?
|
||||
> 早期设计为减轻路由器负担——只校验 20 bytes 首部远比校验整个 64KB 包高效。数据完整性由上层协议(TCP/UDP Checksum)保证。这也是 IP 被称为"尽力而为"协议的原因之一。
|
||||
|
||||
## TTL 深度解析
|
||||
|
||||
### TTL 的常见误解
|
||||
|
||||
很多人以为 TTL 的单位是秒。其实在 IPv4 中它是**跳数计数器**——每经过一个路由器(三层转发节点)减 1,不是按时间递减。
|
||||
> [!question] TTL 是时间吗?
|
||||
> 很多人以为 TTL 的单位是秒——这其实是 IPv6 的 Hop Limit 的前身历史遗留误解。在 IPv4 中 TTL 是**跳数计数器**——每经过一个路由器(三层转发节点)减 1,不是按时间递减。名字容易误导,但行为很简单:减到 0 就丢弃。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
@@ -109,10 +146,10 @@ MTU(Maximum Transmission Unit)是链路层允许的最大 IP 包载荷大小
|
||||
|----------|---------|-------------|
|
||||
| Ethernet | 1500 bytes | 9000 bytes |
|
||||
| Wi-Fi (802.11n/ac/ax) | 1500 | ❌ 不支持 |
|
||||
| PPPoE (宽带拨号) | 1492 (预留 8B PPPoE头) | — |
|
||||
| IPv6 | 1280 (硬性最小) | 可选 |
|
||||
| GRE 隧道 | 1476 (预留 24B GRE头) | — |
|
||||
| VXLAN | 1450 (预留 50B VxLAN头) | — |
|
||||
| PPPoE (宽带拨号) | 1492 (8B PPPoE 头开销) | — |
|
||||
| IPv6 | 1280 (硬性最小 MTU) | 可选 |
|
||||
| GRE 隧道 | 1476 (24B GRE 头开销) | — |
|
||||
| VXLAN | 1450 (50B VXLAN 头开销) | — |
|
||||
|
||||
### 分片规则
|
||||
|
||||
@@ -138,10 +175,39 @@ MTU(Maximum Transmission Unit)是链路层允许的最大 IP 包载荷大小
|
||||
| — | 攻击者可以利用分片做碎片攻击(Teardrop 等) |
|
||||
| — | IPv6 已取消中间路由器分片,改由端到端 PMTUD |
|
||||
|
||||
### Wireshark 中识别分片
|
||||
|
||||
实战排查时,抓包识别分片是最常见需求:
|
||||
|
||||
```bash
|
||||
# tcpdump 过滤分片包(offset > 0 或 MF=1 的包)
|
||||
$ tcpdump -i eth0 'ip[6:2] & 0x3fff != 0' -nn
|
||||
|
||||
# Wireshark 显示过滤器
|
||||
# ip.frag_offset > 0 → 非首片
|
||||
# ip.flags.mf == 1 → 还有后续分片
|
||||
# ip.id == 0x1234 → 按 Identification 追踪同组分片
|
||||
```
|
||||
|
||||
> [!tip] 分片重组技巧
|
||||
> Wireshark 默认会自动重组分片。如果需要查看原始分片:`Edit → Preferences → IPv4 → 去勾选 "Reassemble fragmented IPv4 datagrams"`。
|
||||
|
||||
### 常见分片攻击
|
||||
|
||||
| 攻击手法 | 原理 | 防御 |
|
||||
|---------|------|------|
|
||||
| **Teardrop** | 构造重叠偏移的分片,重组时内核缓冲区溢出 | 更新内核补丁(已修复) |
|
||||
| **Ping of Death** | 分片重组后 IP 包 > 65535 bytes,触发整数溢出 | 现代系统已防御 |
|
||||
| **Rose Attack** | 发送大量不完整的分片,消耗重组缓冲区 | 设置重组超时、限制并发分片数 |
|
||||
| **Fragment Overlap** | 后续分片覆盖前片数据,绕过基于首片的防火墙规则 | 防火墙需做完整分片重组后再检测 |
|
||||
|
||||
### PMTUD(Path MTU Discovery)
|
||||
|
||||
现代网络推荐使用 **路径 MTU 发现**而非分片:
|
||||
|
||||
> [!warning] PMTUD Black Hole
|
||||
> 如果中间路由器**静默丢弃**了 DF=1 的大包,但不返回 ICMP,源主机会一直重传失败——这就是 PMTUD Black Hole。常见于错误配置的防火墙(拦截了 ICMP "Fragmentation Needed")。TCP 的对策是 `net.ipv4.tcp_mtu_probing`(值设为 2),主动探测合适的 MSS。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Src as 源主机
|
||||
@@ -152,8 +218,8 @@ sequenceDiagram
|
||||
Note over Src: DF(Do Not Fragment)=1
|
||||
R->>R: MTU=1400 < 1500, DF=1 → 无法分片!
|
||||
R->>Src: ICMP Type 3 Code 4 "Fragmentation Needed"
|
||||
Note over Src: Path MTU = 1400 - IP头(20) - Eth(14) = 1366
|
||||
Src->>Dst: IP Packet (size=1366, DF=1) ✅
|
||||
Note over Src: Path MTU = 1400, Max IP Payload = 1400 - 20(IP头) = 1380
|
||||
Src->>Dst: IP Packet (size=1400, DF=1) ✅
|
||||
Dst-->>Src: ACK ✅
|
||||
```
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ CIDR(Classless Inter-Domain Routing,无类别域间路由)是现代 IP 寻
|
||||
|
||||
### 常见子网对照表
|
||||
|
||||
| CIDR | 子网掩码 | 主机数 ( usable) | 典型用途 |
|
||||
| CIDR | 子网掩码 | 主机数 (usable) | 典型用途 |
|
||||
|------|---------|-----------------|---------|
|
||||
| /30 | 255.255.255.252 | 2 | 点对点链路 |
|
||||
| /29 | 255.255.255.248 | 6 | 小型办公室 |
|
||||
@@ -31,16 +31,30 @@ CIDR(Classless Inter-Domain Routing,无类别域间路由)是现代 IP 寻
|
||||
| /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 | 大型企业内网 |
|
||||
| /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 个子网
|
||||
@@ -64,11 +78,11 @@ Subnet 3: 192.168.1.192/26 — 范围: .192~.255 — 可用: .193~.254 — Bca
|
||||
|
||||
```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]
|
||||
|
||||
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
|
||||
@@ -77,17 +91,39 @@ flowchart LR
|
||||
|
||||
### VLSM(可变长子网掩码)
|
||||
|
||||
不同子网可以用不同长度的掩码——这就是**可变长子掩码**。
|
||||
不同子网可以用不同长度的掩码——这就是**可变长子网掩码**(VLSM)。在 CIDR 出现之前,同一网络内所有子网必须等长,造成大量地址浪费。VLSM 允许按需"量体裁衣"。
|
||||
|
||||
场景:公司有 4 个部门,需要不同规模:
|
||||
- 研发部:200 台 → 需要至少 202 → /24(254 台)
|
||||
- 市场部:50 台 → 需要至少 52 → /26(62 台)
|
||||
- 行政部:10 台 → 需要至少 12 → /28(14 台)
|
||||
- 互联链:2 台 → 需要 2 → /30(2 台)
|
||||
场景:公司申请了 `192.168.0.0/22`(1022 个可用 IP),4 个部门需求如下:
|
||||
|
||||
总计借用:32 - 24 = 8 位主机位
|
||||
- 研发 /24: 借回 0 位子网位 → 用掉 1 个大块
|
||||
- 剩余给其他 3 个 /26, /28, /30...
|
||||
| 部门 | 主机数 | 需要 ≥ | 分配掩码 | 可用 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` 工具辅助规划
|
||||
|
||||
## 实用计算工具
|
||||
|
||||
@@ -112,10 +148,35 @@ $ python3 -c "import ipaddress; print(list(ipaddress.ip_network('192.168.10.0/26
|
||||
|
||||
| 步骤 | 操作 |
|
||||
|------|------|
|
||||
| 1 | 将最后一个 octet 转换为二进制 |
|
||||
| 2 | 前 prefix%32 位为网络位,其余为主机位 |
|
||||
| 3 | 网络地址 = 全置 0;广播地址 = 全置 1 |
|
||||
| 4 | 可用范围 = 网络地址+1 ~ 广播地址-1 |
|
||||
| 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)
|
||||
|
||||
@@ -132,6 +193,38 @@ $ python3 -c "import ipaddress; print(list(ipaddress.ip_network('192.168.10.0/26
|
||||
> - 多机房、分布式 → `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)
|
||||
|
||||
将多个连续的小网合并为一个更大的网:
|
||||
@@ -145,7 +238,7 @@ $ python3 -c "import ipaddress; print(list(ipaddress.ip_network('192.168.10.0/26
|
||||
共同前缀: 11000000.10101000.000000XX → /22
|
||||
|
||||
聚合后: 192.168.0.0/22 包含 1024 个 IP
|
||||
(注意:实际只用了其中两个 /24 块,浪费了中间两个)
|
||||
(注意:实际只用了其中 3 个 /24 块,浪费了 192.168.0.0/24 这 1 个)
|
||||
```
|
||||
|
||||
路由聚合减少了全球 BGP 路由表的大小。没有 CIDR 聚合,BGP 路由表会膨胀到百万级别。
|
||||
@@ -153,30 +246,48 @@ $ python3 -c "import ipaddress; print(list(ipaddress.ip_network('192.168.10.0/26
|
||||
## Go 中的子网判断
|
||||
|
||||
```go
|
||||
import "net"
|
||||
package main
|
||||
|
||||
ip := net.ParseIP("192.168.1.50")
|
||||
_, cidr, _ := net.ParseCIDR("192.168.1.0/26")
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
if cidr.Contains(ip) {
|
||||
fmt.Println("IP 在子网范围内 ✅")
|
||||
}
|
||||
func main() {
|
||||
ip := net.ParseIP("192.168.1.50")
|
||||
_, cidr, _ := net.ParseCIDR("192.168.1.0/26")
|
||||
|
||||
// 遍历所有可用 IP
|
||||
for ip := cidr.IP.Mask(cidr.Mask); cidr.Contains(ip); inc(ip) {
|
||||
if ip.Equal(cidr.IP) || ip.Equal(lastIP) {
|
||||
continue // 跳过网络地址和广播地址
|
||||
// 判断 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 }
|
||||
}
|
||||
// 处理每个可用 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 通常映射到私有地址空间
|
||||
- [[hhs/NETWORK/03-网络层/01-IPv4首部与分段重组]] — IP 地址在 IPv4 包中的位置
|
||||
- [[hhs/NETWORK/03-网络层/03-IPv6地址与扩展头部]] — IPv6 同样使用 CIDR 表示法
|
||||
- [[hhs/NETWORK/03-网络层/08-NAT原理与应用]] — NAT 通常映射到私有地址空间
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
tags: [计算机网络, IPv6, SLAAC, 地址空间]
|
||||
tags: [计算机网络, IPv6, SLAAC, 地址空间, ICMPv6, NDP, 扩展头部]
|
||||
create time: 2026-05-18 00:20
|
||||
---
|
||||
|
||||
@@ -44,8 +44,30 @@ IPv4映射: 0000:0000:0000:0000:0000:ffff:c0a8:0101
|
||||
| `::1/128` | 环回 (Loopback) | `::1` | 本机 |
|
||||
| `fc00::/7` | ULA (唯一本地地址) | `fd00::1` | 私有地址,等价 RFC1918 |
|
||||
| `fe80::/10` | 链路本地 (Link-Local) | `fe80::1` | 仅同链路可用,自动配置 |
|
||||
| `ff00::/8` | 组播 (Multicast) | `ff02::1` | 多播组 |
|
||||
| — | 未指定地址 | `::` | 类似 IPv4 的 0.0.0.0 |
|
||||
| `ff00::/8` | 组播 (Multicast) | `ff02::1` | 多播组(作用域由第二段决定) |
|
||||
| — | 未指定地址 | `::` | 仅用于 DAD、默认路由等,不可作为目标地址 |
|
||||
| `2000::/3` 中分配 | 任播 (Anycast) | 与单播地址格式相同 | 一组节点共享同一地址,报文送达最近的一个 |
|
||||
|
||||
> [!question] IPv6 地址空间到底有多大?
|
||||
> 128 位 = 2¹²⁸ ≈ 3.4 × 10³⁸ 个地址。对比:IPv4 仅 2³² ≈ 43 亿,全球沙粒总数约 7.5 × 10¹⁸。IPv6 的地址数足以给地球上每一粒沙子分配约 4.5 × 10¹⁹ 个地址。实际上,全球单播地址仅使用 `2000::/3`(约 1/8 空间),但已绰绰有余。
|
||||
|
||||
> [!tip] 链路本地地址 (Link-Local) 是强制的
|
||||
> 每个启用 IPv6 的接口**必须**自动配置一个 `fe80::` 开头的链路本地地址——即使没有路由器、没有全球前缀。因为 NDP(邻居发现)、RA 接收、DAD 等基础协议都依赖它运作。
|
||||
|
||||
### 组播地址作用域
|
||||
|
||||
IPv6 组播地址格式为 `ff0s::/8`,其中 `s` 是作用域标志:
|
||||
|
||||
| 第二段 (scope) | 前缀 | 作用域 | 常见地址 |
|
||||
|:-:|:-:|:-:|:-:|
|
||||
| 1 | `ff01::/16` | 本机 (Interface-Local) | `ff01::1` 所有节点 |
|
||||
| 2 | `ff02::/16` | 链路 (Link-Local) | `ff02::1` 所有节点, `ff02::2` 所有路由器 |
|
||||
| 5 | `ff05::/16` | 站点 (Site-Local) | `ff05::1:3` 所有 DHCP 服务器 |
|
||||
| 8 | `ff08::/16` | 组织 (Organization) | 企业级范围 |
|
||||
| e | `ff0e::/16` | 全球 (Global) | 跨互联网组播 |
|
||||
|
||||
> [!question] IPv6 去掉了广播,用什么替代?
|
||||
> IPv4 有广播(`255.255.255.255`),IPv6 **完全取消广播**,用组播 + 任播替代。`ff02::1`(所有节点组播)在功能上等价于链路广播,但更精准——只有加入该组的节点才处理。
|
||||
|
||||
## IPv6 地址组成
|
||||
|
||||
@@ -56,11 +78,16 @@ IPv4映射: 0000:0000:0000:0000:0000:ffff:c0a8:0101
|
||||
```
|
||||
MAC: aa:bb:cc:dd:ee:ff
|
||||
插入 ff:fe → aa:bb:cc:ff:fe:dd:ee:ff
|
||||
翻转第七位(UD bit) → ab:bb:cc:ff:fe:dd:ee:ff
|
||||
翻转第七位(U/L bit) → a8:bb:cc:ff:fe:dd:ee:ff
|
||||
↑ aa=10101010 → 第7位取反 → 10101000=a8
|
||||
|
||||
结果: fe80::ab:bb:cc:ff:fe:dd:ee:ff (link-local)
|
||||
结果: fe80::a8bb:ccff:fedd:eeff (link-local)
|
||||
```
|
||||
|
||||
> [!warning] EUI-64 的隐私隐患与 RFC 4941
|
||||
> EUI-64 将 MAC 地址嵌入 IPv6 地址,意味着你的设备在全球可被追踪(MAC 不变 → IPv6 接口 ID 不变)。
|
||||
> **RFC 4941 隐私扩展**:主机周期性生成**随机临时地址**用于对外通信(如上网浏览),EUI-64 地址仅用于入站连接。现代 OS(Linux/Windows/macOS)默认启用此机制。
|
||||
|
||||
### SLAAC( Stateless Address Autoconfiguration )
|
||||
|
||||
主机无需 DHCP 服务器,通过 Router Advertisement(RA)消息自行配置:
|
||||
@@ -70,24 +97,45 @@ sequenceDiagram
|
||||
participant Host as 主机
|
||||
participant R as 路由器
|
||||
|
||||
R->>Host: Router Advertisement (每 200s~60min)
|
||||
R->>Host: Router Advertisement (每 200s~1800s)
|
||||
Note over Host: RA 中包含:
|
||||
Note over Host: - 前缀 (如 2001:db8:abcd::/64)
|
||||
Note over Host: - prefix length
|
||||
Note over Host: - 默认网关
|
||||
Note over Host: - DNS (RFC 6106 / RDNSS)
|
||||
Note over Host: - M/O 标志位
|
||||
|
||||
Host->>Host: 自构 IPv6 地址
|
||||
Host->>R: NDP Neighbor Solicitation (查重)
|
||||
R-->>Host: (无冲突 → 无回复)
|
||||
Host->>Host: DAD: 以 :: 为源地址发送 NS
|
||||
Host->>R: NDP Neighbor Solicitation (目标=候选地址)
|
||||
R-->>Host: (无冲突 → 无 NA 回复)
|
||||
Host->>Host: ✅ 分配 2001:db8:abcd::<interface-id>
|
||||
```
|
||||
|
||||
### DUID-DHCPv6
|
||||
> [!info] RA 中的 M/O 标志位决定地址获取方式
|
||||
> | 标志 | 含义 | 行为 |
|
||||
> |------|------|------|
|
||||
> | **M** (Managed) | 管理地址配置 | 主机**必须**使用 DHCPv6 获取地址 |
|
||||
> | **O** (Other) | 其他配置 | SLAAC 获取地址 + DHCPv6 获取 DNS 等信息 |
|
||||
> | M=0, O=0 | 纯 SLAAC | 前缀 + 接口标识符,无 DHCPv6 |
|
||||
|
||||
DHCPv6 服务器分配地址时需要客户端标识:
|
||||
- **DUID-LLT**: DUID + Link-Layer Time + Link-Layer Address
|
||||
- **DUID-UUID**: DUID + UUID (持久不变)
|
||||
> [!tip] DAD(重复地址检测)为什么用 `::` 作为源地址?
|
||||
> 因为发送 DAD 时,该地址还未正式归属于主机——正在验证中。所以用未指定地址 `::` 作为源,避免"自己宣布了一个还没确认的地址"的矛盾。
|
||||
|
||||
### DHCPv6 与 DUID
|
||||
|
||||
DHCPv6 相比 SLAAC 提供更多控制(DNS、NTP、域名等),适用于企业环境。客户端标识使用 DUID(DHCP Unique Identifier):
|
||||
|
||||
| DUID 类型 | 组成 | 特点 |
|
||||
|:-:|:-:|:-:|
|
||||
| **DUID-LLT** | MAC + 时间戳 | 最常用,基于链路层地址+生成时间 |
|
||||
| **DUID-LL** | 仅 MAC | 无时间戳,适合无持久存储设备 |
|
||||
| **DUID-UUID** | UUID | 持久不变,虚拟机场景 |
|
||||
|
||||
> [!info] SLAAC vs DHCPv6 速查
|
||||
> - **SLAAC**: 零配置、去中心化,适合家庭/小型网络
|
||||
> - **DHCPv6 Stateful**: 完整地址分配+配置管理,适合企业
|
||||
> - **DHCPv6 Stateless**: SLAAC 分配地址 + DHCPv6 仅下发 DNS 等信息(最常见组合)
|
||||
|
||||
## IPv6 首部 vs IPv4 首部
|
||||
|
||||
@@ -102,6 +150,42 @@ DHCPv6 服务器分配地址时需要客户端标识:
|
||||
| 首部 Checksum | ✅ | ❌ **已删除!** |
|
||||
| 分片 | ID/DF/MF/Offset | 移到扩展头(由源端分片) |
|
||||
|
||||
IPv6 固定首部仅 40 字节,结构如下:
|
||||
|
||||
```mermaid
|
||||
block-beta
|
||||
columns 4
|
||||
block:header:4
|
||||
columns 4
|
||||
A["Version 4bit"]:1
|
||||
B["Traffic Class 8bit"]:1
|
||||
C["Flow Label 20bit"]:2
|
||||
end
|
||||
block:body:4
|
||||
columns 4
|
||||
D["Payload Length 16bit"]:1
|
||||
E["Next Header 8bit"]:1
|
||||
F["Hop Limit 8bit"]:1
|
||||
G["reserved"]:1
|
||||
end
|
||||
block:src:4
|
||||
columns 1
|
||||
H["Source Address 128bit"]
|
||||
end
|
||||
block:dst:4
|
||||
columns 1
|
||||
I["Destination Address 128bit"]
|
||||
end
|
||||
|
||||
style header fill:#e8f4fd,stroke:#2196f3
|
||||
style body fill:#f3e5f5,stroke:#9c27b0
|
||||
style src fill:#e8f5e9,stroke:#4caf50
|
||||
style dst fill:#fff3e0,stroke:#ff9800
|
||||
```
|
||||
|
||||
> [!question] IPv6 固定首部 40 字节 vs IPv4 最小 20 字节,更大反而更好?
|
||||
> IPv6 首部虽然更大,但**字段更少且固定**(无 Options),路由器处理时无需条件分支判断——这就是"简化首部"的设计哲学。用空间换时间,线速转发更容易实现。
|
||||
|
||||
### IPv6 为什么去掉 Checksum?
|
||||
|
||||
- TCP/UDP/ICMPv6 自带端到端校验
|
||||
@@ -112,27 +196,68 @@ DHCPv6 服务器分配地址时需要客户端标识:
|
||||
|
||||
新引入的 20-bit 字段,用于标识属于同一"流"的数据包序列。中间网络设备可据此做 QoS 或负载均衡——无需深度解析载荷。
|
||||
|
||||
典型场景:同一 TCP 连接的所有包携带相同的 Flow Label,路由器据此快速分类转发路径,避免逐包查表。结合 SRv6 使用效果更佳。
|
||||
|
||||
## IPv6 扩展头部(Extension Headers)
|
||||
|
||||
IPv6 将可选功能移出固定首部,放在扩展头链中,由 Next Header 字段串联:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
FH["Fixed Header"]
|
||||
HBH["Hop-by-Hop Options"]
|
||||
DO["Destination Options"]
|
||||
RT["Routing"]
|
||||
FR["Fragment"]
|
||||
AH["AH"]
|
||||
ESP["ESP"]
|
||||
UL["Upper Layer TCP/UDP/ICMPv6"]
|
||||
|
||||
FH -->|"Next Header=0"| HBH
|
||||
FH -->|"Next Header=60"| DO
|
||||
HBH -->|"Next Header=43"| RT
|
||||
RT -->|"Next Header=44"| FR
|
||||
FR -->|"Next Header=51"| AH
|
||||
AH -->|"Next Header=50"| ESP
|
||||
ESP -->|"Next Header=6/17/58"| UL
|
||||
DO -->|"Next Header=6/17/58"| UL
|
||||
```
|
||||
Fixed Header → Hop-by-Hop Options (可选) → Destination Options → Routing → Fragment → Authentication (AH) → Encapsulating Security Payload (ESP) → Upper Layer (TCP/UDP/ICMPv6)
|
||||
```
|
||||
|
||||
> [!info] 扩展头的顺序是有规定的
|
||||
> RFC 8200 建议的推荐顺序:Hop-by-Hop → Destination(路由头之前) → Routing → Fragment → AH → ESP → Destination(上层头之前)。但中间路由器**只需处理** Hop-by-Hop Options,其余扩展头在到达最终目的地时才解析——这对转发性能非常友好。
|
||||
|
||||
| 扩展头 | 类型值 | 用途 |
|
||||
|--------|-------|------|
|
||||
| Hop-by-Hop Options | 0 | 逐跳选项(极少使用) |
|
||||
| Routing | 43 | 路由类型 0 (SRv6 前身)、Type 2 (Mobile IPv6) |
|
||||
| Routing | 43 | Type 2 (Mobile IPv6);~~Type 0 已废弃~~(见下方警告) |
|
||||
| Fragment | 44 | 分片信息(ID/offset/MF),仅出现在原始包中 |
|
||||
| Authentication (AH) | 51 | IPsec 认证(较少独立使用) |
|
||||
| Encapsulating Security Payload (ESP) | 50 | IPsec 加密封装 |
|
||||
| Destination Options | 60 | 终点选项(MIO 移动 IPv6、Jumbo Payload) |
|
||||
| Destination Options | 60 | 终点选项(MIPv6、Jumbo Payload) |
|
||||
| Mobility Header | 135 | Mobile IPv6 |
|
||||
|
||||
> [!danger] Routing Header Type 0 (RH0) 已被废弃
|
||||
> RFC 5095 (2007) **禁止使用 RH0**。原因是攻击者可在 RH0 中列出多个中间节点,让数据包在两个路由器之间反复弹跳(Amplification Attack),造成 DDoS。现代路由器收到 RH0 会直接丢弃。SRv6(Segment Routing over IPv6)使用新的 SRH 扩展头(Type 4)来实现类似功能,且设计上避免了此安全问题。
|
||||
|
||||
> [!warning] Path MTU Discovery in IPv6
|
||||
> IPv6 规定**路由器不允许对 IP 包分片**。如果包超过链路 MTU,路由器直接丢弃并返回 ICMPv6 "Packet Too Big"。这就是 PMTUD 在 IPv6 中成为强制要求的原因。
|
||||
|
||||
## ICMPv6:IPv6 的"万能胶水"
|
||||
|
||||
ICMPv6(Next Header = 58)在 IPv6 中的角色远超 IPv4 的 ICMP——它同时承载了 IPv4 中 ARP、IGMP 的功能:
|
||||
|
||||
| 功能 | IPv4 中的协议 | IPv6 中统一由 ICMPv6 承担 |
|
||||
|------|:--:|:-:|
|
||||
| 错误/信息报告 | ICMP | ICMPv6 |
|
||||
| 地址解析(MAC↔IP) | **ARP** | **NDP** (Neighbor Discovery Protocol) |
|
||||
| 组播组管理 | **IGMP** | **MLD** (Multicast Listener Discovery) |
|
||||
| 路由器发现 | 无标准协议 | **RA/RS** (Router Advertisement/Solicitation) |
|
||||
| 重复地址检测 | 无 | **DAD** (Duplicate Address Detection) |
|
||||
| Path MTU 发现 | ICMP "Fragmentation Needed" | ICMPv6 "Packet Too Big" |
|
||||
|
||||
> [!question] 为什么 IPv6 去掉了 ARP?
|
||||
> ARP 是一个独立的 L2/L3 之间的"怪胎"协议(既不是 IP 也不是 TCP/UDP),有广播泛洪、无认证等先天缺陷。IPv6 用 NDP(基于 ICMPv6 组播)替代它——地址解析只发到目标节点所在的组播组,而非广播全网,更高效也更安全。
|
||||
|
||||
## IPv4 到 IPv6 的过渡机制
|
||||
|
||||
| 技术 | 原理 | 适用场景 |
|
||||
@@ -144,22 +269,42 @@ Fixed Header → Hop-by-Hop Options (可选) → Destination Options → Routing
|
||||
|
||||
## Go 中的 IPv6 支持
|
||||
|
||||
Go 的 `net` 包天然支持 IPv6,`"tcp6"` 和 `"tcp4"` 区分协议族:
|
||||
|
||||
```go
|
||||
import "net"
|
||||
|
||||
// 创建双栈监听器(同时支持 v4/v6)
|
||||
ln, err := net.Listen("tcp6", ":8080")
|
||||
// 设置 IPv6Only = false 可同时接受 IPv4 连接 (DualStack)
|
||||
|
||||
// 解析 IPv6 地址
|
||||
ip := net.ParseIP("2001:db8::1") // net.IP 天然支持 v4/v6
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// 解析与判断
|
||||
ip := net.ParseIP("2001:db8::1") // net.IP 同时支持 v4/v6
|
||||
_, cidr, _ := net.ParseCIDR("2001:db8::/32")
|
||||
fmt.Println(cidr.Contains(ip)) // true
|
||||
|
||||
// Dual Stack 监听(同时接受 v4/v6 连接)
|
||||
// 注意:Linux 默认 IPv6 socket 也接受 v4(IPV6_V6ONLY=0)
|
||||
ln, _ := net.Listen("tcp", ":8080")
|
||||
|
||||
// 仅 IPv6 监听(不接受 v4 映射地址)
|
||||
lc := net.ListenConfig{
|
||||
Control: func(network, address string, c syscall.RawConn) error {
|
||||
return c.Control(func(fd uintptr) {
|
||||
syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6,
|
||||
syscall.IPV6_V6ONLY, 1) // 强制 IPv6 only
|
||||
})
|
||||
},
|
||||
}
|
||||
ln6, _ := lc.Listen(context.Background(), "tcp6", ":8080")
|
||||
```
|
||||
|
||||
> [!tip] 实战建议
|
||||
> 生产环境建议使用 `"tcp"` (Dual Stack) 监听,让 OS 同时处理 v4/v6。仅在需要明确区分协议族时才使用 `tcp4`/`tcp6`。
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[hhs/NETWORK/IPv4首部与分段重组]] — IPv4 和 IPv6 首部的差异对比
|
||||
- [[hhs/NETWORK/DNS原理与优化]] — AAAA 记录解析 IPv6 地址
|
||||
- [[hhs/NETWORK/CIDR与子网划分]] — CIDR 在 IPv6 中的应用方式相同
|
||||
- [[hhs/NETWORK/02-网络层/02-ARP与ICMP]] — ARP 协议(被 NDP 替代)
|
||||
|
||||
Reference in New Issue
Block a user