ICMPv6 解析:使 IPv6 工作的协议
了解 ICMPv6,IPv6 网络的基本协议。了解邻居发现、路由器通告以及为什么阻止 ICMPv6 会破坏你的网络。
ICMPv6 不是可选的#
在 IPv4 中,ICMP 处理诊断。你可以阻止它,大多数事情仍然可以工作。IPv6 不是这样工作的。
ICMPv6 是 IPv6 的神经系统。它处理错误报告、诊断、邻居发现、路由器发现和地址解析。阻止错误的 ICMPv6 消息类型,你将破坏基本连接,阻止主机找到它们的默认网关,并导致需要数小时调试的神秘连接挂起。
IPv6 规范不将 ICMPv6 视为附加组件。它是一个强制性的、不可分割的组件。理解 ICMPv6 就是理解 IPv6 实际上是如何工作的。
ICMPv6 与 ICMP(IPv4)#
ICMPv6 从 IPv4 的 ICMP 演变而来,但承担了更多的责任。
| 功能 | ICMPv4 | ICMPv6 | 影响 |
|---|---|---|---|
| 协议号 | 1 | 58 | 不同的 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:信息消息
这种划分使过滤更容易。错误消息报告数据包传递问题。信息消息处理查询、响应和邻居/路由器发现。
常见消息类型#
| 类型 | 代码 | 名称 | 类别 | 目的 |
|---|---|---|---|---|
| 1 | 0-6 | 目标不可达 | 错误 | 端口关闭,路由不可用等 |
| 2 | 0 | 数据包太大 | 错误 | 路径上超过 MTU |
| 3 | 0-1 | 超时 | 错误 | 跳数限制达到或分片超时 |
| 4 | 0-2 | 参数问题 | 错误 | 格式错误的数据包头部 |
| 128 | 0 | 回显请求 | 信息 | Ping 请求 |
| 129 | 0 | 回显应答 | 信息 | Ping 响应 |
| 133 | 0 | 路由器请求 | 信息 | 请求路由器信息 |
| 134 | 0 | 路由器通告 | 信息 | 路由器宣布存在和配置 |
| 135 | 0 | 邻居请求 | 信息 | 地址解析和可达性 |
| 136 | 0 | 邻居通告 | 信息 | 对请求的响应 |
| 137 | 0 | 重定向 | 信息 | 存在更好的第一跳路由器 |
类型号很重要
与 IPv4 不同,IPv4 中 ICMP 类型使用分散的数字,ICMPv6 的有组织类型空间使防火墙规则更清晰。类型 133-137 处理邻居发现协议,使得作为一个组允许或阻止 NDP 变得容易。
邻居发现协议(NDP)#
邻居发现协议取代了 IPv4 的 ARP,并添加了 IPv4 通过多个协议处理的功能。NDP 完全通过 ICMPv6 运行,处理五个关键功能:
- 地址解析 - 将 IPv6 地址映射到 MAC 地址(取代 ARP)
- 路由器发现 - 在没有配置的情况下查找本地路由器
- 前缀发现 - 了解用于自动配置的网络前缀
- 参数发现 - 获取 MTU、跳数限制和其他参数
- 下一跳确定 - 识别目标的最佳路由器
五种 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 地址时:
- 检查邻居缓存 - MAC 地址是否已知?
- 发送邻居请求 - 如果没有,向请求节点多播地址发送 NS
- 接收邻居通告 - 目标响应 MAC 地址
- 更新缓存 - 存储映射以供将来使用
- 发送数据包 - 传递原始数据包
请求节点多播地址从目标的 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 时:
- 采用前缀 - 例如,
2001:db8:1234::/64 - 生成接口标识符 - 从 MAC 或随机派生的 64 位
- 组合它们 -
2001:db8:1234::a4b2:c3d4:e5f6:7890 - 运行重复地址检测 - 确保没有其他人使用它
- 配置地址 - 添加到接口
- 设置默认路由 - 使用路由器作为下一跳
这在没有用户干预或 DHCP 服务器的情况下自动发生。
控制配置的标志#
路由器通告包含告诉主机如何配置自己的标志:
- M(托管):使用 DHCPv6 获取地址(不是 SLAAC)
- O(其他):使用 DHCPv6 获取其他配置(DNS、NTP 等)
常见组合:
| M | O | 行为 |
|---|---|---|
| 0 | 0 | 仅 SLAAC,无 DHCPv6 |
| 0 | 1 | SLAAC 用于地址,DHCPv6 用于 DNS 等 |
| 1 | 0 | DHCPv6 用于地址(不常见) |
| 1 | 1 | DHCPv6 用于所有内容 |
大多数网络使用 M=0,O=0(纯 SLAAC)或 M=0,O=1(SLAAC + DHCPv6 用于 DNS)。
路径 MTU 发现#
IPv6 路由器不分片数据包。源必须发送足够小的数据包以适应整个路径。这就是 ICMPv6 类型 2 变得至关重要的地方。
PMTUD 的工作原理#
- 主机发送数据包 - 使用接口 MTU(通常为 1500 字节)
- 路由器遇到较小的 MTU - 在 IPv6 中无法分片
- 路由器丢弃数据包 - 发送 ICMPv6 数据包太大消息
- 消息包含 MTU - 告诉发送者允许的最大大小
- 主机减小数据包大小 - 使用较小的 MTU 重传
- 连接继续 - 使用适当的数据包大小
数据包太大消息格式:
类型: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,在终端主机上禁用转发,验证路由器配置。
相关文章#
- IPv6 基础知识 - 了解 IPv6 地址的基础知识以及 IPv6 存在的原因
- IPv6 防火墙配置 - 配置你的防火墙以允许必不可少的 ICMPv6,同时保持安全性
- 如何启用 IPv6 - 使用正确的 NDP 配置在你的设备和网络上启用 IPv6
测试你的网络
使用我们的 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 的数据包,主机可以防止来自本地网络外部的恶意路由器通告和邻居通告。