你的主机要给 192.168.1.1 发一个数据包。IP 地址有了,目的端口有了,应用层的数据也装好了——但网卡不认 IP 地址。网卡只认 MAC 地址。以太网帧头里填的源和目的,是六字节的硬件地址,不是四字节的 IP。
这个鸿沟,就是 ARP 要填的坑。
本章从”IP 和 MAC 为什么不能统一”这个问题出发,逐步展开 ARP 协议的工作机制、路由表的查找逻辑,最终追踪一个数据包从应用程序的 send() 调用出发,穿过内核协议栈、经过 ARP 解析、封装成以太网帧、到达交换机、被转发到路由器的完整旅程。
一、问题:IP 地址与 MAC 地址的鸿沟
1.1 两套地址,两个世界
网络层用 IP 地址寻址,链路层用 MAC 地址寻址。这两套地址体系服务于不同的目标:
- IP 地址是逻辑地址,标识”你在网络中的位置”。它随拓扑变化——你从家里换到公司,IP 地址就变了。IP 地址的核心功能是路由:让数据包跨越多个网络,从源端走到目的端。
- MAC 地址是物理地址,标识”你是哪块网卡”。它烧录在网卡的 ROM 里,全球唯一(理论上)。MAC 地址的核心功能是局部交付:在同一个广播域内,把帧从一台主机送到另一台主机。
为什么不能只用 IP?因为以太网——以及几乎所有链路层技术——在物理层面只认硬件地址。交换机根据 MAC 地址转发帧,不解析 IP 头。你可以在以太网上跑 IPv4、IPv6 甚至 IPX,交换机统统不关心——它只看帧头里的 MAC 地址。
为什么不能只用 MAC?因为 MAC 地址是扁平的,没有层次结构。00:1a:2b:3c:4d:5e 这串数字不包含任何”你在哪个网络”的信息。路由器如果按 MAC 地址转发,就需要一张包含全世界每块网卡地址的转发表——这在规模上不可行。
IP 地址的层次结构(网络号 + 主机号)使得路由表只需记录”网络号 → 下一跳”的映射,而非每台主机的映射。这是互联网能扩展到数十亿设备的关键设计。关于 IP 地址的层次结构,详见 IP地址与子网。
1.2 发送数据包时的核心问题
当主机 A 要向主机 B 发送 IP 数据包时,内核的发送流程大致如下:
- 查路由表,确定下一跳的 IP 地址
- 根据下一跳 IP,查找对应的 MAC 地址
- 用这个 MAC 地址封装以太网帧
- 网卡将帧发送到物理介质
第 2 步就是 ARP 的工作。ARP(Address Resolution Protocol,地址解析协议)解决的核心问题只有一句话:已知下一跳的 IP 地址,如何获得对应的 MAC 地址?
二、ARP 协议:IP 到 MAC 的翻译器
2.1 ARP 解析的完整过程
假设主机 A(IP: 192.168.1.100,MAC: aa:aa:aa:aa:aa:aa)要向网关路由器(IP: 192.168.1.1)发送数据包,但 A 的 ARP 缓存中没有 192.168.1.1 的 MAC 地址。
整个过程的关键点:
- ARP 请求是广播帧——目的 MAC 填
ff:ff:ff:ff:ff:ff,同一广播域内所有主机都会收到 - ARP 应答是单播帧——只有被请求的主机回复,直接发给请求者
- 收到 ARP 请求的主机都会更新缓存——即使你没有应答,你也能从请求中提取”谁在问”的 IP-MAC 映射
- ARP 是无状态协议——没有握手、没有确认,请求和应答之间没有连接
2.2 ARP 报文格式
ARP 报文直接封装在以太网帧中,以太网类型字段为 0x0806。报文本身非常简洁,总共 28 字节:
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+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| 硬件类型(htype) | 协议类型(ptype) |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| hlen | plen | 操作码(opcode) |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| 发送方MAC地址(前4字节) |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| 发送方MAC(后2字节) | 发送方IP地址(前2字节) |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| 发送方IP(后2字节) | 目标MAC地址(前2字节) |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| 目标MAC地址(后4字节) |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| 目标IP地址 |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ARP 请求与应答使用相同的报文格式,区别只在操作码和填充内容:
| 字段 | ARP 请求 | ARP 应答 |
|---|---|---|
| 硬件类型(htype) | 1(以太网) | 1(以太网) |
| 协议类型(ptype) | 0x0800(IPv4) | 0x0800(IPv4) |
| 硬件地址长度(hlen) | 6 | 6 |
| 协议地址长度(plen) | 4 | 4 |
| 操作码(opcode) | 1(请求) | 2(应答) |
| 发送方MAC | 主机 A 的 MAC | 网关的 MAC |
| 发送方IP | 主机 A 的 IP | 网关的 IP |
| 目标MAC | 00:00:00:00:00:00(未知) | 主机 A 的 MAC |
| 目标IP | 网关的 IP | 主机 A 的 IP |
| 以太网目的MAC | ff:ff:ff:ff:ff:ff(广播) | 主机 A 的 MAC(单播) |
注意一个容易忽略的细节:ARP 请求中目标 MAC 填全零(因为还不知道),但以太网帧头的目的 MAC 填全 F(广播)。这两层目的地址的含义不同——帧头的全 F 是告诉交换机”这个帧要发给所有人”,ARP 报文里的全零是告诉接收方”我不知道你的 MAC,请告诉我”。
2.3 用 tcpdump 抓 ARP 包
# 在主机A上抓取ARP报文sudo tcpdump -i eth0 -nn arp
# 输出示例:# ARP, Request who-has 192.168.1.1 tell 192.168.1.100, length 28# ARP, Reply 192.168.1.1 is-at bb:bb:bb:bb:bb:bb, length 28加上 -e 选项可以看到以太网帧头:
sudo tcpdump -i eth0 -nn -e arp
# 输出示例:# aa:aa:aa:aa:aa:aa > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806),# length 42: Request who-has 192.168.1.1 tell 192.168.1.100# bb:bb:bb:bb:bb:bb > aa:aa:aa:aa:aa:aa, ethertype ARP (0x0806),# length 42: Reply 192.168.1.1 is-at bb:bb:bb:bb:bb:bb注意帧长度是 42 字节——14 字节以太网帧头 + 28 字节 ARP 报文。但以太网要求最小帧长 64 字节,不足的部分由网卡自动填充。
2.4 ARP 缓存表
每次 ARP 解析都需要广播一次请求,代价不小。所以主机把解析结果缓存起来,后续发送直接查缓存:
# 查看ARP缓存表arp -a
# 输出示例:# ? (192.168.1.1) at bb:bb:bb:bb:bb:bb [ether] on eth0# ? (192.168.1.50) at cc:cc:cc:cc:cc:cc [ether] on eth0# ? (10.0.0.1) at dd:dd:dd:dd:dd:dd [ether] on eth1Linux 上更推荐使用 ip neigh 命令:
# 查看邻居表(ARP缓存的现代替代)ip neigh show
# 输出示例:# 192.168.1.1 dev eth0 lladdr bb:bb:bb:bb:bb:bb REACHABLE# 192.168.1.50 dev eth0 lladdr cc:cc:cc:cc:cc:cc STALE# 10.0.0.1 dev eth1 lladdr dd:dd:dd:dd:dd:dd DELAYip neigh 的输出比 arp -a 多了一个状态字段,这个状态来自 Linux 内核的邻居状态机:
- REACHABLE:可达,缓存有效,可直接使用
- STALE:过期,缓存超时但尚未验证,发送数据前需重新确认
- DELAY:延迟等待,已发送单播探测,等待回应
- PROBE:探测中,正在发送 ARP 请求
- FAILED:解析失败
2.5 ARP 超时与更新
ARP 缓存不是永久的。Linux 内核中,REACHABLE 状态的默认超时时间是 30 秒(由 gc_stale_time 控制),超时后转为 STALE。STALE 状态的条目不会立即删除——下次发送数据时,内核会先发一个单播 ARP 探测,如果对方回应则刷新缓存,否则进入完整的 ARP 解析流程。
# 手动删除ARP缓存条目(强制下次重新解析)sudo arp -d 192.168.1.1
# 或者用ip命令sudo ip neigh del 192.168.1.1 dev eth0
# 验证删除成功ip neigh show | grep 192.168.1.1# (无输出表示已删除)删除 ARP 缓存会导致短暂的通信中断——在新的 ARP 解析完成之前,发往该 IP 的数据包无法封装以太网帧。在生产环境中操作前,确保你了解后果。
2.6 用 arping 主动探测
arping 工具可以主动发送 ARP 请求,用于检测 IP 冲突或手动触发 ARP 解析:
# 向192.168.1.1发送ARP请求sudo arping -I eth0 192.168.1.1
# 输出示例:# ARPING 192.168.1.1 from 192.168.1.100 eth0# Unicast reply from 192.168.1.1 [bb:bb:bb:bb:bb:bb] 0.695ms# Unicast reply from 192.168.1.1 [bb:bb:bb:bb:bb:bb] 0.712ms# Sent 2 probes (1 broadcast(s))# Received 2 response(s)
# 检测IP冲突:如果收到多个不同MAC的应答,说明存在冲突sudo arping -D -I eth0 192.168.1.100# 返回0表示无冲突,返回1表示有冲突三、ARP 的变体:免费 ARP 与代理 ARP
3.1 免费 ARP(Gratuitous ARP)
免费 ARP 是一种特殊的 ARP 请求:主机询问自己的 IP 地址对应的 MAC。看起来荒谬——自己问自己?但这个操作有两个实际用途:
地址冲突检测:主机配置 IP 地址时,发送免费 ARP 请求。如果网络中有另一台主机使用了相同的 IP,它会回应——这就检测到了 IP 冲突。Linux 在设置 IP 地址时会自动做这个检测。
通知 MAC 变更:当主机的 MAC 地址发生变化(比如更换网卡、高可用集群的 VIP 漂移),发送免费 ARP 可以让网络中其他主机更新它们的 ARP 缓存。否则,其他主机还会用旧的 MAC 地址发包,导致通信中断。
# 手动发送免费ARPsudo arping -A -I eth0 -c 3 192.168.1.100
# -A: 使用ARP应答格式(而非请求)# -c 3: 发送3次# 这会广播"192.168.1.1的MAC是bb:bb:bb:bb:bb:bb"# 所有收到此包的主机都会更新ARP缓存免费 ARP 在高可用场景中特别重要。当 Keepalived 或 VRRP 执行主备切换时,新的主节点会立即广播免费 ARP,让交换机和其他主机将 VIP 的 MAC 地址更新为新主节点的 MAC,从而实现秒级切换。
3.2 代理 ARP(Proxy ARP)
代理 ARP 让路由器代替远端主机应答 ARP 请求。考虑这个场景:主机 A(192.168.1.100/24)要访问主机 B(10.0.0.50),但 B 不在 A 的子网内。正常情况下,A 应该查路由表、找到默认网关、ARP 解析网关的 MAC。但如果 A 没有配置默认网关呢?
这时,路由器可以开启代理 ARP。当路由器收到 A 的 ARP 请求(“谁有 10.0.0.50?”),它发现 10.0.0.50 在自己的另一个接口可达,于是代替 B 回应:“10.0.0.50 的 MAC 是路由器自己的 MAC。“A 收到应答后,把数据包发给了路由器,路由器再转发给 B。
# 在Linux路由器上开启代理ARPsudo sysctl -w net.ipv4.conf.eth0.proxy_arp=1
# 查看代理ARP状态cat /proc/sys/net/ipv4/conf/eth0/proxy_arp# 1 = 开启, 0 = 关闭3.3 免费 ARP vs 普通 ARP vs 代理 ARP
| 特性 | 普通 ARP | 免费 ARP | 代理 ARP |
|---|---|---|---|
| 发送方IP = 目标IP | 否 | 是 | 否 |
| 操作码 | 1(请求)/ 2(应答) | 1 或 2 | 2(应答) |
| 应答者 | 目标主机本身 | 无人应答(或冲突主机应答) | 路由器代替目标主机 |
| 以太网目的 | 广播(请求)/ 单播(应答) | 广播 | 单播(给请求者) |
| 核心用途 | 解析 IP→MAC | 冲突检测 + MAC 变更通知 | 代替远端主机应答 |
| 安全风险 | 可被欺骗 | 无 | 可被滥用 |
代理 ARP 看起来方便——主机不需要配置网关也能跨网段通信。但它有几个严重问题:
- 广播域扩大:每个跨网段通信都会触发 ARP 广播,增加网络负担
- 故障排查困难:ARP 缓存中远端主机的 MAC 实际上是路由器的 MAC,排查时容易混淆
- 子网规划失效:原本通过子网划分隔离的广播域被代理 ARP 打通了
- 安全风险:恶意主机可以伪造代理 ARP 应答,劫持流量
现代网络中,代理 ARP 已不推荐使用。正确做法是给主机配置默认网关,让主机明确知道”出网段的流量发给路由器”。代理 ARP 主要存在于一些遗留网络和特定场景(如 OpenStack Neutron 的网络模型)中。
四、路由表:数据包的导航地图
ARP 解决了”已知 IP 如何获取 MAC”的问题,但在此之前还有一个前置问题:数据包应该发给谁? 答案在路由表里。
4.1 路由表的结构
# 传统格式查看路由表route -n
# 输出示例:# Kernel IP routing table# Destination Gateway Genmask Flags Metric Ref Use Iface# 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0# 10.0.0.0 192.168.1.1 255.255.0.0 UG 0 0 0 eth0# 0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0# 推荐的现代格式ip route show
# 输出示例:# default via 192.168.1.1 dev eth0# 10.0.0.0/16 via 192.168.1.1 dev eth0# 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100每条路由记录包含以下关键字段:
- 目的网络:数据包要去的网络地址
- 下一跳/网关:数据包应该转发给谁(
0.0.0.0表示直连,不需要网关) - 子网掩码/前缀长度:决定这条路由的匹配范围
- 出接口:从哪个网卡发出去
- 度量值(Metric):相同目的网络有多条路由时,优先选度量值小的
4.2 最长前缀匹配
路由表中可能有多条路由都能匹配目的地址。比如目的地址 10.0.1.50 同时匹配 10.0.0.0/16 和 10.0.1.0/24,选哪条?
答案是最长前缀匹配(Longest Prefix Match):选掩码最长、最精确的那条。10.0.1.0/24 的前缀长度是 24,比 10.0.0.0/16 的 16 更长,所以选 /24 那条。
最长前缀匹配是路由查找的核心算法。它允许路由表中同时存在粗粒度和细粒度的路由——默认路由覆盖”所有其他”,而更具体的路由覆盖特定子网。这种分层设计让路由表既紧凑又灵活。
4.3 默认路由 0.0.0.0/0
默认路由是前缀长度为 0 的路由——它匹配任何目的地址。当所有更具体的路由都不匹配时,数据包就走默认路由。对于大多数终端主机,路由表只有两条:
# 典型终端主机的路由表ip route show# default via 192.168.1.1 dev eth0 # 默认路由:其他都走网关# 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 # 直连路由直连路由是内核自动生成的——当你给网卡配置 IP 地址时,内核会自动添加一条指向该子网的路由。默认路由通常由 DHCP 下发,或者手动配置。
4.4 路由表的生成:静态配置 vs 动态协议
| 特性 | 静态路由 | 动态路由(OSPF/BGP/EIGRP) |
|---|---|---|
| 配置方式 | 手动添加 ip route add | 路由协议自动学习 |
| 拓扑变化响应 | 不自动更新,需人工修改 | 自动收敛,秒级~分钟级 |
| 适用规模 | 小型网络(几十条路由) | 大型网络(数千~数百万条路由) |
| 资源消耗 | 无额外开销 | CPU、内存、带宽开销 |
| 可靠性 | 依赖管理员操作 | 协议保证冗余和备份路径 |
| 典型场景 | 末梢网络、默认路由 | 企业骨干、ISP、数据中心 |
# 添加静态路由sudo ip route add 10.0.0.0/16 via 192.168.1.1 dev eth0
# 删除静态路由sudo ip route del 10.0.0.0/16
# 添加默认路由sudo ip route add default via 192.168.1.1 dev eth0
# 持久化静态路由(Ubuntu/Debian)# 写入 /etc/network/interfaces 或 Netplan 配置4.5 一个路由查找的完整过程
假设主机 A(192.168.1.100)要向 10.0.1.50 发送数据包,路由表如下:
default via 192.168.1.1 dev eth010.0.0.0/16 via 192.168.1.2 dev eth010.0.1.0/24 via 192.168.1.3 dev eth0192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100查找过程:
- 目的地址
10.0.1.50,逐条匹配 192.168.1.0/24:不匹配(10 开头,不是 192.168.1)10.0.1.0/24:匹配!前缀长度 2410.0.0.0/16:也匹配,但前缀长度 16 < 24,不如上一条精确default:也匹配,前缀长度 0,最不精确- 选择
10.0.1.0/24,下一跳192.168.1.3
接下来,内核需要 ARP 解析 192.168.1.3 的 MAC 地址——这就回到了上一节的内容。
五、首跳:从主机到路由器
5.1 完整链路追踪
“首跳”指的是数据包从源主机出发的第一步——到达默认网关。这一步涉及应用层、内核协议栈、ARP、以太网封装、交换机转发等多个环节。追踪一个完整的 ping 10.0.0.1 的首跳过程:
5.2 逐步拆解
第 1 步:应用调用 send()
用户执行 ping 10.0.0.1,ping 程序构造 ICMP Echo Request,调用系统调用发送数据。
第 2 步:内核查路由表
内核收到目的地址 10.0.0.1,查找路由表:
default via 192.168.1.1 dev eth0192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10010.0.0.1 不在 192.168.1.0/24 内,匹配默认路由,下一跳为 192.168.1.1。
第 3 步:ARP 解析网关 MAC
内核检查 ARP 缓存中是否有 192.168.1.1 的 MAC 地址。如果有,直接使用;如果没有,发起 ARP 解析(见第二节的过程)。
第 4 步:封装以太网帧
内核构造以太网帧:
| 字段 | 值 | 说明 |
|---|---|---|
| 以太网目的 MAC | bb:bb:bb:bb:bb:bb | 网关的 MAC(ARP 解析得到) |
| 以太网源 MAC | aa:aa:aa:aa:aa:aa | 主机 A 的 MAC |
| EtherType | 0x0800 | IPv4 |
| IP 源地址 | 192.168.1.100 | 主机 A 的 IP |
| IP 目的地址 | 10.0.0.1 | 最终目的地(不是网关的 IP!) |
| IP TTL | 64 | 初始 TTL |
注意一个关键点:以太网帧的目的 MAC 是网关的,但 IP 包的目的地址是最终目的地。这就是”逐跳转发”——每一跳只关心下一跳的 MAC,IP 层的源和目的地址在整个传输过程中不变(NAT 除外)。
第 5 步:发送到交换机
网卡将帧编码为电信号(或光信号),通过网线发送到交换机。
第 6 步:交换机转发
交换机收到帧后,执行两个操作:
- 学习:记录源 MAC
aa:aa:aa:aa:aa:aa从端口 1 收到,更新 MAC 地址表 - 转发:查 MAC 地址表,
bb:bb:bb:bb:bb:bb在端口 3,将帧从端口 3 发出
如果 MAC 地址表中没有 bb:bb:bb:bb:bb:bb 的记录,交换机会将帧从所有端口(除接收端口外)泛洪——这就是广播域内交换机处理未知单播帧的方式。
第 7 步:路由器接收
路由器从端口 3 收到帧,检查目的 MAC 是自己的 MAC,接收此帧。解封装提取 IP 数据包,查路由表决定下一步转发。
5.3 用 Wireshark 抓包验证
在主机 A 上用 Wireshark 抓取 ping 10.0.0.1 的首跳过程,可以看到以下关键帧:
# 先清空ARP缓存,强制触发ARP解析sudo ip neigh del 192.168.1.1 dev eth0
# 开始抓包sudo tcpdump -i eth0 -nn -e -vv
# 另一个终端执行pingping -c 1 10.0.0.1抓包输出(简化):
# 帧1: ARP请求(广播)aa:aa:aa:aa:aa:aa > ff:ff:ff:ff:ff:ff, ARP, Request who-has 192.168.1.1 tell 192.168.1.100
# 帧2: ARP应答(单播)bb:bb:bb:bb:bb:bb > aa:aa:aa:aa:aa:aa, ARP, Reply 192.168.1.1 is-at bb:bb:bb:bb:bb:bb
# 帧3: ICMP Echo Requestaa:aa:aa:aa:aa:aa > bb:bb:bb:bb:bb:bb, IPv4, 192.168.1.100 > 10.0.0.1, ICMP Echo Request
# 帧4: ICMP Echo Reply(路由器转发回来)bb:bb:bb:bb:bb:bb > aa:aa:aa:aa:aa:aa, IPv4, 10.0.0.1 > 192.168.1.100, ICMP Echo Reply帧 1 和帧 2 是 ARP 解析过程,帧 3 和帧 4 是实际的 ICMP 通信。注意帧 3 的以太网目的 MAC 是网关的 bb:bb:bb:bb:bb:bb,而 IP 目的地址是 10.0.0.1——这就是”以太网层逐跳、IP 层端到端”的体现。
六、ARP 安全与防御
6.1 ARP 欺骗原理
ARP 协议设计时没有考虑安全——它信任所有收到的 ARP 报文。任何人都可以发送伪造的 ARP 应答,声称”某个 IP 的 MAC 是我的 MAC”,从而将流量劫持到自己这里。
ARP 欺骗之所以容易得手,是因为:
- ARP 是无状态协议——没有请求也可以发应答,主机无法区分合法应答和伪造应答
- ARP 缓存可被覆盖——收到新的 ARP 应答就更新缓存,即使旧条目还在有效期内
- ARP 没有认证机制——报文中没有任何字段可以验证发送者的身份
6.2 用 scapy 构造 ARP 包
scapy 是一个强大的 Python 网络包操作库,可以用来构造和发送自定义 ARP 报文:
from scapy.all import *
# 构造ARP请求arp_request = ARP( pdst="192.168.1.1" # 目标IP)# scapy会自动填充:# hwdst=00:00:00:00:00:00(未知)# psrc=本机IP# hwsrc=本机MAC# op=1(请求)
# 发送并接收应答ans, unans = srp( Ether(dst="ff:ff:ff:ff:ff:ff") / arp_request, timeout=2, iface="eth0")
# 打印结果for sent, received in ans: print(f"{received.psrc} 的MAC地址是 {received.hwsrc}")
# 构造ARP应答(用于测试,勿用于攻击)# 以下代码仅用于理解协议,实际使用可能违反法律arp_reply = ARP( op=2, # 应答 pdst="192.168.1.100", # 目标主机IP hwdst="aa:aa:aa:aa:aa:aa", # 目标主机MAC psrc="192.168.1.1", # 伪装的IP(网关) hwsrc="cc:cc:cc:cc:cc:cc" # 攻击者的MAC)6.3 静态 ARP 绑定
最简单的防御手段是静态绑定——手动指定 IP 和 MAC 的对应关系,禁止动态更新:
# 添加静态ARP条目sudo arp -s 192.168.1.1 bb:bb:bb:bb:bb:bb
# 验证arp -a | grep 192.168.1.1# ? (192.168.1.1) at bb:bb:bb:bb:bb:bb [ether] PERM on eth0# 注意 PERM 标记,表示永久条目
# 删除静态条目sudo arp -d 192.168.1.1静态绑定的缺点显而易见:不适合大型网络,MAC 地址变更时需要手动更新所有主机的绑定。它只适用于关键设备(网关、服务器)的防护。
6.4 DAI 动态 ARP 检测
DAI(Dynamic ARP Inspection)是交换机层面的防御机制。它的核心思路是:交换机只转发合法的 ARP 报文。
DAI 的工作流程:
- 交换机维护一个 DHCP Snooping 绑定表,记录每个端口通过 DHCP 获得的 IP-MAC 映射
- 当交换机收到 ARP 报文时,检查报文中的 IP-MAC 对是否在绑定表中
- 如果匹配,转发;如果不匹配,丢弃
# Cisco交换机配置DAIip arp inspection vlan 10ip arp inspection validate src-mac dst-mac ip
# 信任端口(如连接路由器的端口)interface GigabitEthernet0/1 ip arp inspection trustDAI 的前提是网络使用 DHCP。对于使用静态 IP 的设备,需要手动配置允许列表。
6.5 为什么 ARP 是链路层安全的薄弱环节
ARP 的安全问题不是实现缺陷,而是协议设计的固有局限。1982 年 RFC 826 定义 ARP 时,网络规模小、设备可信,安全不是优先考虑。ARP 的三个根本性弱点:
- 无认证:任何主机都可以发送 ARP 应答,接收方无法验证其真实性
- 可覆盖:新的 ARP 信息无条件覆盖旧信息,即使旧信息来自可信源
- 广播域内全局影响:一个伪造的 ARP 应答可以影响整个广播域的通信
这三个弱点叠加,使得 ARP 欺骗成为局域网攻击的常见手段。彻底解决需要部署 DAI 等交换机层面的防护,或者迁移到 IPv6——IPv6 用 NDP(Neighbor Discovery Protocol)替代 ARP,NDP 基于 ICMPv6,支持 IPsec 认证,安全性显著提升。
七、本章小结
| 概念 | 要点 |
|---|---|
| IP 与 MAC 的鸿沟 | 网络层用 IP 寻址(层次化,可路由),链路层用 MAC 寻址(扁平,局部交付),发送数据包时必须将 IP 映射到 MAC |
| ARP 协议 | 请求广播、应答单播,28 字节报文,操作码区分请求(1)和应答(2),缓存超时约 30 秒 |
| 免费 ARP | 询问自己的 IP,用于地址冲突检测和高可用切换时的 MAC 变更通知 |
| 代理 ARP | 路由器代替远端主机应答 ARP,不推荐使用,应配置默认网关替代 |
| 路由表 | 目的网络 + 下一跳 + 出接口 + 度量值,内核自动生成直连路由,默认路由匹配所有 |
| 最长前缀匹配 | 多条路由匹配时选前缀最长的,实现粗细粒度路由共存 |
| 首跳过程 | send() → 查路由表 → ARP 解析 → 封装以太网帧 → 交换机转发 → 路由器接收 |
| 逐跳转发 | 以太网目的 MAC 每跳变化,IP 目的地址端到端不变 |
| ARP 欺骗 | 伪造 ARP 应答劫持流量,根源是 ARP 无认证、可覆盖、广播域全局影响 |
| 防御手段 | 静态 ARP 绑定(简单但不扩展)、DAI 动态检测(需交换机支持)、IPv6 NDP(根本解决) |
本章追踪了数据包从应用层到路由器的第一步。下一章深入路由器内部——IP地址与子网,看看互联网的坐标系统如何设计、CIDR 如何让路由表可扩展。
参考
- RFC 826 — An Ethernet Address Resolution Protocol
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






