问题
我在不同的子网上有一个 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 netns
,atftpd
和atftp
(或等效的服务器和客户端软件)。
从头开始,没有理由在路由器上加载 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 帮助程序的加载:必须手动加载。