在一个多网卡的计算机中(Linux),假设网卡为 AB 同时联入同一个子网,每个网卡具有唯一的 MAC 地址(MAC_AMAC_B)和 IP 地址(IP_AIP_B)。
当同一子网的主机发起 ARP 请求,询问IP_A的MAC是谁的时候,这个主机该如何应答?
按照正常情况应该是返回MAC_A是吧,但是可能事实并不是这样。

实验1:

  1. 主机有两个网卡:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    enp2s0: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST> mtu 1500
    inet 192.168.2.125 netmask 255.255.255.0 broadcast 192.168.2.255
    ether fc:aa:14:5b:48:8c txqueuelen 1000 (以太网)
    .....

    enp4s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
    inet 192.168.2.174 netmask 255.255.255.0 broadcast 192.168.2.255
    inet6 fe80::c62f:6221:bb0:5bba prefixlen 64 scopeid 0x20<link>
    ether 08:1f:71:0b:36:71 txqueuelen 1000 (以太网)
    ......
  2. 路由表如下:

    1
    2
    3
    0.0.0.0         192.168.2.1     0.0.0.0         UG    0      0        0 enp2s0
    0.0.0.0 192.168.2.1 0.0.0.0 UG 100 0 0 enp4s0
    192.168.2.0 0.0.0.0 255.255.255.0 U 100 0 0 enp4s0

    注意子网的网段 为 192.168.2.0/24, 并且所有包仅通过 enp4s0

  3. 此时另一个主机 192.168.2.112 发起 ping 192.168.2.125 (125 的 IP 分配给了 enp2s0,参考上面打印的信息👆)
    首先 112 的主机会发起 ARP 请求,按照理论应该是返回 enp2s0mac: fc:aa:14:5b:48:8c.
    但是通过抓包发现

    1
    2
    3
    4
    5
    └[~]>  tcpdump arp -i enp7s0 and '(src 192.168.2.125 or dst 192.168.2.125)' -nn
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on enp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
    21:21:34.968758 ARP, Request who-has 192.168.2.125 tell 192.168.2.112, length 28
    21:21:34.968906 ARP, Reply 192.168.2.125 is-at 08:1f:71:0b:36:71, length 46

    可以看出 112 的 ARP 请求 125 的MAC地址,接收到了 08:1f:71:0b:36:71 ,此MAC地址正是 enp4s0,而非期望的 enp2s0

原因

其实这是一个正常并且正确的现象,注意在路由表中,我们设置了子网的所有 IP 通信都是通过 enp4s0的,所以返回enp4s0的 MAC 地址,更加“符合”我们的设置,也提高了通信的成功率。也就是说默认情况下,ARP 如何返回 MAC 地址是根据路由表情况决定的。

那如何改变这一规则让其返回 “正确”(和 IP匹配)的 MAC 地址呢?答案是修改 arp_filter 参数。

arp_filter - BOOLEAN
1 - Allows you to have multiple network interfaces on the same
subnet, and have the ARPs for each interface be answered
based on whether or not the kernel would route a packet from
the ARP’d IP out that interface (therefore you must use source
based routing for this to work). In other words it allows control
of which cards (usually 1) will respond to an arp request.

0 - (default) The kernel can respond to arp requests with addresses
from other interfaces. This may seem wrong but it usually makes
sense, because it increases the chance of successful communication.
IP addresses are owned by the complete host on Linux, not by
particular interfaces. Only for more complex setups like load-
balancing, does this behaviour cause problems.

默认情况下设置为 0, 所以想要恢复“正确”的行为只需要修改为 1 即可。如何修改请参考 Preventing incorrect ARP entries

实验2

为了验证上面的说法,我们试着改变路由表规则,重新重复实验1

  1. 增加路由表,让 192.168.2.112 的路由通过 enp2s0:

    1
    ip route add 192.168.2.112 dev enp2s0

    此时路由表增加一条到 192.168.2.112的规则:

    1
    2
    3
    4
    5
    6
    内核 IP 路由表
    目标 网关 子网掩码 标志 跃点 引用 使用 接口
    0.0.0.0 192.168.2.1 0.0.0.0 UG 0 0 0 enp2s0
    0.0.0.0 192.168.2.1 0.0.0.0 UG 20100 0 0 enp4s0
    192.168.2.0 0.0.0.0 255.255.255.0 U 100 0 0 enp4s0
    192.168.2.112 0.0.0.0 255.255.255.255 UH 0 0 0 enp2s0
  2. 192.168.2.112 发起 ping 192.168.2.125, 并查看 ARP 协议包:

    1
    2
    3
    4
    5
    └[~]> sudo tcpdump arp -i enp7s0 and '(src 192.168.2.125 or dst 192.168.2.125)' -nn
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on enp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
    21:44:12.289258 ARP, Request who-has 192.168.2.125 tell 192.168.2.112, length 28
    21:44:12.289406 ARP, Reply 192.168.2.125 is-at fc:aa:14:5b:48:8c, length 46

    正确返回了 enp2s0的 MAC 地址 fc:aa:14:5b:48:8c

建议保持默认配置,非必要请不要修改网络参数

参考:

[1]. Unix & Linux Stack Exchange. 《sysctl parameter for correct ARP response》. 见于 2021年4月14日. https://unix.stackexchange.com/questions/31096/sysctl-parameter-for-correct-arp-response.

[2]. Server Fault. 《ethernet - Multi-NIC Linux machine’s NICs are responding to each other’s ARPs regardless of arp_filter setting》. 见于 2021年4月14日. https://serverfault.com/questions/314522/multi-nic-linux-machines-nics-are-responding-to-each-others-arps-regardless-of.

[3]. Server Fault. 《linux - Adding a route to specific host go out a specific interface》. 见于 2021年4月14日. https://serverfault.com/questions/466152/adding-a-route-to-specific-host-go-out-a-specific-interface.

[4]. ip-sysctl. 《IBM Docs》, 2014年10月24日. https://www.ibm.com/docs/en/linux-on-systems.

[5].《ip-sysctl》. 见于 2021年4月14日. https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt.