关于 ARP 的一些问题(arp cache, lvs arp, arp hijack)

涉及到 ARP 的问题还不少,包括之前 arp proxy 的问题。这里主要总结三个问题。

ARP 缓存(cache)
关于 Linux 上 ARP cache 的 timeout 时间,根据这篇文档的描述,并不是一个简单的 /proc/sys/net/ipv4/neigh/default/base_reachable_time_ms 开关能够决定的,这里面需要考虑很多的因素。
基本上,在 cache 的里面的 entry 有 stale/invalid/failed/reachable/delay 这几个状态:
# ip neighbor show
192.168.1.24 dev em1 lladdr 00:16:36:4e:3a:56 DELAY
192.168.1.10 dev em1 lladdr 84:2b:2b:76:86:6e REACHABLE
192.168.1.247 dev em1 lladdr 00:16:3e:06:01:20 DELAY

对于 ipv4 的路由表来说,里面有一套复杂的垃圾回收机制,总的来说,这个失效的时间通常在 5-10min 之间,并且跟随不同的 kernel 时间不完全一致,可以通过缩小 /proc/sys/net/ipv4/route/gc_timeout 的值来让 cache 里面的条目尽快的失效,但是依然无法保证。这里面涉及到不少 gc 过程,像 rhash_entries, net.ipv4.route.gc_elasticity, net.ipv4.route.max_size 以及 net.ipv4.route.gc_threshold 这类参数是需要结合起来调整的,具体的原理以及使用方法可以看这里。回答该问题的作者同时还写了一个仅仅通过 ARP 协议来侦测操作系统的 Neighbor-Cache-Fingerprint 工具,相关的原理可以看这里
说了这么多其实是想引出个我们线上遇到的一个问题,估计绝大多数的工程师一辈子都不会遇到。
正常情况下,服务器都只配有一个内网 IP,路由表也很简单,只要添加一条默认的通往 default gateway 的路由就好了,现在假设该路由表是下面这样的:
# 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 em1
0.0.0.0         192.168.1.24    0.0.0.0         UG    0      0        0 em1

可以看到,默认的网关指向了 192.168.1.24,现在,我们有这么一个需求,需要将绑定在 A 机器上的 192.168.1.24 这个 IP(同时也是充当的整个内网的网关机器的) 迁移到 B 机器上。最初,我们猜想的是,由于迁移了物理机器,虽然对于客户端来说,IP 没有变,但是 MAC 地址变了,因此可能需要手动的刷新下所有客户端的 ARP 表,这样才能及时的获取到正确的 MAC 地址。但是猜想毕竟是猜想,这跟我们实际测试以及最后线上碰到的情况完全不同,实际情况是什么了?在我们切换将 A 机器的 IP 下线,接着绑定到 B 机器之后,几乎是同一时刻,所有的机器就正确的识别到了更新后的 B 机器的 MAC 表。 也就是说,切换了网关之后,并不需要我们手动的干预,客户端就能「自动的」发现情况并主动更新。为什么会这样?我到目前为止并没有一个准确的结论,不过我猜想应该跟上面涉及到的几个链接有很大的关系,具体的我没有深究。


下面总结下 LVS DR 下的 arp 问题。
这个问题比较经典,我会以表格的方式说明,首先说下环境。
          IP                MAC
CLIENT    123.45.67.89      00:22:33:44:55:89
LB        119.42.226.101    11:22:33:44:55:AA
LB VIP    119.42.234.200    11:22:33:44:55:AA
RS        119.42.226.102    11:22:33:44:55:BB
RS VIP    119.42.226.200    /

下面是 CLIENT 请求过来之后,包的 IP、MAC 变化情况:
                        SRC MAC               SRC IP          DST MAC             DST IP
原始情况                00:22:33:44:55:89     123.45.67.89    11:22:33:44:55:AA   119.42.234.200
到达 LVS,经过转发之后  11:22:33:44:55:AA     123.45.67.89    11:22:33:44:55:BB   119.42.226.200
RS 回复                 11:22:33:44:55:BB     119.42.226.200  00:22:33:44:55:89   123.45.67.89

可以看到,全程 IP 只涉及到 VIP 以及 CIP,全程的 MAC 涉及到 CLIENT MAC,LVS MAC,RS MAC。下面解释原因。
原始情况: CLIENT 的请求 DR 过来之后,只有 DR 上的 VIP 会应答(RS 关闭了 ARP 响应),从而避免了 DR、RS 同时应答的问题。
到达 DR,经过转发: 可以看到包的 DST IP 仍然是 VIP 而没有修改成 RS 的 IP,对于 CLIENT 来说,仅仅关系 VIP 即可,至于 RIP 是什么,根本没必要关系。DR 修改完 DST MAC 之后,直接通过二层交换将包转到 RS 上,RS 解包发现 DST IP 为自己(lo:0 的 VIP),直接收下。这也解释了 DR 为何要跟 RS 在同一个 VLAN 下(广播域)的原因。
RS 回复: RS 发现  DST IP 跟本级的 lo:0 的 IP 一样,收下处理,然后修改 DST MAC 送出。

全程跟 ARP 相关的在于第一步只有 DR 应答了请求,而绑有同样 VIP 的 RS 却没有响应。最重要的就是 arp_ignore 以及 arp_announce 这两个 proc 值,前者表示是否以及如何忽略来自别人的 ARP 请求,而后者表示是否以及如何修改自己发出的 ARP 请求。
将 arp_ignore = 1 表示只响应 DST IP 是入口网络接口上配置的 IP 的 ARP 请求。对于 CLIENT 发出的针对 VIP 的 ARP 请求,由于 RS 的 lo 上设置了 arp_ignore = 1,则会忽略该请求,对于 RS 的 em1 来说,由于其绑定的 IP 跟 DST IP 不符合,也不会响应。所以只有 DR 来响应该请求,所以 CLIENT 能拿到 119.42.234.200 对应到 11:22:33:44:55:AA 的这个关系。默认情况下,arp_ignore 为 0,表示任何的 ARP 请求,任何配置在本地的目的 IP 都会回应;对于 RS,要设置为 1,也就是说如果该 ARP 请求的目的地址不是该 ARP 请求进入的接口地址,则不回应;除此之外,还有个 2, 这个比 1 的条件更苛刻,他要求 ARP 的源 IP 跟 ARP 请求进入的接口 IP 必须是一个网段。

将 arp_announce = 2 表示是否使用发送端口的 IP 地址来设置 ARP 的源地址。对于 RS,即通过 119.42.226.102 的接口将 119.42.226.200 的 SRC IP 给发送出去。

上面这些注意点不经常用会忘了,但是面试的时候好像经常会提到。


最后顺便说下可能遇到的攻击以及 IP 冲突下 ARP 问题。
攻击的问题基本都是内网,十有八九是中病毒了。要排查很简单,直接 arp -n 核对目前拿到的 GW 对应的 mac 跟真实的是否一样,不一样那就是出问题了。要解决同样很简单,可以直接绑定或者通过文件的方式绑定,注意 client 绑定了 gw 的 gw 的同样也要绑定 client 的。操作方式就不在这里说了。对于那种有部分客户机无法访问网络的情况,同样的十有八九是 ARP 的问题,上三层看看很可能发现意外的惊喜,这是我当年实习兼职 IT support 得到的结论。

另外,跟其相关的就是 IP 冲突造成的 arp 问题了,这个很早在我们的生产环境中出现过两次(1, 2),好在事不过三。
要发现这类问题,除了要对症状敏感之外,还可以借助一些工具,比如上面提到的 nmap(我们现在通过 namp 在每个 VLAN 部署了一个检测 IP 冲突的服务,效果很好) 以及 arpwatch,后者是直接打到 messages 文件里面,当然还有更直接的 arping:
# arping  192.168.1.116 -I em1
ARPING 192.168.1.116 from 192.168.1.15 em1
Unicast reply from 192.168.1.116 [78:2B:CB:4B:64:DA]  0.715ms
Unicast reply from 192.168.1.116 [78:2B:CB:2B:5A:D1]  0.927ms
Unicast reply from 192.168.1.116 [78:2B:CB:2B:5A:D1]  0.907ms

要删除对应的 ARP 列表很简单了,除了使用 arp 之外,ip(command) 也是一个很好的选择:
# arp -i YOU_INTERFACE -d YOU_IP
# ip -s -s neigh flush all
192.168.1.24 dev em1 lladdr 00:16:36:4e:3a:56 ref 10 used 38/0/8 REACHABLE
192.168.1.10 dev em1 lladdr 84:2b:2b:76:86:6e ref 2 used 156/0/3741485
REACHABLE
192.168.1.247 dev em1 lladdr 00:16:3e:06:01:20 ref 1 used 61/1472/56 STALE
192.168.1.13 dev em1 lladdr f0:4d:a2:01:76:58 ref 2 used 14/9/9 REACHABLE

*** Round 1, deleting 4 entries ***
*** Flush is complete after 1 round ***

要是不嫌麻烦,还可以这样:
#!/bin/sh
 
for i in `awk -F ' ' '{ if ( $1 ~ /[0-9{1,3}].[0-9{1,3}].[0-9{1,3}].[0-9{1,3}]/ ) print $1 }' /proc/net/arp`;
do
    arp -d $i
done