我有一台 Rhel 8.7 机器,有 2 个网卡位于不同的子网中。这么说吧eth1-IP:10.10.10.4/24 ,gateway:10.10.10.1
。该网关也是该虚拟机的默认网关,以及第二个 NIC:eth2, IP:10.10.20.2 , gateway:10.10.20.254
。
情况:这台机器已经配置了多个静态路由,我的猜测是,主路由表中存在我不想以某种方式破坏的路由。
两个网卡上有一个特定的 IP (10.10.30.33) 需要通过 ssh 连接(也允许使用 icmp,并用于测试)。由于我在 eth1 上有默认网关,因此位于不同子网中的这个 IP 完全可以连接到 eth1 Ip:10.10.10.4,但不能连接到 eth2:10.10.20.2,无需任何修改。当我设置静态路由来切换响应数据包通过 eth2 网关而不是默认网关时,可以访问,但与默认网关的接口不再可能连接,添加静态路由之前的连接可以工作。
目标:基于传入接口的特定 IP 的仅路由响应流量。如果 10.10.30.33 尝试到达 10.10.10.4,则应在 eth1 上发送响应,如果尝试到达 10.10.20.2,则应在 eth2 上发送响应数据包。基本上,我希望 10.10.30.33 可以同时访问这两个机器接口。
答案1
长话短说
只需运行以下 3 个命令即可使多宿主设置(仅)在 10.10.30.33 上运行:
ip route add 10.10.20.0/24 dev eth2 table 1002
ip route add default via 10.10.20.254 dev eth2 table 1002
ip rule add from 10.10.20.2 to 10.10.30.33 iif lo lookup 1002
详细解释
这需要策略路由,因为默认情况下 Linux 将仅使用主路由表中度量最低的默认路由。策略路由允许使用具有备用默认路由的备用路由表,具体取决于路由规则选择器来选择备用结果。在大多数情况下,选择器与源有关(而不是目的地:路由就足够了),但 OP 明确指定只有 10.10.30.33 应该触发不同的行为,因此还使用了关于 10.10.30.33 的附加选择器路由规则。
要使用 NetworkManager 在 RHEL 8 上正确集成此功能,或者使用已弃用的方法/etc/sysconfig/network-scripts/
(如果安装了 NetworkManager,我无法判断旧方法是否有效):
对于不限于 10.10.30.33 的通用多宿主使用,只需在以下路由规则中删除任何to 10.10.30.33
.
仅复制正在使用 的路由
eth2
,就好像eth1
备用路由表 1002 中不存在一样(随机选择值 1002):ip route add 10.10.20.0/24 dev eth2 table 1002 ip route add default via 10.10.20.254 dev eth2 table 1002
对于来自 10.10.20.2 的本地发起流量(包括回复)选择备用路由表 1002,请使用路由规则:
ip rule add from 10.10.20.2 to 10.10.30.33 iif lo lookup 1002
如果它有助于集成,则在大多数情况下,
iif lo
不需要并且可以从规则中删除。仅当满足以下条件时才需要下一条规则:
- 当(仅)绑定到接口(例如
ping -I eth2 10.10.30.33
:)而不是绑定到其 IP 地址(ping -I 10.10.20.2 10.10.30.33
)时 - 当主路由表中没有使用此绑定接口的默认路由(具有比所选默认路由更高的度量值)时。如果没有此路由,将强制使用默认路由到此接口,但没有网关,从而无法使用正确的路径(症状:系统将向其他网络发送 ARP)。
- 仅与 ICMP 客户端或未连接模式下的 UDP 客户端相关(即不使用
connect(2)
调用),因为在大多数设置中,将从以下位置设置正确的源 IP 地址:主要的如果套接字使用,则地址被设置为暗示源的路由表connect(2)
:在发出时无论如何都会触发先前的路由规则。
ip rule add to 10.10.30.33 oif eth2 lookup 1002
正如OP似乎确实添加了第二条默认路由:
第二个网卡:eth2,IP:10.10.20.2,网关:10.10.20.254
那么上面的规则就不需要了,主要的路由表已经足够好了。
在许多设置中,没有为备用接口 (
eth2
) 定义默认路由,因此该规则很有用。- 当(仅)绑定到接口(例如
传入流量不需要特定规则:传入数据包的路由查找由早期处理当地的路由表。
这可能是路由流量所需要的,例如,如果机器正在运行虚拟机或容器。 OP没有提到这一点。无论如何,这需要更详细的信息,并且可能涉及 Netfilter (iptables或者nftables)适用于路由器外壳。
从现在开始,系统 10.10.30.33 应该能够 ssh 或 ping 到 10.10.10.4 或 10.10.20.2。同样(如果没有防火墙阻止)双宿主机可以选择通常使用主路由表中的默认设置或通过绑定到接口eth2
(例如:ping -I eth2 10.10.30.33
或ssh -B eth2 10.10.30.33
)或地址 10.10.20.2来访问 10.10.30.33 (例如:ping -I 10.10.20.2 10.10.30.33
或ssh -b 10.10.20.2 10.10.30.33
)。
等效配置 abouteth1
是可选的,因为已经在主路由表中正确处理。也会这样做:
ip route add 10.10.10.0/24 dev eth1 table 1001
ip route add default via 10.10.10.1 dev eth1 table 1001
ip rule add from 10.10.10.4 to 10.10.30.33 iif lo lookup 1001
注意:UDP 服务
默认情况下UDP 回复不会继承上下文来了解传入查询的本地系统的 IP 地址或接口是什么。因此,默认情况下,当 UDP 套接字绑定到 INADDR_ANY (0.0.0.0) 时,0.0.0.0 将是呈现给网络堆栈的初始源地址,以解析路由、接口和太晚的源 IP 地址。这意味着之前的路由规则都不会匹配,因为 10.10.20.2 没有呈现给这些规则:主要的接口仍然可以选择。一旦知道接口,就会为该接口选择适当的 IP 地址,并且默认情况下将是所选接口的路由主 IP 地址。这意味着,不支持多宿主感知的 UDP 服务eth1
在收到对 10.10.20.2 的请求时,仍将使用错误的接口 ( ) 和地址 (10.10.10.4) 进行回复(除非它来自 LAN 10.10.20.0/24)工作)。此行为可能不仅限于 Linux,还可能出于类似原因影响其他使用弱功能的操作系统。主机型号和 BSD 套接字 API。
如果此类 UDP 服务需要从两个接口进行访问,有两种方法可以解决此问题:
显式绑定到每个地址,而不是
INADDR_ANY
.例如,如果 HTTP/3 服务使用 UDP 端口 80,而不是绑定到 0.0.0.0:80,则应将其配置为使用两个单独的套接字绑定两次:绑定到 10.10.10.4:80 和 10.10.20.2:80。在这种情况下,通过UDP套接字的回复将选择绑定的地址作为源,触发正确的路由规则和接口。例如,这就是 DNS 服务器通常所做的事情绑定9。大多数应用程序对此都有特定的配置。让应用程序在处理 UDP 时能够感知多宿主。这需要应用程序激活辅助消息检索实际传入接口和/或接收 UDP 数据包的地址,使用
IP_PKTINFO
套接字选项并在回复时使用此信息。例如,这就是 DNS 服务器通常所做的事情不受约束的。这需要应用程序中的特定代码。
TCP 不会遇到这个问题:回复具有上下文来选择正确的源地址,从而触发正确的路由规则。