Я заметил, что netfilter меняет исходный порт при установке соединения в модуле conntrack. Мне нужно предотвратить это поведение.
Вот что я сделал, чтобы воспроизвести свою проблему:
- Я создаю правило netfilter, которое будет выполнять DNAT с порта 2002 по 2003.
sudo iptables -w -t nat -A OUTPUT -s 192.168.30.3 -d 192.168.30.1 -p udp --sport 2001 --dport 2002 -j DNAT --to-destination :2003
- Затем я создаю запись conntrack для имитации соединения с 192.168.30.1:2001 (мой компьютер) на 192.168.30.1:2003.
sudo /sbin/conntrack -I -s 192.168.30.1 -d 192.168.30.3 -p udp --sport 2003 --dport 2001 --timeout 100000
- В конце концов я выполняю подключение к 192.168.30.1:2002 со своего компьютера с исходным портом 2001:
sudo nc -u -p 2001 192.168.30.1 2002
Из-за правила DNAT netfilter я ожидал выходной пакет с портом назначения 2003 и портом источника 2001. Однако на wireshark я заметил, что порт источника изменился на случайное число. Я предполагаю, что это потому, что мой компьютер считает, что на порту 2001 есть существующее соединение (из-за записи conntrack), а затем не допускает, чтобы порт источника был 2001 (верно?). Но я не хочу такого поведения? Как я могу принудительно использовать номер порта 2001?
решение1
Для работы DNAT (в том смысле, чтобы программа могла распознавать ответы) необходимо выполнить «обратный NAT», который изменяет исходный порт ответного трафика с 192.168.30.1
(на 192.168.30.3:2001
) с 2003
на .2002
Однако, когда есть трафик, поступающий 192.168.30.1:2003
с 192.168.30.3:2001
, который с точки зрения conntrack не является следствием DNAT (поскольку, согласно созданной записи conntrack, хост не является тем, кто инициировал соединение), обратный NAT будет нецелесообразным.
Поэтому netfilter «вынужден» также выполнять SNAT для трафика, соответствующего правилу DNAT, чтобы иметь возможность дифференцировать ответный трафик (то есть также от 192.168.30.1:2003
) по месту назначения 192.168.30.3:$random
.
Я предполагаю, что netfilter либо выполнит обратное NAT для DNAT (что является SNAT) до обратного NAT для SNAT (что является DNAT), либо сумеет использовать пункт назначения до обратного NAT для SNAT (т. е. 192.168.30.3:$random
) в качестве сопоставления для обратного NAT для DNAT, в противном случае принудительный SNAT будет бессмысленным. (Однако в случае отсутствия обратного NAT ни один из этих пунктов не является верным AFAIK: DNAT будет выполнен в PREROUTING до SNAT в INPUT, а сопоставление пункта назначения в правиле SNAT, если таковое имеется, будет использовать значение, полученное в DNAT)
Дело в том, что история выше / «проблема» в вашем вопросе вряд ли имеет какой-либо смысл в реальности. Возьмем в качестве примера VPN с двумя хостами Wireguard: предположим, вы хотите иметь Endpoint=
настройки на обоих хостах (чтобы любой из них мог инициировать связь) и не хотите, чтобы значения неожиданно «обновлялись» из-за принудительного SNAT (предполагая, что он действительно может быть запущен), то вам следует просто использовать «всегда включенный» SNAT, который «дополняет» DNAT / эквивалентен резервному NAT:
iptables -t nat -A INPUT -s 192.168.30.1 -d 192.168.30.3 -p udp --sport 2003 --dport 2001 -j SNAT --to-source :2002
что обычно не требуется в модели клиент-сервер из-за автоматического обратного NAT для DNAT.
PS Вы все еще не должны достичь 192.168.30.1:2003
по 192.168.30.1:2003
, в противном случае принудительный источник NAT также произойдет, если вы достигнете его снова по 192.168.30.1:2002
до того, как запись conntrack прежнего будет удалена. Дополнительное правило SNAT в INPUT также не должно вызвать у вас дополнительных проблем.
решение2
Вы можете установить два потока, которые обычно сталкиваются вconntrackтаблица поиска (обычно вызывающая перезапись исходного порта в новом потоке для избежания коллизии) должна быть в разныхзоны conntrack. Это дополнительное свойство зоны делаетconntrackне совпадает/не конфликтует с существующим потоком в другой зоне conntrack: перезапись исходного порта не произойдет.
Для вашего конкретного примера вот конкретное правило, которое предотвратит коллизию и, таким образом, предотвратит перезапись исходного порта:
iptables -t raw -A OUTPUT -s 192.168.30.3 -d 192.168.30.1 -p udp --sport 2001 --dport 2002 -j CT --zone-orig 1
Обычно в зависимости от варианта использования используется более разумный селектор. Он часто используется в цепочке PREROUTING с входящим интерфейсом в качестве селектора при маршрутизации и часто связан со значением метки, поэтому маршрутизация также может быть затронута.
Первоначальный вариант использования, который привел к появлению этой опции, — когдаconntrackв одном сетевом стеке (без дополнительного сетевого пространства имен) со сложной настройкой маршрутизации (например: маршрутизация между 4 различными частными локальными сетями, использующими одни и те же IP-адреса. например, между 192.168.1.0/24 eth0 <-> eth1 10.1.0.0/24, иснова192.168.1.0/24 eth2 <-> 10.1.0.0/24 eth3) можно увидеть два несвязанных потока с одинаковыми адресами/портами. КакСетевой фильтриconntrackничего не знаю о маршрутизации (conntrackтаблица поиска включает только адреса) их необходимо научить рассматривать эти потоки отдельно, добавив свойство зоны, вручную привязанное к топологии маршрутизации вconntrackСправочная таблица.
(ВотСсылка LWN(когда эта функция была первоначально предложена.)
решение3
NAT изменяет исходный порт, чтобы снизить риск конфликта портов. Что произойдет, если этот sport 2002 уже занят на машине NAT?
Если у вас есть особые требования к определенным портам, то вы можете добавить SNAT
для них особые правила, но опять же, что делать, если несколько внутренних клиентов попытаются использовать один и тот же исходный порт?
Здесь мы должны вернуться и признать, что NAT — это хак, который был создан для уменьшения проблемы отсутствия публичных IP-адресов. Реальное решение здесь — иметь всем публичные IP-адреса без NAT.
Когда мы говорим о NAT в наши дни, мы чаще всего подразумеваем частные IP-адреса за одним IP. В этих случаях это на самом делеНАПТ
аПохожий вопрос, связанный с этим, я думал о MASQUERADE
цели, а не оDNAT