Linux 内核中的邻居缓存并不像人们想象的那么简单。我将尝试解释它的一些怪癖。
邻居缓存条目实际上完全从缓存中掉出来或只是被标记为陈旧/无效之间存在细微的差异。在base_reachable_time /2 和 3* base_reachable_time /2 之间的某个时间点,该条目仍将在缓存中,但它会被标记为 STALE 状态。您应该可以使用“ip -s neighbor show”查看状态,
pherricoxide@midigaurd:~$ ip -s neighbor list
192.168.42.1 dev eth0 lladdr 00:25:90:7d:7e:cd ref 2 used 184/184/139 probes 4 STALE
192.168.10.2 dev eth0 lladdr 00:1c:23:cf:0b:6a ref 3 used 33/28/0 probes 1 REACHABLE
192.168.10.1 dev eth0 lladdr 00:17:c5:d8:90:a4 ref 219 used 275/4/121 probes 1 REACHABLE
当处于 STALE 状态时,如果我 ping 192.168.42.1,它将立即将数据包发送到 00:25:90:7d:7e:cd。大约一秒钟后,它通常会向拥有 192.168.42.1 的人发送 ARP 请求,以便将其缓存更新回 REACHABLE 状态。但是,为了让事情更加混乱,内核有时会根据来自更高级别协议的积极反馈来更改超时值。这意味着如果我 ping 192.168.42.1 并且它回复了,那么内核可能不会打扰发送 ARP 请求,因为它假设 pong 意味着它的 ARP 缓存条目是有效的。如果条目处于 STALE 状态,它也将被它碰巧看到的未经请求的 ARP 回复更新。
现在,对于大多数情况,您只需要担心处于 STALE 状态的条目。为什么需要将条目从缓存中完全删除?内核通过仅仅改变缓存条目的状态而不是实际删除它们并将它们添加到缓存中来付出很多努力来不破坏内存。
如果你真的坚持它不仅会被标记为 STALE,而且实际上会从邻居缓存使用的 hashmap 中删除,那么你必须注意一些事情。首先,如果条目没有被使用并且在gc_stale_time秒内是陈旧的,它应该有资格被删除。如果gc_stale_time已通过并将条目标记为可以删除,则它将在垃圾收集器运行时删除(通常在gc_interval秒之后)。
现在的问题是,如果邻居条目被引用,它不会被删除。您将遇到的主要问题是来自ipv4 路由表的引用。有很多复杂的垃圾收集东西,但需要注意的重要一点是,路由缓存的垃圾收集器仅在很多内核上每 5 分钟( /proc/sys/net/ipv4/route/gc_timeout秒)使条目过期. 这意味着必须将邻居条目标记为过时(可能需要 30 秒,具体取决于base_reachable_time),然后在路由缓存停止引用该条目之前必须经过 5 分钟(如果幸运的话),然后是某种组合gc_stale_time和_gc_interval在它实际被清理之前通过(因此,总的来说,5-10 分钟之间的某个地方会过去)。
总结:您可以尝试将/proc/sys/net/ipv4/route/gc_timeout 减小到更短的值,但是变量很多,很难全部控制。通过不过早地删除缓存中的条目(而只是将它们标记为 STALE 甚至 FAILED),我们付出了很多努力来使事情表现良好。