Linux - ARP Cache:从 `ip neigh` 到交换机转发,一次讲透主机路由表、ARP 缓存与 MAC 表

· 编程技术杂谈

长期和Linux网络打交道的开发者,大概率会碰到这样的困惑:同网段主机ping不通但路由表显示正常,SSH连接超时且抓包看不到ARP请求,容器间通信偶尔失灵但清空ARP缓存后就能恢复,这些问题大多和主机路由表、ARP缓存、交换机MAC表这三个容易弄混的概念有关。

很多人对这三者的认知只停留在“都用来存地址对应关系”,但实际上它们分工清楚、各干各的活,一起把IP数据包从主机发送到目标设备的整个过程完成,今天我们就从最常用的ip neigh命令入手,一步步拆解它们的工作原理、理清相互间的联动关系,以后遇到网络问题就能精准找到问题所在,不用再瞎排查。

一、先澄清一个误区:ARP 缓存不是“目标主机的 MAC 缓存”

在讲具体内容前,我们先纠正一个最常见的理解偏差,很多人觉得ARP缓存是用来存“目标主机IP和MAC对应关系”的,这种想法其实只对了一半。

在Linux系统里,ARP缓存更准确的名字是“邻居缓存”(neighbor table),它的核心作用是存“下一跳IP地址和MAC地址的对应关系”,而不是直接记目标主机的IP-MAC对应关系,这里说的“下一跳”是决定数据包下一步发给谁的关键,也是连接路由表和ARP缓存的核心关键。

举个简单例子,当你从主机A(192.168.1.10)访问主机B(192.168.1.20)时,因为两者在同一个网段,下一跳就是主机B本身,这时候ARP缓存里存的就是主机B的IP-MAC对应关系,但如果你访问的是外网地址(比如1.1.1.1),下一跳就是你的默认网关(比如192.168.1.1),这时候ARP缓存存的是网关的IP-MAC对应关系而不是1.1.1.1的MAC地址,因为跨网段的数据包,主机只要交给网关,后面的转发工作就由网关和交换机一起完成。

这也是现代Linux更推荐用ip neigh命令查看ARP缓存而不是老的arp命令的原因,ip neigh更贴合“邻居表”的定位,能显示更多邻居状态和接口信息,而arp命令更偏向传统的ARP表查询,功能比较单一。

二、从 ip neigh 说起:看懂 ARP 缓存的每一项内容

在Linux终端输入ip neigh命令,会看到类似下面的结果(不同主机显示会有点不一样):

192.168.1.1 dev eth0 lladdr 00:50:56:c0:00:01 REACHABLE
192.168.1.20 dev eth0 lladdr 00:0c:29:12:34:56 STALE
192.168.1.30 dev eth0 INCOMPLETE

这些结果看起来简单,却包含了ARP缓存的核心信息,我们一步步拆解就能弄明白每一项的具体意思。

最左边的IP地址就是我们前面说的“下一跳IP”,它可能是同网段的目标主机也可能是网关,后面的dev eth0表示这个邻居条目对应的网络接口,说明这个下一跳能通过eth0接口访问,要是主机有多个网卡(比如eth0、eth1),这里会明确区分,避免数据包发错接口。

lladdr后面跟的是下一跳的MAC地址,也就是我们需要的“二层物理地址”,数据帧只有封装了这个MAC地址才能在局域网内传输,要是没有lladdr字段,就说明这个条目还没解析出MAC地址,后面的状态通常是INCOMPLETE

最后一部分是邻居状态(nud state,全称Neighbour Unreachability Detection,也就是邻居不可达检测),这是判断ARP缓存是否正常的关键,常见的状态有以下几种,不用死记硬背,结合实际情况理解就行。

REACHABLE(可达状态)就是当前下一跳的MAC地址有效,而且最近有通信记录,内核已经确认这个邻居能访问,这时候发数据包会直接用缓存里的MAC地址,不用触发ARP请求。

STALE(过期状态)是指缓存条目已经过了有效期,但内核还没确认这个邻居能不能访问,这时候发数据包不会马上触发ARP广播,会先试着用旧的MAC地址发送,要是失败再触发ARP请求更新条目,很多人看到STALE就以为有问题,其实这是正常的老化过渡状态,不用在意。

INCOMPLETE(未完成状态)意思是内核已经发了ARP请求,但还没收到响应,这种情况通常出现在目标主机没开机、网络断了或者目标IP不存在的时候,这时候数据包会暂时存起来等ARP响应,超时后这个条目就会被删掉。

除此之外还有DELAY(延迟状态,等ARP响应的间隙)、PROBE(探测状态,主动发ARP探测确认邻居能访问)、FAILED(失败状态,多次ARP请求都没响应,邻居访问不了)、PERMANENT(永久状态,手动添加的静态ARP条目,不会老化)等状态,日常排查时重点看INCOMPLETEFAILED就好,这两种状态通常是网络不通的直接信号。

另外,ip neigh还有几个实用的扩展命令,日常排查时能直接用,ip neigh show dev eth0用来查看指定接口的邻居条目,ip neigh flush dev eth0能清空指定接口的ARP缓存,ip neigh add 192.168.1.100 lladdr 00:0c:29:78:90:ab dev eth0可以添加静态ARP条目,静态ARP条目能有效防止ARP欺骗,适合绑定网关或核心服务器的IP-MAC对应关系。

三、主机路由表:决定“下一跳”是谁的核心

搞懂ARP缓存后,我们再看主机路由表,它是ARP缓存的“前置指引”,没有路由表的话,ARP缓存就不知道该解析哪个IP的MAC地址。

在Linux系统里,输入ip route就能查看路由表,核心条目通常包含“目的网络、网关、接口”三部分,路由表的核心作用就是根据数据包的目的IP地址,判断这个数据包该通过哪个接口、交给哪个下一跳(网关或目标主机)来转发。

我们用两种最常见的场景,就能理解路由表和ARP缓存的联动关系。

场景一就是同网段访问(比如主机A访问192.168.1.20),这时候路由表中会有一条“直连路由”,通常显示为192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10,这条路由的意思是目的网络为192.168.1.0/24的数据包,直接通过eth0接口转发、不用经过网关,下一跳就是目标主机本身,这时候主机A会查询ARP缓存寻找192.168.1.20对应的MAC地址,要是缓存里没有相关记录,就发ARP广播请求解析,解析成功后存到缓存里,后面通信直接用这个MAC地址。

场景二是跨网段访问(比如主机A访问1.1.1.1),这时候路由表中会有一条“默认路由”,通常显示为default via 192.168.1.1 dev eth0 proto static,这条路由的意思是所有没匹配到其他路由的数据包,都通过eth0接口交给网关192.168.1.1转发,这时候主机A不会去解析1.1.1.1的MAC地址,而是解析网关192.168.1.1的MAC地址,因为主机只要负责把数据包交给下一跳(网关),后面的跨网段转发就由网关和交换机完成,主机不用多管。

总结一下,路由表的作用是“决定方向”,告诉主机数据包该往哪发、下一跳是谁,ARP缓存的作用是“解析地址”,告诉主机下一跳的MAC地址具体是什么,两者配合才能完成数据包的二层封装。

四、交换机 MAC 表:局域网内的“精准投递”工具

主机完成ARP解析、把数据包封装成包含源MAC、目标MAC和IP数据包的数据帧并发送出去后,就轮到交换机发挥作用了,很多人以为交换机需要靠IP地址才能转发,其实不是这样,交换机工作在数据链路层,完全不管IP地址,只认MAC地址,而MAC表就是它的“转发指南”。

交换机的MAC表本质上是“端口和MAC地址的对应表”,它的核心作用是根据数据帧中的目标MAC地址,把数据帧从正确的端口转发出去,避免广播泛滥、实现精准投递,和ARP缓存一样,MAC表也有动态学习和老化机制,我们一步步看它的工作过程。

  1. MAC表的学习过程:交换机刚启动时MAC表是空的,当主机A(MAC:00:0c:29:12:34:56)通过交换机的端口1发送数据帧时,交换机会解析数据帧中的源MAC地址,把“端口1→00:0c:29:12:34:56”的对应关系存到MAC表,还会设置一个老化时间(通常是300秒),要是主机A后来换到了交换机的端口2,交换机收到新的数据帧后,会更新MAC表中的对应关系,用端口2替换原来的端口1。
  2. 数据转发逻辑:交换机收到数据帧后,会先读取帧头部的目标MAC地址,再查询自己的MAC表:

要是目标MAC地址在MAC表中有对应的端口(比如目标MAC对应端口2),交换机就只把数据帧从端口2发送出去,其他端口收不到,这就是效率最高的单播转发;

要是目标MAC地址是广播地址(FF:FF:FF:FF:FF:FF),比如ARP广播请求,交换机会把数据帧从除接收端口外的所有端口发送出去,保证局域网内所有设备都能收到;

要是目标MAC地址不在MAC表中(比如新接入的设备),交换机会采用泛洪方式,把数据帧从除接收端口外的所有端口发送出去,直到学习到目标MAC对应的端口后,后面的数据就会进行单播转发,不用再泛洪。

这里要特别区分ARP缓存和MAC表的核心区别、别弄混,ARP缓存是主机层面的,存的是IP-MAC对应关系,用来解决“下一跳IP对应的MAC是什么”的问题,MAC表是交换机层面的,存的是MAC-端口对应关系,用来解决“目标MAC对应的端口是什么”的问题,两者一个负责IP到MAC的解析,一个负责MAC到端口的转发,分工清楚、少了哪个都不行。

五、完整流程串讲:从发包到转发,三者如何协同工作?

讲完三个概念各自的作用后,我们用一个完整场景把它们串起来,就能彻底搞懂整个通信过程,假设主机A(192.168.1.10,MAC:00:0c:29:12:34:56)访问同网段的主机B(192.168.1.20,MAC:00:0c:29:78:90:ab),而且交换机刚启动、MAC表是空的。

  1. 主机A的应用层发起通信(比如ping 192.168.1.20),数据包到达网络层后,内核查询路由表,判断192.168.1.20属于同网段,下一跳就是主机B本身,转发接口是eth0。
  2. 内核查询ARP缓存寻找192.168.1.20对应的MAC地址,发现缓存里没有这个条目,状态是INCOMPLETE。
  3. 主机A发送ARP广播请求,数据帧的源MAC是主机A的MAC,目标MAC是广播地址(FF:FF:FF:FF:FF:FF),payload中包含“谁是192.168.1.20?请告诉我你的MAC地址”。
  4. 交换机收到ARP广播帧后,先学习源MAC地址,把“主机A的MAC→端口1”的对应关系存到MAC表,再因为目标MAC是广播地址,把该帧从除端口1外的所有端口泛洪出去。
  5. 局域网内所有主机都收到ARP广播帧,只有主机B匹配到目标IP(192.168.1.20),于是主机B把主机A的IP-MAC对应关系记到自己的ARP缓存,再发送ARP单播响应,数据帧的源MAC是主机B的MAC,目标MAC是主机A的MAC,payload中包含“我是192.168.1.20,我的MAC地址是00:0c:29:78:90:ab”。
  6. 交换机收到主机B的ARP响应帧后,先学习源MAC地址,把“主机B的MAC→端口2”的对应关系存到MAC表,再查询MAC表找到目标MAC(主机A的MAC)对应的端口1,把该响应帧从端口1转发给主机A。
  7. 主机A收到ARP响应后更新ARP缓存,把192.168.1.20和主机B的MAC对应关系存到缓存,状态变成REACHABLE。
  8. 主机A封装数据帧,源MAC是主机A的MAC,目标MAC是主机B的MAC,payload是ping请求的IP数据包,通过eth0发送出去。
  9. 交换机收到该数据帧后查询MAC表,找到目标MAC(主机B的MAC)对应的端口2,直接把数据帧从端口2转发给主机B,不用再泛洪。
  10. 主机B收到数据帧后解析并返回响应,后面的通信直接用各自ARP缓存中的MAC地址,交换机也通过MAC表实现精准转发,整个过程高效且没有多余的广播。

要是跨网段访问,流程大致一样,唯一的区别是主机A的路由表会指向网关,ARP缓存解析的是网关的MAC地址,数据包先交给网关,网关再通过自己的路由表和ARP缓存把数据包转发到其他网段,交换机则负责网关和主机之间的局域网转发工作。

六、常见排查思路:遇到网络问题,先查哪一步?

理解了三者的协同关系后,以后遇到网络不通的问题,就可以按照“路由表→ARP缓存→MAC表”的顺序排查,精准找到问题所在,不用瞎操作。

  1. 先查路由表,用ip route查看有没有到达目标IP的路由条目,要是没有对应的路由(比如跨网段没有默认网关),数据包会直接被丢掉,这时候要检查路由配置或网关设置。
  2. 再查ARP缓存,用ip neigh查看下一跳IP对应的MAC地址是否存在、状态是否正常,要是状态是INCOMPLETE或FAILED,说明ARP解析失败,可能是目标主机没开机、网络断了或者目标IP不存在,要是MAC地址错了,可能是遇到了ARP欺骗,这时候可以添加静态ARP条目解决。
  3. 最后查交换机MAC表,要是路由和ARP都正常,但数据包还是到不了目标主机,可能是交换机MAC表有问题(比如没学习到目标MAC地址),可以登录交换机查看MAC表,确认目标MAC对应的端口是否正确,要是没有对应的条目,可以试着清空交换机MAC表,让它重新学习。

另外还有几个常见误区要注意,误区一就是以为ARP缓存会存所有远端主机的MAC地址,其实它只存下一跳的MAC,跨网段时只存网关的MAC;误区二是以为交换机需要靠IP地址转发,其实交换机只认MAC地址,IP地址的解析工作由主机和网关负责;误区三是以为STALE状态是异常的,其实这是正常的老化过渡状态,不会影响通信。

七、总结:记住核心,无需死记硬背

其实不用死记硬背路由表、ARP缓存、MAC表的具体定义,只要记住它们的核心分工和联动关系,就能轻松理解。

路由表管“方向”,决定数据包的下一跳是谁;ARP缓存管“解析”,把下一跳的IP解析成MAC地址,给数据帧封装提供支持;交换机MAC表管“转发”,把封装好的data frame根据目标MAC地址转发到正确的端口。

ip neigh是我们查看和操作ARP缓存的核心命令,看懂它的输出就能快速判断ARP缓存的状态,而理解三者的协同流程,不仅能帮我们快速排查网络问题,还能加深对Linux网络栈和局域网通信原理的理解。