Что: f8e2e4dfc6870c4dad7e62be9bb02ae9dd73d180
Когда: 2023-06-06 12:14:56+03:00
Темы: bsd dns ipv6 tip
Многие годы мой firewall был совершенно некорректно настроен https://docs.freebsd.org/en/books/handbook/firewalls/ Чуть ли не десять лет firewall/NAT у меня был в постыднейшем состоянии. Он работал, но жутко сильно грузил процессор. С переездом на свой btrtrc BitTorrent клиент (c2928fb18b27df55c19dd745753b2e76bdf2172b), который значительно больше соединений поддерживает и UDP-транспорт, проблемы с firewall/NAT стали очень видны. С переездом на новую FreeBSD (0cfadc4b3f5ed39dba025a4aecf5cede864f9ad1), проблема ещё сильнее усугубилась: если активно что-то начинает качаться, то я даже не могу сделать SSH временами, отваливается NFS и всё в таком духе. Когда я поднимал (6e7bec429305b04a664e01e6913d54d49fc0aaff, 394eb39744924cc673fcc8815eae1508440c9d44) публичный BitTorrent трэкер, то всё было просто неюзабельно. Причём при переезде (0aca426f64a513068a0bfffa563ab85f238a7d9f) с IPsec на WireGuard я оставил IPv4 трафик вне туннеля зашифрованного, так как IPv4 это всякая legacy Web-фигня и BitTorrent трафик -- штатно я уже не помню когда пришлось бы явно руками что-то делать с IPv4 адресами. То есть нагрузка на сетевую подсистему и CPU стала ещё меньше, но отзывчивость системы ужасна. Я понимаю что это явно косяк с моей стороны: ну не может жалкий 100Mbps канал так нагружать всё, что 4-core Xeon не справляется. На 100Mbps спокойно VIA C7 700-800MHz процессор с FreeBSD, NAT, ipf+ipfw+dummynet справлялся без проблем. tracker.cypherpunks.ru много где остаётся прописанным -- выключил его, ибо не справляется ничего. Но стало "много" (речь про несколько тысяч пакетов в секунду всего) трафика на DNS -- "решил" traffic shaper-ом. Позже я просто drop-ал пакеты на firewall к порту трэкера. Полная фигня. По top/*stat утилитам ничего не понятно что и где именно тормозит всё: они показывают что на 4-х ядерном Xeon 80+% CPU ест исключительно ядро. Где-то нашёл вот такой DTrace скрипт: callout_execute:::callout-start { self->cstart = timestamp; } callout_execute:::callout-end { @callouts[((struct callout *)arg0)->c_func] = sum(timestamp - self->cstart); } tick-1sec { printa("%40a %10@d\n", @callouts); clear(@callouts); printf("\n"); } BEGIN { printf("%40s | %s\n", "function", "nanoseconds per second"); } который показал что в dummynet модуле (traffic shaper) больше всего времени проводится. Удалил shaping вообще -- хуже ничего не стало, dummynet исчез, теперь libalias самый дорогой по вызовам. А это NAT. Ещё вот такой простой DTrace скрипт интересен был: profile:::profile-1001hz /arg0/ { @[ stack() ] = count(); } И так было очевидно что дело в NAT-е. Просто отключая его, вижу что нагрузка пропадает, всё тип топ. Пора начать за эти десять лет всё же разбираться как корректно прописать правила в firewall. Я использую ipfw и не хочу его менять, ибо привык (хотя и не научился настраивать :-)), нравится ясность и простота правил (хоть и не логики работы). Против PF ничего против не имею, знаю что там много есть отсутствующего в ipfw, но мне не нужно оно. ------------------------ >8 ------------------------ Короче говоря, достаточно было *внимательно* читать самый обычный родной FreeBSD handbook на тему ipfw (ага, в кой да веки за десять лет, RTFM). Основная проблема это то, как я заруливал трафик в NAT: [...] $add nat 1 ip4 from any to $ip4 in via igb_wan $add nat 1 ip4 from 192.168.20.0/24 to any out via igb_wan $add check-state [...] $add allow [...] $add deny all from any to any эти правила шли друг за дружкой. И только после "nat"-ов шли всякие разрешающие по всяким портам и интерфейсам. Для stateful firewall выглядело всё так, что весь трафик инициировался самим сервером. И короче из-за всего этого и была полная жопа. Надо делать так, чтобы сначала отрабатывал stateful firewall правила, а потом ещё в довесок на выходе nat для исходящего трафика, как бы: [...] $add nat 1 ip4 from any to $ip4 in via igb_wan $add check-state $add allow [...] keep-state [...] $add nat 1 ip4 from 192.168.20.0/24 to any out via igb_wan $add deny all from any to any но засада в том, что allow правила уже дальше ничего не будет делать с пакетом, он разрешён к прохождению и точка. Поэтому используют skipto действие вместо allow, указывая куда именно надо в правилах ipfw перепрыгнуть, на строчку с исходящим nat, как бы: [...] $add nat 1 ip4 from any to $ip4 in via igb_wan $add check-state $add skipto 1234 [...] keep-state [...] $add 1234 nat 1 ip4 from 192.168.20.0/24 to any out via igb_wan $add deny all from any to any но и тут засада в том, что будет отрабатывать deny правило всегда. Поэтому nat+allow добавляется после deny: [...] $add nat 1 ip4 from any to $ip4 in via igb_wan $add check-state $add skipto 1234 [...] out via igb_wan keep-state [...] $add deny all from any to any $add 1234 nat 1 ip4 from 192.168.20.0/24 to any out via igb_wan $add allow all from any to any Меня пугал этот последний allow, но штатно до него пакет не дойдёт. В часть с 1234-им правилом можно попасть только через отработавшее skipto правило, которое явно разрешило прохождение трафика. И после этого нагруженность ядра упала чуть ли не до нуля. ------------------------ >8 ------------------------ Не то чтобы проблема, но обратил внимание на тысячи динамических правил в stateful firewall создаваемых. И среди них почти все это DNS трафик от сервера в ответ кому-то ещё. Явно их быть не должно, ибо DNS трафик извне на 53-ий порт разрешён, а сам сервер отправляет данные точно так же с этого же 53-го. Было у меня такое правило: $add allow { tcp or udp } from any to "table(mein)" domain [...] $add allow ip4 from $ip4 to any out via igb_wan keep-state $add allow ip6 from any to any out via gif_tb keep-state и исходящий ответный DNS трафик создавал state-ы из-за последних правил. Если же сделать: $add allow { tcp or udp } from any to "table(mein)" domain $add allow { tcp or udp } from "table(mein)" domain to any то проблема решается конечно же. И динамических правил уже меньше тысячи остаётся, почти все инициированные BitTorrent-ом с другого сервера. ------------------------ >8 ------------------------ Насколько же всё проще с IPv6, когда нет NAT.
Сгенерирован: SGBlog 0.34.0