ping6.net
基础知识

IPv6邻居发现协议(NDP):设备如何相互发现

深入探讨NDP,这是IPv6中替代ARP的协议。了解路由器请求、邻居请求以及地址解析的工作原理。

ping6.net2024年12月14日12 min read
IPv6NDPNeighbor DiscoveryARP replacementnetworking

NDP不仅仅是替代ARP#

在IPv4中,地址解析使用ARP——一个独立的第2层协议。路由器发现使用ICMP路由器发现或DHCP。重定向消息使用ICMP。重复地址检测根本不存在。

IPv6将所有这些功能整合到邻居发现协议中。NDP处理:

  • 地址解析(ARP的工作)
  • 路由器发现(查找默认网关)
  • 前缀发现(学习用于自动配置的网络前缀)
  • 参数发现(MTU、跳数限制)
  • 地址自动配置(SLAAC)
  • 重复地址检测(防止冲突)
  • 邻居可达性(我的邻居还在吗?)
  • 下一跳确定(我应该使用哪个路由器?)
  • 重定向消息(存在更好的路由)

NDP完全基于ICMPv6运行,使用五种消息类型(133-137)。它不是可选的。阻止NDP,IPv6就会停止工作。

TL;DR - 快速摘要

要点:

  • NDP 将 ARP、路由器发现和自动配置整合到一个运行在 ICMPv6 上的协议中
  • 五种消息类型(133-137)处理从地址解析到路由器通告的所有事情
  • 安全性:所有 NDP 消息要求跃点限制为 255 以防止链路外攻击

跳转至: 路由器通告了解 SLAAC 详细信息,地址解析实战了解实际示例,或故障排除了解常见问题。


五种NDP消息类型#

所有NDP消息都是具有特定类型编号的ICMPv6数据包。理解这五种消息意味着在基本层面上理解IPv6网络的运作方式。

类型名称发送者目的目标地址
133路由器请求(RS)主机请求路由器信息ff02::2(所有路由器)
134路由器通告(RA)路由器宣告存在和配置ff02::1(所有节点)或单播
135邻居请求(NS)任何节点地址解析、可达性请求节点组播或单播
136邻居通告(NA)任何节点对NS的响应单播或ff02::1
137重定向路由器存在更好的下一跳单播(原始发送者)

每条NDP消息都有一个共同的安全要求:跳数限制必须为255。这可以防止链路外攻击,因为路由器会递减跳数限制。来自远程攻击者的数据包到达时跳数限制小于255并被丢弃。

路由器请求(类型133)#

当主机希望路由器立即宣告自己,而不是等待下一次计划的路由器通告时,主机会发送路由器请求。

ICMPv6结构:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |     Code      |          Checksum             |
|     (133)     |      (0)      |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            Reserved                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Options ...
+-+-+-+-+-+-+-+-+-+-+-+-

字段:

  • Type: 133
  • Code: 0
  • Reserved: 32位,必须为零
  • Options: 源链路层地址(可选,如果源地址不是::则包含)

发送时机:

  • 接口启动
  • 主机启动
  • IPv6协议栈初始化
  • 主机需要快速配置

源地址:

  • 链路本地地址(如果已配置)
  • ::(未指定,如果还没有地址)

目标地址: ff02::2(所有路由器组播地址)

跳数限制: 255(必需)

示例数据包:

IPv6 Header:
  Source: fe80::a4b2:c3d4:e5f6:7890
  Destination: ff02::2
  Next Header: 58 (ICMPv6)
  Hop Limit: 255
 
ICMPv6:
  Type: 133 (Router Solicitation)
  Code: 0
  Options:
    Source Link-Layer Address: 00:1a:2b:3c:4d:5e

速率限制:

主机不得过于频繁地发送路由器请求。RFC 4861建议最多每3秒一次。这可以防止在网络不稳定期间淹没路由器。

为什么重要:

没有RS,主机将等待定期的路由器通告(每几分钟发送一次)。RS允许主机在几秒钟而不是几分钟内获得配置,加快网络初始化速度。

路由器通告(类型134)#

路由器发送路由器通告以宣告其存在、通告网络前缀并提供配置参数。这是无状态地址自动配置(SLAAC)的基础。

ICMPv6结构:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |     Code      |          Checksum             |
|     (134)     |      (0)      |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Cur Hop Limit |M|O|H|Prf|Resvd|       Router Lifetime         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Reachable Time                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Retrans Timer                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Options ...
+-+-+-+-+-+-+-+-+-+-+-+-

字段:

Cur Hop Limit(8位): 建议在传出数据包中使用的跳数限制字段值。通常为64。主机将此值复制到其IPv6头部的跳数限制字段中。

标志(8位):

  • M(托管地址配置): 使用DHCPv6获取地址
  • O(其他配置): 使用DHCPv6获取其他配置(DNS、NTP等)
  • H(家乡代理): 路由器是移动IPv6家乡代理(很少使用)
  • Prf(路由器优先级): 2位优先级(00=中等,01=高,11=低)
  • Reserved: 3位

Router Lifetime(16位): 将此路由器用作默认网关的时长(以秒为单位)。0表示「不是默认路由器」。最大9000秒(2.5小时)。

Reachable Time(32位): 在收到可达性确认后,认为邻居可达的时长(以毫秒为单位)。0表示未指定(使用默认值或先前接收的值)。

Retrans Timer(32位): 重传邻居请求之间的时间(以毫秒为单位)。用于地址解析和邻居不可达检测。

常见选项:

Source Link-Layer Address(类型1): 路由器的MAC地址。允许主机跳过对路由器的邻居请求。

MTU(类型5): 链路的推荐MTU。通常为1500。

Prefix Information(类型3): 用于地址自动配置的网络前缀。这是SLAAC的关键选项。

RDNSS(类型25): 递归DNS服务器地址。RFC 8106。

DNSSL(类型31): DNS搜索列表。RFC 8106。

发送时机:

  • 定期(每200-600秒,通常为200)
  • 响应路由器请求
  • 路由器配置更改时

源地址: 路由器的链路本地地址(fe80::/10)

目标地址:

  • ff02::1(所有节点组播)用于定期通告
  • 单播到请求主机,当响应RS时

跳数限制: 255(必需)

示例数据包:

IPv6 Header:
  Source: fe80::1
  Destination: ff02::1
  Next Header: 58 (ICMPv6)
  Hop Limit: 255
 
ICMPv6:
  Type: 134 (Router Advertisement)
  Code: 0
  Cur Hop Limit: 64
  Flags: M=0, O=0 (SLAAC only)
  Router Lifetime: 1800 seconds (30 minutes)
  Reachable Time: 30000 ms
  Retrans Timer: 1000 ms
 
  Options:
    Source Link-Layer Address: 00:11:22:33:44:55
    MTU: 1500
    Prefix Information:
      Prefix: 2001:db8:1234:5678::/64
      Valid Lifetime: 86400 (24 hours)
      Preferred Lifetime: 14400 (4 hours)
      Flags: L=1 (on-link), A=1 (autonomous/SLAAC)
    RDNSS:
      Lifetime: 3600
      Addresses: 2001:4860:4860::8888, 2001:4860:4860::8844

标志组合:

MO行为
00仅SLAAC,无DHCPv6(最常见)
01SLAAC获取地址,DHCPv6获取DNS/NTP等
10DHCPv6获取地址(不常见,很少使用)
11DHCPv6获取所有内容(企业网络)

前缀信息选项:

此选项使SLAAC成为可能。它告诉主机在自动配置地址时使用什么网络前缀。

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |    Length     | Prefix Length |L|A|R|Reserved1|
|      (3)      |      (4)      |               | | | |         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Valid Lifetime                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Preferred Lifetime                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Reserved2                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
|                                                               |
+                            Prefix                             +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

字段:

  • Prefix Length: SLAAC通常为64
  • L标志: 链路本地(前缀在本地链路上)
  • A标志: 自治(用于SLAAC)
  • R标志: 路由器地址(前缀包含路由器的接口ID)
  • Valid Lifetime: 地址有效的时长(通常为86400=24小时)
  • Preferred Lifetime: 用于新连接的地址使用时长(通常为14400=4小时)
  • Prefix: 实际的网络前缀(例如,2001:db8:1234:5678::)

当A=1时,主机通过将前缀与接口标识符组合来生成地址:

Prefix from RA:      2001:db8:1234:5678::/64
Interface ID:        a4b2:c3d4:e5f6:7890  (derived from MAC or random)
Resulting address:   2001:db8:1234:5678:a4b2:c3d4:e5f6:7890

路由器通告至关重要

阻止ICMPv6类型134,SLAAC将停止工作。主机将只有链路本地地址(fe80::/10),没有全球连接。这是最常见的IPv6配置错误之一——防火墙阻止所有ICMPv6,而不了解哪些类型是必需的。

邻居请求(类型135)#

邻居请求是IPv6对ARP的替代。它将IPv6地址解析为链路层(MAC)地址,并验证邻居是否仍然可达。

ICMPv6结构:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |     Code      |          Checksum             |
|     (135)     |      (0)      |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Reserved                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
|                                                               |
+                       Target Address                          +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Options ...
+-+-+-+-+-+-+-+-+-+-+-+-

字段:

  • Type: 135
  • Code: 0
  • Reserved: 32位,必须为零
  • Target Address: 正在查询的IPv6地址
  • Options: 源链路层地址(除非源是::,否则包含)

三个用例:

1. 地址解析(ARP替代)#

主机A需要向同一链路上的主机B发送数据包,但不知道B的MAC地址。

流程:

  1. A检查其邻居缓存——没有B的条目
  2. A向B的请求节点组播地址发送NS
  3. B接收NS并用包含其MAC的NA响应
  4. A缓存映射并发送原始数据包

示例:

# Host A wants to send to 2001:db8::10
 
IPv6 Header:
  Source: 2001:db8::1
  Destination: ff02::1:ff00:10  (solicited-node multicast)
  Hop Limit: 255
 
ICMPv6:
  Type: 135 (Neighbor Solicitation)
  Target Address: 2001:db8::10
  Options:
    Source Link-Layer Address: 00:1a:2b:3c:4d:5e

请求节点组播地址计算:

IPv6使用请求节点组播而不是广播以提高效率。只有具有相似地址的主机才会监听每个请求节点组。

Formula: ff02::1:ff + last 24 bits of target address
 
Examples:
  2001:db8::10              -> ff02::1:ff00:10
  2001:db8::a4b2:c3d4:e5f6:7890 -> ff02::1:ff:f6:7890
  fe80::1                   -> ff02::1:ff00:1

这减少了不必要的处理。在IPv4中,ARP广播到达网络上的每个主机。在IPv6中,只有地址以相同24位结尾的主机才加入组播组并接收NS。

2. 邻居不可达检测(NUD)#

验证先前可达的邻居是否仍然可达。

流程:

  1. 主机最近没有从邻居接收到流量
  2. 主机向邻居发送NS(单播)
  3. 如果收到NA,邻居可达
  4. 如果重试后没有响应,则认为邻居不可达

示例:

IPv6 Header:
  Source: 2001:db8::1
  Destination: 2001:db8::10  (unicast to neighbor)
  Hop Limit: 255
 
ICMPv6:
  Type: 135 (Neighbor Solicitation)
  Target Address: 2001:db8::10
  Options:
    Source Link-Layer Address: 00:1a:2b:3c:4d:5e

NUD在后台自动运行。当主机发送流量时,它们跟踪邻居是否响应。如果邻居沉默,NUD会在宣布其死亡之前验证可达性。

3. 重复地址检测(DAD)#

在使用IPv6地址之前,主机执行DAD以确保没有其他节点使用它。这可以防止SLAAC中的地址冲突。

流程:

  1. 主机生成临时地址(通过SLAAC、DHCPv6或手动配置)
  2. 主机发送源为::且目标=临时地址的NS
  3. 如果有人用NA响应,则地址已被使用(冲突)
  4. 如果超时后没有响应,则地址是唯一的,可以使用

示例:

# Host wants to use 2001:db8::a4b2:c3d4:e5f6:7890
# Sends DAD NS first
 
IPv6 Header:
  Source: ::  (unspecified address—host has no address yet)
  Destination: ff02::1:ff:f6:7890  (solicited-node multicast)
  Hop Limit: 255
 
ICMPv6:
  Type: 135 (Neighbor Solicitation)
  Target Address: 2001:db8::a4b2:c3d4:e5f6:7890
  Options: (none—source is ::)

如果另一个主机正在使用该地址,它会发送邻居通告。请求主机检测到冲突,必须选择不同的地址。

DAD超时: 通常为1秒。主机等待这么长时间以获得响应。如果静默,则假定地址是唯一的。

安全问题: DAD没有身份验证。攻击者可以响应每个DAD请求,阻止主机配置地址。这称为DAD DoS攻击。解决方案包括SEND(安全邻居发现)或RA Guard,但采用率有限。

邻居通告(类型136)#

作为对邻居请求的响应发送,或主动发送以宣告地址/链路层更改。

ICMPv6结构:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |     Code      |          Checksum             |
|     (136)     |      (0)      |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|R|S|O|                     Reserved                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
|                                                               |
+                       Target Address                          +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Options ...
+-+-+-+-+-+-+-+-+-+-+-+-

字段:

标志:

  • R(路由器): 发送者是路由器
  • S(请求的): 通告是对NS的响应
  • O(覆盖): 覆盖现有缓存条目

Target Address: 发送此通告的地址

Options: 目标链路层地址(MAC地址)

发送时机:

  • 对邻居请求的响应(S=1)
  • 地址更改的主动通告(S=0)
  • 链路层地址更改后更新邻居的免费NA

目标地址:

  • 单播到请求节点(响应NS时)
  • ff02::1(所有节点)用于主动通告

跳数限制: 255(必需)

对NS的响应示例:

IPv6 Header:
  Source: 2001:db8::10
  Destination: 2001:db8::1  (unicast to requester)
  Hop Limit: 255
 
ICMPv6:
  Type: 136 (Neighbor Advertisement)
  Flags: R=0, S=1 (solicited response), O=1 (override)
  Target Address: 2001:db8::10
  Options:
    Target Link-Layer Address: 00:aa:bb:cc:dd:ee

标志含义:

S=1: 这是对您的NS的响应。更新您的缓存。

S=0: 主动通告。我更改了某些内容。

O=1: 用此信息覆盖您现有的缓存条目。

O=0: 仅在不存在条目时更新缓存。

主动NA示例:

主机更改其MAC地址(罕见,但在虚拟机或接口重新配置时会发生)。它发送主动NA以更新邻居:

IPv6 Header:
  Source: 2001:db8::10
  Destination: ff02::1  (all-nodes)
  Hop Limit: 255
 
ICMPv6:
  Type: 136 (Neighbor Advertisement)
  Flags: R=0, S=0 (unsolicited), O=1 (override)
  Target Address: 2001:db8::10
  Options:
    Target Link-Layer Address: 00:ff:ee:dd:cc:bb  (new MAC)

所有邻居接收此消息并更新其缓存。

重定向(类型137)#

路由器发送重定向消息以通知主机对于特定目标存在更好的第一跳路由器。

ICMPv6结构:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |     Code      |          Checksum             |
|     (137)     |      (0)      |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Reserved                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
|                                                               |
+                       Target Address                          +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
|                                                               |
+                     Destination Address                       +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Options ...
+-+-+-+-+-+-+-+-+-+-+-+-

字段:

  • Type: 137
  • Code: 0
  • Target Address: 更好的下一跳路由器(或链路本地时的目标)
  • Destination Address: 应使用更好路由的目标
  • Options: 目标链路层地址、重定向头部(原始数据包的部分)

发送时机:

路由器R1从主机A接收到目标为主机B的数据包。R1知道路由器R2(与A在同一链路上)是到B的更好下一跳。R1将数据包转发到R2,并向A发送重定向,告诉它将来直接使用R2。

示例场景:

Network topology:
  Host A: 2001:db8:1::10
  Router R1: 2001:db8:1::1 (default gateway for A)
  Router R2: 2001:db8:1::2 (better route to 2001:db8:2::/64)
  Destination B: 2001:db8:2::20
 
Flow:
1. A sends packet to B via R1 (default gateway)
2. R1 sees that R2 is a better next-hop for 2001:db8:2::/64
3. R1 forwards packet to R2
4. R1 sends Redirect to A:
   Target Address: 2001:db8:1::2 (R2)
   Destination Address: 2001:db8:2::20 (B)
5. A updates routing table: use R2 for 2001:db8:2::/64
6. A's future packets to B go directly to R2

从R1到A的重定向消息:

IPv6 Header:
  Source: fe80::1  (R1's link-local)
  Destination: 2001:db8:1::10  (Host A)
  Hop Limit: 255
 
ICMPv6:
  Type: 137 (Redirect)
  Target Address: fe80::2  (R2—better next-hop)
  Destination Address: 2001:db8:2::20  (B—destination to reach via R2)
  Options:
    Target Link-Layer Address: 00:11:22:33:44:55 (R2's MAC)
    Redirected Header: (original packet header that triggered redirect)

安全考虑:

重定向消息可能被滥用。本地链路上的攻击者可以发送恶意重定向以:

  • 通过攻击者的机器路由流量(中间人攻击)
  • 通过重定向到不存在的路由器来黑洞流量
  • 创建路由循环

保护措施:

  • SEND(安全邻居发现): 验证重定向消息
  • 许多主机忽略来自非默认网关路由器的重定向
  • 一些注重安全的网络完全禁用重定向处理
  • 重定向必须来自当前的第一跳路由器(由主机检查)

您应该允许重定向吗?

家庭/小型网络: 安全。当存在多个路由器时优化路由。

企业网络: 通常禁用。受控路由表和潜在的安全风险超过了好处。

数据中心: 通常禁用。静态路由或动态协议(BGP、OSPF)管理路由。

实际地址解析#

让我们跟踪主机A在同一链路上向主机B发送数据包的完整过程。

初始状态:

  • 主机A:2001:db8::1(MAC:aa:aa:aa:aa:aa:aa)
  • 主机B:2001:db8::10(MAC:bb:bb:bb:bb:bb:bb)
  • A的邻居缓存为空

步骤1:应用程序启动连接

# On Host A
ping6 2001:db8::10

步骤2:检查邻居缓存

# A checks neighbor cache
ip -6 neigh show 2001:db8::10
# (empty—no entry)

步骤3:发送邻居请求

A向请求节点组播地址发送NS:

IPv6 Header:
  Source: 2001:db8::1
  Destination: ff02::1:ff00:10  (solicited-node for 2001:db8::10)
  Hop Limit: 255
 
Ethernet Header:
  Source MAC: aa:aa:aa:aa:aa:aa
  Dest MAC: 33:33:ff:00:00:10  (multicast MAC for ff02::1:ff00:10)
 
ICMPv6:
  Type: 135 (Neighbor Solicitation)
  Target Address: 2001:db8::10
  Options:
    Source Link-Layer Address: aa:aa:aa:aa:aa:aa

步骤4:B接收NS

主机B正在监听ff02::1:ff00:10(因为其地址以:10结尾)。它接收NS并处理它。

步骤5:B发送邻居通告

IPv6 Header:
  Source: 2001:db8::10
  Destination: 2001:db8::1  (unicast to A)
  Hop Limit: 255
 
Ethernet Header:
  Source MAC: bb:bb:bb:bb:bb:bb
  Dest MAC: aa:aa:aa:aa:aa:aa
 
ICMPv6:
  Type: 136 (Neighbor Advertisement)
  Flags: R=0, S=1 (solicited), O=1 (override)
  Target Address: 2001:db8::10
  Options:
    Target Link-Layer Address: bb:bb:bb:bb:bb:bb

步骤6:A更新邻居缓存

# A's neighbor cache now has entry
ip -6 neigh show 2001:db8::10
# 2001:db8::10 dev eth0 lladdr bb:bb:bb:bb:bb:bb REACHABLE

步骤7:A发送原始数据包

现在A知道了B的MAC,它发送ping数据包:

IPv6 Header:
  Source: 2001:db8::1
  Destination: 2001:db8::10
 
Ethernet Header:
  Source MAC: aa:aa:aa:aa:aa:aa
  Dest MAC: bb:bb:bb:bb:bb:bb  (learned from NA)
 
ICMPv6:
  Type: 128 (Echo Request)

总时间:毫秒。该过程对应用程序是不可见的。

邻居缓存状态#

当NUD跟踪可达性时,邻居缓存条目会经历几个状态。

状态含义下一步操作
INCOMPLETE已发送NS,等待NA超时或接收NA
REACHABLE最近收到NA,确认可达发送流量或超时
STALE条目旧,最近未确认发送流量(触发探测)
DELAY流量已发送到陈旧条目,等待中超时触发探测
PROBE发送NS以验证可达性接收NA或超时
FAILED对探测无响应删除条目

状态转换:

INCOMPLETE --NA received--> REACHABLE
REACHABLE --timeout--> STALE
STALE --traffic sent--> DELAY
DELAY --timeout--> PROBE
PROBE --NA received--> REACHABLE
PROBE --timeout--> FAILED

检查邻居缓存(Linux):

ip -6 neigh show
 
# Example output:
# 2001:db8::1 dev eth0 lladdr aa:aa:aa:aa:aa:aa REACHABLE
# 2001:db8::10 dev eth0 lladdr bb:bb:bb:bb:bb:bb STALE
# fe80::1 dev eth0 lladdr cc:cc:cc:cc:cc:cc DELAY

计时器:

  • REACHABLE超时: 30秒(可配置,来自RA Reachable Time)
  • DELAY超时: 5秒,然后转换到PROBE
  • 重传间隔: 1秒(可配置,来自RA Retrans Timer)
  • MAX_MULTICAST_SOLICIT: 在声明FAILED之前尝试3次

这些确保快速检测死邻居,同时最小化不必要的NUD流量。

SLAAC:地址自动配置#

将路由器通告与重复地址检测结合,创建无状态地址自动配置。主机在没有DHCP服务器的情况下自我配置。

完整的SLAAC过程:

步骤1:接口启动

主机在接口eth0上启用IPv6。

步骤2:生成链路本地地址

fe80:: + interface identifier
fe80::a4b2:c3d4:e5f6:7890  (if using MAC-derived IID)
fe80::1234:5678:9abc:def0  (if using random IID)

步骤3:在链路本地上执行DAD

发送源为::的NS以验证fe80::地址是唯一的。

步骤4:发送路由器请求

IPv6 Header:
  Source: fe80::a4b2:c3d4:e5f6:7890
  Destination: ff02::2
  Hop Limit: 255
 
ICMPv6:
  Type: 133 (Router Solicitation)

步骤5:接收路由器通告

ICMPv6:
  Type: 134 (Router Advertisement)
  Flags: M=0, O=0 (SLAAC)
  Prefix Information:
    Prefix: 2001:db8:1234:5678::/64
    Flags: A=1 (autonomous)
    Valid Lifetime: 86400
    Preferred Lifetime: 14400

步骤6:生成全局地址

Prefix: 2001:db8:1234:5678::/64
IID: a4b2:c3d4:e5f6:7890
Result: 2001:db8:1234:5678:a4b2:c3d4:e5f6:7890

步骤7:在全局地址上执行DAD

发送NS以验证2001:db8:1234:5678:a4b2:c3d4:e5f6:7890是唯一的。

步骤8:配置地址

ip -6 addr show eth0
 
# Output:
# inet6 2001:db8:1234:5678:a4b2:c3d4:e5f6:7890/64 scope global dynamic
# inet6 fe80::a4b2:c3d4:e5f6:7890/64 scope link

步骤9:设置默认路由

使用路由器的链路本地地址作为默认网关:

ip -6 route show
 
# Output:
# default via fe80::1 dev eth0 metric 1024
# 2001:db8:1234:5678::/64 dev eth0 proto kernel metric 256

总时间: 几秒钟。

隐私扩展(RFC 4941):

MAC派生的接口ID可在网络之间跟踪。隐私扩展生成定期更改的随机IID。

Original: 2001:db8:1234:5678:a4b2:c3d4:e5f6:7890  (static)
Privacy:  2001:db8:1234:5678:1a2b:3c4d:5e6f:7a8b  (changes daily)

大多数现代操作系统默认为传出连接启用隐私扩展,同时保留稳定地址用于传入连接。

安全考虑#

NDP存在漏洞,因为它是在假设可信本地网络的基础上设计的。同一链路上的攻击者可以:

流氓路由器通告#

攻击者发送带有以下内容的虚假RA:

  • 错误的前缀(黑洞流量或通过攻击者路由)
  • 短生命周期(强制频繁的RA处理)
  • M=1标志(强制DHCPv6到攻击者的服务器)

影响: 完全控制主机配置。

缓解措施:

  • RA Guard: 交换机功能,阻止来自非路由器端口的RA
  • SEND(安全邻居发现): 加密身份验证(复杂,很少部署)
  • 路由器优先级: 在合法路由器上使用高优先级值

邻居通告欺骗#

攻击者发送声称是另一个主机的虚假NA。

影响: 中间人攻击、流量拦截。

缓解措施:

  • SEND: 验证NA
  • 端口安全: 交换机强制执行MAC地址绑定
  • 监控: 检测重复的MAC地址

DAD DoS#

攻击者响应每个DAD请求,阻止地址配置。

影响: 主机无法获取地址,无连接。

缓解措施:

  • SEND: 验证DAD消息
  • 速率限制: 限制来自单一源的NA
  • 静态地址: 完全绕过SLAAC(不可扩展)

重定向攻击#

攻击者发送通过攻击者路由流量的虚假重定向。

影响: 中间人攻击、流量拦截。

缓解措施:

  • 禁用重定向: 许多操作系统允许禁用重定向处理
  • SEND: 验证重定向
  • 验证源: 主机应验证重定向来自当前的第一跳路由器

现实: 由于复杂性,大多数网络不使用SEND。交换机上的RA Guard是企业网络的实用防御措施。

排查NDP问题#

主机没有全局地址#

症状:

  • 仅fe80::地址
  • 可以ping链路本地,不能ping全局
  • 无默认路由

诊断:

# Check for Router Advertisements
sudo tcpdump -i eth0 -vv 'icmp6 && ip6[40] == 134'
 
# Check RA acceptance
sysctl net.ipv6.conf.eth0.accept_ra
# Should be 1 or 2 for hosts
 
# Manually send RS
rdisc6 eth0

常见原因:

  • 链路上没有路由器
  • 防火墙阻止类型134
  • accept_ra已禁用
  • IPv6转发已启用(禁用RA处理)

修复: 启用路由器,允许ICMPv6类型134,设置accept_ra=1。

邻居缓存始终FAILED#

症状:

  • 无法到达邻居
  • 已发送NS但未收到NA
  • 邻居缓存显示FAILED

诊断:

# Watch NS/NA exchange
sudo tcpdump -i eth0 -vv 'icmp6 && (ip6[40] == 135 || ip6[40] == 136)'
 
# Check neighbor cache
ip -6 neigh show
 
# Manual NS (Linux ndisc6 tool)
ndisc6 2001:db8::10 eth0

常见原因:

  • 防火墙阻止类型135/136
  • 目标主机IPv6已禁用
  • 交换机过滤组播
  • 错误的链路(目标在不同的子网上)

修复: 通过防火墙允许NDP,验证目标上已启用IPv6。

DAD失败#

症状:

  • 地址显示「dadfailed」
  • 接口无法配置地址
  • 内核日志显示DAD冲突

诊断:

# Check address status
ip -6 addr show eth0
 
# Example output with DAD failure:
# inet6 2001:db8::10/64 scope global tentative dadfailed
 
# Check for duplicate
ping6 -c 1 2001:db8::10

常见原因:

  • 另一个主机使用相同地址
  • 攻击者欺骗NA(DAD DoS)
  • 具有克隆MAC地址的虚拟机

修复: 更改地址,调查重复主机,检查VM配置。

使用工具的实际示例#

使用tcpdump监控NDP#

# All NDP traffic
sudo tcpdump -i eth0 -vv 'icmp6 && ip6[40] >= 133 && ip6[40] <= 137'
 
# Router Advertisements only
sudo tcpdump -i eth0 -vv 'icmp6 && ip6[40] == 134'
 
# Address resolution (NS/NA)
sudo tcpdump -i eth0 -vv 'icmp6 && (ip6[40] == 135 || ip6[40] == 136)'
 
# Filter by target address
sudo tcpdump -i eth0 -vv 'icmp6 && ip6[40] == 135' | grep '2001:db8::10'

手动邻居缓存管理#

# Show all neighbors
ip -6 neigh show
 
# Show specific neighbor
ip -6 neigh show 2001:db8::10
 
# Delete entry (force re-resolution)
sudo ip -6 neigh del 2001:db8::10 dev eth0
 
# Add static entry
sudo ip -6 neigh add 2001:db8::10 lladdr aa:bb:cc:dd:ee:ff dev eth0

测试SLAAC#

# Flush addresses
sudo ip -6 addr flush dev eth0
 
# Re-enable IPv6
sudo sysctl -w net.ipv6.conf.eth0.disable_ipv6=0
 
# Watch autoconfiguration
ip -6 addr show eth0
ip -6 route show

相关文章#

  • ICMPv6解析 - NDP基于ICMPv6运行。了解底层协议。
  • IPv6地址类型 - 了解NDP使用的链路本地、请求节点组播和其他地址类型。
  • IPv6安全 - 在允许基本功能的同时保护NDP免受攻击。

测试实际的NDP

使用我们的Ping工具触发地址解析,使用Traceroute查看主机如何发现下一跳路由器。

常见问题#

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

组播更高效。ARP广播到达网络上的每个主机——即使它们不是目标,所有主机都必须处理帧。NDP使用从目标IPv6地址计算的请求节点组播地址。只有具有匹配地址的主机才加入该组播组并接收数据包。主机在硬件(NIC)中过滤不匹配的组播,减少CPU负载。在拥有数千台主机的大型网络上,这会产生显著的性能差异。

我可以像禁用ARP一样禁用NDP吗?

不可以。NDP是IPv6操作的必需项。它处理地址解析、路由器发现和自动配置。禁用NDP,IPv6将完全停止工作。与IPv4不同,您可以使用静态ARP条目,IPv6需要NDP用于路由器通告(提供默认网关和前缀)。即使使用静态邻居,您也无法学习路由器或自动配置地址。

重复地址检测如何防止冲突?

在使用任何地址之前,主机发送源地址为::(未指定)且目标设置为它们想要使用的地址的邻居请求。如果另一个主机已经在使用该地址,它会用邻居通告响应。请求主机看到响应,检测到冲突,必须选择不同的地址。如果在约1秒内没有响应到达,则假定地址是唯一的。DAD对链路本地和全局地址都运行。

什么是请求节点组播地址?

请求节点组播地址使用公式ff02::1:ff:XX:XXXX从IPv6地址的最后24位派生。每个IPv6地址自动加入其相应的请求节点组播组。在执行地址解析时,发送者将NS发送到请求节点地址而不是广播。只有地址以相同24位结尾的主机才接收它。与基于广播的ARP相比,这减少了不必要的处理。

为什么NDP数据包必须具有跳数限制255?

安全。合法的NDP始终源自本地链路并将跳数限制设置为255。路由器在转发时递减跳数限制,因此来自远程攻击者的数据包到达时跳数限制小于255。通过拒绝跳数限制!=255的NDP数据包,主机可以防止链路外攻击者发送伪造的路由器通告、邻居通告或重定向。这个简单的检查可以防止许多远程攻击,而无需复杂的身份验证。

我应该部署SEND(安全邻居发现)吗?

可能不需要。SEND使用公钥加密为NDP提供加密身份验证,防止欺骗攻击。理论上很好。实际上它很复杂,需要PKI基础设施,操作系统支持不佳,并且会增加地址解析等时间敏感操作的延迟。大多数网络使用更简单的缓解措施:交换机上的RA Guard(阻止来自非路由器端口的RA)、监控流氓RA以及物理/链路层安全。SEND适用于愿意投资基础设施和复杂性的高安全性环境。