Files
cs-note/hhs/NETWORK/03-网络层/03-IPv6地址与扩展头部.md
T
2026-05-27 23:01:37 +08:00

13 KiB
Raw Blame History

tags, create time
tags create time
计算机网络
IPv6
SLAAC
地址空间
ICMPv6
NDP
扩展头部
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)消息自行配置:

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 字节,结构如下:

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 字段串联:

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" 区分协议族:

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。

关联笔记