网络知识
Modern IPv6 · RFC 8200 / 4861 / 4862 / 4443

IPv6 现代栈

全书对 NDP、SLAAC、ICMPv6 几乎只点到名字。这一页把地址、40 字节首部、邻居发现五种报文、地址配置决策和排障命令按抓包能对上的粒度写完。

STD 86 RFC 8200 NDP ICMPv6 133–137 SLAAC RFC 4862 最小 MTU 1280
主规范
RFC 8200 · 4861 · 4862 · 4443 · 8981
寻址与范围
RFC 4291 · 4193 · 4007
过渡
RFC 6146 / 6147 · 8305
对照全书
5.5 IPv6 小节 · RFC 791 · RFC 826

地址架构

RFC 4291 · 4193 · 4007 · 8981

IPv6 地址 128 bit,写成 8 组 4 位十六进制。每组可省前导零;连续的全零组只能用一次 :: 压缩。没有广播,广播的活交给组播。

单播三类:GUA / ULA / LLA

Unicast · Global

GUA

2000::/3

全球单播。IANA 当前分配块是 2000::/3(即最高 3 bit 为 001)。教学文档里的 2001:db8::/32 专供举例,路由表里不该出现。

Unicast · Local

ULA

fc00::/7

唯一本地(RFC 4193)。本地自行分配时 L 位置 1,实际用 fd00::/8 再加 40 bit 随机 Global ID。fc00::/8(L=0)留给尚未启用的中心分配。

Unicast · Link

LLA

fe80::/10

链路本地。前缀 10 bit 1111111010 + 54 bit 全 0 + 64 bit 接口标识,所以线上几乎总是 fe80::/64。每个接口必须有一个。路由器不得把 LLA 源或目的转发到别的链路。

Special

:: 与 ::1

::/128 · ::1/128

:: 是未指定地址,表示“还没有地址”(DAD 的 NS 源地址)。::1 是环回。二者不可对调,也不可配到物理接口上。

RFC 4291 §2.5.6

LLA 的规范前缀是 FE80::/10,但格式图把接下来 54 bit 钉死为 0。写地址时用 fe80::IID;写前缀长度时教学和实现都按 /64。看到 fec0::/10 是已废弃的站点本地(RFC 3879),新实现按全球单播对待。

Zone ID:fe80::1%en0

链路本地和有限范围组播在节点上可能对应多条链路。RFC 4007 规定文本形式 <address>%<zone_id>。zone id 只在本机有意义:macOS / BSD 常用接口名(en0),Linux 用接口名或 ifindex(eth0 / 2)。% 后面那截不上线,抓包里看不到。

# 本机能解析;发出去的 IPv6 首部只有 fe80::1 ping6 fe80::1%en0 # Linux 同义: ping -6 fe80::1%eth0
易错

同一条链路在两台机器上的 zone id 不必相同。A 机的 %en0 和 B 机的 %eth0 可以指同一条线。把 %en0 抄进配置文件、DNS 或对端命令,是错的。

组播 ff00::/8

   |   8    |  4 |  4 |                  112 bits                   |
   +--------+----+----+---------------------------------------------+
   |11111111|flgs|scop|                  group ID                   |
   +--------+----+----+---------------------------------------------+
                        flgs = |0|R|P|T|     (RFC 4291 §2.7)

最高 8 bit 全 1。接着 4 bit 标志(T=0 是 IANA 永久组,T=1 是临时组;P / R 见 RFC 3306 / 3956),再 4 bit 范围(scop):

scop范围常见地址
1接口本地ff01::1 本接口所有节点
2链路本地ff02::1 全节点 · ff02::2 全路由器
5站点本地ff05::2 站点内全路由器
e全球跨站点组播

以太网把 IPv6 组播映成 33:33:xx:xx:xx:xx(RFC 2464):取 IPv6 组播地址的最后 32 bit 接到 33:33 后面。交换机按这个 MAC 做组播转发,不是广播。

Solicited-node 组播

NDP 不用以太网广播做地址解析。每个单播 / 任播地址对应一个 solicited-node 组播:取该地址的低 24 bit,接到前缀 FF02:0:0:0:0:1:FF00::/104 上(RFC 4291 §2.7.1)。

  单播   2001:0db8:0000:0000:021a:2bff:fe3c:4d5e
  低 24 bit .............................. 3c:4d5e
  SNMA   ff02:0000:0000:0000:0000:0001:ff3c:4d5e
  压缩   ff02::1:ff3c:4d5e
  以太网 33:33:ff:3c:4d:5e

同一链路上 IID 后 24 bit 相同的地址会撞到同一个组,但这比“全节点组播”安静两个数量级。节点必须为自己配置过的每个单播 / 任播加入对应 SNMA。

EUI-64 与隐私扩展

Modified EUI-64(RFC 4291 附录 A)曾是 SLAAC 生成接口标识的默认算法。从 48 bit MAC 做:中间插入 ff:fe,再翻转第一字节的 U/L 位(universal/local,书面形式第一字节从左数第 7 bit,也就是该字节值异或 0x02)。

  MAC        00:1a:2b:3c:4d:5e
  插入 ff:fe  00:1a:2b:ff:fe:3c:4d:5e
  翻转 U/L    02:1a:2b:ff:fe:3c:4d:5e
  IID         021a:2bff:fe3c:4d5e
  LLA         fe80::21a:2bff:fe3c:4d5e
RFC 8064 · 7217 · 8981

RFC 8064 起,SLAAC 不应再用 EUI-64 当默认 IID:MAC 会泄漏进全球地址,跨前缀也能追踪同一台机器。现代栈两套并行:

稳定地址用 RFC 7217 语义不透明 IID(同一前缀、同一台机器上稳定,换前缀就变)。临时地址用 RFC 8981(取代 RFC 4941):每个启用了 A 位的前缀再生成随机 IID,定期轮换,外出连接优先用它当源地址。

Wireshark 里看到 ff:fe 夹在 IID 中间,才是旧式 EUI-64;macOS / iOS / Android / 现代 Windows 的全球地址通常没有这段。

抓包对照 · 地址

# 本机有哪些地址、scope、临时位 ifconfig en0 # macOS:注意 fe80::…%en0 与 temporary ip -6 addr show # Linux:temporary / mngtmpaddr / dadfailed # Wireshark ipv6.src == fe80::/10 ipv6.dst == ff02::1 # 全节点 ipv6.dst == ff02::2 # 全路由器 ipv6.dst == ff02::1:ff00:0/104 eth.dst[0:2] == 33:33 # IPv6 组播 MAC
  • LLA 出现在几乎所有 NDP 的源地址;RA 的源地址 必须是发送接口的 LLA(RFC 4861 §4.2)。
  • 目的是 SNMA 时,以太网目的一定是 33:33:ff:xx:xx:xx,不是广播 ff:ff:ff:ff:ff:ff。
  • zone id 不会出现在 IPv6 首部。过滤器里写 fe80::1%en0 没有意义。

IPv6 首部

RFC 8200 §3–§5

固定 40 字节。没有 IHL、没有首部校验和、没有分片字段。可选项全部挪到扩展头链,由 Next Header 串起来。

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version| Traffic Class |           Flow Label                  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Payload Length        |  Next Header  |   Hop Limit   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +                         Source Address                        +
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +                      Destination Address                      +
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                         RFC 8200 §3 · 40 octets
Version4 bit = 6

抓包第一字节高半字节是 6。看到 4 就不是这份首部。

Traffic Class8 bit

差分服务 + ECN,对应 IPv4 的 ToS / DSCP 字节。RFC 8200 把它交给 RFC 2474 / 3168 解释。

Flow Label20 bit

源端给“同一流”打的标签。中间节点可用来做等价路径上的粘滞转发。IPv4 没有对等物。

Payload Length16 bit

固定 40 字节首部之后的八位组数,含全部扩展头。这是和 IPv4 Total Length(含首部)最容易考反的一点。值为 0 时可能是 jumbogram(Hop-by-Hop 选项,少见)。

Next Header8 bit

紧随其后的头类型,取值与 IPv4 Protocol 同一张 IANA 表:6 TCP、17 UDP、58 ICMPv6,或某个扩展头。

Hop Limit8 bit

每转发一次减 1;收到已是 0 或减到 0 则丢弃。目的节点收到 Hop Limit = 0 的包应照常处理,不丢。NDP 要求发送时填 255,接收时也必须是 255。

Source / Destination各 128 bit

各占 16 字节,合计 32 字节,所以固定首部是 8 + 32 = 40。存在 Routing 头时,目的地址字段沿途会被改写成下一个中间点。

对照 IPv4:少了什么

IPv4(RFC 791)IPv6(RFC 8200)为什么
IHL 4 bit没有(定长 40 B)选项不再挤在基本首部里
Total Length(含首部)Payload Length(不含 40 B)首部长度已知,只报后面有多长
Identification / Flags / Frag Off基本首部里没有分片只允许源端做,走 Fragment 扩展头
Header Checksum 16 bit没有见下
ProtocolNext Header同一张号表,但可以指向扩展头
TTLHop Limit名字改成名副其实的跳数;算法相同
Options + Padding扩展头链仅显式配置的节点才沿途处理 Hop-by-Hop;未配置不影响转发
—Flow Label 20 bit新增
为什么没有首部校验和

RFC 8200 开篇把“丢掉若干 IPv4 字段”写成降低每包转发成本。校验和是其中最贵的一个:IPv4 每跳改 TTL 都必须重算 16 bit 一补码。IPv6 认定两层已经够用——链路层 FCS(以太网 CRC-32)覆盖整帧;TCP / UDP / ICMPv6 的校验和带伪首部,覆盖源、目的、长度和真正的上层 Next Header。中间节点递减 Hop Limit 时不再碰校验和。

连带后果:IPv6 上 UDP 校验和原则上必须算(不能再像 IPv4 那样填 0 关掉);ICMPv6 校验和始终含伪首部,Next Header 取值 58(RFC 4443 §2.3)。

扩展头链

可选的网络层信息放在基本首部和上层头之间,用 Next Header 逐个串。RFC 8200 §4.1 推荐顺序:

  IPv6 header
  Hop-by-Hop Options          Next Header = 0     唯一允许沿途处理;须紧跟基本首部
  Destination Options         60                  给 Routing 头里的中间目的
  Routing                     43
  Fragment                    44                  仅源端插入
  Authentication (AH)         51
  Encapsulating Security (ESP) 50
  Destination Options         60                  只给最终目的
  Upper-Layer                 6 / 17 / 58 / …
  No Next Header              59                  后面没有上层头

除 Destination Options 最多两次外,每种扩展头建议至多一次。Hop-by-Hop 是唯一允许沿途处理的扩展头,且必须紧跟 IPv6 基本首部(RFC 8200 §4 / §4.3)。RFC 8200 已改为:只有显式配置处理的节点才查看它;未配置的节点跳过该头、正常转发,存在 HbH 不构成丢包理由(RFC 9673 §5.1)。节点必须能接受乱序和重复的扩展头(Hop-by-Hop 除外),但源端应按上面的顺序发。

分片只在源端

中间路由器不得把 IPv6 包切开。包大于出接口 MTU 时,路由器丢弃并回 ICMPv6 Packet Too Big(Type 2)。源端若仍要发送超过路径 MTU 的包,自己插 Fragment 头(Next Header 44),在目的地重组。RFC 8200 §5 劝阻这种用法:能适配到路径 MTU(下限 1280)就不要源端分片。

抓包对照 · 首部

Ethernet II … Type: IPv6 (0x86dd) Internet Protocol Version 6, Src: 2001:db8::1, Dst: 2001:db8::2 0110 .... = Version: 6 .... 0000 0000 .... .... .... .... .... = Traffic Class: 0 .... .... .... 0000 0000 0000 0000 0000 = Flow Label: 0 Payload Length: 40 Next Header: ICMPv6 (58) Hop Limit: 255 Source / Destination: 2001:db8::1 → 2001:db8::2 # 过滤器 ipv6 eth.type == 0x86dd ipv6.nxt == 58 ipv6.hlim == 255 ipv6.fraghdr # 源端分片
  • 对比 IPv4:没有 ip.checksum、ip.flags、ip.frag_offset、ip.hdr_len。
  • ipv6.plen 对的是 Payload Length,把它跟整帧长度相加时应再加 40,不要当 IPv4 的 Total Length 用。
  • 有扩展头时,Wireshark 的 ipv6.nxt 是第一个 Next Header;真正的上层协议看链尾,或用 tcp / udp / icmpv6。

NDP 完整讲透

RFC 4861 · ICMPv6 133–137

Neighbor Discovery 取代 IPv4 的 ARP、ICMP 路由器发现和 Redirect。五种报文全是 ICMPv6,Code 一律 0,IPv6 Hop Limit 发送填 255、接收也必须是 255——跨 hop 改过 Hop Limit 的伪造包直接丢。

报文Type典型目的干什么
Router Solicitation133ff02::2主机催路由器立刻发 RA
Router Advertisement134ff02::1 或单播前缀、MTU、M/O、默认路由器
Neighbor Solicitation135SNMA 或单播解析 / DAD / NUD
Neighbor Advertisement136单播或 ff02::1回答 NS,或主动更新缓存
Redirect137被重定向主机更好的下一跳
Type 133 · Code 0

Router Solicitation

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     Type      |     Code      |          Checksum             |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                            Reserved                           |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |   Options ...
                    RFC 4861 §4.1

源地址:本接口已有的单播,或还没有地址时用 ::。目的:通常 ff02::2(全路由器)。选项:源链路层地址(Source LLA,Type 1);源是 :: 时不得带这个选项。

Type 134 · Code 0

Router Advertisement

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     Type      |     Code      |          Checksum             |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | Cur Hop Limit |M|O|  Reserved |       Router Lifetime         |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                         Reachable Time                        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                          Retrans Timer                        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |   Options ...
                    RFC 4861 §4.2

源地址必须是发送接口的链路本地。目的:对端 RS 的源,或 ff02::1。固定头里和考试最相关的三位:

  • M(Managed address configuration,1 bit):置 1 表示地址可经 DHCPv6 拿到——这是信息可用性指示,不是强制开关。M=1 时 O 位冗余,可忽略——有状态 DHCPv6 会带回全部配置。
  • O(Other configuration,1 bit):置 1 表示“其他信息”(DNS 等)可经 DHCPv6 拿。M、O 都是 0:RA 不声明 DHCPv6 信息可用;主机仍可按本地策略独立发起(RFC 8415 §18)。
  • Reserved:RFC 4861 原文是 6 bit,发送填 0、接收忽略。后来 RFC 3775 抽走 H(Home Agent),RFC 4191 抽走 2 bit Prf(默认路由器优先级)。读 4861 时按 M|O|6-bit Reserved 记。
  • Router Lifetime:16 bit,秒。0 表示“我不是默认路由器”,不要把它写进默认路由。发送规则把上限收在 9000 秒,接收方仍须能消化 0–65535。
  • Cur Hop Limit:主机发出去的包应填的 Hop Limit;0 表示本路由器不指定。
  • Reachable Time / Retrans Timer:各 32 bit,毫秒。NUD 用:邻居保持 REACHABLE 多久、重发 NS 间隔。0 表示不指定。

SLAAC 真正看的 A 位不在这 16 字节固定头,而在选项 Prefix Information(Type 3):

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | Type=3        | Length=4      | Prefix Length |L|A|P| Reserved1|
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                         Valid Lifetime                        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                       Preferred Lifetime                      |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                           Reserved2                           |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                            Prefix (128 bit)                   |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                    RFC 4861 §4.6.2 · RFC 9762 P · 32 octets

L:此前缀可用于判断“在链上”。A:此前缀可用于无状态自动配置(RFC 4862)。RFC 9762 在原 Reserved1 里定义了 P 位(DHCPv6-PD Preferred;实际位序为 L|A|R|P|Reserved——R 是 RFC 6275 移动 IPv6 使用,本图略去)。Valid / Preferred Lifetime 各 32 bit 秒;全 1(0xffffffff)表示无穷。Preferred 不得超过 Valid。路由器不应通告链路本地前缀,主机应忽略这样的选项。

常见其它选项:Source LLA(Type 1)、MTU(Type 5)、RDNSS(RFC 8106,Type 25)。

Type 135 · Code 0

Neighbor Solicitation

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     Type      |     Code      |          Checksum             |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                           Reserved                            |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                       Target Address (128 bit)                |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |   Options ...
                    RFC 4861 §4.3

Target Address 是“我想解析 / 探测 / 验重的那个单播或任播”,不能是组播。三种用法目的地址不同:

用途源目的Source LLA
地址解析本机单播目标的 SNMA应当带
NUD本机单播邻居单播可选
DAD::试探地址的 SNMA不得带
Type 136 · Code 0

Neighbor Advertisement

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     Type      |     Code      |          Checksum             |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |R|S|O|                     Reserved                            |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                       Target Address (128 bit)                |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |   Options ...
                    RFC 4861 §4.4
  • R Router:发送者是路由器。NUD 靠它发现“路由器变成了主机”。
  • S Solicited:这是对某条 NS 的应答,用作可达性确认。组播 NA、非请求单播 NA 必须 S=0。DAD 冲突时回的 NA 目的是 ff02::1,因此 S=0。
  • O Override:应覆盖邻居缓存里已有的链路层地址。任播的请求应答、代理应答 SHOULD NOT 置 O。其余请求应答按 4861 SHOULD 置 O;非请求通告的 O 位依用途 / 规范——RFC 9131 对 preferred address 的 GNA SHOULD O=0。

若 NS 的源是 ::(对方在做 DAD),NA 不能单播回去,目的改成 ff02::1。选项 Target LLA(Type 2)给出自己的 MAC。

Type 137 · Code 0

Redirect

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     Type      |     Code      |          Checksum             |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                           Reserved                            |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                       Target Address                          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                     Destination Address                       |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |   Options ...
                    RFC 4861 §4.5

源必须是发送接口 LLA。目的是触发重定向的那台主机。Target 是更好的下一跳;若目的就在链上,Target 等于 Destination,主机以后直接把该目的当邻居。选项常带 Target LLA 和 Redirected Header(Type 4,截一段原包)。

ARP → NS / NA 逐项对应

IPv4 ARP(RFC 826)IPv6 NDP(RFC 4861)
独立以太类型 0x0806,不走 IP走 IPv6 + ICMPv6(NH=58),类型 135 / 136
Request 以太网广播NS 发到目标的 solicited-node 组播
Reply 通常单播NA 单播(S=1);DAD 冲突则 NA 到 ff02::1
Who-has TPA? Tell SPATarget Address + 源 IPv6 + Source LLA 选项
Sender HA 在 ARP 固定头MAC 在 ICMPv6 选项里,不在固定头
Gratuitous ARP非请求 NA(S=0;O 依用途 / 规范可 0 或 1)。经典通告目的 ff02::1;RFC 9131 GNA 对 preferred address SHOULD O=0、目的 SHOULD ff02::2
ARP cacheNeighbor Cache(INCOMPLETE / REACHABLE / STALE / DELAY / PROBE)
无路由器发现另有 RS / RA;默认路由来自 RA 的 Router Lifetime
无 DADDAD 复用 NS / NA,源地址为 ::

DAD

RFC 4862 §5.4:任何单播地址——手工、SLAAC、DHCPv6——赋给接口之前都必须做重复地址检测,除非管理员关了该接口的 DAD。anycast 地址 MUST NOT 做 DAD(任播本就不唯一)。过程:

地址进入 Tentative

先加入该地址的 solicited-node 组,才能听到别人的冲突声明。

发 DAD NS

源 ::,目的为试探地址的 SNMA,Target = 试探地址,不带 Source LLA。默认 DupAddrDetectTransmits = 1,间隔为 RA / 协议常量里的 RetransTimer(缺省 1000 ms)。

判定

收到针对同一 Target 的 NA,或收到另一台机器发来、Target 相同的 NS:冲突——须排除本机组播回环(自己发出的 DAD NS 再收进来不算,RFC 4862 §5.4.3)。失败处置(§5.4.5):仅当重复的是 link-local、且 IID 源自本应唯一的硬件地址时,才停用该接口的 IPv6;其它情况放弃这个地址再试即可。

通过

超时未闻冲突,地址离开 Tentative,变为 Preferred(或按寿命进入 Deprecated)。

Optimistic DAD(RFC 4429)允许在探测未完成时用该地址做外出连接,本页按 4862 基线讲。

抓包对照 · 一次解析 + 一次 DAD

主机 A(fe80::a,MAC 02:00:00:00:00:0a)解析主机 B 的 2001:db8::b。B 的 SNMA 是 ff02::1:ff00:b。

Address resolution
A fe80::aNS 135 → ff02::1:ff00:b
Target=2001:db8::b SLLA=02:00:00:00:00:0a
B
A← NA 136 单播 S=1 R=? O=1
Target=2001:db8::b TLLA=…:0b
B 2001:db8::b
Ethernet II, Src: 02:00:00:00:00:0a, Dst: 33:33:ff:00:00:0b IPv6, Hop Limit: 255, Next Header: ICMPv6 (58) Src: fe80::a Dst: ff02::1:ff00:b ICMPv6 Neighbor Solicitation (135) Target Address: 2001:db8::b ICMPv6 Option (Source link-layer address): 02:00:00:00:00:0a # DAD:源是未指定,没有 SLLA,以太网目的仍是 33:33:ff:… IPv6 Src: :: Dst: ff02::1:ff3c:4d5e hlim=255 ICMPv6 NS (135) Target: 2001:db8::21a:2bff:fe3c:4d5e # 无 ICMPv6 Option # 过滤器 icmpv6.type == 133 # RS icmpv6.type == 134 # RA icmpv6.type == 135 # NS icmpv6.type == 136 # NA icmpv6.type == 137 # Redirect icmpv6.nd.ns.target_address == 2001:db8::b icmpv6.nd.na.flag.s == 1 icmpv6.nd.ra.flag.m == 1 icmpv6.type >= 133 && icmpv6.type <= 137 # NDP 133–137;勿写 ndp(那是 Nortel Discovery)
  • NDP 五条的以太网目的从来不是 ff:ff:ff:ff:ff:ff。解析走 33:33:ff:…,RA / 经典非请求 NA 走全节点组播 MAC 33:33:00:00:00:01;RFC 9131 GNA 目的 SHOULD 是全路由器 ff02::2(MAC 33:33:00:00:00:02)。O 位依用途 / 规范,不必恒为 1。
  • Hop Limit 不是 255 的“NDP”应视为无效(RFC 4861 校验规则),常见于被路由过来的伪造包。
  • 看 RA 时先拆 M/O,再点开每个 Prefix Information 看 L/A/P 和寿命,不要只看 IPv6 地址列表。

地址配置决策

RFC 4862 · 4861 M/O/A · DHCPv6

主机先有链路本地,再听 RA。RA 的 M/O 是信息可用性指示,典型主机策略据此决定要不要找 DHCPv6,但不是强制开关——RFC 8415 §18 允许本地策略独立发起。每个前缀选项上的 A 位决定要不要用 SLAAC 拼地址。三套旋钮是正交的,不是“开了 DHCP 就没有 SLAAC”。

SLAAC 步骤(RFC 4862)

生成链路本地

fe80::/64 + 本接口 IID。IID 如今多半是 RFC 7217,不再是 EUI-64。

对 LLA 做 DAD

通过后,接口才有可用的链路本地,才能把 LLA 当源去发 RS。

发 RS / 等周期 RA

不必串行死等:4862 允许 LLA 的 DAD 和等 RA 并行。路由器对 RS 可以随机延迟至多 0.5 秒(MAX_RA_DELAY_TIME,RFC 4861 §6.2.6),避免一齐应答。

处理每个 Prefix Information

A=1 且前缀长度 + IID 长度 = 128(通常是 /64 + 64)才形成全球 / ULA 地址,再对该地址做 DAD。A=0 只可能更新“在链上”信息(还要看 L),不拼地址。

装默认路由与参数

RA 源 LLA 在 Router Lifetime ≠ 0 时成为默认路由器。Hop Limit、Reachable Time、Retrans Timer、MTU 选项按 4861 §6.3.4 更新。

按 M/O 的典型策略决定是否找 DHCPv6

见下表。M/O 是可用性指示,不是强制开关(RFC 8415 §18 允许本地策略独立发起)。DNS 也可以只靠 RA 里的 RDNSS(RFC 8106),不必为此开 DHCPv6。

地址寿命

Preferred Lifetime 到期 → Deprecated:已有连接可继续用,新连接不应再选它当源。Valid Lifetime 到期 → Invalid:从接口上拿掉。Valid 全 1 表示无穷。这是站点重编号的挂钩。

M / O / A 怎么组合

MOA主机行为
001典型:纯 SLAAC。DNS 若需要,看 RA 的 RDNSS,不发起 DHCPv6。
011典型:SLAAC 配地址;无状态 DHCPv6(Information-Request)拿 DNS 等。
010该 PIO 不做 SLAAC;可用无状态 DHCPv6 取其他信息(DNS 等)。
1×0典型:有状态 DHCPv6 配地址(IA_NA 等)。O 可忽略。
1×1典型:SLAAC 和有状态 DHCPv6 都可以配地址,两套可同时在接口上。
000不自动配全球地址;只剩 LLA(再加手工)。本地策略仍可发起 DHCPv6。
RFC 4861 §4.2 原文口径

M:addresses are available via DHCPv6。O:other configuration information is available via DHCPv6。If the M flag is set, the O flag is redundant and can be ignored。If neither M nor O flags are set, this indicates that no information is available via DHCPv6。

这是可用性指示,不是强制开关。RFC 8415 §18 允许主机按本地策略独立发起 DHCPv6,不必等 M/O。A 位按每个前缀独立。一条 RA 可以同时带 2001:db8:1::/64 A=1 和 2001:db8:2::/64 A=0。

无状态 DHCPv6 vs 有状态 DHCPv6

DHCPv6 现行文本是 RFC 8415(吸收了 3315)。UDP:客户端 546,服务器 / 中继 547。链路上的服务器组播 ff02::1:2。

IPv4 的“DHCP 失败就 APIPA(169.254/16)”在 v6 不成立:LLA 是每个接口的必选项,不是兜底失败态。

抓包对照 · 开机配置

典型家庭网:M=0 O=0 A=1 + RDNSS
主机DAD NS 源=::SNMA(LLA)
主机RS 133 → ff02::2路由器
主机← RA 134 M=0 O=0
PIO A=1 L=1 + RDNSS
路由器 fe80::1
主机DAD NS 源=:: 对 GUASNMA(GUA)
icmpv6.type == 134 dhcpv6 # 不该在纯 SLAAC 网上出现 dhcpv6.msgtype == 11 # Information-Request(无状态) dhcpv6.msgtype == 1 # Solicit(有状态) udp.port == 546 || udp.port == 547
  • 只看到 RS/RA、没有任何 DHCPv6:M=O=0,或主机实现选择只信 RDNSS。
  • 有 Information-Request / Reply、没有 IA_NA:无状态 DHCPv6,O=1。
  • 有 Solicit 且 Reply 里带 IA_NA:有状态,M=1。
  • 接口上同时出现 SLAAC 地址和 dhcp 标记的地址:M=1 且 A=1,不是配置错误。

ICMPv6

RFC 4443 · PMTUD RFC 8201

ICMPv6 的 Next Header 是 58,不是 IPv4 的 Protocol 1。它是 IPv6 的一部分:每个节点必须实现 RFC 4443 的基线。NDP、MLD 都挂在这张类型表上,但定义在别的 RFC。

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     Type      |     Code      |          Checksum             |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                         Message Body                          |
                    RFC 4443 §2.1

Type 最高位为 0(0–127)是差错,为 1(128–255)是信息。校验和覆盖整条 ICMPv6 消息外加 IPv6 伪首部,伪首部里的 Next Header 固定写 58(RFC 4443 §2.3)。这是相对 ICMPv4 的明确变化。

Type名字关键 Code / 字段
1Destination Unreachable0 无路由 · 1 管理禁止 · 2 超出源地址范围 · 3 地址不可达 · 4 端口不可达 · 5 出入策略 · 6 拒绝路由
2Packet Too BigCode 必须 0;紧接着 32 bit 下一跳出接口 MTU
3Time Exceeded0 Hop Limit 在转发中耗尽 · 1 分片重组超时
4Parameter Problem0 首部字段错 · 1 无法识别的 Next Header · 2 无法识别的 IPv6 选项;指针指出出错八位组
128 / 129Echo Request / ReplyIdentifier + Sequence,ping6
130–132 / 143MLD / MLDv2IPv6 侧的 IGMP 对等物
133–137NDP见上一节

与 ICMPv4 的差异

PMTUD 在 v6 是强制路径

RFC 8200 §5:每条链路 MTU 至少 1280;不能一次塞下 1280 的链路必须在 IPv6 之下自己做分片重组。中间路由器不得在 IPv6 层切开数据报。于是:

防火墙

把 ICMPv6 一刀切掉,IPv6 会在“大于 1280 的路径”上静默失败:TCP 看起来能握手(小包),传输大段时卡住。至少放行 Type 2,以及 NDP 133–137、Echo 128/129。这是 IPv6 排障里最常见的自伤。

抓包对照 · ICMPv6

ICMPv6 Packet Too Big (2) Code: 0 MTU: 1280 # 后面跟引发包的 IPv6 首部 + 尽量多的数据 icmpv6.type == 2 icmpv6.type == 1 && icmpv6.code == 4 # 端口不可达 icmpv6.type == 3 && icmpv6.code == 0 # traceroute6 icmpv6.type == 128 || icmpv6.type == 129 ipv6.hlim == 1 # 探针
  • traceroute6 吃的是 Type 3 Code 0,不是 IPv4 的 Time Exceeded Type 11。
  • PTB 的 MTU 字段是 32 bit,不是 IPv4 ICMP 里那 16 bit Next-Hop MTU。
  • ping6 是 128/129。过滤器写 icmp 只会命中 IPv4。

过渡与共存

Dual-stack · NAT64/DNS64 · RFC 8305

今天的默认答案是双栈。隧道(6to4、Teredo)大多已淘汰。IPv6-only 接入网靠 NAT64/DNS64;应用层靠 Happy Eyeballs 避免“AAAA 能解析、路径却是死的”。

双栈

同一接口同时有 IPv4 和 IPv6。DNS 既回 A 也回 AAAA。选哪套地址由 Happy Eyeballs 和目的地址选择(RFC 6724)决定,不是“有 AAAA 就只走 v6”。

NAT64 / DNS64(RFC 6146 / 6147)

给只有 IPv6 的主机访问只有 IPv4 的服务器(DNS64 见 RFC 6147,NAT64 见 RFC 6146):

DNS64

递归解析器替客户端查 A。若没有 AAAA,就把 IPv4 地址嵌入一个 IPv6 前缀,合成 AAAA。熟知前缀是 RFC 6052 的 64:ff9b::/96:64:ff9b::c000:201 表示 192.0.2.1。

主机当普通 IPv6 发出

目的落在 NAT64 前缀上,路由把包送到 NAT64。

NAT64 做无状态或有状态翻译

RFC 6146 是有状态的:IPv6 首部 ↔ IPv4 首部,传输层端口按需改写。回来的 v4 包再译回 v6。

和 NAT44 的差别

NAT64 翻译的是协议,不是“把内网 v6 藏到一个公网 v6 后面”。IPv6 侧通常仍是全球地址。纯 IPv6 目的(真正的 AAAA)不经过 NAT64。移动网常见的 464XLAT(RFC 6877)是在主机上再加一层 CLAT,给那些写死 IPv4 字面量的应用用。

Happy Eyeballs v2(RFC 8305)

偏好 IPv6,但不为它空等。

“v6 优先”不是“AAAA 解析失败才试 A”,更不是“SYN 发给 v6 之后空等三次超时”。抓包里常见:一条 AAAA、一条 A,两条 SYN 前后相差约 250 ms,先回来的那条留下。

抓包对照 · 双栈与翻译

dns.qry.type == 28 # AAAA dns.qry.type == 1 # A ipv6.dst == 64:ff9b::/96 # 熟知 NAT64 前缀 tcp.flags.syn == 1 and (ip or ipv6) # Happy Eyeballs:同一 hostname / 目标服务的 v6 与 v4 连接尝试 tcp.stream eq N
  • 目的落在熟知前缀 64:ff9b::/96 通常表示地址合成,须结合 DNS 应答确认(是 DNS64 填的 AAAA,还是应用自己拼的);确认后再把它当 NAT64 路径,而不是原生双栈。
  • v6 SYN 发出后约 250 ms 出现 v4 SYN,随后 v6 被 RST:RST 说明该次尝试失败,但恰证明往返可达,不能断言路径坏;Eyeballs 用先完成握手的那条。
  • 6to4(2002::/16)和 Teredo(2001:0::/32)今天应视为遗物,新部署不要再开。

排障速查

macOS · Linux · Wireshark

先看本机地址和默认路由,再看邻居缓存,最后抓 NDP / PTB。IPv6 坏了却“能 ping 通网关 LLA”,多半是 RA 没来或 ICMPv6 被滤。

目的macOS / BSDLinux
地址与标志ifconfig en0
ifconfig -a inet6
ip -6 addr
pingping6 2001:db8::1
ping6 fe80::1%en0
ping -6 2001:db8::1
部分发行版保留兼容命令 ping6
邻居缓存ndp -anip -6 neigh
默认路由器 / 前缀ndp -r · ndp -p看 ip -6 route 与 RA 守护进程日志
路由表netstat -rnf inet6
route -n get -inet6 default
ip -6 route
ip -6 route show default
组播组成员netstat -gip -6 maddr
traceroutetraceroute6traceroute -6 / tracepath -6 / tracepath6
主动要 RA(内核自动发 RS)rdisc6 eth0(ndisc6 包)
# macOS:邻居与默认路由器 ndp -an ndp -r netstat -rnf inet6 | head # Linux ip -6 neigh show ip -6 route show ip -6 addr show dev eth0 # 逼出 NS/NA:清掉邻居再 ping 链路本地 sudo ndp -d 2001:db8::1 # macOS sudo ip -6 neigh del 2001:db8::1 dev eth0

邻居缓存状态(RFC 4861 §7.3.2)

状态含义
INCOMPLETE已发 NS,还没等到 NA(解析中)
REACHABLE近期得到正向确认(S=1 的 NA 或上层提示)
STALEReachable Time 到期,下次发包前先用缓存,再进入 DELAY
DELAY等上层确认;超时则 PROBE
PROBE单播 NS 做 NUD

Linux 的 FAILED 是实现层对“解析失败”的汇总,规范五态里没有这个词。长时间 INCOMPLETE / FAILED 盯网关:RA 里的路由器 LLA 和你 ping 的不是同一个,或二层隔离了组播。

Wireshark / tcpdump

过滤器命中
ipv6所有 IPv6
icmpv6Next Header 58
icmpv6.type==133 … 137RS / RA / NS / NA / Redirect
icmpv6.type==128 || icmpv6.type==129ping6
icmpv6.type==2Packet Too Big
icmpv6.type==3Time Exceeded
dhcpv6UDP 546/547
ipv6.dst==ff02::1:ff00:0/104solicited-node
eth.type==0x86dd以太网 IPv6
sudo tcpdump -i en0 -n -vv ip6 sudo tcpdump -i en0 -n icmp6 sudo tcpdump -i en0 -n 'icmp6 && ip6[40] == 134' # RA:NH 紧贴基本首部时 Type 在偏移 40 sudo tcpdump -i en0 -n 'icmp6 && ip6[40] == 135' # NS
tcpdump 偏移

ip6[40] 假定 ICMPv6 紧跟 40 字节基本首部。中间插入 Hop-by-Hop 等扩展头时,这个写法会偏掉。Wireshark 显示过滤器没有这个问题,优先用 icmpv6.type。

考点与易错点

  1. Payload Length 不含 40 字节固定首部,但含扩展头。IPv4 Total Length 含首部。Wireshark 字段是 ipv6.plen。
  2. IPv6 基本首部没有校验和。Hop Limit 每跳递减不必重算首部校验和。TCP / UDP / ICMPv6 的校验和带伪首部;IPv6 上 UDP 不能再靠填 0 关闭校验和。
  3. 中间路由器不得分片。过大就丢,回 ICMPv6 Type 2 Packet Too Big。源端若要切,用 Fragment 扩展头(Next Header 44)。最小链路 MTU 是 1280,不是 IPv4 的 576。
  4. LLA 规范前缀 fe80::/10,格式上随后 54 bit 为 0,所以按 fe80::/64 用。指定具体地址必须带 zone id(%en0);zone id 不上线。
  5. Solicited-node 取单播的低 24 bit 接到 ff02::1:ff00:0/104。不是低 32 bit。以太网目的是 33:33:ff:xx:xx:xx,不是广播。
  6. EUI-64 必须异或第一字节 0x02(翻转 U/L)。现代 SLAAC 默认不再用 EUI-64(RFC 8064);稳定 IID 走 RFC 7217,临时地址走 RFC 8981。
  7. NDP 类型号 133–137,Code 全 0,Hop Limit 必须 255。RS 133 / RA 134 / NS 135 / NA 136 / Redirect 137。写 1/2 或当成 ARP 操作码就错了。
  8. DAD 的 NS:源地址 ::,目的 SNMA,禁止 Source LLA 选项。冲突时对方回 NA 到 ff02::1,S 位为 0。
  9. NA 的 S 位只在“应答某条 NS、且目的是单播”时为 1。组播 NA、非请求 NA、DAD 防御都必须 S=0。R/S/O 是三个独立标志。
  10. RA 固定头只有 M 和 O;A 位在 Prefix Information(Type 3)里。M=1 则 O 冗余。M/O 是 DHCPv6 信息可用性指示 / 典型主机策略,不是强制开关(RFC 8415 §18);A 管 SLAAC,可以同时开。
  11. ICMPv6 Next Header = 58,不是 1。Packet Too Big 是 Type 2,不是 Destination Unreachable 的某个 Code。Type 1 Code 4 在 v6 里是端口不可达。
  12. ULA 是 fc00::/7,本地分配用 fd00::/8。fec0::/10 站点本地已废弃。:: 是未指定,::1 是环回。
  13. Happy Eyeballs v2 不是“先 v6 失败再 v4”。正 AAAA 先到立即试 IPv6;正 A 先到才短等 AAAA(Resolution Delay 推荐 50 ms)。Connection Attempt Delay 建议 250 ms、最大 2 s,谁先握手成功用谁。
  14. 封禁 ICMPv6 等于拆掉 IPv6。至少 Type 2(PMTUD)和 133–137(NDP)必须放行。只禁 ping(128/129)通常还能通,禁 Type 2 会在大包上“假死”。