文章目录
- 概述
- [一、ARP cache 不是"附属品",而是 Linux 发包路径上的关键节点](#一、ARP cache 不是“附属品”,而是 Linux 发包路径上的关键节点)
- [二、先看整体图:主机路由表、ARP cache、交换机 MAC 表如何串起来](#二、先看整体图:主机路由表、ARP cache、交换机 MAC 表如何串起来)
- 三、两个最常见场景:同网段访问与跨网段访问
-
- [1. 同网段访问:ARP 查的是目标主机](#1. 同网段访问:ARP 查的是目标主机)
- [2. 跨网段访问:ARP 查的是网关](#2. 跨网段访问:ARP 查的是网关)
- [四、现代 Linux 下为什么应该优先使用 `ip neigh`](#四、现代 Linux 下为什么应该优先使用
ip neigh) - [五、`ip neigh` 的每一列到底在说什么](#五、
ip neigh的每一列到底在说什么) -
- [1. IP 地址](#1. IP 地址)
- [2. `dev eth0`](#2.
dev eth0) - [3. `lladdr`](#3.
lladdr) - [4. 邻居状态](#4. 邻居状态)
- `REACHABLE`
- `STALE`
- `DELAY`
- `PROBE`
- `INCOMPLETE`
- `FAILED`
- `PERMANENT`
- [5. `router`](#5.
router)
- [六、一次真实 ARP 过程怎么抓出来看](#六、一次真实 ARP 过程怎么抓出来看)
-
- [1. 查看当前邻居缓存](#1. 查看当前邻居缓存)
- [2. 删除一个已有条目](#2. 删除一个已有条目)
- [3. 开始抓 ARP 包](#3. 开始抓 ARP 包)
- [4. 在另一个终端触发访问](#4. 在另一个终端触发访问)
- [5. 观察实时状态变化](#5. 观察实时状态变化)
- [七、ARP cache、主机路由表、交换机 MAC 表,最容易混淆的地方在哪里](#七、ARP cache、主机路由表、交换机 MAC 表,最容易混淆的地方在哪里)
- 八、排障时应该怎么判断问题落在哪一层
-
- [1. 先看路由是否正确](#1. 先看路由是否正确)
- [2. 再看邻居是否可解析](#2. 再看邻居是否可解析)
- [3. 最后再看交换层或抓包](#3. 最后再看交换层或抓包)
- 九、几个开发者最常见的误区
-
- [误区一:ARP cache 缓存的是"所有远端主机的 MAC"](#误区一:ARP cache 缓存的是“所有远端主机的 MAC”)
- [误区二:交换机也看 IP 转发](#误区二:交换机也看 IP 转发)
- [误区三:`STALE` 就等于异常](#误区三:
STALE就等于异常) - [误区四:`arp -n` 和 `ip neigh` 只是两个写法不同的命令](#误区四:
arp -n和ip neigh只是两个写法不同的命令) - [误区五:IPv6 也用 ARP](#误区五:IPv6 也用 ARP)
- 十、几个实用命令,建议直接收藏
-
- 查看邻居表
- 查看指定接口的邻居项
- 查看某个地址的邻居项
- 删除单个邻居项
- 刷新某个接口上的邻居缓存
- [添加静态 ARP 项](#添加静态 ARP 项)
- 实时观察邻居变化
- [抓取 ARP 报文](#抓取 ARP 报文)
- 验证某个目标的实际路由决策
- [十一、从开发视角看,为什么理解 ARP cache 仍然很重要](#十一、从开发视角看,为什么理解 ARP cache 仍然很重要)
- [结语:真正要记住的,不是 ARP 定义,而是它在数据路径中的位置](#结语:真正要记住的,不是 ARP 定义,而是它在数据路径中的位置)

概述
很多开发者第一次系统接触 Linux 网络栈时,都会被三个看起来相似、实际职责完全不同的概念绕晕:
- 主机路由表
- ARP cache
- 交换机 MAC 表
现象往往很像:ping 不通、SSH 连不上、容器间偶发访问失败、局域网里明明能看到网卡却收不到包。于是有人查路由,有人清 ARP,有人去交换机上看端口学习表,但并不清楚每一步到底在解决什么问题。
本文面向开发者、运维工程师、研究人员和技术爱好者,聚焦 Linux 中的 ARP cache,并把它放回完整的数据路径里理解。读完你应该能回答下面这些问题:
- ARP cache 到底缓存了什么?
- Linux 为什么更推荐用
ip neigh,而不是arp -n? - 发一个 IP 包出去时,主机路由表、ARP cache、交换机 MAC 表分别在什么时候起作用?
- 同网段访问与跨网段访问时,ARP 查到的对象为什么不同?
REACHABLE、STALE、DELAY、PROBE这些状态是什么意思?- 出现
INCOMPLETE、FAILED时,应该优先怀疑哪里?
如果你过去只把 ARP 当成"IP 到 MAC 的映射协议",这篇文章会帮你把它真正嵌进 Linux 网络栈的执行路径里。
一、ARP cache 不是"附属品",而是 Linux 发包路径上的关键节点
先下结论:
在 Linux 中,ARP cache 是内核维护的邻居缓存,用来保存"下一跳 IPv4 地址 -> MAC 地址"的映射关系。
这里有两个关键词非常重要:
- 不是"目的 IP"必然映射成 MAC,而是"下一跳 IP"映射成 MAC
- 它在现代 Linux 里更准确地属于 neighbor table(邻居表) 的一部分,因此现在更推荐使用
ip neigh查看,而不是老的arp命令
很多人把 ARP cache 理解成"目标主机 IP 对应的 MAC 地址缓存",这个理解只对了一半。
因为真正发包时,内核不会先问"远端主机是谁",而是先问:
这个目的 IP,应该交给谁发?
这个"谁",才是下一跳。
如果目的地址与本机同网段,下一跳就是目标主机本身;如果目的地址不在本地网段,下一跳通常是默认网关。
也就是说,ARP cache 真正解决的问题是:
我已经知道包该交给哪个下一跳 IP 了,但我还需要知道这个下一跳对应的二层 MAC 地址。
这就是它在发送路径中的位置。
二、先看整体图:主机路由表、ARP cache、交换机 MAC 表如何串起来
理解三者关系,最有效的方法不是背定义,而是把一次发包过程完整走一遍。
text
主机 A 内核 交换机
+-------------------------------------------+ +----------------------------------+
| 应用要访问 192.168.1.10 | | MAC 表 / CAM 表 |
+---------------------+---------------------+ | 00:aa... -> port 1 |
| | 00:bb... -> port 7 |
v +----------------+-----------------+
+-----------------------+ ^
| 1. 路由表 `ip route` | |
| 目的 IP 该怎么走? | |
| - 同网段: 直连 dev eth0| |
| - 异网段: 走网关 192.168.1.1| |
+-----------+-----------+ |
| 产出"下一跳 IP" |
v |
+-----------------------+ |
| 2. ARP cache `ip neigh`| |
| 下一跳 IP 对应哪个 MAC?| |
| - 同网段: 查目标主机 IP | |
| - 异网段: 查网关 IP | |
+-----------+-----------+ |
| 产出"目标 MAC" |
v |
+-------------------------------+ |
| 3. 封装二层帧后发出 |---------------------------+
| src MAC = 主机A网卡MAC |
| dst MAC = 目标MAC/网关MAC |
| payload = IP包(dst=192.168.1.10)|
+-------------------------------+
交换机收到帧后:
1. 先学习 src MAC 来自哪个端口
2. 再查 MAC 表找 dst MAC 对应端口
3. 找到就单播转发;找不到就泛洪
4. 交换机只看 MAC,不看 IP
这张图里最值得记住的不是流程本身,而是三个组件各自回答的问题:
- 路由表:这个目的 IP 应该交给谁?
- ARP cache:这个下一跳 IP 对应哪个 MAC?
- 交换机 MAC 表:这个 MAC 从哪个端口转发出去?
它们分别工作在不同层面:
- 路由表偏三层决策
- ARP cache 是三层到二层之间的桥梁
- 交换机 MAC 表是纯二层转发依据
如果把这三者混在一起,排障时就会南辕北辙。
三、两个最常见场景:同网段访问与跨网段访问
1. 同网段访问:ARP 查的是目标主机
假设主机 A 地址是 192.168.1.20/24,要访问 192.168.1.10。
路由表一看,192.168.1.10 落在本地直连网段 192.168.1.0/24 内,于是给出结论:
- 从
eth0发 - 下一跳就是
192.168.1.10
这时 ARP cache 查的是:
192.168.1.10 -> ?
如果缓存中没有,就发 ARP 广播:
text
Who has 192.168.1.10? Tell 192.168.1.20
目标主机回复自己的 MAC 后,内核把这个映射写入 ARP cache,后续一段时间直接复用。
2. 跨网段访问:ARP 查的是网关
再看主机 A 访问 8.8.8.8。
路由表不会说"去找 8.8.8.8 的 MAC",因为它不在本地二层广播域。路由表给出的结论通常是:
- 走默认路由
- 下一跳是
192.168.1.1
于是 ARP cache 实际查的是:
192.168.1.1 -> ?
注意这个细节极其重要:
跨网段通信时,本机 ARP 通常解析的是默认网关的 MAC,而不是远端主机的 MAC。
所以你在本机执行 ip neigh,不会看到公网目标 8.8.8.8 对应的 MAC;你看到的往往是本地网关的 MAC。
这也是很多初学者在排查"为什么能到网关但不能上网"时容易误判的地方。
四、现代 Linux 下为什么应该优先使用 ip neigh
在很多老教程里,你会看到下面的命令:
bash
arp -n
它仍然能用,但这已经不是当前 Linux 网络管理的推荐方式。现代 Linux 更推荐使用 iproute2 工具集,也就是:
bash
ip neigh
原因并不只是"命令更现代",而是因为 Linux 的实现视角已经不再把它单独看成一个"ARP 表",而是统一放进 邻居子系统 中管理。对 IPv4 来说,邻居解析主要体现为 ARP;对 IPv6,则对应的是 NDP(Neighbor Discovery Protocol)。
换句话说:
arp -n更像是在看一个旧时代的工具视图ip neigh更接近 Linux 内核当前的网络对象模型
实际工作中,建议优先记住这几个命令:
bash
ip neigh show
ip neigh show dev eth0
ip neigh show 192.168.1.10
ip monitor neigh
最后一条尤其适合排查间歇性故障,它能实时观察邻居状态变化。
五、ip neigh 的每一列到底在说什么
来看一个典型输出:
bash
192.168.1.10 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE
192.168.1.1 dev eth0 lladdr 00:11:22:33:44:55 STALE router
逐字段解释如下。
1. IP 地址
例如:
text
192.168.1.10
这是邻居项关联的 IPv4 地址。注意它不是"所有远端地址",而是当前主机需要做二层解析的那个地址,也就是前面说的 下一跳 IP。
2. dev eth0
表示这个邻居项属于哪个网络接口。
同一个 IP 在不同网络命名空间、不同接口或特殊网络拓扑下,可能具有不同的邻居条目。
3. lladdr
lladdr 是 link-layer address,也就是链路层地址,本质上就是 MAC 地址。
text
lladdr aa:bb:cc:dd:ee:ff
这就是内核最终要用来封装二层帧目的地址的值。
4. 邻居状态
这是最容易被忽略、但对排障最有价值的一列。Linux 的邻居表使用一套状态机来管理条目的新鲜度与可达性,常见状态包括:
REACHABLESTALEDELAYPROBEINCOMPLETEFAILEDPERMANENT
下面分别解释。
REACHABLE
表示最近确认过这个邻居是可达的,可以放心使用。
这通常是最"健康"的状态。
STALE
条目还在,但已经老化。
它不意味着立刻不可用,而是表示"这份映射有一段时间没被确认了"。
Linux 常常会先继续使用它,而不是马上重发 ARP。这样可以减少不必要的广播流量。
DELAY
系统注意到这个条目可能需要重新确认,但暂时还没有立即发起探测。
它常出现在从 STALE 向"重新验证"过渡的阶段。
PROBE
系统正在主动探测该邻居是否仍然可达,一般会发送 ARP 请求进行确认。
如果你看到大量 PROBE,要考虑链路抖动、对端丢包、二层异常或网卡驱动问题。
INCOMPLETE
说明内核已经发起了解析,但还没有拿到 MAC。
这时最典型的症状是:
- 目标看起来"应该能通"
- 但实际访问一直卡住或超时
- 抓包能看到 ARP Request,看不到 Reply
这通常优先怀疑:
- 对端不在线
- VLAN 配置不对
- 中间交换设备异常
- 广播帧被隔离
- IP 地址冲突或错误配置
FAILED
邻居解析失败。
一般意味着系统尝试过,但最终没有拿到可用结果。
PERMANENT
静态配置项,不参与普通的老化与探测机制。
例如你手动添加:
bash
sudo ip neigh add 192.168.1.10 lladdr aa:bb:cc:dd:ee:ff dev eth0 nud permanent
得到的就是永久邻居项。
5. router
如果输出末尾带有 router,表示这个邻居被视为路由器使用,通常就是默认网关。
六、一次真实 ARP 过程怎么抓出来看
理解协议最好的方式,不是死记状态名,而是亲手看一次包。
下面是一套最小实验流程。
1. 查看当前邻居缓存
bash
ip neigh show
如果只想看一个地址:
bash
ip neigh show 192.168.1.10
2. 删除一个已有条目
bash
sudo ip neigh del 192.168.1.10 dev eth0
这样可以强制下一次访问重新触发 ARP 解析。
3. 开始抓 ARP 包
bash
sudo tcpdump -ni eth0 arp
4. 在另一个终端触发访问
bash
ping -c 1 192.168.1.10
你通常会看到类似输出:
text
ARP, Request who-has 192.168.1.10 tell 192.168.1.20
ARP, Reply 192.168.1.10 is-at aa:bb:cc:dd:ee:ff
第一行表示:
- 主机 A 问:谁是
192.168.1.10? - 请告诉
192.168.1.20
第二行表示:
192.168.1.10回复:我的 MAC 是aa:bb:cc:dd:ee:ff
这时再看邻居表:
bash
ip neigh show 192.168.1.10
你会看到一个新条目,状态通常是 REACHABLE。
5. 观察实时状态变化
如果你想看状态从 REACHABLE 变为 STALE、再进入 PROBE 的过程,可以执行:
bash
ip monitor neigh
然后持续访问目标。这个命令对分析偶发性网络问题尤其有用。
七、ARP cache、主机路由表、交换机 MAC 表,最容易混淆的地方在哪里
用一张表来对比会更清楚。
| 项目 | 所在位置 | 记录内容 | 解决的问题 | 常用查看方式 |
|---|---|---|---|---|
| 主机路由表 | Linux 主机内核 | 目的网段、下一跳、出口接口 | 这个 IP 包该往哪儿走? | ip route |
| ARP cache / 邻居表 | Linux 主机内核 | 下一跳 IP 到 MAC 的映射 | 这个下一跳 IP 对应哪个 MAC? | ip neigh |
| 交换机 MAC 表 | 二层交换机 | MAC 到物理端口的映射 | 这个帧应该从哪个端口转发? | show mac address-table 等设备命令 |
一句话记忆:
- 路由表决定方向
- ARP cache 决定二层目的地址
- 交换机 MAC 表决定物理出口端口
这也是为什么一条链路不通时,你不能只盯着一个表看。
例如:
ip route错了,包可能连正确接口都出不去ip neigh异常,包可能知道该走哪儿,但封不出正确的 MAC- 交换机 MAC 学习错误或端口异常,帧可能已经发出去,却在交换层被丢掉或泛洪
八、排障时应该怎么判断问题落在哪一层
很多网络排障的低效,来自于"工具先行、模型滞后"。
你看到了一个失败现象,但不知道它发生在哪一步。
一个更有效的诊断顺序是:
1. 先看路由是否正确
bash
ip route get 192.168.1.10
ip route get 8.8.8.8
这一步回答的是:
- 目标是否匹配了预期路由?
- 会从哪个接口发?
- 下一跳是谁?
如果这里已经不对,后面 ARP 再正常也没用。
2. 再看邻居是否可解析
bash
ip neigh show
ip neigh show dev eth0
重点观察:
- 有没有条目
- 状态是不是
INCOMPLETE/FAILED - MAC 是否符合预期
- 网关是否被错误解析
3. 最后再看交换层或抓包
bash
sudo tcpdump -ni eth0 arp or icmp
如果本机反复发 ARP Request 却没有 Reply,通常优先怀疑:
- 对端不在线
- 二层不可达
- VLAN 隔离
- 接口 down
- 防火墙设备或中间网络异常
- IP 地址冲突导致 ARP 混乱
如果 ARP 正常、ICMP 也出去了,但还是不通,就要继续看更高层的 ACL、策略路由、主机防火墙或远端服务状态。
九、几个开发者最常见的误区
误区一:ARP cache 缓存的是"所有远端主机的 MAC"
不准确。
它缓存的是当前主机在二层上需要解析的 下一跳。跨网段时,通常是网关,不是最终远端主机。
误区二:交换机也看 IP 转发
普通二层交换机看的是 MAC,不看 IP。
它学习源 MAC 所在端口,再根据目的 MAC 决定转发方向。
误区三:STALE 就等于异常
不对。
STALE 更多表示"条目老化了",并不等于当前不可用。Linux 会在合适的时机重新确认,而不是每次都立即 ARP 广播。
误区四:arp -n 和 ip neigh 只是两个写法不同的命令
也不完全对。
从现代 Linux 运维视角看,ip neigh 更贴近内核的邻居子系统模型,表达能力更完整,也更适合和 ip route、ip addr 一起使用。
误区五:IPv6 也用 ARP
错误。
IPv6 不使用 ARP,而是使用 NDP(Neighbor Discovery Protocol)。不过从运维体验上看,仍然可以通过邻居表思路去理解"地址解析与邻居可达性"。
十、几个实用命令,建议直接收藏
查看邻居表
bash
ip neigh show
查看指定接口的邻居项
bash
ip neigh show dev eth0
查看某个地址的邻居项
bash
ip neigh show 192.168.1.10
删除单个邻居项
bash
sudo ip neigh del 192.168.1.10 dev eth0
刷新某个接口上的邻居缓存
bash
sudo ip neigh flush dev eth0
添加静态 ARP 项
bash
sudo ip neigh add 192.168.1.10 lladdr aa:bb:cc:dd:ee:ff dev eth0 nud permanent
实时观察邻居变化
bash
ip monitor neigh
抓取 ARP 报文
bash
sudo tcpdump -ni eth0 arp
验证某个目标的实际路由决策
bash
ip route get 192.168.1.10
ip route get 8.8.8.8
十一、从开发视角看,为什么理解 ARP cache 仍然很重要
很多人会觉得 ARP 太底层,像是"网工才需要关心的内容"。但现实恰恰相反,在今天的云原生、容器网络、虚拟交换和多网卡环境里,ARP 相关问题并没有消失,只是换了更复杂的外衣。
例如你可能会遇到:
- 容器 Pod 偶发连不上同节点服务
- 虚拟机迁移后出现短暂的网络黑洞
- 多网卡主机上访问路径不稳定
- 同一个 IP 被错误漂移或重复配置
- 局域网内的服务发现正常,但请求偶发超时
这些问题看起来像"应用不稳定",实际根因往往落在邻居解析、二层转发或路由决策上。
理解 ARP cache 的最大价值,不在于背命令,而在于建立一个清晰的网络心智模型:
- 路由先决定下一跳
- 邻居表再把下一跳变成 MAC
- 二层设备根据 MAC 继续转发
一旦模型建立起来,很多"玄学网络问题"都会变成可定位的问题。
结语:真正要记住的,不是 ARP 定义,而是它在数据路径中的位置
如果只背一句"ARP 是把 IP 转成 MAC 的协议",你在面试里可能能答对,但在真实环境里依然很容易排障失败。
更有用的记忆方式是下面这条链路:
text
先看路由表 -> 再查 ARP cache -> 再交给交换机查 MAC 表
对应地,它们回答的是三个不同问题:
- 路由表:包该交给谁?
- ARP cache:这个下一跳是谁的 MAC?
- 交换机 MAC 表:这个 MAC 从哪个端口发?
在现代 Linux 中,ARP cache 更推荐从 neighbor table 的视角去理解,因此日常查看也应尽量使用 ip neigh。这不是命令风格的差异,而是理解模型的升级。
如果你希望把 Linux 网络真正学明白,ARP cache 是一个非常好的切入点。因为它恰好站在三层与二层之间,既能解释协议,又能解释系统行为,还能直接服务于排障实践。
当你下次再看到 REACHABLE、STALE、INCOMPLETE 时,不要把它们当成一堆状态名,而要把它们看成内核在告诉你:
我现在如何理解这个邻居,以及我准备如何与它通信。
这才是 ARP cache 在 Linux 里的真正价值。
