当存在多个网络时允许往返网络的路由

当存在多个网络时允许往返网络的路由

我在这里遇到了难题,但似乎无法找出正确的路线。

我有一台服务器 (serverA),它位于两个独立的网络上: 192.168.200.x/24& 192.168.117.0/26。此服务器在 & 上有一个主机名 ( serverA.example.com),192.168.117.70通过它有一个单独的连接192.168.200.45(这是在此网络上安装存储/等所必需的)。

我没有办法在内部进行拆分 DNS,因此我需要将所有内容路由到此 IP/主机名以确保用户的理智。

目前的一切不是网络上的192.168.200.x能够serverA.example.com正常访问。但是 上的所有系统192.168.200.x都无法路由到IP 地址,但它们可以正常192.168.117.70访问。192.168.200.45

这是我在服务器A上设置的路由:

0.0.0.0           192.168.117.65    0.0.0.0         UG    0      0        0 onboard-10Gb-1
192.168.117.64    0.0.0.0         255.255.255.192 U     0      0        0 onboard-10Gb-1
192.168.200.0     0.0.0.0         255.255.255.0   U     0      0        0 bond0

我对路由等方面没有太多经验,所以我知道我在这里遗漏了一些东西。

答案1

总结

ip route add 192.168.117.64/26 dev onboard-10Gb-1 table 1000 
ip route add default via 192.168.117.65 dev onboard-10Gb-1 table 1000
ip rule add from 192.168.117.70 lookup 1000

有关更多详细信息以及如何正确处理 UDP 服务,请阅读下文。


注意:在 Linux 上,routeifconfig是过时的命令。它们不适用于策略路由等高级路由。应系统地使用iproute2替换为:ip routeip linkip address(以及所有其他相关命令iproute2套房)。


策略路由

默认情况下,多宿主服务器在从连接的 LAN 192.168.200.0/24 进行查询时将直接回复该 LAN(以下示例将使用位于 192.168.200.101 的假设系统),而不是遵循查询的来源路径。这是一种非对称流程,可能会因各种原因而失败,其中包括:

  • 服务器本身配置为rp_filter=1将丢弃以下不对称流量严格反向路径转发規則。
  • 路径上看不到回复的防火墙可能被配置为丢弃流量(例如当跟踪 TCP 窗口
  • 如果某处发生 NAT:直接回复未解除 NAT 并被客户端丢弃
  • 服务器在托管不支持多宿主的 UDP 服务时会选择错误的回复地址,导致客户端丢弃此类回复。

即使不存在前 3 种情况,TCP 也可能起作用,但 UDP 在最后一种情况下会更加困难,并且单独的策略路由对于 UDP 而言通常是不够的(请参阅下面的警告),但仍然是必需的。

这需要策略路由以便在进行路由决策以回复时分别考虑每个地址。

在 Linux 上,这是通过使用带有选择器的路由规则来实现的,以使用备用路由表,这些路由表将只知道所选目标所需的路径:仅是所有可能路由的部分副本。所选的选择器通常是一个标准,取决于目的地以外的其他东西(目的地已经提供有标准路由条目)。大多数情况下它是源地址,但它取决于目标。

这里的目标是拥有一个不具体了解 192.168.200.0/24 的路由表,因此当从 192.168.117.70 发出回复时,它会使用默认路由onboard-10Gb-1而不是 LAN 路由进行路由。bond0

仅复制路由表 1000 中所需的路由(值 1000 任意选择):

ip route add 192.168.117.64/26 dev onboard-10Gb-1 table 1000 
ip route add default via 192.168.117.65 dev onboard-10Gb-1 table 1000

当源地址来自 192.168.117.70(即服务器地址)时,查找备用路由表 1000(在查找主要的路由表:如果查找成功给出路由,则主要的表将不会被使用):

ip rule add from 192.168.117.70 lookup 1000

可以添加针对其他 LAN 的等效表和规则,但它已经由主要的路由表。传入流量已首先由当地的路由表,因此此设置不再需要做任何事情:

# ip rule
0:      from all lookup local
32765:  from 192.168.117.70 lookup 1000
32766:  from all lookup main
32767:  from all lookup default

然后服务器,具体取决于服务的使用方式:

  • TCP 服务绑定或不绑定到特定地址始终有效

    任何accept(2)-ed 套接字将其源(本地)地址设置为查询使用的目标地址,因此发出的数据包将在需要时匹配路由规则选择器:这种情况下无需执行任何其他操作。

  • TCP 客户端或 UDP 客户端可以在执行查询/连接时绑定源地址,以更改路径:

    TCP 和 UDP 示例:

    ssh -b 192.168.117.70 [email protected]
    
    traceroute -n -s 192.168.117.70 192.168.200.101
    

将根据所选的源地址选择预期的备用路由表 1000:

# ip route get from 192.168.117.70 to 192.168.200.101
192.168.200.101 from 192.168.117.70 via 192.168.117.65 dev onboard-10Gb-1 table 1000 uid 0 
    cache 

警告:UDP 服务

UDP 和 BSD 套接字 API 的工作方式,默认情况下未绑定到地址的 UDP 套接字(即使用 0.0.0.0 又名 INADDR_ANY),当它用于回复收到的 UDP 消息时,它不具有查询的所有上下文,特别是它不会具有查询发送到的服务器的本地地址,与 TCP 相反:它只会使用套接字的 0.0.0.0 地址。

因此,当回复从 192.168.200.101 到 192.168.117.70 的查询时,它将向路由堆栈呈现源 0.0.0.0,以将实际源地址的选择交给路由堆栈。这将与 192.168.117.70 的路由规则选择器不匹配,回复将使用主要的路由表,选择了错误的源回复地址:192.168.200.45。当客户端收到这样的回复(直接来自同一个 LAN)时,它不会将其识别为对其查询的回复:它来自其他地址,并将忽略它。

有两种方法可以让 UDP 服务器应用程序正确处理此问题:

  • bind(2)到特定地址。

    使用此套接字的任何回复都将使用它绑定到的地址。从而选择路由规则和预期的路由行为。如果服务器必须在其所有地址上提供服务,则它应该以相同的方式绑定多次:每个地址一次。

    大多数守护进程中都有设置可以做到这一点。例如,默认情况下,ISC 的 DNS 服务器绑定 9绑定到属于服务器的所有地址并遵循动态变化。当查询到达此类套接字时,绑定套接字的地址查询的目标地址。它将被重新用作源地址,从而选择正确的路由规则。

  • 否则使用套接字选项IP_PKTINFO

    这使得应用程序能够接收辅助数据,使其知道数据包是在哪个地址和哪个接口上接收的,并提供正确回复所需的所有信息。这需要特定的应用程序支持,包括使用其他功能,例如cmsg(3)

    例如,这是 NLnet Labs 的 DNS 服务器的运行模式未绑定使用服务器选项时interface-automatic: yes

如果 UDP 服务器应用程序根本无法改变,那么就只剩下糟糕的选择。

使用网络过滤器连接跟踪iptables行不通:可以改​​变目的地在输出钩子中,但它是来源必须改变。可以改变后布线钩子,但顾名思义,它是在路由之后:选择备用路由为时已晚。即使允许这样做,因为它是关于 NAT 的回复对于现有的流程,网络过滤器无法正确应对它,并且会改变用于回复的源端口以避免假定的冲突,而不是重新使用相同的流程。

在这种情况下,可以使用额外的选择器,根据服务和目的地(在本例中是回复)选择备用源,以强制另一个选择:源 192.168.117.70 而不是 192.168.200.45(现在从 192.168.200.101 到 192.168.200.45 的直接查询会因类似的原因而失败)。

例如,如果服务器要在端口 5555 上托管一个简单的 UDP 服务,该服务无法配置为绑定到 192.168.117.70 或使用IP_PKTINFO,并且永远不应在 192.168.200.0/24 上直接进行 LAN 到 LAN 连接,可以使用(这需要内核 >= 4.17):

ip rule add from 0.0.0.0/32 ipproto udp sport 5555 to 192.168.200.0/24 lookup 1000

此处 0.0.0.0/32 用作其 INADDR_ANY 角色。路由堆栈最终会将其替换为合适的源,但这次是从路由表 1000 中选择的。

前:

# ip route get ipproto udp sport 5555  to 192.168.200.101
192.168.200.101 dev bond0 src 192.168.200.45 uid 0 
    cache 

后:

# ip route get ipproto udp sport 5555 to 192.168.200.101
192.168.200.101 via 192.168.117.65 dev onboard-10Gb-1 table 1000 src 192.168.117.70 uid 0 
    cache 

不会影响其他情况(例如:源端口 5556):

# ip route get ipproto udp sport 5556 to 192.168.200.101
192.168.200.101 dev bond0 src 192.168.200.45 uid 0 
    cache 

nftables

ip rule实际上,除了上面添加的第二个解决方案之外,还存在一个较差的解决方案,即使用nftables而是进行不依赖于网络过滤器连接跟踪。以下是要加载的规则集nft -f ...

table t_statelessnat
delete table t_statelessnat

table ip t_statelessnat {
        chain c_snat {
                type route hook output priority raw; policy accept;
                ip daddr 192.168.200.0/24 ip saddr != 192.168.117.70 udp sport 5555 ip saddr set 192.168.117.70
        }
}

类型路线hook 将负责重新路由现在将遍历路由表 1000 的数据包。

最好使用路由规则,除非ip rule需要无法使用的复杂过滤器。

相关内容