无法对 TFTP 流量进行 NAT,因为尽管 TFTP 助手创建了期望,但 iptables 并未将返回连接转发到客户端

无法对 TFTP 流量进行 NAT,因为尽管 TFTP 助手创建了期望,但 iptables 并未将返回连接转发到客户端

问题

我在不同的子网上有一个 TFTP 服务器(计算机“S”)和一个 TFTP 客户端(计算机“C”)。它们通过路由器(计算机“R”)连接。所有 3 台机器都是 Debian 9/Stretch。路由器正在运行 iptables,并设置为伪装从客户端网络到服务器网络的连接。我已将 iptables 配置为使用 Netfilter TFTP 助手来建立到 TFTP 服务器的 tftp 连接。

我遇到的问题是,TFTP 助手设置了对返回 tftp 连接的期望(如预期),但尽管如此,只有来自 TFTP 服务器上端口 69 的流量被转换并发送回客户端。因此,只使用常规 MASQUERADE 连接跟踪尽管 conntrack 表显示预期返回连接。 根据RFC1350,服务器应该选择一个随机的源端口进行通信,并将其指向客户端最初用作源端口的端口(呼……)。

结果是路由器对从客户端到服务器的连接进行 NAT,为返回连接设置转换规则,并愉快地等待来自源端口=69 的服务器的永远不会到达的返回数据包。

设置

为了清楚起见,地址如下:

  • TFTP 服务器:1.1.1.1

  • TFTP 客户端(C):2.2.2.1

  • 路由器 (R):1.1.1.2 / 2.2.2.2

路由器上的 iptables 有以下规则。所有表都有默认的 ACCEPT 策略:

======== RAW Table ========
Chain PREROUTING (policy ACCEPT 464K packets, 432M bytes)
 pkts bytes target     prot opt in     out     source       destination
   59  2504 CT         udp  --  *      *       0.0.0.0/0    0.0.0.0/0       udp dpt:69 CT helper tftp

Chain OUTPUT (policy ACCEPT 280K packets, 36M bytes)
 pkts bytes target     prot opt in     out     source       destination

======== NAT Table ========
Chain POSTROUTING (policy ACCEPT 398 packets, 40794 bytes)
 pkts bytes target     prot opt in     out     source       destination
 5678  349K MASQUERADE  all  --  *     enp1s0  0.0.0.0/0    0.0.0.0/0

一旦 TFTP 客户端尝试连接,conntrack -L就会显示以下内容:

udp      17 28 src=2.2.2.1 dst=1.1.1.1 sport=45084 dport=69 [UNREPLIED] src=1.1.1.1 dst=1.1.1.2 sport=69 dport=45084 mark=0 helper=tftp use=1

conntrack -L EXPECT

298 proto=17 src=1.1.1.1 dst=1.1.1.2 sport=0 dport=45084 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=2.2.2.1 master-dst=1.1.1.1 sport=45084 dport=69 class=0 helper=tftp

如您所见,TFTP 辅助规则运行正常,一旦客户端尝试连接就会触发。您还可以看到,在 EXPECT 表中创建的期望具有源端口 0,我假设这意味着“任何端口”。但是,正如您所看到的,仅当服务器的源端口为 69 时,连接才会路由回客户端(常规旧 NAT)!这是为什么?据我所知,这不是正确的行为。

如果可以避免的话,我不会再使这篇文章变得混乱,但所显示的内容tcpdump udp and host 1.1.1.1证实了 iptables 和 conntrack 向我展示的内容。

大约一年前,我在几个 Debian 8/Jessie 设置上进行了同样的设置,TFTP 助手按预期工作,我从未遇到任何问题。有人能帮我找出我是否做错了什么吗?问题出在 TFTP 助手上吗?为什么它的行为会从 Debian 8/Jessie 发生变化?

答案1

如果我的猜测是正确的,那么 TL;DR,在路由器上执行以下操作:

modprobe nf_nat_tftp

要么是之前的 jessie 内核 (3.16) 自动加载了 nf_nat_tftp,要么是脚本执行了此操作,但似乎不再如此。

无论如何,如果这没有帮助,这里是如何轻松地重现 OP 问题中的设置,允许在任何 Linux 系统上轻松进行任何类型的测试(但请记住,这里只是虚拟化网络,没有其他)。如果它没有解决,我希望这可以有所帮助。

需要:root 用户,ip netnsatftpdatftp(或等效的服务器和客户端软件)。

从头开始,没有理由在路由器上加载 TFTP 帮助程序(因此在测试主机上)。

lsmod | grep _tftp

应该什么都不返回。假设它什么都不返回,或者返回仅有的 nf_conntrack_tftp从以前的规则使用来看,但不是nf_nat_tftp(只是rmmod nf_nat_tftp如果存在,遵循下面的思路)。

使用这些命令来初始化命名空间:

ip netns del tftp || :
ip netns del router || :
ip netns del client || :

ip netns add client
ip netns add router
ip netns add tftp
ip -n tftp link add eth0 type veth peer netns router name tftp0
ip -n client link add eth0 type veth peer netns router name client0
ip -n router link set client0 up
ip -n router link set tftp0 up
ip -n tftp link set eth0 up
ip -n client link set eth0 up
ip -n tftp addr add dev eth0 1.1.1.1/24
ip -n router addr add dev tftp0 1.1.1.2/24 
ip -n router addr add dev client0 2.2.2.2/24 
ip -n client addr add dev eth0 2.2.2.1/24
ip -n client route add default via 2.2.2.2
ip netns exec router sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
ip netns exec router sh -c 'echo 0 > /proc/sys/net/netfilter/nf_conntrack_helper' # this one might be global on former kernels and might need to be executed without "ip netns exec router"
ip netns exec router iptables -t nat -A POSTROUTING -o tftp0 -j MASQUERADE
ip netns exec router iptables -t raw -A PREROUTING -p udp --dport 69 -j CT --helper tftp

最后一个命令应该自动触发加载nf_conntrack_tftp

# uname -r
4.19.7
# lsmod | grep _tftp
nf_conntrack_tftp      16384  1
nf_conntrack          163840  20 xt_conntrack,[...],nf_nat,nf_conntrack_tftp,[...],nf_nat_ipv4,[...]

(根据内核,可能会nf_conntrack_ipv4出现nf_conntrack

准备文件:

mkdir -p /tmp/tftp
echo test > /tmp/tftp/test.txt
mkdir -p /tmp/client

期限1:

ip netns exec router tcpdump -e -n -s0 -i any ip

第二期:

ip netns exec tftp atftpd --daemon --no-fork /tmp/tftp

第三期:

ip netns exec client atftp -g -l /tmp/client/test.txt -r test.txt 1.1.1.1

将显示:

timeout: retrying...
^Ctftp: unknown error.
tftp: aborting

第 1 术语将显示以下内容:

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
17:46:52.958631  In 46:b9:b2:13:ec:51 ethertype IPv4 (0x0800), length 61: 2.2.2.1.36231 > 1.1.1.1.69:  17 RRQ "test.txt" octet
17:46:52.958719 Out 1a:0c:61:3d:f9:00 ethertype IPv4 (0x0800), length 61: 1.1.1.2.36231 > 1.1.1.1.69:  17 RRQ "test.txt" octet
17:46:52.959324  In d6:49:0e:de:b7:27 ethertype IPv4 (0x0800), length 53: 1.1.1.1.38635 > 1.1.1.2.36231: UDP, length 9
17:46:52.959397 Out 1a:0c:61:3d:f9:00 ethertype IPv4 (0x0800), length 81: 1.1.1.2 > 1.1.1.1: ICMP 1.1.1.2 udp port 36231 unreachable, length 45
17:46:57.960151  In 46:b9:b2:13:ec:51 ethertype IPv4 (0x0800), length 61: 2.2.2.1.36231 > 1.1.1.1.69:  17 RRQ "test.txt" octet
17:46:57.960213 Out 1a:0c:61:3d:f9:00 ethertype IPv4 (0x0800), length 61: 1.1.1.2.36231 > 1.1.1.1.69:  17 RRQ "test.txt" octet
17:46:57.960834  In d6:49:0e:de:b7:27 ethertype IPv4 (0x0800), length 53: 1.1.1.1.44001 > 1.1.1.2.36231: UDP, length 9
17:46:57.960932 Out 1a:0c:61:3d:f9:00 ethertype IPv4 (0x0800), length 81: 1.1.1.2 > 1.1.1.1: ICMP 1.1.1.2 udp port 36231 unreachable, length 45

现在执行完上述操作后(在路由器上,也就是在测试主机上)modprobe nf_nat_tftp,Term3 中的客户端命令将会成功执行,并且 Term1 将显示:

17:54:11.142487  In 46:b9:b2:13:ec:51 ethertype IPv4 (0x0800), length 61: 2.2.2.1.49514 > 1.1.1.1.69:  17 RRQ "test.txt" octet
17:54:11.142556 Out 1a:0c:61:3d:f9:00 ethertype IPv4 (0x0800), length 61: 1.1.1.2.49514 > 1.1.1.1.69:  17 RRQ "test.txt" octet
17:54:11.143090  In d6:49:0e:de:b7:27 ethertype IPv4 (0x0800), length 53: 1.1.1.1.36339 > 1.1.1.2.49514: UDP, length 9
17:54:11.143146 Out 96:c2:ee:fb:cc:07 ethertype IPv4 (0x0800), length 53: 1.1.1.1.36339 > 2.2.2.1.49514: UDP, length 9
17:54:11.143226  In 46:b9:b2:13:ec:51 ethertype IPv4 (0x0800), length 48: 2.2.2.1.49514 > 1.1.1.1.36339: UDP, length 4
17:54:11.143262 Out 1a:0c:61:3d:f9:00 ethertype IPv4 (0x0800), length 48: 1.1.1.2.49514 > 1.1.1.1.36339: UDP, length 4

查看 conntrack 预期流程(与上述示例不匹配的示例):

没有nf_nat_tftp

# ip netns exec router sh -c 'conntrack -E & conntrack -E expect'
    [NEW] 300 proto=17 src=1.1.1.1 dst=1.1.1.2 sport=0 dport=56876 mask-src=0.0.0.0 mask-dst=0.0.0.0 sport=0 dport=65535 master-src=2.2.2.1 master-dst=1.1.1.1 sport=56876 dport=69 class=0 helper=tftp
    [NEW] udp      17 30 src=2.2.2.1 dst=1.1.1.1 sport=56876 dport=69 [UNREPLIED] src=1.1.1.1 dst=1.1.1.2 sport=69 dport=56876 helper=tftp
[DESTROY] 299 proto=17 src=1.1.1.1 dst=1.1.1.2 sport=0 dport=56876 mask-src=0.0.0.0 mask-dst=0.0.0.0 sport=0 dport=65535 master-src=2.2.2.1 master-dst=1.1.1.1 sport=56876 dport=69 class=0 helper=tftp
    [NEW] udp      17 30 src=1.1.1.1 dst=1.1.1.2 sport=58241 dport=56876 [UNREPLIED] src=1.1.1.2 dst=1.1.1.1 sport=56876 dport=58241
^Cconntrack v1.4.4 (conntrack-tools): conntrack v1.4.4 (conntrack-tools): 2 flow events have been shown.
2 expectation events have been shown.

上面,第二个流程(最后一行)没有引用2.2.2.1

nf_nat_tftp

# ip netns exec router sh -c 'conntrack -E & conntrack -E expect'
    [NEW] 300 proto=17 src=1.1.1.1 dst=1.1.1.2 sport=0 dport=38115 mask-src=0.0.0.0 mask-dst=0.0.0.0 sport=0 dport=65535 master-src=2.2.2.1 master-dst=1.1.1.1 sport=38115 dport=69 class=0 helper=tftp
    [NEW] udp      17 30 src=2.2.2.1 dst=1.1.1.1 sport=38115 dport=69 [UNREPLIED] src=1.1.1.1 dst=1.1.1.2 sport=69 dport=38115 helper=tftp
[DESTROY] 299 proto=17 src=1.1.1.1 dst=1.1.1.2 sport=0 dport=38115 mask-src=0.0.0.0 mask-dst=0.0.0.0 sport=0 dport=65535 master-src=2.2.2.1 master-dst=1.1.1.1 sport=38115 dport=69 class=0 helper=tftp
    [NEW] udp      17 30 src=1.1.1.1 dst=1.1.1.2 sport=35725 dport=38115 [UNREPLIED] src=2.2.2.1 dst=1.1.1.1 sport=38115 dport=35725
 [UPDATE] udp      17 30 src=1.1.1.1 dst=1.1.1.2 sport=35725 dport=38115 src=2.2.2.1 dst=1.1.1.1 sport=38115 dport=35725
^Cconntrack v1.4.4 (conntrack-tools): 2 expectation events have been shown.
conntrack v1.4.4 (conntrack-tools): 3 flow events have been shown.

第二个流程确实提到了2.2.2.1

因此看起来虽然nf_conntrack_tftp足够好允许例如-m ctstate --ctstate RELATED在防火墙内使用第二个流程,nf_nat_tftp实际上仍然需要改变在使用 NAT 时,第二个流中的目标 IP(有时可能是端口)。据我所知,至少在最近的内核中,除了 conntrack TFTP 帮助程序之外,没有什么可以触发配套 NAT TFTP 帮助程序的加载:必须手动加载。

相关内容