将流量路由到没有命名空间的网络虚拟副本

将流量路由到没有命名空间的网络虚拟副本

如下图所示,是否可以像这样通过 Linux 内核路由流量?我希望模拟“内部”网络之外的设备的精确副本,具有相同的 IP 范围,同时使内部和外部设备能够相互通信,而不知道对方具有与其自身相同的 IP。

机器上网络接口的低级描述

例如:“内部”的设备 X 联系 192.168.3.5,该数据会转到中间人桥并转发到 IP 192.168.2.5 的“外部”设备 Y。然后响应被发送回中间人,并发送到 IP 192.168.2.5 的设备 X。

我知道这可以通过网络命名空间实现,并且可以进行有效的模拟。但是,我希望避免使用命名空间,而是针对不同的流量方向使用不同的路由表之类的东西。这可能吗?

如果我理解正确的话,由于 IP 范围重复,我无法使用 NAT。它是否正确?

答案1

拓扑结构

我们首先配置 上的网络接口middleman。我假设您已登录系统控制台,或者可以通过不涉及 或inner网络的界面进行访问outer。出于本答案的目的,我们假设接口middleman-eth0onmiddleman连接到“内部”网络并middleman-eth1连接到“外部”网络。这为我们提供了以下网络拓扑:

网络拓扑图

启用转发

我们需要确保我们已启用数据包转发middleman

sysctl -w net.ipv4.ip_forward=1

我们应该从一个空的 netfilter 配置开始。运行iptables-save不应产生任何输出。

接口配置

为此, 和middleman-eth0middleman-eth1具有相同的网络配置:

ip addr add 192.168.2.1/24 dev middleman-eth0
ip addr add 192.168.2.1/24 dev middleman-eth1

如果您认为这看起来很奇怪,那么您是对的!目前,路由表middleman如下所示:

192.168.2.0/24 dev middleman-eth1 proto kernel scope link src 192.168.2.1
192.168.2.0/24 dev middleman-eth0 proto kernel scope link src 192.168.2.1

这不会特别有用。

VRF配置

我们将利用 Linux 对“虚拟路由和转发“(“VRF”)。这允许我们在系统上创建多个隔离的路由域,以便传入的流量eth0将看到与传入的流量不同的路由表eth1

我们首先创建 VRF 接口:

ip link add vrf-inner type vrf table 100
ip link set vrf-inner up
ip link add vrf-outer type vrf table 200
ip link set vrf-outer up

这些命令设置两个 VRF 设备,将每个设备与特定的路由表相关联。

接下来,我们将每个物理接口连接到 VRF 设备:

ip link set dev middleman-eth0 master vrf-inner
ip link set dev middleman-eth1 master vrf-outer

经过这些更改,主路由表现在为空:

middleman# ip route show
<no output>

middleman-eth0在表 100 中,我们看到与(“内部”网络)相关的规则:

middleman# ip route show table 100
broadcast 192.168.2.0 dev middleman-eth0 proto kernel scope link src 192.168.2.1
192.168.2.0/24 dev middleman-eth0 proto kernel scope link src 192.168.2.1
local 192.168.2.1 dev middleman-eth0 proto kernel scope host src 192.168.2.1
broadcast 192.168.2.255 dev middleman-eth0 proto kernel scope link src 192.168.2.1

middleman-eth1在表 200 中,我们看到(“外部”网络)的规则:

middleman# ip route show table 200
broadcast 192.168.2.0 dev middleman-eth1 proto kernel scope link src 192.168.2.1
192.168.2.0/24 dev middleman-eth1 proto kernel scope link src 192.168.2.1
local 192.168.2.1 dev middleman-eth1 proto kernel scope host src 192.168.2.1
broadcast 192.168.2.255 dev middleman-eth1 proto kernel scope link src 192.168.2.1

此时,我们实际上有两个断开连接的网络,如下所示:

显示隔离路由域的网络拓扑

“内部”网络上的主机可以联系 192.168.2.1,他们将与middleman-eth0。 “外部”网络上的主机可以联系 192.168.2.1,但他们将与 通话middleman-eth1

两人相遇的地方

我们现在需要做的就是设置映射,以便任何一方都可以使用192.168.3.0/24另一方联系节点的地址。

首先,我们需要告诉所有节点它们192.168.3.0/24通过以下方式路由到网络middleman:这意味着在所有节点上,在“内部”和“外部”网络上,我们需要:

ip route add 192.168.3.0/24 via 192.168.2.1

在 上middleman,我们需要 (a) 将192.168.3.0/24范围内的地址映射到192.168.2.0/24范围中,并且 (b) 确保在路由连接时使用正确的路由表。为了实现(a),我们可以创建一些NETMAP规则:

iptables -t nat -A PREROUTING -d 192.168.3.0/24 -j NETMAP --to 192.168.2.0/24
iptables -t nat -A POSTROUTING -s 192.168.2.0/24 -j NETMAP --to 192.168.3.0/24

为了实现 (b),我们首先根据数据包的入口接口来标记数据包:

iptables -t mangle -A PREROUTING -i middleman-eth0 -d 192.168.3.0/24 -j MARK --set-mark 100
iptables -t mangle -A PREROUTING -i middleman-eth1 -d 192.168.3.0/24 -j MARK --set-mark 200

然后使用这些标记来选择路由表:

ip rule add prio 100 fwmark 100 lookup 200
ip rule add prio 200 fwmark 200 lookup 100

回想一下之前的内容,表 100 具有针对“内部”网络的规则,而表 200 具有针对“外部”网络的规则,因此这些规则表示“对于到达接口 的数据包middleman-eth0,使用与 关联的路由表做出路由决策middleman-eth1” ,反之亦然。

跟随弹跳的球

完成所有这些后,如果192.168.2.10“内部”网络上的节点尝试 ping 192.168.3.10

  1. 由于192.168.3.0/24 via 192.168.2.1路由条目,数据包被路由到中间人
  2. 数据包到达middleman-eth0
  3. 数据包到达MANGLE表链PREROUTING并将 fwmark 设置为100
  4. 数据包到达NAT表链PREROUTING并映射到目的地192.168.2.10
  5. 数据包进入路由子系统,在那里它符合fwmark 100 lookup 200规则
  6. 在路由表200中,它命中了192.168.2.0/24 dev middleman-eth1,因此内核会将其发送到设备middleman-eth1
  7. 数据包到达NAT表链POSTROUTING,其源映射到192.168.3.10
  8. 数据包到达地址为 的“外部”节点192.168.2.10
  9. ……深吸一口气……
  10. 外部节点发送回复192.168.3.10
  11. 回复到达middleman-eth1
  12. 回复到达MANGLE表链PREROUTING并将 fwmark 设置为200
  13. 回复到达NAT表链PREROUTING并映射到目的地192.168.2.10
  14. 回复进入路由子系统,在那里它符合fwmark 200 lookup 100规则
  15. 在路由表100中,它符合192.168.2.0/24 dev middleman-eth0规则,因此内核会将其发送到设备middleman-eth0
  16. 回复到达NAT表链POSTROUTING,其源映射到表链192.168.3.10
  17. 回复到达“内部”节点192.168.2.10,该节点看到对其最初发出的请求的回复。

验证

如果在“内部”节点 0 ( 192.168.2.10) 上,我们尝试使用 地址 ping “外部”节点 0 192.168.3.10,则tcpdump -nn -i any icmp在内部节点 0 上运行会显示:

07:01:58.125370 innernode0-eth0 Out IP 192.168.2.10 > 192.168.3.10: ICMP echo request, id 12999, seq 1, length 64
07:01:58.125533 innernode0-eth0 In  IP 192.168.3.10 > 192.168.2.10: ICMP echo reply, id 12999, seq 1, length 64

我们middleman看到:

07:01:58.125440 middleman-eth0 In  IP 192.168.2.10 > 192.168.3.10: ICMP echo request, id 12999, seq 1, length 64
07:01:58.125459 middleman-eth1 Out IP 192.168.3.10 > 192.168.2.10: ICMP echo request, id 12999, seq 1, length 64
07:01:58.125514 middleman-eth1 In  IP 192.168.2.10 > 192.168.3.10: ICMP echo reply, id 12999, seq 1, length 64
07:01:58.125518 middleman-eth0 Out IP 192.168.3.10 > 192.168.2.10: ICMP echo reply, id 12999, seq 1, length 64

在“外部”节点 0 上我们看到:

07:01:58.125489 outernode0-eth0 In  IP 192.168.3.10 > 192.168.2.10: ICMP echo request, id 12999, seq 1, length 64
07:01:58.125497 outernode0-eth0 Out IP 192.168.2.10 > 192.168.3.10: ICMP echo reply, id 12999, seq 1, length 64

所以我认为我们已经实现了你的目标!


我用了迷你网测试此配置;您可以找到我的测试环境的完整源代码这里。有此配置的实际操作视频这里

更新

正如AB在评论中指出的,这个配置有问题!默认情况下,内核的连接跟踪逻辑仅查看源/目标地址和源/目标端口。从innernode0端口 4000 到端口 80 的连接outernode0似乎是相同的连接为相反方向的连接...也就是说,假设我有一个在所有节点上的端口 80 上运行的网络服务器,这两个命令:

innernode0# curl --local-port 4000 192.168.3.10

和:

outernode0# curl --local-port 4000 192.168.3.10

将导致在以下位置出现单个连接跟踪条目middleman

middleman# conntrack  -L
tcp      6 118 TIME_WAIT src=192.168.2.10 dst=192.168.3.10 sport=4000 dport=80 src=192.168.2.10 dstroot@mininet-vm:/proc/net=192.168.3.10 sport=80 dport=4000 [ASSURED] mark=0 use=1

我们告诉 conntrack 子系统如何区分这些连接。我们可以通过向表中的链添加一对CT规则来做到这一点:PREROUTINGRAW

iptables -t raw -A PREROUTING -s 192.168.2.0/24 -i middleman-eth0 -j CT --zone-orig 100
iptables -t raw -A PREROUTING -s 192.168.2.0/24 -i middleman-eth1 -j CT --zone-orig 200

有了这些规则,我们现在可以在 conntrack 表中看到两个单独的连接:

middleman# conntrack -L
tcp      6 113 TIME_WAIT src=192.168.2.10 dst=192.168.3.10 sport=4000 dport=80 zone-orig=200 src=192.168.2.10 dst=192.168.3.10 sport=80 dport=40568 [ASSURED] mark=0 use=1
tcp      6 112 TIME_WAIT src=192.168.2.10 dst=192.168.3.10 sport=4000 dport=80 zone-orig=100 src=192.168.2.10 dst=192.168.3.10 sport=80 dport=4000 [ASSURED] mark=0 use=1

相关内容