本页目录
ARP 与 NDP
IP 地址到 MAC 地址的翻译——IPv4 用 ARP(广播请求+单播应答+缓存老化),IPv6 用 NDP(多播邻居发现+SLAAC 自动配置)。二层和三层之间的这道桥梁,是本地网络通信的第一步。
概述
没有 ARP,IP 包无法在以太网上传输——ARP 是将 IP 地址翻译为 MAC 地址的胶水协议。ARP 请求("谁是 192.168.1.1?")以广播帧发送,目标主机回复其 MAC。IPv6 用 NDP 替代了 ARP,基于 ICMPv6 实现,除了地址解析还提供路由器发现(RS/RA)和地址自动配置(SLAAC)。ARP 设计于 1982 年,其"信任局域网所有人"的安全模型在今天看来十分脆弱,但低开销和简单性使其在封闭网络中仍然高效。
ARP 协议 (RFC 826)
帧格式
ARP 直接封装在 Ethernet frame 中 (EtherType=0x0806), 无 IP 层:
Hardware Type: 2 bytes — 1 = Ethernet
Protocol Type: 2 bytes — 0x0800 = IPv4
HW Addr Len: 1 byte — 6 (MAC)
Protocol Addr Len: 1 byte — 4 (IPv4)
Operation: 2 bytes — 1=Request, 2=Reply, (还有 RARP 3/4, 极少用)
Sender HW Addr: 6 bytes (MAC)
Sender Protocol Addr: 4 bytes (IPv4)
Target HW Addr: 6 bytes (0 填充 for Request)
Target Protocol Addr: 4 bytes (IPv4)
Request 发到广播 MAC (ff:ff:ff:ff:ff:ff), Reply 是单播。ARP 消息总长 28 bytes + 18 bytes Ethernet header = 46 bytes — 刚好是 Ethernet 最小 payload (不需 padding)。
内核 ARP 状态机
每个 neighbor entry 有以下状态 (include/net/neighbour.h):
NUD_NONE: 刚创建, 无状态
NUD_INCOMPLETE: ARP request 已发送, 等待 reply — timeout 后重试
NUD_REACHABLE: 最近确认可达 (正常状态)
NUD_STALE: 过期但未清理 — 下一次使用时转为 DELAY
NUD_DELAY: 等待 TCP 确认在此期间到达 (5s)
NUD_PROBE: 发送 ARP probe (最多 3 次, 1s 间隔)
NUD_FAILED: 3 次 probe 失败 — 删除 entry
状态转换:
关键: base_reachable_time (默认 30s) 后的 NUD_STALE 状态不主动发 ARP。内核只在有实际数据包要发往该 neighbor 时,才通过 DELAY → PROBE 链确认连通性。这避免了在网络空闲时产生大量无意义的 ARP 流量。
/proc 调优
Gratuitous ARP
即 sender IP = target IP 且 sender MAC != 0 的 ARP Request。Ethernet 广播 + target IP 是自己的 IP, 这个"自问自答"的特殊形式用途:
- 切换 IP (VRRP/HSRP/CARP): 虚拟 IP 从一个物理 host 接管到另一个 → 发 gratuitous ARP → 更新交换机和邻居的 ARP cache → 新 owner 立刻收到流量
- 重复 IP 检测: 刚配置 IP → 发 gratuitous ARP → 如果有人回应 → IP 冲突
- 刷新 ARP cache: 主动通知"我的 MAC 没变但你们该刷新 cache 了" (很少用)
Proxy ARP
路由器代替不在同一个物理网段的目标应答 ARP Request:
Host A: (ping 10.0.1.100, 但它不在自己的子网)
→ ARP "Who has 10.0.1.100?"
Router: (看到 Host A 的 ARP, 自己有路由到 10.0.1.0/24)
→ ARP Reply (用自己的 MAC): "10.0.1.100 is at <router_MAC>"
Host A: → 把 IP 包发到 router 的 MAC
Router: → 按路由表转发
不推荐 — 掩盖了配置错误 (Host A 应该有路由指向 router 而不是认为 10.0.1.100 本地可达)。Linux 默认 proxy_arp=0。
NDP (IPv6 邻居发现, RFC 4861)
NDP 用 ICMPv6 承载 5 种消息,替代了 ARP 的所有功能并加入地址自动配置:
ICMPv6 消息格式
所有 NDP 消息共享相同的 ICMPv6 header 格式 (在 IPv6 Next Header=58 之后):
Type (1 byte): 133=RS, 134=RA, 135=NS, 136=NA, 137=Redirect
Code (1 byte): 0 (除了 Redirect)
Checksum: ICMPv6 pseudo-header checksum (含 IPv6 src/dst)
Reserved: (4 bytes, 零)
Message Body: type-specific options
Options (TLV 格式):
[Type 1] [Length 1] [Value...]
Length = 8-byte units (含 Type+Length 本身)
→ Type=1 Len=1 → 8 bytes total, Value=6 bytes
关键 Option:
Type 1: Source/Target Link-Layer Address (MAC)
Type 2: Prefix Information (RA 中含: prefix + prefix_len + flags)
Type 3: Redirected Header
Type 5: MTU (RA)
Neighbor Solicitation (NS, type 135)
目标: "Who has this IPv6? Tell me your MAC"
发送到目标 IPv6 的 solicited-node multicast address:
ff02::1:ff<low 24 bits of target IPv6 地址>
源地址: 发送接口的 link-local (或单播, 如果已有)
Option: Source Link-Layer Address (自己的 MAC)
Neighbor Advertisement (NA, type 136)
目标: "I have this IPv6, my MAC is X"
发送到 NS 的源地址 (单播) 或所有节点 (组播, 如果是 DAD 冲突)
Flags:
R (Router): 发送者是路由器 (根据 RA 的 Router Lifetime)
S (Solicited): 这是响应 NS 的 (设为 1)
O (Override): 覆盖现存的 NCE (如果为 0, 收到者可保留旧条目)
Option: Target Link-Layer Address
SLAAC (无状态地址自动配置, RFC 4862)
主机:
1. 生成 link-local 地址: fe80::/64 前缀 + IID
2. 发 RS (Router Solicitation, type 133)
源: :: (未指定地址) or link-local
目标: ff02::2 (all-routers multicast)
路由器:
3. 响应 RA (Router Advertisement, type 134)
源: 路由器的 link-local
目标: 如果响应 RS → 发到 RS 源地址 (单播)
如果是周期性 → ff02::1 (all-nodes multicast)
RA 含 Prefix Information (Option 2):
Prefix: (如 2001:db8:abcd::)
Prefix Len: (如 64)
L (on-link): 此 prefix 在同一个 link 上 → 可以直接通信
A (autonomous): 用此 prefix 做 SLAAC → 生成 global address
Valid Lifetime: 地址的有效周期 (通常 2592000s = 30 天)
Preferred Lifetime: 地址的"优先"周期 (达到后 deprecated 但不移除)
RA 含 Router Lifetime:
0: 此路由器不可用做默认网关
>0: 默认路由的有效秒数 (最大 65535 ≈ 18h)
SLAAC 地址生成
EUI-64 (deprecated, 暴露 MAC):
MAC: aa:bb:cc:dd:ee:ff
→ IID: aa:bb:cc:ff:fe:dd:ee:ff
(插入 fffe, 翻转 U/L bit)
→ 完整地址: 2001:db8:abcd:0:a8bb:ccff:fedd:eeff/64
隐私扩展 (RFC 8981, Linux 默认):
随机 IID + 短 lifetime → 周期性生成新地址
→ 旧的 deprecated 但保留 (已有连接不断)
→ 新的 preferred → 新连接用新地址
→ 防止跨网络追踪
DAD (重复地址检测)
任何新生成的 IPv6 地址 (link-local or global) 在能用之前必须 DAD:
1. 发 NS: target = 要验证的地址, 源 = :: (未指定地址)
2. 如果有人回 NA (DAD conflict) → 地址不能用了
3. 最多发 DupAddrDetectTransmits 次 (默认 1)
4. 收到 0 个 NA → 地址可用 → 从 tentative 转为 preferred
注意: DAD 必须在地址配置时、网络连接后立即执行
→ 如果 2 个设备同时生成相同地址 (碰撞概率极低) → 第二个检测到 → 禁用
ARP 攻击面与防御
ARP spoofing (Man-in-the-Middle):
攻击者: 发伪造 ARP Reply → 把目标的 IP 映射到攻击者的 MAC
→ 流量被重定向到攻击者
防御:
1. DAI (Dynamic ARP Inspection): 交换机检查 ARP 合法性
(DHCP snooping DB + 静态绑定) — 企业网络
2. 加密隧道: 即使 ARP 被 poison → 流量加密 → 中间人看不到
这实际上是唯一完全的解决方案 — 如果你的流量
通过 WireGuard/Tailscale 加密, ARP 欺骗无效
3. 静态 ARP: `ip neigh add 192.168.1.1 lladdr xx:xx:xx dev eth0 nud permanent`
最安全但不可大规模管理
参考
- RFC: 826 (ARP), 4861 (NDP), 4862 (SLAAC), 8981 (Privacy Extensions)
- 源码:
net/ipv4/arp.c,net/core/neighbour.c(ARP state machine),net/ipv6/ndp.c - 工具:
ip neigh,arp -a,arping -I eth0 192.168.1.1,tcpdump arp/tcpdump icmp6
关键词: ARP, NUD state machine, gratuitous ARP, proxy ARP, NDP, NS/NA, RS/RA, SLAAC, EUI-64, DAD, solicited-node multicast