Linux内核参数之arp_ignore和arp_announce

Linux内核参数之arp_ignore和arp_announce

微信搜索 zze_coding 或扫描 👉 二维码关注我的微信公众号获取更多资源推送:

介绍

在配置 LVS 和一些 Linux 服务器作为网关的场景中,都会需要设置 arp_announcearp_ignore 这两个参数。网上也有很多文章对这两个参数做相应的解释,并且也有对照内核官方说明文档的相应翻译,关于 arp_ignore 的意义还是比较容易理解的,但是关于 arp_announce 的解释,我觉得很多都不到位,甚至有越看越糊涂的情况。下面我就结合自己的思考对这两个参数给出我的理解。

顾名思义,arp_announcearp_ignore 是和 ARP 协议(Address Resolution Protocal,地址解释协议)有关的,所以要真正了解这两个参数,首先要理解 ARP 的原理。ARP 协议的作用就是根据 IP 地址获取其对应的 MAC 地址。在以太网中,主机要发送一个数据包,首先需要获取目标 MAC 地址(如果通信的目标主机在同一网段,那目标 MAC 地址就是目标主机的 MAC 地址,否则应该为网关的 MAC 地址或下一跳的 MAC 地址),而获取目标 MAC 地址的方法就是使用 ARP 协议发送 ARP 请求数据包,ARP 请求数据包中会包含发送方的 IP 地址和 MAC 地址,并且包含想要获取 MAC 地址的目标 IP 地址,该 ARP 请求数据包的数据链路层目标地址为广播地址,也就是说它是一个二层广播数据包(因为不知道谁拥有指定目标 IP 地址,所以只能使用广播方式发送)。在同一局域网的所有主机都将收到该 ARP 请求数据包,但是只有拥有目标 IP 地址的主机才会发送 ARP 响应数据包,该 ARP 响应数据包包含发送方的 IP 地址和 MAC 地址(即对方想要知道的目标 MAC 地址),并且目标 IP 地址和 MAC 地址设置为对方的地址(即 ARP 请求数据包发送方的地址),并且该数据包为单播数据包(其数据链路层目标地址为对方的 MAC 地址),下图中为 ARP 请求数据包和 ARP 响应数据包的抓包截图,大家可结合抓包信息理解上面的内容:

image.png

image.png

arp_announce

理解了 ARP 协议的原理后,我们再来看上面所说的两个参数。
arp_announce 参数是定义 Linux 主机发送 ARP 请求数据包时如何选择数据包中使用的发送方 IP 地址(即 Sender IP address)。在系统准备通过网卡发送一个 IP 数据包前,该 IP 数据包的源 IP 地址和目标 IP 地址通常是已经知道的,同时发送的网卡也已经确定,那数据链路层的源 MAC 地址当然也确定了,最后剩下的就是确定数据链路层的目标 MAC 地址了,而该目标 MAC 地址就需要通过 ARP 地址解释协议来获取,于是系统首先需要发送 ARP 请求数据包获取目标 MAC 地址。结合上面关于 ARP 原理的描述,我们知道,发送 ARP 请求数据包时需要包含发送方 IP 地址,该 IP 地址应该是什么呢?大家可能想当然的以为就是要发送的 IP 数据包的源 IP 地址,其实这个是不一定的,尤其是主机有多个网络接口和 IP 地址时,而 arp_announce 正是控制该发送方 IP 地址的选择条件的。arp_announce 参数的取值分别是 012,这些取值的意义如下:

  • 0(默认值):允许使用任一网络接口配置的 IP 地址(即任一本地地址),通常就是待发送的 IP 数据包的源 IP 地址;
  • 1:尽量避免使用不属于该网络接口(即发送数据包的网络接口)子网的本地地址作为发送方 IP 地址。根据我对官方原文的理解,就是说如果主机包含多个子网,而 IP 数据包的源 IP 地址属于其中一个子网,虽然该 IP 地址不属于发送网口的子网,但是也可以作为 ARP 请求数据包的发送方 IP 地址,否则就会按照取值为 2 的方式选择发送方 IP 地址;
  • 2:忽略 IP 数据包的源 IP 地址,总是选择网口所配置的最合适的 IP 地址作为 ARP 请求数据包的发送方 IP 地址(一个网口可能会配置多个IP地址);

arp_ignore

arp_ignore 参数是定义 Linux 主机在收到 ARP 请求数据包后,发送 ARP 响应数据包的条件级别,该参数的取值范围是 0~8,各取值的意义分别是:

  • 0(默认值):只要 ARP 请求数据包所请求的 IP 地址属于任一本地地址(即任意一个本机配置的 IP 地址),就会回应 ARP 响应数据包,即使该IP地址不属于接收到 ARP 请求数据包的网卡;
  • 1:只有 ARP 请求数据包所请求的 IP 地址属于当前网卡的 IP 地址,才会回应 ARP 响应数据包;
  • 2:除了满足 1 的条件外,还要满足 ARP 请求数据包的发送方 IP 地址也属于当前网卡所属子网,这样才会回应 ARP 响应数据包;
  • 3:如果 ARP 请求数据包所请求的 IP 地址对应的本地地址其作用域(scope)为主机(host),则不回应 ARP 响应数据包,如果作用域为全局(global)或链路(link),则回应 ARP 响应数据包。
  • 4~7:保留;
  • 8:即使 ARP 请求数据所请求的 IP 地址属于任何一个本地地址,也不回应 ARP 响应数据包;

arp_ignore 参数应该很好理解,它在网络配置中的作用也很清楚,但是 arp_announce 的作用是什么呢?设置为不同的值会有什么不同的作用呢?或者说有什么不同的影响呢?下面我们来举个例子说明不同值的影响。不过要理解以下的影响,我们要了解,对于网络设备(三层交换机、路由器)及主机,其 `ARP 地址表(包含 IP 地址和 MAC 地址对应关系的表)是如何建立的:当设备或主机收到一个 ARP 请求数据包时,它会把 ARP 请求数据包的发送方 IP 地址和 MAC 地址的对应关系放入自身的 ARP 地址表。

案例

理解了上面所说的,我们来看看实际的案例场景:

假设 Linux 主机有 A、B 两块网卡,其对应的 IP 地址分别为 IP_A、IP_B,对应的 MAC 地址为 MAC_A、MAC_B,假设一个应用程序准备与外部通信,它的 socket 绑定了源 IP 地址为IP_A,但是根据系统路由及相关设置,其通信数据包将会从 B 网卡发送,在发送数据包前,系统会通过网卡 B 发送 ARP 请求数据包。
如果我们将 arp_announce 的值设定为 0,那该 ARP 请求数据包的发送方 IP 地址是 IP_A,而发送方 MAC 地址为 MAC_B,这样就会在网络设备或对方主机的 ARP 地址表上留下 IP_A 与 MAC_B 的对应记录,但是实际正确的应该是 IP_A 对应 MAC_A、IP_B 对应 MAC_B,所以这可能会引起潜在的网络问题,具体问题和表现与网络的拓扑结构及网络配置有关。而如果我们将 arp_announce 设置为 2,那在发送 ARP 请求数据包时,发送方 IP 地址将不是 IP_A,而是 IP_B,这样就不会引起刚才所说的问题。至于 arp_announce=1 的情景,我还不是很清楚,所以无法举出具体的例子。

如果你还是没懂,那就看一下对这两个参数的图解示例~~

1、当 arp_ignore 参数配置为 0 时,eth0 网卡上收到目的 IP 为回环网卡 IP 的 ARP 请求,但是 eth0 也会返回 ARP 响应,把自己的 MAC 地址告诉对端。

image.png

2、当 arp_ignore 参数配置为 1 时,eth1 网卡上收到目的 IP 为回环网卡 IP 的 ARP 请求,发现请求的 IP 不是自己网卡上的 IP,不会回 ARP 响应。

image.png

3、当 arp_announce 参数配置为 0 时,系统要发送的 IP 包源地址为 eth0 的地址,IP 包目的地址根据路由表查询判断需要从 eth1 网卡发出,这时会先从 eth1 网卡发起一个 ARP 请求,用于获取目的 IP 地址的 MAC 地址。该 ARP 请求的源 MAC 自然是 eth1 网卡的 MAC 地址,但是源 IP 地址会选择 eth0 网卡的地址。

image.png

4、当 arp_announce 参数配置为 2 时,eth1 网卡发起 ARP 请求时,源 IP 地址会选择 eth1 网卡自身的 IP 地址。
image.png

在 lvs-dr 模式下的应用

arp_ignore

因为 lvs-dr 模式下,每个真实服务器节点都要在环回网卡上绑定虚拟服务 IP。这时候,如果客户端对于虚拟服务 IP(VIP)的 ARP 请求广播到了各个真实服务器(RS)节点,如果 arp_ignore 参数配置为 0,则各个 RS 节点都会响应该 arp 请求,此时客户端就无法正确获取 LVS 节点上正确的 VIP 所在网卡的 MAC 地址。假如某个真实服务器节点 A 的网卡 eth1 响应了该 ARP 请求,客户端把 A 节点的 eth1 网卡的 MAC 地址误认为是 LVS 节点的 VIP 所在网卡的 MAC,从而将业务请求消息直接发到了 A 节点的 eth1 网卡。这时候虽然因为 A 节点在环回网卡上也绑定了虚拟服务IP,所以 A 节点也能正常处理请求,业务暂时不会受到影响。但是此时由于客户端请求没有发到 LVS 的 VIP 上,所以 LVS 的负载均衡能力没有生效。造成的后果就是,A 节点一直在单节点运行,业务量过大时可能会出现性能瓶颈。

所以 DR 模式下要求 arp_ignore 参数要求配置为 1

arp_announce

上面有说到,每个机器或者交换机中都有一张 ARP 表,该表用于存储对端通信节点 IP 地址和 MAC 地址的对应关系。当收到一个未知 IP 地址的 ARP 请求,就会在本机的 ARP 表中新增对端的 IP 和 MAC 记录;当收到一个已知 IP 地址(ARP 表中已有记录的地址)的 ARP 请求,则会根据 ARP 请求中的源 MAC 刷新自己的 ARP 表。

如果 arp_announce 参数配置为 0,则网卡在发送 ARP 请求时,可能选择的源 IP 地址并不是该网卡自身的 IP 地址,这时候收到该 ARP 请求的其他节点或者交换机上的 ARP 表中记录的该网卡 IP 和 MAC 的对应关系就不正确,可能会引发一些未知的网络问题,存在安全隐患。
所以 DR 模式下要求 arp_announce 参数要求配置为 2

配置方法

arp_ignorearp_announce 可以针对单个网卡进行配置,此时使用网卡名,也可以针对所有网卡进行配置,此时使用 all 代指所有网卡。当 all 和具体网卡的参数值不一致时,取较大值生效。

一般只需修改 all 和某个具体网卡的参数即可(取决于你需要修改哪个网卡)。下面以修改 lo 网卡为例:

永久生效

修改 /etc/sysctl.conf 文件,然后 sysctl -p 刷新到内存。

net.ipv4.conf.all.arp_ignore=1
net.ipv4.conf.lo.arp_ignore=1
net.ipv4.conf.all.arp_announce=2
net.ipv4.conf.lo.arp_announce=2

临时生效

设定临时生效方式有两种,一是使用 sysctl -w 直接写入内存:

$ sysctl -w net.ipv4.conf.all.arp_ignore=1
$ sysctl -w net.ipv4.conf.lo.arp_ignore=1
$ sysctl -w net.ipv4.conf.all.arp_announce=2
$ sysctl -w net.ipv4.conf.lo.arp_announce=2

二是修改 /proc 伪文件系统,如下:

$ echo "1">/proc/sys/net/ipv4/conf/all/arp_ignore
$ echo "1">/proc/sys/net/ipv4/conf/lo/arp_ignore
$ echo "2">/proc/sys/net/ipv4/conf/all/arp_announce
$ echo "2">/proc/sys/net/ipv4/conf/lo/arp_announce

整理自:

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://www.zze.xyz/archives/lvs3.html

Buy me a cup of coffee ☕.