本页目录

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

状态转换:

内核 ARP 状态机:NUD 邻居表项的生命周期 NUD_NONE 刚创建·无状态 NUD_INCOMPLETE 已发 ARP request·等待 reply NUD_REACHABLE 最近确认可达(正常) NUD_FAILED 删除 entry(需重新创建) NUD_STALE 过期未清理 NUD_DELAY 等 TCP 确认(5s) NUD_PROBE 发 ARP probe(最多3次,1s间隔) ARP request sent timeout (1s) ARP reply 收到 30s 到期 数据包发往此 entry 5s 内 TCP 确认 → 不需要发 ARP! 5s 到期未确认 ARP reply 3 次失败 关键:NUD_STALE 不会主动发 ARP——内核只在有数据包要发往该 neighbor 时, 才通过 DELAY → PROBE 链确认连通性,避免网络空闲时产生无意义的 ARP 流量。

关键: base_reachable_time (默认 30s) 后的 NUD_STALE 状态不主动发 ARP。内核只在有实际数据包要发往该 neighbor 时,才通过 DELAY → PROBE 链确认连通性。这避免了在网络空闲时产生大量无意义的 ARP 流量。

/proc 调优

cat /proc/sys/net/ipv4/neigh/default/gc_stale_time    # STALE entry 存活时间 (默认 60s)
cat /proc/sys/net/ipv4/neigh/default/base_reachable_time # REACHABLE 状态超时 (默认 30s)
cat /proc/sys/net/ipv4/neigh/default/delay_first_probe_time # DELAY 状态持续时间 (默认 5s)
cat /proc/sys/net/ipv4/neigh/default/mcast_solicit     # 多播 probe 次数 (默认 3)

Gratuitous ARP

即 sender IP = target IP 且 sender MAC != 0 的 ARP Request。Ethernet 广播 + target IP 是自己的 IP, 这个"自问自答"的特殊形式用途:

  1. 切换 IP (VRRP/HSRP/CARP): 虚拟 IP 从一个物理 host 接管到另一个 → 发 gratuitous ARP → 更新交换机和邻居的 ARP cache → 新 owner 立刻收到流量
  2. 重复 IP 检测: 刚配置 IP → 发 gratuitous ARP → 如果有人回应 → IP 冲突
  3. 刷新 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