Linux中arp表的老化机制

3,568 阅读3分钟

Linux内核网络协议栈中自动维护着一堆网络协议,这堆网络协议在内核中也是不同的存在,对于arp,Linux内核就提供了一种缓存机制来维护这张表。

实际的使用场景是,网络设备维护的arp表与内核维护的arp表相同步,一旦有变化,便通过钩子通知到上层的arp模块。但对于网络设备的arp模块来说,还需要实现一个arp老化时间的功能,这个功能的实现便也依靠了linux内核的arp表老化。

内核是如何维护arp表

首先来看内核是如何维护arp表的:

如果调用命令(需要在busybox中编译):

' ip -s neigh '

可以看到一个完整的arp条目变化的流程。其中有三个计时器,第一个表示到达delay之后的计时,第二个表示到达reachable的计时,第三个表示条目状态变成stale之后的计时。 对于delay和reachable我们不做多解释,重要的状态在于stale。 当条目处于stale状态时,可能会发现几个问题,为什么没有变成delay状态?为什么过了stale_time,条目还不老化? 先一个一个问题解决。

为什么没有变成delay状态?

之所以使用stale状态,是因为要减少网络中arp交互的开销,也就是尽量减少arp交互,但当条目变为stale之后,就说明,使用这个条目是不安全的不确定的。但是如果有应用用到这个条目发包,linux内核仍不进行arp交互,而是设立了一个delay的状态,在这个状态的持续过程中,收到了来自邻居的包,那么说明,此条目可用,则转为reachable的状态,如果没有,那么则会真正的进行arp交互,如果还没有,那么说明在这个网络中,此ip已经不对应任何一个mac地址,也没有必要存在了,置为failed。

所以可见,linux设计这个缓存机制的目的主要有二,一减少arp交互,二减少条目的增加和删除操作造成的开销。 第二个问题,为什么过了这个时间还不老化,这也是这一次的主要bug。

首先我们先知道几个参数的设置:

如果一个条目从reachable到老化需要经过多少时间

Timeout =random( base_reachable_time/2,  3*base_reachable_time/2) + gc_stale_time

这个时候,表项将被置为invalid状态,等待下一个gc时间的到来,回收删除。

所以问题,看定出在gc的时间。同时我们也得知了,设定老化时间的存在,必然不会非常准确,而是一个大概的值,所需要配置的项就是gc_stale_time。 这时我们留意到:

net.ipv4.neigh.default.gc_thresh1

存在于ARP高速缓存中的最少个数,如果少于这个数,垃圾收集器将不会运行 问题似乎就是在此,默认设置为128条,也就是说当条目大于128时,gc才会工作,只要将此项配置成0,便可以正常的实现arp老化。

当然,其实还是有简单粗暴的方法,比如当已经过了stale_time状态时,调用一下

ip neigh flush 

就好了。 其实还是有一个疑问,这个bug在linux4.4中出现,本人使用linux host也是一样的表现,而其他的使用linux3.6或者更低版本的linux中,只需要配置gc_stale_time便可以实现老化,其他的参数配置完全一致,却没有出现不能老化的问题,或许内核版本差异,表项和gc的关系之间存在着不同,或是其他原因,尚未解决。

还记得在在查找资料的过程中,看到的,内核其实已经对维护arp表的存在进行了很多的努力,但是你却对着这些优化绞尽脑汁,这就有点。。。。。