Приветствую.
Коллеги, обнаружил неописуемую граблю. А именно -- если клиент, сидящий за натом на базе pf отвечает серверу icmp-пакетом "port unreachable", то этот пакет проходит сквозь NAT как сквозь пальцы, и его SRC адрес не меняется. В итоге видим на внешнем интерфейсе вот такое:
16:25:58.242200 IP 10.12.0.99 > 213.180.204.179: ICMP host 213.208.167.84 unreachable, length 129
Обнаружил
упоминание о таком баге в pf. Но ветка старая, 2011-го года, и в конце там заявлено, что вроде как пофиксили. Однако FreeBSD 9.3 вышла год назад, и получается, что там этот баг не пофиксен?
Есть, конечно, костыль в виде запрещения любого ICMP от клиентов, кроме пинга. Но как-то это костыльно... Может, можно как-то вылечить такое поведение?
Скажу даже больше. На внутреннем интерфейсе такой ответ (ICMP port unreachable) выглядит так:
16:46:10.334993 IP (tos 0xc0, ttl 64, id 63254, offset 0, flags [none], proto ICMP (1), length 289)
10.12.0.198 > 84.47.177.77: ICMP 10.12.0.198 udp port 8293 unreachable, length 269
IP (tos 0x0, ttl 60, id 34284, offset 0, flags [none], proto UDP (17), length 261)
84.47.177.77.53 > 10.12.0.198.8293: 37288 2/4/4 www.jdm022.com. CNAME sbsfe-p8.geo.mf0.yahoodns.net., sbsfe-p8.geo.mf0.yahoodns.net. A 98.138.19.143 (233)
А на внешнем -- вот так:
16:46:10.335012 IP (tos 0xc0, ttl 63, id 63254, offset 0, flags [none], proto ICMP (1), length 289)
10.12.0.198 > 84.47.177.77: ICMP 213.208.167.85 udp port 61534 unreachable, length 269
IP (tos 0x0, ttl 60, id 34284, offset 0, flags [none], proto UDP (17), length 261)
84.47.177.77.53 > 213.208.167.85.61534: 37288 2/4/4 www.jdm022.com. CNAME sbsfe-p8.geo.mf0.yahoodns.net., sbsfe-p8.geo.mf0.yahoodns.net. A 98.138.19.143 (233)
Насколько я понял, в пакете ICMP port unreachable в качестве "полезной нагрузки" включается заголовок UDP-пакета, который вызвал генерацию у получателя ICMP-пакета port unreachable. Исходя из разницы пакетов на внешнем и внутреннем интерфейсах, получается, что pf меняет адрес и порт как раз в этом самом UDP-заголовке, который вложен в ICMP-пакет, но забывает это сделать у собственно ICMP-пакета. В итоге наружу уходит пакет, где корректно странслирована нагрузка (UDP-заголовок), но не странслирован заголовок самого пакета. Косяк налицо.
UPD2: Кстати, pf не только криво NATит такие пакеты, но и также не может их блокировать. Правило вида
block out quick inet proto icmp from 10.0.0.0/8 to any
просто не работает. Похоже, что pf анализирует опять-таки заголовок UDP-пакета, который идёт в нагрузке, и SRC-address берёт именно оттуда, а не из заголовка ICMP-пакета. Пришлось запилить блокировку такого трафика на ipfw.