虚拟接口和 PREROUTING 链

虚拟接口和 PREROUTING 链

我的一般问题是:在命令行上在单个主机(即没有网络连接)上本地验证 iptables NAT 规则的最佳方法(最简单、最容易、最快、最不容易出错等)是什么?

以下是使用 NetCat 检查简单 DNAT 规则的特定(失败)尝试的详细信息。我希望在这种情况下能够解决我的具体问题,同时也希望能够回答我的一般问题。


我正在运行 Debian 8 (Jessie) 的 VirtualBox 虚拟机。我想使用 netcat 来执行简单 DNAT 规则的基本测试。

对于我的测试,我想做的就是将一些数据发送到一个本地地址(例如192.168.0.1)并让它到达另一个本地地址(例如192.168.0.2)。

到目前为止我已经尝试了几种不同的方法:

  1. 虚拟接口和 PREROUTING 链

  2. 虚拟接口和 PREROUTING 链

  3. 使用 OUTPUT 链代替 PREROUTING

虚拟接口和 PREROUTING 链

我的第一次尝试是向 PREROUTING 链添加 DNAT 规则,并添加两个具有适当地址的虚拟接口。

这是我的规则:

sudo iptables \
-t nat \
-A PREROUTING \
-d 192.168.0.1 \
-j DNAT --to-destination 192.168.0.2

我的防火墙中没有其他 netfilter 规则。但为了确定,这里是输出iptables-save

# 由 iptables-save v1.4.21 生成

*nat
:预路由接受[0:0]
:输入接受[0:0]
:输出接受[0:0]
: 后路由接受 [0:0]
-A 预路由 -d 192.168.0.1/32 -j DNAT --到目的地 192.168.0.2
犯罪

*筛选
:输入接受[0:0]
:转发接受[0:0]
:输出接受[0:0]
犯罪

重申一下,我想做的就是向该192.168.0.1地址发送一些数据并让它到达该192.168.0.2地址。

可能值得一提的是,192.168.0.0/24我的虚拟机上未使用子网。首先,我添加几个虚拟接口:

sudo ip link add dummy1 type dummy

sudo ip link add dummy2 type dummy

接下来,我将 IP 地址分配给所需子网范围上的虚拟接口:

sudo ip addr add 192.168.0.1/24 dev dummy1

sudo ip addr add 192.168.0.2/24 dev dummy2

然后我打开界面:

sudo ip link set dummy1 up

sudo ip link set dummy2 up

这是我的路由表现在的样子:

默认通过 10.0.2.2 dev eth0
10.0.2.0/24 dev eth0 原型内核范围链接 src 10.0.2.15
192.168.0.0/24 dev dummy1 proto 内核范围链接 src 192.168.0.1
192.168.0.0/24 dev dummy2 proto 内核范围链接 src 192.168.0.2
192.168.56.0/24 dev eth1 原型内核范围链接 src 192.168.56.100

现在我使用 netcat 监听第一个(源)地址:

nc -l -p 1234 -s 192.168.0.1

我使用 netcat 客户端连接到 netcat 服务器(在单独的终端窗口中):

nc 192.168.0.1 1234

在一个窗口中输入的文本会出现在另一个窗口中 - 正如预期的那样。

我对第二个地址也做了同样的事情:

nc -l -p 1234 -s 192.168.0.2

nc 192.168.0.2 1234

同样,在一个窗口中输入的文本会出现在另一个窗口中 - 正如预期的那样。

最后,我尝试监听目标 (DNAT) 地址并通过源 (DNAT) 地址进行连接:

nc -l -p 1234 -s 192.168.0.2

nc 192.168.0.1 1234

不幸的是,连接失败并出现以下错误:

(未知)[192.168.0.1] 1234 (?):连接被拒绝

我也尝试使用ping -c 1 -R 192.168.0.1查看 DNAT 是否生效,但看起来并非如此:

PING 192.168.0.1 (192.168.0.1) 56(124) 字节数据。
来自 192.168.0.1 的 64 字节:icmp_seq=1 ttl=64 时间=0.047 ms
RR:192.168.0.1
        192.168.0.1
        192.168.0.1
        192.168.0.1


--- 192.168.0.1 ping 统计 ---
发送 1 个数据包,接收 1 个数据包,丢包 0%,时间 0ms
rtt 最小值/平均值/最大值/mdev = 0.047/0.047/0.047/0.000 毫秒

为什么这不起作用?我究竟做错了什么?

使用 tcpdump 进行诊断

为了诊断这个问题,我尝试使用tcpdump监听虚拟接口上的流量。我尝试监听所有接口(并过滤掉 SSH 和 DNS):。

sudo tcpdump -i any -e port not 22 and port not 53

然后我 pingdummy1接口:

ping -n -c 1 -I dummy1 192.168.0.1

这产生了以下结果:

listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
In 00:00:00:00:00:00 (oui Ethernet) ethertype IPv4 (0x0800), length 100: 192.168.0.1 > 192.168.0.1: ICMP echo request, id 8071, seq 1, length 64
In 00:00:00:00:00:00 (oui Ethernet) ethertype IPv4 (0x0800), length 100: 192.168.0.1 > 192.168.0.1: ICMP echo reply, id 8071, seq 1, length 64

因此看起来虚拟接口已连接到环回接口。这可能意味着 iptables 规则被完全规避。

虚拟接口和 PREROUTING 链

作为第二次尝试,我尝试使用所谓的虚拟 IP 地址而不是虚拟接口。

以下是我将“虚拟”IP 地址添加到 eth0 和 eth1 接口的方法:

sudo ip addr add 192.168.0.100/24 dev eth0
sudo ip addr add 192.168.0.101/24 dev eth1

笔记:我为这些接口使用了与虚拟接口不同的 IP 地址。

然后我刷新并更新了 iptables NAT 规则:

sudo iptables -F -t nat

sudo iptables \
-t nat \
-A PREROUTING \
-d 192.168.0.100 \
-j DNAT --to-destination 192.168.0.101

我重试了 ping 测试:

ping -n -c 1 -R 192.168.0.100

没有骰子:

PING 192.168.0.100 (192.168.0.100) 56(124) 字节数据。
来自 192.168.0.100 的 64 个字节:icmp_seq=1 ttl=64 时间=0.023 ms
路由:192.168.0.100
        192.168.0.100
        192.168.0.100
        192.168.0.100


--- 192.168.0.100 ping 统计 ---
发送 1 个数据包,接收 1 个数据包,丢包 0%,时间 0ms
rtt 最小值/平均值/最大值/mdev = 0.023/0.023/0.023/0.000 毫秒

然后再次进行netcat测试。启动服务器:

nc -l -p 1234 -s 192.168.0.101

尝试连接客户端:

nc 192.168.0.100 1234

也没有骰子:

(未知)[192.168.0.100] 1234(?):连接被拒绝

使用 OUTPUT 链代替 PREROUTING

然后我尝试将两个 DNAT 规则从 PREROUTING 链移动到 OUTPUT 链:

sudo iptables -F -t nat

sudo iptables \
-t nat \
-A OUTPUT \
-d 192.168.0.1 \
-j DNAT --to-destination 192.168.0.2

sudo iptables \
-t nat \
-A OUTPUT \
-d 192.168.0.100 \
-j DNAT --to-destination 192.168.0.101

现在我尝试在虚拟接口和虚拟接口上执行 ping 操作:

用户@主机:~$ ping -c 1 -R 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(124) 字节数据。
来自 192.168.0.1 的 64 字节:icmp_seq=1 ttl=64 时间=0.061 ms
RR:192.168.0.1
        192.168.0.2
        192.168.0.2
        192.168.0.1


--- 192.168.0.1 ping 统计 ---
发送 1 个数据包,接收 1 个数据包,丢包 0%,时间 0ms
rtt 最小值/平均值/最大值/mdev = 0.061/0.061/0.061/0.000 毫秒

用户@主机:~$ ping -c 1 -R 192.168.0.100

PING 192.168.0.100 (192.168.0.100) 56(124) 字节数据。
来自 192.168.0.100 的 64 字节:icmp_seq=1 ttl=64 时间=0.058 ms
路由:192.168.0.100
        192.168.0.101
        192.168.0.101
        192.168.0.100


--- 192.168.0.100 ping 统计 ---
发送 1 个数据包,接收 1 个数据包,丢包 0%,时间 0ms
rtt 最小值/平均值/最大值/mdev = 0.058/0.058/0.058/0.000 毫秒

我还尝试对每对 IP 地址进行 netcat 客户端-服务器测试:

nc -l -p 1234 -s 192.168.0.2

nc 192.168.0.1 1234

和:

nc -l -p 1234 -s 192.168.0.101

nc 192.168.0.100 1234

这次测试也成功了。

因此,当 DNAT 规则位于 OUTPUT 链而不是 PREROUTING 链中时,虚拟接口和虚拟接口似乎都可以工作。

看来我的部分问题是我不清楚哪些数据包穿过哪些链。

答案1

简短说明:虚拟接口和虚拟 IP 地址通过环回接口发送数据包,该接口不受 PREROUTING 链的影响。通过使用带有 veth 接口的网络命名空间,我们可以将流量从一个 IP 地址发送到另一个 IP 地址,从而更准确地模拟多主机网络流量,并允许我们根据需要在 PREROUTING 链上测试 DNAT 规则。

下面是该解决方案的更详细描述。


以下是一个 Bash 脚本,用于配置一对网络接口并测试 DNAT 规则是否按预期运行:

# Create a network namespace to represent a client
sudo ip netns add 'client'

# Create a network namespace to represent a server
sudo ip netns add 'server'

# Create a veth virtual-interface pair
sudo ip link add 'client-eth0' type veth peer name 'server-eth0'

# Assign the interfaces to the namespaces
sudo ip link set 'client-eth0' netns 'client'
sudo ip link set 'server-eth0' netns 'server'

# Change the names of the interfaces (I prefer to use standard interface names)
sudo ip netns exec 'client' ip link set 'client-eth0' name 'eth0'
sudo ip netns exec 'server' ip link set 'server-eth0' name 'eth0'

# Assign an address to each interface
sudo ip netns exec 'client' ip addr add 192.168.1.1/24 dev eth0
sudo ip netns exec 'server' ip addr add 192.168.2.1/24 dev eth0

# Bring up the interfaces (the veth interfaces the loopback interfaces)
sudo ip netns exec 'client' ip link set 'lo' up
sudo ip netns exec 'client' ip link set 'eth0' up
sudo ip netns exec 'server' ip link set 'lo' up
sudo ip netns exec 'server' ip link set 'eth0' up

# Configure routes
sudo ip netns exec 'client' ip route add default via 192.168.1.1 dev eth0
sudo ip netns exec 'server' ip route add default via 192.168.2.1 dev eth0

# Test the connection (in both directions)
sudo ip netns exec 'client' ping -c 1 192.168.2.1
sudo ip netns exec 'server' ping -c 1 192.168.1.1

# Add a DNAT rule to the server namespace
sudo ip netns exec 'server' \
iptables \
-t nat \
-A PREROUTING \
-d 192.168.2.1 \
-j DNAT --to-destination 192.168.2.2

# Add a dummy interface to the server (we need a target for the destination address)
sudo ip netns exec 'server' ip link add dummy type dummy
sudo ip netns exec 'server' ip addr add 192.168.2.2/24 dev dummy
sudo ip netns exec 'server' ip link set 'dummy' up

# Test the DNAT rule using ping
sudo ip netns exec 'client' ping -c 1 -R 192.168.2.1

ping 测试的输出显示该规则正在运行:

PING 192.168.2.1 (192.168.2.1) 56(124) bytes of data.
64 bytes from 192.168.2.1: icmp_seq=1 ttl=64 time=0.025 ms
RR:     192.168.1.1
        192.168.2.2
        192.168.2.2
        192.168.1.1


--- 192.168.2.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.025/0.025/0.025/0.000 ms

现在我也可以执行我的 NetCat 测试。首先我在服务器上监听:

sudo ip netns exec 'server' nc -l -p 1234 -s 192.168.2.2

然后我通过客户端连接(在单独的终端窗口中):

sudo ip netns exec 'client' nc 192.168.2.1 1234

在一个终端窗口中输入的文本会出现在另一个终端窗口中 - 成功!

相关内容