Назад | Перейти на главную страницу

Невозможно передать трафик TFTP через NAT, поскольку iptables не пересылает обратное соединение клиенту, несмотря на то, что вспомогательный модуль TFTP создает ожидание

Эта проблема

У меня есть сервер TFTP (машина «S») и клиент TFTP (машина «C») в разных подсетях. Они подключены через маршрутизатор (машина «R»). Все 3 машины - Debian 9 / Stretch. Маршрутизатор работает под управлением iptables и настроен на маскировку подключений из клиентской сети к сети сервера. Я настроил iptables для использования вспомогательной функции Netfilter TFTP для tftp-соединений, идущих к TFTP-серверу.

Проблема, с которой я сталкиваюсь, заключается в том, что помощник TFTP устанавливает ожидание для возврата tftp-соединения (как и ожидалось), но, несмотря на это, только трафик с порта 69 на сервере TFTP переводится и отправляется обратно клиенту. Таким образом, используется только обычное отслеживание соединений MASQUERADE хотя таблица conntrack показывает ожидаемое обратное соединение. В соответствии с RFC1350, сервер должен выбрать случайный исходный порт для обмена данными и направить его на тот порт, который клиент изначально использовал как исходный порт (вот как ...).

В результате маршрутизатор преобразует соединение клиента с сервером через NAT, устанавливает правило трансляции для обратного соединения и с радостью ожидает ответного пакета от сервера с исходным портом = 69, который никогда не приходит.

Установка

Адреса составлены для наглядности:

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: его нужно загружать вручную.