ping6.net
基础知识

ICMPv6 解析:使 IPv6 工作的协议

了解 ICMPv6,IPv6 网络的基本协议。了解邻居发现、路由器通告以及为什么阻止 ICMPv6 会破坏你的网络。

ping6.net2024年12月14日9 min read
IPv6ICMPv6NDP邻居发现路由器通告网络

ICMPv6 不是可选的#

在 IPv4 中,ICMP 处理诊断。你可以阻止它,大多数事情仍然可以工作。IPv6 不是这样工作的。

TL;DR - 快速摘要

要点:

  • ICMPv6 是强制性的(不像 IPv4 ICMP 那样可选);它处理 NDP、路由器发现和 PMTUD
  • 邻居发现协议(类型 133-137)取代 ARP 并启用 SLAAC
  • 类型 2(Packet Too Big)对于路径 MTU 发现至关重要——阻止它会破坏连接
  • 允许的基本类型:1(不可达)、2(Too Big)、128-129(Ping)、133-137(NDP)

跳转至: 邻居发现协议 | 路径 MTU 发现 | 防火墙注意事项

ICMPv6 是 IPv6 的神经系统。它处理错误报告、诊断、邻居发现、路由器发现和地址解析。阻止错误的 ICMPv6 消息类型,你将破坏基本连接,阻止主机找到它们的默认网关,并导致需要数小时调试的神秘连接挂起。

IPv6 规范不将 ICMPv6 视为附加组件。它是一个强制性的、不可分割的组件。理解 ICMPv6 就是理解 IPv6 实际上是如何工作的。

ICMPv6 与 ICMP(IPv4)#

ICMPv6 从 IPv4 的 ICMP 演变而来,但承担了更多的责任。

功能ICMPv4ICMPv6影响
协议号158不同的 IP 下一个头部值
错误消息目标不可达、超时等相同错误,经过改进类似功能
诊断回显请求/应答(ping)回显请求/应答(ping)相同目的
地址解析ARP(独立协议)邻居发现(ICMPv6)ICMPv6 完全取代 ARP
路由器发现ICMP 路由器发现(可选)路由器通告(强制)对于自动配置至关重要
分片由路由器静默处理数据包太大消息需要路径 MTU 发现
多播管理IGMP(独立协议)MLD 消息(ICMPv6)ICMPv6 取代 IGMP

关键区别:ICMPv6 吸收了在 IPv4 中使用单独协议的功能。ARP 在 IPv4 中在第 2 层运行。IPv6 没有 ARP——邻居发现使用 ICMPv6 代替。IGMP 在 IPv4 中管理多播组。IPv6 在 ICMPv6 中使用多播侦听器发现(MLD)消息。

这种整合简化了协议栈,但使 ICMPv6 绝对必不可少。你不能在不破坏核心功能的情况下阻止它。

消息类型结构#

ICMPv6 消息具有简单的结构:类型、代码、校验和和消息特定数据。类型字段确定消息类别。

类型号范围:

  • 0-127:错误消息
  • 128-255:信息消息

这种划分使过滤更容易。错误消息报告数据包传递问题。信息消息处理查询、响应和邻居/路由器发现。

常见消息类型#

类型代码名称类别目的
10-6目标不可达错误端口关闭,路由不可用等
20数据包太大错误路径上超过 MTU
30-1超时错误跳数限制达到或分片超时
40-2参数问题错误格式错误的数据包头部
1280回显请求信息Ping 请求
1290回显应答信息Ping 响应
1330路由器请求信息请求路由器信息
1340路由器通告信息路由器宣布存在和配置
1350邻居请求信息地址解析和可达性
1360邻居通告信息对请求的响应
1370重定向信息存在更好的第一跳路由器

类型号很重要

与 IPv4 不同,IPv4 中 ICMP 类型使用分散的数字,ICMPv6 的有组织类型空间使防火墙规则更清晰。类型 133-137 处理邻居发现协议,使得作为一个组允许或阻止 NDP 变得容易。

邻居发现协议(NDP)#

邻居发现协议取代了 IPv4 的 ARP,并添加了 IPv4 通过多个协议处理的功能。NDP 完全通过 ICMPv6 运行,处理五个关键功能:

  1. 地址解析 - 将 IPv6 地址映射到 MAC 地址(取代 ARP)
  2. 路由器发现 - 在没有配置的情况下查找本地路由器
  3. 前缀发现 - 了解用于自动配置的网络前缀
  4. 参数发现 - 获取 MTU、跳数限制和其他参数
  5. 下一跳确定 - 识别目标的最佳路由器

五种 NDP 消息类型#

路由器请求(类型 133)#

由主机发送以请求路由器立即宣布自己,而不是等待下一个计划的路由器通告。

何时发送:

  • 主机启动
  • 接口上线
  • 主机想要快速配置

格式:

源:链路本地地址或 ::
目标:ff02::2(所有路由器多播)
跳数限制:255

路由器请求让主机在几秒钟内获得网络配置,而不是等待定期通告。

路由器通告(类型 134)#

由路由器发送以宣布它们的存在,为 SLAAC 通告前缀,并提供配置参数。

何时发送:

  • 定期(每几分钟)
  • 响应路由器请求
  • 路由器配置更改时

它包含什么:

  • 路由器生存期(使用此路由器的时间)
  • 网络前缀及其有效性
  • MTU 建议
  • 跳数限制建议
  • SLAAC 和 DHCPv6 的标志

格式:

源:路由器的链路本地地址
目标:ff02::1(所有节点多播)或请求主机
跳数限制:255

路由器通告是主机自动了解其网络配置的方式。不需要 DHCP——路由器广播主机配置自己所需的一切。

SLAAC 依赖于路由器通告

无状态地址自动配置(SLAAC)完全依赖于路由器通告。阻止类型 134,主机无法自动配置。它们将只有链路本地地址,没有默认路由,也没有全局可路由地址。

邻居请求(类型 135)#

IPv6 的 ARP 请求等价物。发送以发现邻居的 MAC 地址或验证邻居是否仍然可达。

何时发送:

  • 将 IPv6 地址解析为 MAC 地址
  • 验证邻居是否仍然可达
  • 重复地址检测(检查地址是否已在使用中)

格式:

源:发送者的地址(对于 DAD 为 ::)
目标:请求节点多播地址或目标地址
跳数限制:255

邻居请求消息使用请求节点多播地址而不是广播。这减少了不必要的处理——只有目标主机和具有类似地址的主机接收数据包。

邻居通告(类型 136)#

对邻居请求的响应。提供发送者的 MAC 地址或确认可达性。

何时发送:

  • 响应邻居请求
  • 主动宣布地址更改

格式:

源:发送者的链路本地或全局地址
目标:请求者的地址或所有节点多播
跳数限制:255

重定向(类型 137)#

由路由器发送以通知主机存在特定目标的更好的第一跳路由器。

何时发送:

  • 主机将数据包发送到路由器
  • 路由器知道同一链路上有更好的下一跳
  • 路由器转发数据包并发送重定向

格式:

源:路由器的链路本地地址
目标:原始发送者
跳数限制:255

重定向优化路由,而不需要主机维护复杂的路由表。

地址解析的工作原理#

当主机需要将数据包发送到本地链路上的另一个 IPv6 地址时:

  1. 检查邻居缓存 - MAC 地址是否已知?
  2. 发送邻居请求 - 如果没有,向请求节点多播地址发送 NS
  3. 接收邻居通告 - 目标响应 MAC 地址
  4. 更新缓存 - 存储映射以供将来使用
  5. 发送数据包 - 传递原始数据包

请求节点多播地址从目标的 IPv6 地址计算:

ff02::1:ff + IPv6 地址的最后 24 位
 
示例:
IPv6:2001:db8::a4b2:c3d4:e5f6:7890
请求节点:ff02::1:ff:f6:7890

与 IPv4 基于广播的 ARP 相比,这种多播方法减少了网络流量。

重复地址检测

在使用地址之前,主机发送源为 :: 且目标设置为它们想要使用的地址的邻居请求。如果另一个主机响应,则地址已在使用中。这防止了 SLAAC 中的地址冲突。

路由器发现详解#

路由器通过路由器通告消息通告自己和网络配置。主机侦听并根据这些通告自动配置。

路由器通告内容#

典型的 RA 包含:

路由器信息:

  • 路由器生存期(0-9000 秒,0 表示「不是默认路由器」)
  • 可达性时间(将邻居视为可达的时间)
  • 重传计时器(邻居请求之间的延迟)

前缀信息:

  • 网络前缀(例如,2001:db8:1234::/64)
  • 有效生存期(地址有效的时间)
  • 首选生存期(用于新连接的时间)
  • 标志:
    • L(在链路上):前缀在本地链路上
    • A(自治):用于 SLAAC

其他选项:

  • MTU 建议
  • DNS 服务器(RDNSS 选项)
  • DNS 搜索域(DNSSL 选项)

SLAAC 地址形成#

当主机接收到设置了 A 标志的 RA 时:

  1. 采用前缀 - 例如,2001:db8:1234::/64
  2. 生成接口标识符 - 从 MAC 或随机派生的 64 位
  3. 组合它们 - 2001:db8:1234::a4b2:c3d4:e5f6:7890
  4. 运行重复地址检测 - 确保没有其他人使用它
  5. 配置地址 - 添加到接口
  6. 设置默认路由 - 使用路由器作为下一跳

这在没有用户干预或 DHCP 服务器的情况下自动发生。

控制配置的标志#

路由器通告包含告诉主机如何配置自己的标志:

  • M(托管):使用 DHCPv6 获取地址(不是 SLAAC)
  • O(其他):使用 DHCPv6 获取其他配置(DNS、NTP 等)

常见组合:

MO行为
00仅 SLAAC,无 DHCPv6
01SLAAC 用于地址,DHCPv6 用于 DNS 等
10DHCPv6 用于地址(不常见)
11DHCPv6 用于所有内容

大多数网络使用 M=0,O=0(纯 SLAAC)或 M=0,O=1(SLAAC + DHCPv6 用于 DNS)。

路径 MTU 发现#

IPv6 路由器不分片数据包。源必须发送足够小的数据包以适应整个路径。这就是 ICMPv6 类型 2 变得至关重要的地方。

PMTUD 的工作原理#

  1. 主机发送数据包 - 使用接口 MTU(通常为 1500 字节)
  2. 路由器遇到较小的 MTU - 在 IPv6 中无法分片
  3. 路由器丢弃数据包 - 发送 ICMPv6 数据包太大消息
  4. 消息包含 MTU - 告诉发送者允许的最大大小
  5. 主机减小数据包大小 - 使用较小的 MTU 重传
  6. 连接继续 - 使用适当的数据包大小

数据包太大消息格式:

类型:2
代码:0
MTU:1280(或下一跳支持的任何值)
原始数据包:丢弃的数据包的第一部分

最小 IPv6 MTU 为 1280 字节。所有链路必须至少支持此大小。更大的数据包需要 PMTUD 才能工作。

阻止类型 2 时会发生什么#

阻止数据包太大消息时的连接症状:

  • 初始连接有效 - SYN、SYN-ACK、ACK 数据包很小
  • 数据传输挂起 - 大数据包被静默丢弃
  • 无错误消息 - 连接只是停滞
  • 几分钟后超时 - TCP 最终放弃

这是最令人沮丧的调试问题之一,因为连接在失败之前成功建立。

真实场景:

$ curl -6 https://example.com/
# TLS 握手后连接挂起
# 浏览器显示「正在加载...」永远
# SSH 连接但在横幅交换期间挂起

所有这些都是因为某个中间盒阻止了 ICMPv6 类型 2。

永远不要阻止数据包太大

阻止 ICMPv6 类型 2 会以微妙的、特定于应用程序的方式破坏连接。始终允许此消息类型在防火墙和路由器的两个方向上通过。

回显请求和回显应答(Ping)#

类型 128(回显请求)和类型 129(回显应答)的工作方式与 IPv4 ping 完全相同。

格式:

类型:128(请求)或 129(应答)
代码:0
标识符:任意(匹配请求/应答)
序列:每次 ping 递增
数据:任意有效负载

诊断用途#

Ping 是最简单的连接测试:

# 基本 ping
ping6 2001:4860:4860::8888
 
# 指定源地址
ping6 -I 2001:db8::10 2001:4860:4860::8888
 
# 大数据包以测试 PMTUD
ping6 -s 1400 google.com
 
# 洪水 ping(需要 root)
sudo ping6 -f 2001:4860:4860::8888

速率限制考虑#

许多管理员限制 ping 速率以防止侦察和 DoS 攻击。这对于生产服务器是合理的,但完全阻止会使故障排除更加困难。

推荐方法:

  • 允许回显请求/应答
  • 速率限制以防止滥用
  • 记录过多尝试

Linux 示例:

# 允许 ping 但速率限制
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 128 \
  -m limit --limit 10/sec --limit-burst 20 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 128 -j DROP

这允许每秒 10 次 ping,突发最多 20 次,丢弃多余的流量。

防火墙注意事项#

ICMPv6 过滤需要了解哪些类型是必不可少的,哪些是可选的。

必须允许(破坏连接)#

类型 2:数据包太大

  • 方向:入站和出站
  • 原因:没有它路径 MTU 发现会中断
  • 范围:所有连接

类型 133-136:邻居发现

  • 方向:仅链路本地(跳数限制 255)
  • 原因:地址解析和路由器发现失败
  • 范围:仅本地网络

类型 1:目标不可达

  • 方向:入站(对你的流量的响应)
  • 原因:TCP 需要知道端口/路由何时不存在
  • 范围:所有连接

应该允许(破坏诊断)#

类型 3:超时

  • 方向:入站
  • 原因:没有它 Traceroute 失败
  • 影响:无法诊断路由问题

类型 128-129:回显请求/应答

  • 方向:两者
  • 原因:Ping 是主要的连接测试
  • 影响:无法验证基本可达性

可以阻止(仅信息)#

类型 130-132:多播侦听器发现

  • 范围:仅本地网络
  • 影响:多播可能无法最佳工作

类型 137:重定向

  • 影响:本地网络上的次优路由
  • 安全性:一些管理员阻止以防止路由操纵

iptables 规则示例#

最小主机防火墙:

# 必不可少的 ICMPv6
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 1 -j ACCEPT   # 目标不可达
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 2 -j ACCEPT   # 数据包太大
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 3 -j ACCEPT   # 超时
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 128 -j ACCEPT # 回显请求
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 129 -j ACCEPT # 回显应答
 
# 邻居发现(仅链路本地,跳数限制 255)
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 133 -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 134 -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 135 -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 136 -m hl --hl-eq 255 -j ACCEPT

跳数限制检查(--hl-eq 255)确保邻居发现数据包源自本地链路。合法的 NDP 始终使用跳数限制 255。来自路由器的数据包将具有递减的值。

nftables 等价物:

# 必不可少的 ICMPv6
nft add rule ip6 filter input icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, echo-request, echo-reply } accept
 
# 具有跳数限制检查的邻居发现
nft add rule ip6 filter input icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 hoplimit 255 accept

常见错误:阻止所有 ICMPv6

最常见的 IPv6 防火墙错误是阻止所有 ICMPv6。管理员移植阻止所有 ICMP 的 IPv4 规则,然后想知道为什么 IPv6 中断。ICMPv6 不是可选的——允许必不可少的类型,否则会出现连接故障。

使用 ICMPv6 进行故障排除#

读取错误消息#

当连接失败时,使用 tcpdump 检查 ICMPv6 错误消息:

# 捕获所有 ICMPv6
sudo tcpdump -i eth0 -n ip6 and icmp6
 
# 特定类型
sudo tcpdump -i eth0 -n 'ip6 and icmp6 and ip6[40] == 1'  # 类型 1(目标不可达)
sudo tcpdump -i eth0 -n 'ip6 and icmp6 and ip6[40] == 2'  # 类型 2(数据包太大)

常见错误场景:

类型 1,代码 1:通信被管理性禁止

# 防火墙阻止流量
18:23:45.123456 IP6 2001:db8::1 > 2001:db8::10: ICMP6, destination unreachable,
  administratively prohibited, length 68

防火墙或访问控制列表阻止连接。

类型 1,代码 4:端口不可达

# 服务未运行
18:23:45.234567 IP6 2001:db8::10 > 2001:db8::1: ICMP6, destination unreachable,
  port unreachable, length 68

目标端口上没有监听。

类型 2:数据包太大

# MTU 问题
18:23:45.345678 IP6 2001:db8:1234::1 > 2001:db8::10: ICMP6, packet too big,
  mtu 1280, length 1240

路径 MTU 小于发送者假设的。发送者应将数据包大小减少到 1280 字节。

使用 Wireshark 进行 NDP#

Wireshark 通过显示过滤器使 NDP 分析更容易:

# 所有 ICMPv6
icmpv6
 
# 仅邻居发现
icmpv6.type >= 133 && icmpv6.type <= 137
 
# 路由器通告
icmpv6.type == 134
 
# 特定地址的邻居请求
icmpv6.type == 135 && icmpv6.nd.ns.target_address == 2001:db8::10

观察路由器通告以查看你的网络提供什么配置:

# 过滤 RA,在数据包详细信息中展开 ICMPv6 层
# 检查:
# - 标志(M、O)
# - 前缀信息
# - DNS 服务器(RDNSS 选项)
# - MTU 选项

常见问题和解决方案#

问题:主机没有全局 IPv6 地址,只有 fe80::*

诊断:

# 检查路由器通告
sudo tcpdump -i eth0 -n 'icmp6 && ip6[40] == 134'

原因:

  • 网络上没有路由器
  • 路由器未发送 RA
  • 防火墙阻止类型 134

解决方案: 在路由器上启用 IPv6,验证 RA 配置,检查防火墙规则。


问题:Ping 有效但大型传输挂起

诊断:

# 使用大 ping 数据包测试
ping6 -s 1400 target.example.com

原因:

  • ICMPv6 类型 2 在某处被阻止
  • 路径中的 MTU 不匹配
  • 防火墙过滤数据包太大

解决方案: 允许类型 2 通过所有防火墙,检查 MTU 配置。


问题:无法访问同一子网上的邻居

诊断:

# 检查邻居缓存
ip -6 neigh show
 
# 观察邻居请求
sudo tcpdump -i eth0 -n 'icmp6 && ip6[40] == 135'

原因:

  • 防火墙阻止类型 135/136
  • 交换机过滤多播
  • 目标上禁用 IPv6

解决方案: 允许 NDP 通过防火墙,验证交换机多播配置。


问题:主机忽略路由器通告

诊断:

# 验证 RA 是否到达
sudo tcpdump -i eth0 -n 'icmp6 && ip6[40] == 134'
 
# 检查 accept_ra 设置(Linux)
sysctl net.ipv6.conf.eth0.accept_ra

原因:

  • accept_ra 禁用(Linux)
  • 主机上启用 IPv6 转发(禁用 RA 处理)
  • RA 具有无效的跳数限制(!= 255)

解决方案: 启用 accept_ra,在终端主机上禁用转发,验证路由器配置。

相关文章#

测试你的网络

使用我们的 Ping 工具测试 ICMPv6 连接,使用我们的 Traceroute 工具查看 ICMPv6 超时消息的实际应用。

常见问题#

我可以像阻止 IPv4 ICMP 一样阻止 ICMPv6 ping 吗?

你可以速率限制或阻止回显请求/应答(类型 128-129)而不破坏连接,但这会使故障排除更加困难。与 IPv4 不同,ICMP 主要是诊断性的,ICMPv6 包含你绝不能阻止的邻居发现和路径 MTU 发现等基本功能。如果你想阻止 ping,但永远不要阻止类型 1、2 或 133-136。

为什么 NDP 使用多播而不是像 ARP 那样的广播?

多播更有效。IPv4 ARP 发送广播,网络上的每个主机都必须处理,即使它们不是目标。IPv6 NDP 使用从目标的 IPv6 地址计算的请求节点多播地址。只有具有匹配地址的主机处理数据包——其他所有人在硬件中过滤它。这减少了 CPU 负载和网络噪音,特别是在大型网络上。

路由器请求和路由器通告之间有什么区别?

路由器请求(类型 133)是主机发送的请求,要求路由器立即宣布自己。路由器通告(类型 134)是路由器发送的响应,包含网络前缀、配置参数和路由器信息。路由器也会在没有请求的情况下定期发送 RA。RS 让主机在启动时快速获得配置,而不是等待下一个定期 RA。

路由器应该多久发送一次路由器通告?

RFC 4861 建议 RA 间隔 200-600 秒(3.3-10 分钟)。大多数实现默认为 200 秒。路由器也会立即响应路由器请求。间隔平衡了新主机的快速配置与最小的网络开销。太频繁浪费带宽;太不频繁延迟自动配置。

为什么某些 ICMPv6 消息需要跳数限制 255?

跳数限制 255 要求防止链路外攻击者发送伪造的邻居发现消息。合法的 NDP 数据包源自本地链路并始终使用跳数限制 255。来自远程攻击者的数据包将通过路由器,递减跳数限制。通过拒绝跳数限制 < 255 的数据包,主机可以防止来自本地网络外部的恶意路由器通告和邻居通告。