iptables connection tracker
Столкнулся с любопытной проблемой:
Дано:
- сервер А, посылает непрерывный поток UDP-трафика на некий порт NNNN сервера B
- сервер B - гипервизор (KVM, Linux), через простой DNAT отправляет поток дальше, на виртуальный KVM-контейнер Z
Результат неожиданный - ICMP Port Unreachable в ответ серверу А от ядра сервера B , хотя трафик на интерфейс приходит, видно через tcpdump.
Т.к. в моем мозгу не укладывалось, как такой простой сценарий может не работать (более того, с того же гипервизора другие порты, и TCP, и UDP успешно прокинуты на другие KVM-контейнеры) - начал думать.
Очевидной мыслью стало попробовать проследить прохождение по цепочкам.
iptables -t nat -A PREROUTING --src A.A.A.A -p udp --dport NNNN -j ACCEPT
iptables -t mangle -A PREROUTING --src A.A.A.A -p udp --dport NNNN -j ACCEPT
Просматривая дальше счетчики этих правил через iptables -Lnv -t nat
и iptables -Lnv -t mangle
соответственно - увидел самое интересное -
счетчик в mangle растёт, а в nat нет! Т.е. пакет в ядро приходил, но до таблицы nat не доходил, что было очень любопытно.
Причина оказалась интересной - хоть я и знал это в теории, но на практике никогда раньше не видел.
Дело в том, что трафик от хоста A был мной отправлен на хост B до того, как я запустил приложение, обрабатывающее этот трафик в контейнере Z. В итоге, будучи по умолчанию активным, модуль Netfiler "ip_conntrack" запомнил, что по этому направлению никто порт не слушает, а т.к. поток был непрерывным - не забывал об этом по истечению времени жизни соединения внутри conntrack, и продолжал отправлять ICMP Port Unreachable, не пытаясь пропустить пакет.
Помогло с помощью утилиты conntrack удалить вручную соответствующую запись из таблиц conntrack.
Если у вас активен ip_conntrack - имейте ввиду, что он может на высоконагруженных системах творить подобные пакости. Ну он не со зла, он хороший и полезный.