遇到一个奇怪的问题:从指定 IP 地址的 VLAN 接口执行 ping 操作有效,但指定接口名称时却不起作用。
该系统运行的是 Rocky Linux 8.5 (4.18.0-348.el8.0.2.x86_64)。它插入了一个网络接口,使用旧式网络脚本设置了 4 个桥接接口(请参见下面的脚本)。
当在不指定任何接口/IP 的情况下 ping 到 8.8.8.8 时,一切都会按预期进行。如果我指定br0
,一切都会按预期工作。如果我指定br1
、br2
或br3
,我不会得到回复。如果我指定与br1
等关联的 IP 地址,那么它会按预期工作。
Ping 输出
# PING WORKS
$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=104 ms
^C
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 104.224/104.224/104.224/0.000 ms
# PING WORKS
$ ping -I br0 8.8.8.8
PING 8.8.8.8 (8.8.8.8) from 10.3.15.22 br0: 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=101 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=118 time=101 ms
# PING FAILS
$ ping -I br1 8.8.8.8
PING 8.8.8.8 (8.8.8.8) from 10.4.15.22 br1: 56(84) bytes of data.
^C
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2031ms
# PING WORKS
$ ping -I 10.4.15.22 8.8.8.8
PING 8.8.8.8 (8.8.8.8) from 10.4.15.22 : 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=119 time=4.72 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=119 time=4.65 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=119 time=4.65 ms
^C
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 4.645/4.672/4.721/0.065 ms
从下面的输出中可以看到ip addr
,接口br1
的 IP 地址为 10.4.15.22。同样,它被设置为使用路由表 vlan4 并具有与其关联的正确规则。
输出来自ip addr show
# ip addr show
3: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq master br0 state UP group default qlen 1000
link/ether 00:25:90:8e:b4:98 brd ff:ff:ff:ff:ff:ff
6: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP group default qlen 1000
link/ether 00:25:90:8e:b4:98 brd ff:ff:ff:ff:ff:ff
inet 10.3.15.22/16 brd 10.3.255.255 scope global br0
valid_lft forever preferred_lft forever
7: eno1.4@eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master br1 state UP group default qlen 1000
link/ether 00:25:90:8e:b4:98 brd ff:ff:ff:ff:ff:ff
8: br1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP group default qlen 1000
link/ether 00:25:90:8e:b4:98 brd ff:ff:ff:ff:ff:ff
inet 10.4.15.22/16 brd 10.4.255.255 scope global br1
valid_lft forever preferred_lft forever
9: eno1.5@eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master br2 state UP group default qlen 1000
link/ether 00:25:90:8e:b4:98 brd ff:ff:ff:ff:ff:ff
10: br2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP group default qlen 1000
link/ether 00:25:90:8e:b4:98 brd ff:ff:ff:ff:ff:ff
inet 10.5.15.22/16 brd 10.5.255.255 scope global br2
valid_lft forever preferred_lft forever
11: eno1.6@eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue master br3 state UP group default qlen 1000
link/ether 00:25:90:8e:b4:98 brd ff:ff:ff:ff:ff:ff
12: br3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP group default qlen 1000
link/ether 00:25:90:8e:b4:98 brd ff:ff:ff:ff:ff:ff
inet 10.6.15.22/16 brd 10.6.255.255 scope global br3
valid_lft forever preferred_lft forever
系统配置/网络脚本
$ cat /etc/sysconfig/network-scripts/ifcfg-eno1
TYPE=Ethernet
BOOTPROTO=none
DEVICE=eno1
ONBOOT=yes
BRIDGE=br0
NM_CONTROLLED=no
MTU=9000
$ cat /etc/sysconfig/network-scripts/ifcfg-eno1.4
DEVICE=eno1.4
BRIDGE=br1
TYPE=Ethernet
BOOTPROTO=none
ONBOOT=yes
NM_CONTROLLED=no
MTU=9000
VLAN=yes
$ cat /etc/sysconfig/network-scripts/ifcfg-br0
DEVICE=br0
TYPE=Bridge
BOOTPROTO=static
IPADDR=10.3.15.22
GATEWAY=10.3.0.1
DNS1=10.3.0.1
NETMASK=255.255.0.0
ONBOOT=yes
NM_CONTROLLED=no
DEFROUTE=yes
$ cat /etc/sysconfig/network-scripts/ifcfg-br1
DEVICE=br1
IPADDR=10.4.15.22
GATEWAY=10.4.0.1
DNS1=10.4.0.1
DEFROUTE=no
TYPE=Bridge
BOOTPROTO=static
NETMASK=255.255.0.0
ONBOOT=yes
NM_CONTROLLED=no
$ cat /etc/sysconfig/network-scripts/route-br1
10.4.0.0/16 dev br1 src 10.4.15.22 table vlan4
default via 10.4.0.1 dev br1 table vlan4
$ cat /etc/sysconfig/network-scripts/rule-br1
from 10.4.15.22/32 table vlan4
to 10.4.15.22/32 table vlan4
/etc/iproute2/rt_tables
$ cat /etc/iproute2/rt_tables
3 vlan3
4 vlan4
5 vlan5
6 vlan6
ip route
和ip rule
输出
$ ip route show
default via 10.3.0.1 dev br0
10.3.0.0/16 dev br0 proto kernel scope link src 10.3.15.22
10.4.0.0/16 dev br1 proto kernel scope link src 10.4.15.22
10.5.0.0/16 dev br2 proto kernel scope link src 10.5.15.22
10.6.0.0/16 dev br3 proto kernel scope link src 10.6.15.22
169.254.0.0/16 dev br0 scope link metric 1006
169.254.0.0/16 dev br1 scope link metric 1008
169.254.0.0/16 dev br2 scope link metric 1010
169.254.0.0/16 dev br3 scope link metric 1012
$ ip route show table vlan4
default via 10.4.0.1 dev br1
10.4.0.0/16 dev br1 scope link src 10.4.15.22
$ ip rule
0: from all lookup local
32758: from all to 10.6.15.22 lookup vlan6
32759: from 10.6.15.22 lookup vlan6
32760: from all to 10.5.15.22 lookup vlan5
32761: from 10.5.15.22 lookup vlan5
32762: from all to 10.4.15.22 lookup vlan4
32763: from 10.4.15.22 lookup vlan4
32764: from all to 10.3.15.22 lookup vlan3
32765: from 10.3.15.22 lookup vlan3
32766: from all lookup main
32767: from all lookup default
答案1
通过使用绑定接口的路由规则充分选择路由表SO_BINDTODEVICE
, 带有关键字的规则oif
还应该使用:
oif
姓名选择要匹配的传出设备。传出接口仅适用于源自绑定到设备的本地套接字的数据包。
否则,在初始查找期间,当未指定源地址时,查找将使用默认值INADDR_ANY
(又名 0.0.0.0/0)作为源选择器,然后再计算出正确的源 IP 地址(例如,从提示源或其他算法)。由于没有添加的路由规则将与INADDR_ANY
所有其他表相匹配,因此将跳过所有附加表,这最终将仅使用主要的带有绑定设备附加过滤器的路由表:没有定义默认路由,因此没有使用足够的网关,除非br0
主表中也有默认路由。
因此,应该添加这些命令,每一行都在其各自的/etc/sysconfig/network-scripts/rule-brX
文件中(减号ip rule add
):
ip rule add oif br0 lookup 3
ip rule add oif br1 lookup 4
ip rule add oif br2 lookup 5
ip rule add oif br3 lookup 6
以便在绑定到接口时查找适当的路由表,从而在每种情况下提供正确的网关。
所有内核评估都可以通过以下方式验证ip route get
:
ip route get
获取单一路线
该命令获取到目的地的单个路由并完全按照内核所看到的方式打印其内容。
附加规则之前
br0
假设为了对称性,还有一个像这样的路由表(但这并不重要:主要的路由表足以满足
br0
):default via 10.3.0.1 dev br0 table 3 10.3.0.0/16 dev br0 table 3 scope link src 10.3.15.22 # ip route get from 10.3.15.22 to 8.8.8.8 8.8.8.8 from 10.3.15.22 via 10.3.0.1 dev br0 table 3 uid 0 cache # ip route get oif br0 to 8.8.8.8 8.8.8.8 via 10.3.0.1 dev br0 src 10.3.15.22 uid 0 cache
在第二种情况下,当仅绑定到接口而不选择源地址时,将使用主表,但这仍然是正确的,因为主表也有默认路由
br0
:它可以工作br1
# ip route get from 10.4.15.22 to 8.8.8.8 8.8.8.8 from 10.4.15.22 via 10.4.0.1 dev br1 table 4 uid 0 cache # ip route get oif br1 to 8.8.8.8 8.8.8.8 dev br1 src 10.4.15.22 uid 0 cache
在第二种情况下,由于没有定义网关主要的表中,路由不使用网关,并且由于接口是强制的,因此假设 8.8.8.8 是直接连接的(
tcpdump
可能会显示关于 8.8.8.8 发出的 ARP 查询,如果有的话)。这不起作用。
绑定接口案例的附加规则之后
br0
# ip route get oif br0 to 8.8.8.8 8.8.8.8 via 10.3.0.1 dev br0 table 3 src 10.3.15.22 uid 0 cache
虽然总体结果是相同的:它有效,但这次使用了附加表。
br1
# ip route get oif br1 to 8.8.8.8 8.8.8.8 via 10.4.0.1 dev br1 table 4 src 10.4.15.22 uid 0 cache
这次,
oif br1
选择器选择了路由表 4,它确实定义了具有正确网关的正确默认路由:它有效。
注意:使用 L3 接口的类似设置(例如:VPN 接口,如 WireGuard 或 OpenVPN)屯模式)不需要这个,因为 L3 接口不需要任何网关(即使定义了它也不会被使用,因为 IP 下面没有 L2 层需要解析),因此绑定到时发出的流量不需要特定的路由定义这样的L3接口。无论如何,使用它仍然是一个好主意,特别是当路由表包含路由否定时。