У меня есть сервер TFTP (машина «S») и клиент TFTP (машина «C») в разных подсетях. Они подключены через маршрутизатор (машина «R»). Все 3 машины - Debian 9 / Stretch. Маршрутизатор работает под управлением iptables и настроен на маскировку подключений из клиентской сети к сети сервера. Я настроил iptables для использования вспомогательной функции Netfilter TFTP для tftp-соединений, идущих к TFTP-серверу.
Проблема, с которой я сталкиваюсь, заключается в том, что помощник TFTP устанавливает ожидание для возврата tftp-соединения (как и ожидалось), но, несмотря на это, только трафик с порта 69 на сервере TFTP переводится и отправляется обратно клиенту. Таким образом, используется только обычное отслеживание соединений MASQUERADE хотя таблица conntrack показывает ожидаемое обратное соединение. В соответствии с RFC1350, сервер должен выбрать случайный исходный порт для обмена данными и направить его на тот порт, который клиент изначально использовал как исходный порт (вот как ...).
В результате маршрутизатор преобразует соединение клиента с сервером через NAT, устанавливает правило трансляции для обратного соединения и с радостью ожидает ответного пакета от сервера с исходным портом = 69, который никогда не приходит.
Адреса составлены для наглядности:
Сервер TFTP (S): 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?
Если мое предположение верно, то 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
Term2:
ip netns exec tftp atftpd --daemon --no-fork /tmp/tftp
Term3:
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
по-прежнему требуется также на самом деле изменить во втором потоке IP-адрес назначения (и, возможно, иногда порт), когда используется NAT. AFAIK, по крайней мере, в последних ядрах, ничто не вызовет загрузку сопутствующего помощника NAT TFTP в дополнение к помощнику conntrack TFTP: его нужно загружать вручную.