Linux - 两个子网、一个接口、一个网关 - 对附加子网进行 NAT

Linux - 两个子网、一个接口、一个网关 - 对附加子网进行 NAT

我们目前有几台服务器都使用同一个 LAN:

Host1: eth0 10.0.0.1/24
Host2: eth0 10.0.0.2/24
Host3: eth0 10.0.0.3/24
Gateway: 10.0.0.254 

我们想在这些服务器上运行一些虚拟机 (VirtualBox)。我们可以将它们设置为桥接到主机的 eth0,但我们不能使用 10.0.0.0/24 范围内的地址,因为它们可能会在将来分配。

因此我们认为我们应该使用不同的子网:

Host1VM: eth0 192.168.0.1/24 (bridge to host eth0)
Host2VM: eth0 192.168.0.2/24 (bridge to host eth0)
Host3VM: eth0 192.168.0.3/24 (bridge to host eth0)

这很好,并且所有虚拟机都可以相互通信,因为它们位于使用相同物理接口的同一子网上。

我们面临的问题是,我们需要让这些虚拟机通过 10.0.0.254 网关访问互联网。所以我们想,为什么不选择其中一台主机并将其用作路由器/NAT?

Host1: eth0 10.0.0.1/24, eth0:0 192.168.0.254/24

现在我们可以为虚拟机指定网关 192.168.0.254。然后我们发现的问题是 Host1 似乎无法正确进行 NAT。

iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j SNAT --to 10.0.0.1

我认为这样可行,而且我们确实看到它匹配数据包。如果虚拟机 ping 互联网,我们会看到 ICMP 数据包进入 Host1(因为它是路由器),然后主机重新发送 ICMP(因为它正在进行 NAT),互联网主机会向主机做出响应 - 但随后它就死机了。我预计主机会将数据包转发回虚拟机,但事实并非如此。

我遗漏了什么,或者这种设置根本就不可能实现?

编辑:需要澄清的是,iptables 中没有 DENY 规则,所有规则都默认为 ACCEPT。我们还启用了 IP 转发。

更新1-iptables

忽略 virbr0 - 这与 VirtualBox VM 无关

# Completed on Fri Sep 20 16:50:45 2013
# Generated by iptables-save v1.4.12 on Fri Sep 20 16:50:45 2013
*nat
:PREROUTING ACCEPT [171383:10358740]
:INPUT ACCEPT [1923:115365]
:OUTPUT ACCEPT [192:21531]
:POSTROUTING ACCEPT [169544:10254463]
-A POSTROUTING -s 192.168.0.0/24 -o eth0 -j SNAT --to-source 10.0.0.1
COMMIT
# Completed on Fri Sep 20 16:50:45 2013
# Generated by iptables-save v1.4.12 on Fri Sep 20 16:50:45 2013
*filter
:INPUT ACCEPT [96628707:25146145432]
:FORWARD ACCEPT [195035595:22524430122]
:OUTPUT ACCEPT [44035412:304951330498]
-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT
-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT
-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT
-A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT
-A FORWARD -i virbr0 -o virbr0 -j ACCEPT
-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable
COMMIT
# Completed on Fri Sep 20 16:50:45 2013
# Generated by iptables-save v1.4.12 on Fri Sep 20 16:50:45 2013
*mangle
:PREROUTING ACCEPT [291641356:47665886851]
:INPUT ACCEPT [96628707:25146145432]
:FORWARD ACCEPT [195035595:22524430122]
:OUTPUT ACCEPT [44035838:304951365412]
:POSTROUTING ACCEPT [239078922:327477732680]
-A POSTROUTING -o virbr0 -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill
-A POSTROUTING -o virbr0 -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill
COMMIT
# Completed on Fri Sep 20 16:50:45 2013

更新 2-tcpdump

16:58:37.189758 IP 192.168.0.2 > 74.125.128.106: ICMP echo request, id 1, seq 2, length 40
16:58:37.189805 IP 10.0.0.1 > 74.125.128.106: ICMP echo request, id 1, seq 2, length 40
16:58:37.194607 IP 74.125.128.106 > 10.0.0.1: ICMP echo reply, id 1, seq 2, length 40
(no final reply back to the VM)

答案1

这说明:

16:58:37.189758 IP 192.168.0.2 > 74.125.128.106: ICMP echo request, id 1, seq 2, length 40
16:58:37.189805 IP 10.0.0.1 > 74.125.128.106: ICMP echo request, id 1, seq 2, length 40
16:58:37.194607 IP 74.125.128.106 > 10.0.0.1: ICMP echo reply, id 1, seq 2, length 40

上游路由器 ( 10.0.0.254) 正在将回复发送回 Host1,因此您的路由和 SNAT 正在运行。问题是 Host1 未将该回复传回网络192.168.0.0/24

您是否已加载适当的连接跟踪内核模块?

确保流量进入连接跟踪表:

grep src=192.168.0.2 /proc/1/net/nf_conntrack

前几天我看到了与 TCP 数据包类似的问题,结果发现是rp_filter内核的问题。我看不出这怎么会是这里的问题,但情况非常相似。您能否ip route show从 Host1 发布路由表 ( ) 并检查您的 rp_filter 设置?( cat /proc/sys/net/ipv4/conf/default/rp_filter)

答案2

我要回答我自己的问题,因为这就是我解决问题的方法。我很确定其他答案在正常情况下是准确的,但我相信它们没有起作用,因为使用接口别名存在错误或警告。

因此,我认为接口别名导致了问题,于是我寻找提供虚拟接口的其他方法,结果发现有一种称为 MAC-VLAN 的虚拟接口类型,它本质上将自己作为具有自己的 MAC 地址的虚拟接口附加到物理接口。

第一次使用 NAT 时它就完美地运行了,所以我推测这只是因为 MAC-VLAN 接口在内核中作为一个完全独立的接口出现,而接口别名在某些地方引起了一些混乱。

作为参考,创建 MAC-VLAN 的命令很简单:

ip link add dev macvlan0 link eth0 type macvlan

这是一个非常好的功能,我不敢相信我以前没有听说过它。

答案3

我想知道为什么你需要对它们进行 NAT?由于前缀不同,并且你正在路由流量,为什么不通过网关中的虚拟机为 10.0.0.0/24 网络设置路由?

此外,当您配置 时eth0:0,通常您会配置eth0,反之亦然。使用eth0:1,或者更好的是,停止使用古老的 net-tools 工具,而改用 iproute2(ip addr add 192.168.0.254/24 dev eth0等)。这可能是问题所在,因为您的 SNAT 规则是正确的。如果您的路由器不知道它是 10.0.0.1,它就不会将返回流量 NAT 回去。

如果ip addr show dev eth0显示您设置的两个地址,则说明您的 NAT 配置正确,应该可以正常工作。您可能遇到了错误,因为 NAT 正在使用不同的源地址通过同一接口发送数据包,这是一种不常见的情况。

但是,使这种情况变得混乱的部分原因是 ICMP 重定向的存在。人们通常认为这些是邪恶的(因为它们允许攻击者在某些情况下修改您的路由表),因此它们几乎总是被禁用,但 Linux 通常会在将流量转发到它进入的同一接口时发送它们。在这种情况下,它基本上是一条消息,说“该主机在您的本地段上;您不需要通过我转发它”。这通常是正确的,但由于 SNAT,(上一跳)主机实际上确实需要这样做。要禁用它们,请使用以下 sysctl 设置:

net.ipv4.conf.eth0.send_redirects = 0
net.ipv4.conf.eth0.accept_redirects = 0

根据下一跳设备,您可能必须重新启动该设备,以某种方式对其进行配置,或者等待一段时间才能让重定向消失(如果这是导致问题的原因)。

答案4

鉴于拓扑结构,据我了解,最好的办法是让路由器上的网络 192.168.0.0/24 网络访问互联网。您必须为路由器提供网络 192.168.0.0/24 中的地址才能实现此目的。

如果您无法做到这一点,那么您必须使用 MASQUERADE,但将其限制在 VM 网络上以外的目的地。每个主机上都类似这样:

iptables -A POSTROUTING -o eth0 ! -d 192.168.0.0/24 -j MASQUERADE

这样,虚拟机之间的所有流量都不会发生变化,从虚拟机到其他网络的所有流量都会在线路上显示主机地址。

相关内容