为什么 Linux 基于策略的路由 (PBR) 不适用于 ping?

为什么 Linux 基于策略的路由 (PBR) 不适用于 ping?

首先,这个问题看起来好像是关于Linux的,但在我看来,它是关于基本路由概念的。

我碰巧有以下配置:

在此处输入图片描述

我想要做的是确保服务器(CentOS 7)上的对称路由,以便来自它的传入和传出流量对于任何一对节点(使用两个网络接口)都采用相同的路径。

假设我192.168.0.210/24eno1和设置了静态 IP 地址(和与其他 Linux 发行版中的和相同)。192.168.1.210/24eno2eno1eno2eth0eth1

然后我创建了2个路由表(每个网络接口一个)/etc/iproute2/rt_tables

...
101     net1
102     net2

然后,我在每个路由表中创建了路由,并创建了策略路由规则,以将出站流量引导到适当的路由表,如下所示:

$ ip route show table net1
default via 192.168.0.1 dev eno1 
192.168.0.0/24 dev eno1 scope link

$ ip route show table net2
default via 192.168.1.1 dev eno2 
192.168.1.0/24 dev eno2 scope link

$ ip rule show
0:      from all lookup local 
101:    from 192.168.0.0/24 lookup net1 
102:    from 192.168.1.0/24 lookup net2 
32766:  from all lookup main 
32767:  from all lookup default

这些是我做的第一批测试(效果符合预期):

$ ip route get 192.168.100.100 from 192.168.0.210
192.168.100.100 from 192.168.0.210 via 192.168.0.1 dev eno1
    cache

$ ip route get 192.168.100.100 from 192.168.1.210
192.168.100.100 from 192.168.1.210 via 192.168.1.1 dev eno2
    cache

$ ip route get 192.168.100.100
192.168.100.100 via 192.168.1.1 dev eno2 src 192.168.1.210
    cache

最后,tshark我使用该工具开始监控网络接口eno1eno2通过每个接口发出请求,例如:

$ curl --interface eno1 https://google.com
$ curl --interface eno2 https://google.com
$ traceroute -i eno1 google.com
$ traceroute -i eno2 google.com
$ ping -I eno1 -c 2 google.com
$ ping -I eno2 -c 2 google.com

前 4 个命令按预期工作(tshark每个网络接口上都能正确捕获传入和传出流量),但其他ping命令则不然。以下是tshark这些ping命令的输出:

在此处输入图片描述

如您所见,ping仅适用于eno2。 经过反复试验,我意识到 仅ping适用于与通用默认网关关联的网络接口:

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 eno2
169.254.0.0     0.0.0.0         255.255.0.0     U     1002   0        0 eno1
169.254.0.0     0.0.0.0         255.255.0.0     U     1003   0        0 eno2
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 eno1
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 eno2

据我理解,ping即使没有设置通用默认网关,命令也应该有效,因为已经设置了默认网关net1并且net2应该使用,这是正确的吗?

为什么会发生这种情况?这与工作方式有关吗ping?为什么前 4 个命令有效?

答案1

两者之间是有区别的绑定到接口绑定到 IP 地址。虽然 2 个工作案例确实绑定到接口,但它们避免了ping遇到的问题(稍后解释)。让我们从修复开始ping。我复制了 OP 的设置以帮助给出说明。

路由查找时仅有的绑定到接口不是:

ip route get  from 192.168.1.210

但:

# ip route get oif eno2 to 192.168.100.100
192.168.100.100 dev eno2 src 192.168.1.210 uid 0 
    cache 

这里不涉及表 101 和 102,因为查找中没有指定本地源地址。此外,主路由表中没有 192.168.100.100 的默认路由。但由于接口被强制eno2,因此会自动创建此类默认路由...无网关. 可见的症状是,将会有从 192.168.1.210 到 192.168.100.100 发出的 ARP 请求,因为虚假路由告诉 192.168.100.100 是可直接访问的。

OP 还添加了(通常无用的)具有更高度量的附加默认路由,例如:

ip route add default via 192.168.1.1 dev eno2 metric 101

然后:

# ip route get oif eno2 to 192.168.100.100
192.168.100.100 via 192.168.1.1 dev eno2 src 192.168.1.210 uid 0 
    cache 

现在,由于已经有一个匹配的默认路由,因此eno2被选中,并且网关正确。ping现在可以正常工作。路由表 102 仍然不涉及。

绑定接口的路由规则选择器

实际正确的方法是使用表 102 中定义的路由oifIP 规则中的选择器:

oif 姓名

选择要匹配的传出设备。传出接口仅适用于源自来自绑定到设备的本地套接字

让我们使用它(并删除第二条默认路线以表明它不再需要):

ip route delete default via 192.168.1.1 dev eno2 metric 101
ip rule add oif eno1 lookup 101
ip rule add oif eno2 lookup 102

现在查找将匹配并变为:

# ip route get oif eno2 to 192.168.100.100
192.168.100.100 via 192.168.1.1 dev eno2 table 102 src 192.168.1.210 uid 0 
    cache 

这次,由于选择器匹配,因此使用了正确的路由表,并使用网关定义了默认值。

这才是必须要做的事。

注意:ping还接受绑定到 IP 地址(ping -I 192.168.1.210 -c 2 google.com)或甚至两者(ping -I eno2 -I 192.168.1.210 -c 2 google.com)。这些情况无需额外的路由规则即可工作,如前所述。


前两个案例为什么会成功呢?

(删除上面的更正然后...)

正如在先前的错误路由解析中所看到的:

# ip route get oif eno2 to 192.168.100.100
192.168.100.100 dev eno2 src 192.168.1.210 uid 0 
    cache 

正确的 IP 源地址仍会被选中。只要 TCP 必须发出数据包,其路由查找就会显示源地址 192.168.1.210。此情况匹配规则首选项 102 中的选择器:

# ip route get from 192.168.1.210 oif eno2 to 192.168.100.100
192.168.100.100 from 192.168.1.210 via 192.168.1.1 dev eno2 table 102 uid 0 
    cache 

表 102 仍然被规则首选项 102 选中。一旦选择了合适的表,无论为什么选择它,都会发生正确的路由。

对于 UDP 来说,情况稍微复杂一些,因为这取决于 UDP 客户端是否使用connect(2)然后其行为类似于之前的 TCP 情况:将使用源地址,或选择不使用connect(2)。例如,如果没有缺少路由规则,此命令将无法正确路由,因为它不使用connect(2),并且将在没有源的情况下查询路由堆栈(即:使用 INADDR_ANY = 0.0.0.0):

echo test | socat udp4-datagram:203.0.113.1:8888,so-bindtodevice=eno2 -

而这个会成功,因为它使用了connect(2)

echo test | socat          udp4:203.0.113.1:8888,so-bindtodevice=eno2 -

当然,一旦添加了两个缺失的路由规则,两者都可以正常工作。

可以connect使用以下命令检查 traceroute 是否使用strace

...
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_BINDTODEVICE, "eno2\0", 5) = 0
...
connect(3, {sa_family=AF_INET, sin_port=htons(33434), sin_addr=inet_addr("203.0.113.1")}, 28) = 0
...

相关内容