我正在使用全新安装的 Ubuntu 服务器 24.04.1 LTS。我以 root 身份运行以下命令来设置网络并进行一些实验:
apt install -y netcat-traditional tcpdump inetutils-ping
ip netns add ns1
ip netns add ns2
ip link add my_veth1 type veth peer name my_veth2
ip link set my_veth1 up netns ns1
ip link set my_veth2 up netns ns2
ip -n ns1 address add 1.2.3.4 dev my_veth1
ip -n ns1 route add 2.3.4.0/24 dev my_veth1
ip -n ns2 address add 2.3.4.5 dev my_veth2
然后我在不同的终端运行这些命令:
# Terminal 1
ip netns exec ns1 tcpdump -l -i my_veth1
# Terminal 2
ip netns exec ns2 tcpdump -l -i my_veth2
# Terminal 3
ip netns exec ns1 ping 2.3.4.5
我在终端 1 和 2 上得到了相同的输出:
02:40:27.511438 ARP, Request who-has 2.3.4.5 tell 1.2.3.4, length 28
02:40:27.511438 ARP, Request who-has 2.3.4.5 tell 1.2.3.4, length 28
02:40:27.511438 ARP, Request who-has 2.3.4.5 tell 1.2.3.4, length 28
...
veth2
具有 IP 地址 2.3.4.5 并正在接收 ARP 请求。为什么它不发送答复?只有当我配置路由表条目时它才会应答:
ip -n ns2 route add 1.2.3.0/24 dev my_veth2
但这不是必需的,因为veth2
应该响应的网络接口的 MAC 地址已经在其响应的请求中编码了。
ARP 是一种 IP 层协议;内核默认认为,响应那些无法通过任何路由到达的地址的 ARP 请求是没有意义的。
如果您对特定的内核逻辑感到好奇,它从函数开始
net/ipv4/arp.c
,arp_rcv
该函数安排调用arp_process
,该函数调用ip_route_input_noref
,这最终导致数据包被丢弃,因为地址不可达。您可以通过禁用 sysctl 来稍微改变行为
rp_filter
,它可以采用以下值:0
- 没有来源验证。1
- 严格模式如 RFC3704 中定义。严格反向路径:每个传入数据包都会根据 FIB 进行测试,如果接口不是最佳反向路径,数据包检查将失败。默认情况下,失败的数据包将被丢弃。
2
- 按照RFC3704 定义的松散模式。松散反向路径:每个传入数据包的源地址也会根据 FIB 进行测试,如果无法通过任何接口访问源地址,则数据包检查将失败。
如果通过设置为禁用源
rp_filter
验证0
:使用的值是的最大值
my_veth2
,all
因此我们也需要禁用它all
(在我的系统上,该all
值已经是0
,但情况并非总是如此,因此最好明确设置它)。然后你会看到内核实际上响应了 ARP 请求。如果我运行 run
ip netns exec ns1 ping 2.3.4.5
,我会看到以下内容ns1
:并且
ns2
:在关闭源验证的情况下,内核响应了 ARP 请求,但是两个命名空间无法通信,因为仍然没有从 ns2 到 ns1 的路由,所以 ns2 无法响应请求
ping
。您可能将
arp_ignore
sysctl 设置为 2。传统上,Linux 会为属于所有接口的本地地址应答 ARP(即,好像这些地址属于整个主机),例如,eth0 将代表分配给 eth2 的本地地址用自己的 MAC 地址应答。这通常是意料之外的(我想不出有多少情况会用到它),许多管理员通过设置来禁用该行为,这使得 Linux 只为分配给同一
arp_ignore=1
接口的地址应答 ARP 。(这是 IPv6 NDP 在 Linux 上始终的行为方式,也与 IPv4 ARP 在其他几个操作系统上的行为方式相同,因此通常是一个不错的选择。)arp_ignore=1
但是有些系统会更进一步,如果发送者的 IP 地址(也在 ARP 查询中编码)不是“同一子网的一部分”,则还会使用
arp_ignore=2
此功能来抑制 ARP 响应,在 Linux 术语中,这是通过检查是否有通向它的链接路由来确定的。这种限制在多个 IP 子网共享同一以太网广播域的情况下可能有意义……尽管通常此类 ARP 请求(如您的请求)在正常使用中不会发生。还有另一个类似的 sysctl,
arp_filter
其中条件是“...内核是否会将数据包从 ARP 的 IP 路由出该接口(因此您必须使用基于源的路由才能使其工作)。”