У меня есть два хоста, которые пытаются установить соединение 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