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

Почему iptables отклоняет второй и последующие фрагменты разрешенного пакета?

У меня есть два хоста, которые пытаются установить соединение IPSec друг с другом. Для этого они должны взаимодействовать через порты UDP 500 и 4500, поэтому я открыл их в брандмауэрах на обоих концах (показано в соответствующей части):

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m udp -p udp --dport 500 -j ACCEPT
-A INPUT -m udp -p udp --dport 4500 -j ACCEPT
#.....
-A INPUT -j REJECT --reject-with icmp6-port-unreachable

Однако обмен ключами никогда не бывает успешным. Каждая сторона пытается повторно передать UDP-пакеты снова и снова, никогда не слыша ответа, пока наконец не сдастся.

Я начал tcpdump на одном конце и заметил, что пакет UDP фрагментировался, и что порт ICMP недоступен после того, как пришел второй фрагмент.

Пример такого неудачного обмена (продезинфицирован для вашей защиты):

04:00:43.311572 IP6 (hlim 51, next-header Fragment (44) payload length: 1240) 2001:db8::be6b:d879 > 2001:db8:f:608::2: frag (0x5efa507c:0|1232) ipsec-nat-t > ipsec-nat-t: NONESP-encap: isakmp 2.0 msgid 00000001 cookie 55fa7f39522011ef->f8259707aad5f995: child_sa  ikev2_auth[I]: [|v2e] (len mismatch: isakmp 1596/ip 1220)
04:00:43.311597 IP6 (hlim 51, next-header Fragment (44) payload length: 384) 2001:db8::be6b:d879 > 2001:db8:f:608::2: frag (0x5efa507c:1232|376)
04:00:43.311722 IP6 (hlim 64, next-header ICMPv6 (58) payload length: 432) 2001:db8:f:608::2 > 2001:db8::be6b:d879: [icmp6 sum ok] ICMP6, destination unreachable, length 432, unreachable port[|icmp6]

В отношении этого пакета межсетевой экран зарегистрировал следующее:

Aug 26 04:00:43 grummle kernel: iptables: REJECT IN=eth0 OUT= MAC=############### SRC=2001:0db8:0000:0000:0000:0000:be6b:d879 DST=2001:0db8:000f:0608:0000:0000:0000:0002 LEN=424 TC=0 HOPLIMIT=51 FLOWLBL=0 OPT ( FRAG:1232 ID:5efa507c ) PROTO=UDP

У меня создалось впечатление, что Linux автоматически собирает фрагменты, прежде чем передать их фильтру пакетов. Так почему же эти фрагменты не собираются заново и, следовательно, второй фрагмент впоследствии отвергается?

Код netfilter собирает только фрагменты для вас перед фильтрацией пакетов если ваши правила брандмауэра используют отслеживание соединений (т.е. правило брандмауэра отслеживает состояние и использует -m conntrack или устаревший -m state) или NAT. В противном случае все фрагменты обрабатываются отдельно и возникают проблемы, подобные этой.

Это делает решение проблемы простым и очевидным (по крайней мере, в ретроспективе). Просто добавьте отслеживание подключения к рассматриваемым правилам межсетевого экрана.

-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m conntrack --ctstate NEW -m udp -p udp --dport 500 -j ACCEPT
-A INPUT -m conntrack --ctstate NEW -m udp -p udp --dport 4500 -j ACCEPT

Или для более старых систем Linux (например, RHEL 5 и ранее):

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m state --state NEW -m udp -p udp --dport 500 -j ACCEPT
-A INPUT -m state --state NEW -m udp -p udp --dport 4500 -j ACCEPT