Linux内核通过NETLINK_ROUTE监听RTM_NEWNEIGH/RTM_DELNEIGH消息实时捕获ARP表变更,需绑定RTNLGRP_NEIGHBORS组播组、解析ndmsg结构并校验NUD状态与接口索引,结合可信基线比对网关MAC实现异常告警与ip neigh replace安全恢复。

ARP表项变更时如何可靠捕获事件
Linux内核不提供标准的“ARP表变更通知”接口,arp -n 或 /proc/net/arp 是静态快照,轮询效率低且易漏变。真正可用的是 netlink socket 的 NETLINK_ROUTE 协议族,监听 RTM_NEWNEIGH/RTM_DELNEIGH 消息——这些是内核在 ARP 表(邻居子系统)更新时主动发出的实时事件。
实操要点:
- 必须用
AF_NETLINK+NETLINK_ROUTE创建 socket,并绑定到RTNLGRP_NEIGHBORS组播组 - 需设置
SO_RCVBUF足够大(如 64KB),避免消息溢出丢弃 - 收到消息后,用
ndmsg结构体解析,检查ndm_state & NUD_VALID和ndm_flags & NTF_PROXY,过滤掉无效或代理条目 - 注意:同一 IP 可能对应多个接口(如 eth0/vlan10),需比对
ndm_ifindex是否属于监控范围
如何判断一条ARP记录是否异常
不能只看 IP-MAC 是否变化,关键看变化是否符合网络拓扑预期。防御逻辑的核心是建立并维护一个可信基线:
- 启动时读取当前网关 IP 对应的 MAC(通过
getdefaultgateway()+arp -n | grep $GW_IP或直接查/proc/net/arp),存为trusted_gw_mac - 对每个新收到的
RTM_NEWNEIGH事件,若ndm_dst是网关 IP 且ndm_lladdr与trusted_gw_mac不同,则触发告警 - 更稳妥的做法是结合 DHCP 日志或交换机端口学习表(若可访问),但本地防御只能依赖“首次观测即信任”+“后续严格校验”
- 注意:某些合法场景也会改网关 MAC(如主备网关切换、虚拟化 vMotion),需支持白名单机制(如允许特定 MAC 前缀)
发现异常后怎样安全地恢复ARP表
直接执行 arp -s 强制写入可能失败(内核会拒绝覆盖未过期条目),且无法保证原子性。正确做法是先清再设:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 调用
ioctl(SIOCDARP)删除目标 IP 的现有条目(需构造struct arpreq并填入arp_ha.sa_data和arp_pa.sin_addr) - 再调用
ioctl(SIOCSARP)写入可信 MAC;两次 ioctl 必须在毫秒级内完成,否则中间窗口仍可被劫持 - 替代方案:用
ip neigh replace命令(如ip neigh replace $GW_IP lladdr $TRUSTED_MAC dev eth0 nud permanent),它底层封装了上述操作,更健壮 - 切忌使用
arp -d && arp -s:shell 命令有 fork 开销,且arp -s默认写入nud_reachable状态,非永久,下次超时仍会重新请求
为什么用户态防御总有滞后性和绕过风险
netlink 监听本身有内核到用户空间的拷贝延迟,ARP 欺骗包(如 gratuitous ARP)到达网卡后,可能已在本地 ARP 缓存中生效几十毫秒——这段时间足够完成一次 DNS 请求或 TCP 握手劫持。
更本质的问题是:防御程序运行在用户态,而攻击者可在数据链路层(如用 libpcap 发包)或内核模块(如 eBPF)层面绕过所有用户态 hook。真正的缓解必须分层:
- 交换机侧启用 DHCP Snooping + Dynamic ARP Inspection(DAI)——这是唯一能从源头阻断伪造 ARP 的方式
- 终端侧可配合启用 IPv4 的
arp_ignore和arp_announce(sysctl),减少自身成为反射点的风险 - 用户态程序只适合作为辅助告警和快速回滚手段,不能当作核心防线
把检测逻辑塞进 eBPF 程序里能降低延迟,但需要 5.10+ 内核且开发复杂度陡增——多数实际部署场景里,老老实实用 netlink + ip neigh 已足够应对脚本化扫描类攻击。

















