我在这里遇到了难题,但似乎无法找出正确的路线。
我有一台服务器 (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 上,route
和ifconfig
是过时的命令。它们不适用于策略路由等高级路由。应系统地使用iproute2替换为:ip route
,ip link
和ip 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
需要无法使用的复杂过滤器。