мне нужно перенаправить все пакеты UDP с портом назначения 15000 на порт 15001, если пакет содержит, например, строку test
. у меня есть два простых правила:
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 15000 -m string --string 'test' --algo bm -j LOG --log-prefix='[netfilter] '
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 15000 -m string --string 'test' --algo bm -j REDIRECT --to-ports 15001
Странное поведение:
test
строка, перенаправление выполняется для все пакеты подключения;test
, перенаправление никогда не выполняется, даже если следующий пакет содержит test
Однако все правила соответствия пакетов регистрируются правильно.
Я попытался добавить в правило также информацию о треке:
-m state --state NEW,ESTABLISHED
но поведение то же самое. Некоторые идеи?
Это полный iptables
набор правил:
таблица фильтров:
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
нат таблица:
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
LOG udp -- anywhere anywhere udp dpt:15000 STRING match "test" ALGO name bm TO 65535 LOG level warning prefix "[netfilter] "
REDIRECT udp -- anywhere anywhere udp dpt:15000 STRING match "test" ALGO name bm TO 65535 redir ports 15001
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
таблица манжет:
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
необработанная таблица:
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Это вызвано тем, что iptables применяет отслеживание соединений на PREROUTING
цепь. Каждый раз, когда устанавливается новое соединение, iptables будет обращаться к кешу conntrack. Если совпадение найдено, правило из таблицы nat применяться не будет.
Если вы хотите отключить изменение этого поведения, посмотрите NOTRACK
цель в необработанной таблице.
Обратите внимание, что это применимо даже к UDP (протокол без установления соединения). Первый пакет считается открывающим соединением NEW
а другой - ответ ESTABLISHED
.
Я нашел похожий пост на serverfault.
Правила nat table всегда работают только для первого пакета в соединении. Последующие пакеты того же соединения никогда не проходят через список правил NAT и поддерживаются только кодом conntrack
Поскольку UDP не имеет соединения по своей природе, "соединение" здесь определяется просто адресами, портами и тайм-аутом. Таким образом, если второй пакет UDP с тем же портом источника и адресом и тем же портом назначения и адресом прибывает в течение тайм-аута, Linux считает, что он принадлежит установленному «соединению», и вообще не оценивает для него таблицу правил nat, повторно используя вердикт, вынесенный для предыдущего пакет.
Посмотреть здесь: http://www.netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO-3.html
В функция зон conntrack позволяет иметь два одинаковых коннтрака 5-ипл (или их часть), считающихся разными по их зона свойство. Обычно это, вероятно, используется со сложной маршрутизацией на основе политик, обрабатывающей идентичные IP-адреса, но проходящей по разным путям (маршрутам), чтобы предотвратить conntrack для слияния несвязанных потоков с разных путей.
Здесь это может быть использовано для решения этой проблемы: рассмотрим UDP-пакеты, не имеющие test
и пакет, имеющий test
быть частью двух разных зон происхождения (с помощью CT --zone-orig
): обычная зона и перенаправленная зона. Каждая исходная зона разрешает записи conntrack в NEW
состояние, чтобы не противоречить друг другу и считаться отдельным. Считайте это, как если бы в PREROUTING
крючок: вместо оценки nat
правила происходят один раз для первого пакета потока, это может произойти один раз для первого нормального пакета и один раз для первого test
пакет.
Хотя на самом деле в этом нет необходимости, чтобы избежать еще большего дублирования правил, я поставлю отметку и повторно использую ее позже для упрощения.
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 15000 -m string --string 'test' --algo bm -j MARK --set-mark 1
iptables -t raw -A PREROUTING -m mark --mark 1 -j CT --zone-orig 1
iptables -t nat -A PREROUTING -m mark --mark 1 -j LOG --log-prefix='[netfilter] '
iptables -t nat -A PREROUTING -p udp -m mark --mark 1 -j REDIRECT --to-ports 15001
Тестирование (сервер находится на IP 10.0.3.66
). Хронологически ответы, набранные после запросов (answer1 из term1 и answer2 из term2):
serverterm1$ socat udp4-listen:15000,reuseaddr,fork -
query1normal
query3normal
answer1
serverterm2$ socat udp4-listen:15001,reuseaddr,fork -
query2test
query4test
answer2
client$ socat udp4:10.0.3.66:15000 -
query1normal
query2test
query3normal
query4test
answer1
answer2
serverterm3# conntrack -E -p udp --orig-port-dst 15000
[NEW] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 [UNREPLIED] src=10.0.3.66 dst=10.0.3.1 sport=15000 dport=33150
[NEW] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 zone-orig=1 [UNREPLIED] src=10.0.3.66 dst=10.0.3.1 sport=15001 dport=33150
[UPDATE] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 src=10.0.3.66 dst=10.0.3.1 sport=15000 dport=33150
[UPDATE] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 zone-orig=1 src=10.0.3.66 dst=10.0.3.1 sport=15001 dport=33150
Предупреждение: string
соответствие оценивается для каждого пакета потока. Подумайте, если возможно, используя u32
вместо этого с меньшими накладными расходами.