Эксперимент по настройке Linux для блокирования 10 млн пакетов в секунду

Мaрeк Мaйкoвски (Marek Majkowski), рaзрaбoтчик ядрa Linux, рaбoтaющий в кoмпaнии CloudFlare, oпубликoвaл рeзультaты oцeнки прoизвoдитeльнoсти рaзличныx рeшeний нa бaзe ядрa Linux угоду кому) oтбрaсывaния oгрoмнoгo числa пaкeтoв, пoступaющиx в xoдe DDoS-aтaки. Нaибoлee высoкoпрoизвoдитeльный мeтoд пoзвoлил oтбрaсывaть пoтoки зaпрoсoв, пoступaющиe с интeнсивнoстью 10 млн пaкeтoв в сeкунду. Наибольшей производительности посчастливилось добиться при использовании подсистемы eBPF, представляющей вделанный в ядро Linux интерпретатор байткода, позволяющий организовывать высокопроизводительные обработчики входящих/исходящих пакетов с принятием решений об их перенаправлении али отбрасывании. Благодаря применению JIT-компиляции, байткод eBPF в лету транслируется в машинные инструкции и выполняется с производительностью нативного стих. Блокировка осуществлялась при помощи загруженного в сердечник простого BPF-приложения (около 50 строк стих), написанного на подмножестве языка C и скомпилированного близ помощи Clang. Для загрузки скомпилированного модуля eBPF применяется а уже что-л. делает появившаяся в iproute2 команда "xdp obj" ("ip link set dev ext0 xdp obj xdp-drop-ebpf.o"), позволяющая быть без написания специализированного BPF-загрузчика. Логика блокировки была зашита самотеком в BPF-приложение, например, блокируемые IP-подсеть, пристань назначения и тип протокола были определены вследствие условные операторы (системы типа bpfilter и Cilium умеют зажигать BPF-программы на основе высокоуровневых правил фильтрации): if (h_proto == htons(ETH_P_IP)) { if (iph->protocol == IPPROTO_UDP && (htonl(iph->daddr) & 0xFFFFFF00) == 0xC6120000 // 198.18.0.0/24 && udph->dest == htons(1234)) { return XDP_DROP; } } Иные опробованные методы блокировки: Отбрасывание пакетов порядком создания приложения-заглушки, принимающего требования на целевом сетевом порту, да не выполняющего каких-либо действий ("s.bind(("0.0.0.0", 1234))" и безграничный цикл с "s.recvmmsg()" - использование recvmmsg на смену recvmsg важно с точки зрения снижения числа системных вызовов, в свете накладных расходов присутствие включении в ядре защиты от уязвимости Meltdown). Полезный эффект такого решения составила всего 174 тысячи пакетов в одну секунду, при этом узким местом было безлюдный (=малолюдный) переключение контекста и само приложение (задание была 27% sys и 2% userspace), а полная применение второго ядра CPU при обработке SOFTIRQ. Старшие накладные расходы также возникали изо-за ведения ядром таблиц отслеживания соединений (conntrack), отсоединение которых при помощи правила "iptables -t raw -I PREROUTING -d 198.18.0.12 -p udp -m udp --dport 1234 -j NOTRACK" позволило воспитать производительность почти в два раза, после 333 тысяч пакетов в секунду. Дополнительное утилизация режима SO_BUSY_POLL подняло натиск обработки до 470 тысяч пакетов в побудьте на месте. Использование BPF с привязкой к сетевому сокету и запуском получи и распишись уровне ядра (как с обычным BPF, в такой степени и с eBPF). Специально написанное BPF-приложение bpf-drop, подключающее возделыватель для фильтрации пакетов к сокету выше вызов "setsockopt(SO_ATTACH_FILTER)", продемонстрировало полезный эффект всего 512 тысяч пакетов в повремени (в 20 раз меньше, чем BPF-переработчик на базе XDP). Причиной низкой производительности, т. е. и в первом рассмотренном методе, стали старшие накладные расходы при обработке SOFTIRQ. Действие операции DROP в iptables в цепочке INPUT (немного погодя обработки маршрутизации). При использовании следующих правил iptables -t raw -I PREROUTING -d 198.18.0.12 -p udp -m udp --dport 1234 -j NOTRACK iptables -I INPUT -d 198.18.0.12 -p udp --dport 1234 -j DROP посчастливилось добиться производительности 608 тысяч пакетов в побудьте на месте. Использование iptables DROP на стадии впредь до обработки маршрутизации (PREROUTING). Замена в правиле "-I INPUT" бери "-I PREROUTING -t raw" подняло производительность почти в три раза давно 1.688 млн пакетов в секунду. Использование операции DROP в nftables на стадии давно выполнения стадии отслеживания соединений (про обхода CONNTRACK применяется "hook ingress"): nft add table netdev filter nft -- add chain netdev filter input { type filter hook ingress device vlan100 priority -500 \; policy accept \; } nft add rule netdev filter input ip daddr 198.18.0.0/24 udp dport 1234 counter drop nft add rule netdev filter input ip6 daddr fd00::/64 udp dport 1234 counter drop Режим предложенного решения составила 1.53 млн пакетов в помедли, что немного отстаёт от iptables DROP с PREROUTING. Практика операции DROP в ingress-обработчике tc позволило вымучить производительности в 1.8 млн пакетов в один момент. tc qdisc add dev vlan100 ingress tc filter add dev vlan100 parent ffff: prio 4 protocol ip u32 match ip protocol 17 0xff match ip dport 1234 0xffff match ip dst 198.18.0.0/24 flowid 1:1 action drop tc filter add dev vlan100 parent ffff: protocol ipv6 u32 match ip6 dport 1234 0xffff match ip6 dst fd00::/64 flowid 1:1 action drop Ипокрена: http://www.opennet.ru/opennews/art.shtml?num=48925