311 lines
13 KiB
Markdown
311 lines
13 KiB
Markdown
---
|
||
tags: [计算机网络, IPv6, SLAAC, 地址空间, ICMPv6, NDP, 扩展头部]
|
||
create time: 2026-05-18 00:20
|
||
---
|
||
|
||
# IPv6 地址与扩展头部
|
||
|
||
## 概述
|
||
|
||
IPv6 是为解决 IPv4 地址枯竭而设计的下一代互联网协议。但它的改进远不止于地址空间——简化首部、内置安全支持、无分片(by design)、自动配置等特性使其更适合现代网络架构。
|
||
|
||
## IPv6 地址表示法
|
||
|
||
### 十六进制冒号分隔格式
|
||
|
||
```
|
||
2001:0db8:0000:0000:0000:ff00:0042:8329
|
||
```
|
||
|
||
### 缩写规则
|
||
|
||
| 规则 | 说明 |
|
||
|------|------|
|
||
| 前导零省略 | `0db8` → `db8`;`0042` → `42` |
|
||
| 连续全零段压缩为 `::` | 只能出现**一次**(否则歧义) |
|
||
|
||
示例:
|
||
```
|
||
完整: 2001:0db8:0000:0000:0000:ff00:0042:8329
|
||
缩写: 2001:db8::ff00:42:8329 ← 最简形式
|
||
|
||
loopback: 0000:0000:0000:0000:0000:0000:0000:0001
|
||
::1 ← 仅一个字符
|
||
|
||
IPv4映射: 0000:0000:0000:0000:0000:ffff:c0a8:0101
|
||
::ffff:192.168.1.1 ← 兼容旧系统
|
||
```
|
||
|
||
### 常见 IPv6 地址类型
|
||
|
||
| 前缀 | 类型 | 示例 | 范围 |
|
||
|------|------|------|------|
|
||
| `2000::/3` | 全球单播 (GUA) | `2001:db8::1` | 公网路由 |
|
||
| `::1/128` | 环回 (Loopback) | `::1` | 本机 |
|
||
| `fc00::/7` | ULA (唯一本地地址) | `fd00::1` | 私有地址,等价 RFC1918 |
|
||
| `fe80::/10` | 链路本地 (Link-Local) | `fe80::1` | 仅同链路可用,自动配置 |
|
||
| `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 地址组成
|
||
|
||
### EUI-64(自动生成)
|
||
|
||
从 MAC 地址生成接口标识符(正在被隐私扩展取代):
|
||
|
||
```
|
||
MAC: aa:bb:cc:dd:ee:ff
|
||
插入 ff:fe → aa:bb:cc:ff:fe:dd:ee:ff
|
||
翻转第七位(U/L bit) → a8:bb:cc:ff:fe:dd:ee:ff
|
||
↑ aa=10101010 → 第7位取反 → 10101000=a8
|
||
|
||
结果: 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)消息自行配置:
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Host as 主机
|
||
participant R as 路由器
|
||
|
||
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->>Host: DAD: 以 :: 为源地址发送 NS
|
||
Host->>R: NDP Neighbor Solicitation (目标=候选地址)
|
||
R-->>Host: (无冲突 → 无 NA 回复)
|
||
Host->>Host: ✅ 分配 2001:db8:abcd::<interface-id>
|
||
```
|
||
|
||
> [!info] RA 中的 M/O 标志位决定地址获取方式
|
||
> | 标志 | 含义 | 行为 |
|
||
> |------|------|------|
|
||
> | **M** (Managed) | 管理地址配置 | 主机**必须**使用 DHCPv6 获取地址 |
|
||
> | **O** (Other) | 其他配置 | SLAAC 获取地址 + DHCPv6 获取 DNS 等信息 |
|
||
> | M=0, O=0 | 纯 SLAAC | 前缀 + 接口标识符,无 DHCPv6 |
|
||
|
||
> [!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 首部
|
||
|
||
| 字段 | IPv4 | IPv6 |
|
||
|------|------|------|
|
||
| 版本 | 4 bit | 4 bit |
|
||
| 优先级 | DSCP+ECN (8 bit) | Traffic Class + Flow Label (20 bit) |
|
||
| 长度 | Total Length (含首部) | Payload Length (**不含**首部) |
|
||
| Next Header | Protocol (替代) | Next Header (等同) |
|
||
| Hop Limit | TTL | Hop Limit (改名但不改功能) |
|
||
| 源/目的 IP | ✅ | ✅ |
|
||
| 首部 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 自带端到端校验
|
||
- 链路层 Ethernet FCS 已做底层校验
|
||
- 每跳计算 Checksum 增加路由器负担 → 提升转发性能
|
||
|
||
### Flow Label(流标签)
|
||
|
||
新引入的 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
|
||
```
|
||
|
||
> [!info] 扩展头的顺序是有规定的
|
||
> RFC 8200 建议的推荐顺序:Hop-by-Hop → Destination(路由头之前) → Routing → Fragment → AH → ESP → Destination(上层头之前)。但中间路由器**只需处理** Hop-by-Hop Options,其余扩展头在到达最终目的地时才解析——这对转发性能非常友好。
|
||
|
||
| 扩展头 | 类型值 | 用途 |
|
||
|--------|-------|------|
|
||
| Hop-by-Hop Options | 0 | 逐跳选项(极少使用) |
|
||
| 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 | 终点选项(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 的过渡机制
|
||
|
||
| 技术 | 原理 | 适用场景 |
|
||
|------|------|---------|
|
||
| Dual Stack | 同时运行 IPv4 和 IPv6 | 最常见,逐步迁移 |
|
||
| Tunneling (6in4/6to4) | IPv6 包封装在 IPv4 中传输 | 穿越纯 IPv4 骨干网 |
|
||
| NAT64/DNS64 | 客户端仅有 IPv6,NAT 翻译到 IPv4 | 运营商级部署 |
|
||
| SIIT | 状态less 翻译,地址嵌入 (IPv4-mapped) | ISP、企业边界 |
|
||
|
||
## Go 中的 IPv6 支持
|
||
|
||
Go 的 `net` 包天然支持 IPv6,`"tcp6"` 和 `"tcp4"` 区分协议族:
|
||
|
||
```go
|
||
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 替代)
|