Как стать автором
Обновить

Захват пакетов в Linux на скорости десятки миллионов пакетов в секунду без использования сторонних библиотек

Время на прочтение8 мин
Количество просмотров87K
Всего голосов 113: ↑112 и ↓1+111
Комментарии77

Комментарии 77

Очень интересная статья. Вообще возможности Си поражают в смысле производительности. Недавно у меня была задача преобразовать базу maxmind из бинарного формата в список префиксов по странам, получилось что на питоне программа отрабатывала за 14 часов, на pypy за 7-8, на Go за полтора часа, на Си за полторы минуты. Запускал на 16тиядерном сервере.

Возникли вопросы:
Для балансирования обработчиков прерываний от очередей сетевой карты не разумнее ли воспользоваться irqbalance?
Не пробовали ли вы тюнить сетевую карту и настраивать coalescing ( ethtool -C ) чтобы снизить частоту срабатывания обработчика прерывания?
разница между go и c в 60! раз, подсказывает мне что код был не совсем одинаковый
Кстати, для Go есть отличная библиотека для захвата пакетов — github.com/google/gopacket
на си я пользовался libmaxminddb напрямую и имел возможность не делать многократное преобразование запрашиваемого адреса и запрашивал только нужную информацию вместо полной структуры. тем не менее в Go я разочарован: во-первых, разница не настолько большая по сравнению с питоном, чтобы стоило морочиться со статической типизацией и сравнительно небольшим набором языковых инструментов; во-вторых, сборка программы компилятором Go 1.4.2 вместо 1.2.1 сделало её медленнее еще на 30%. это ужасно
Кстати, такой эффект может давать преобразования бинарщины в текстовый вид, вот даже народ умные либы для оптимизации пишет: github.com/h2o/qrintf
вообще да. либу я бы не потащил, но на последовательный вывод из нескольких буферов заменил бы
как-то у меня не вяжется первая часть со второй. вы вроде признаете что постоянно что-то преобразовывали хотя это не нужно было, но потом делаете вывод о производительности.
про 30% это видимо время на GC какойнить map[string] *GeoData
geoip2-golang принимает запрашиваемый адрес только в строковом виде, который у меня берётся из счётчика, который просто long. затем сам запрос внутрях libmaxminddb из строкового вида преобразуется в число. это преобразования, которые диктует окружение, увы.

мапов у меня нет нигде, это дефект новой версии golang. вот так вот замечательно развивается язык.
Так а в чем конкретно дефект?
И не совсем ясно почему не расширили структуру библиотеки для того чтобы она принимала long…
Делается это очень просто.
1. Используйте maxminddb-golang, вместо непонятной кривой надстройки
2. Посмотрите в вызов Lookup, и увидите там в первых строках ipV4Address := ipAddress.To4()
3. Допишите свою функцию без преобразований
4. profit!
Для каждого языка я пользовался тем, что предлагает maxmind на сайте в разделе API.
Ну не стоит же рассматривать их как последнюю инстанцию в вопросах софта, который они не тестировали…
А на странице пакета, который вы использовали, во втором абзаце написано следующее:

This library is built using the Go maxminddb reader. All data for the database record is decoded using this library. If you only need several fields, you may get superior performance by using maxminddb's Lookup directly with a result struct that only contains the required fields.

Т.е. про конкретно ваш случай и пишут.
Многократное преобразование это одно, насчёт maxminddb-golang я согласен

Есть отдельная проблема, что после компилирования той же самой проги новым компилером 1.4.2 (вместо 1.2.1) время работы стало не полтора часа, а два. Только от обновления версии компилятора. При такой пропорции это явный дефект.
На своем софте я заметил только ускорение с переходом на новую версию.
Сборщик отлично работать начал.

Посмотрите профайлинг, зафайлите баг разработчикам со статистикой.
Там адекватные и отзывчивые люди.
Посмотрите на Nim — околопитонячий синтаксис плюс производительность Си.
Нет там производительности Си, и не может быть по определению.
Он транслируется в C, но не более.
1) Не всегда сгенерированный Си-код будет сильно медленнее написанного руками с нуля кода на Си. Большинство тестов показывают вполне высокую производительность несложного кода на Nim.

2) Не всегда программист имеет квалификацию/время на написание производительного Си-кода руками. В некоторых (скорее всего редких, но все же) случаях, как мне кажется, Nim создаст даже более производительный код, чем низкоквалифицированный программист.
Слишком много допущений в ваших словах и ниодного пруфа.
Медленность Go vs C таки не удивительна (сборщик мусора же), а вот разницу C c Rust было бы интересно оценить, ведь последний именно как системный язык позиционируется.
Разница неудивительная, ведь Go намного медленнее С. По вашей логике, код на Python должен содержать специальные sleep и тоже не быть таким же, как C!
в смысле по моей логике? Go и С как-бы оба компилируемые языки и производительность абсолютно одинакового кода (в цикле число сложить) будет одинаковой и я как бы пишу на обоих, Go до 2-3х раз проигрывает C в задачах разбора бинарных потоков данных, например изображений, т.к. в C можно просто указатель двигать, а в Go будут постоянные проверки на границы при обращении, а также не так удобно сказать что у меня в таком то адресе байт, слово, структура и т.п.
Так вот это по сути максимальная разница для примерно одного и того же кода. (для сравнения в отдаче файлика по http у меня получалось ~ 0.7x от производительности nginx, то есть сервер который я написал на 5 минут vs дорабатываемый годами сервер 30% разницы)
Разница в 60! раз это 2 разные программы чуть более чем полностью
А можно код ради интереса покопаюсь?
Да, пожалуйста:
C
Go

Вывод и мелочи чуть отличаются, так как Си-версия ушла эволюционно дальше. Запускаю для всего пространства IPv4 вот так:
THREADS=32
MMDB=GeoIP2-City.mmdb
time for ((i=0; i<224; i++)) do echo "$MMDB" ; echo $i.0.0.0; echo $i.255.255.255 ; echo geo.txt.part$i ; done | parallel -P $THREADS -N 4 "./geoip2_dump \"{1}\" {2} {3} > {4}"
irqbalance штука довольно cложная и временами делает только хуже. Но есть отличная реализация грамотного подхода к задаче балансировки прерываний, это проект Birq: libcode.org/projects/birq от Serj Kalichev. Я его довольно активно использую там, где нагрузка не распределяется так красиво.

По поводу coalescing — нет, не пробовал. Тут есть более интересная тема, мне обещают дать патч на ixgbe для поддержки batch режима для извлечения пакетов из сетевой, который добавили в 3.19, это должно поидее дать очень хороший прирост скорости.
Рад, что мои изыскания полезны :)
Когда переедете на двухсокетный сервер у вас будет ближний и дальний (по отношению к сетевухе) сокет. Это тоже надо будет учитывать…
Ага, поэтому предпочитаю односокетные машинки с очень мощными ядрами класса.

Вообще, выбор машины под захват пакетов дело целой статьи. Но я постараюсь — либо самосборное либо SuperMicro. Dell/HP замечены на очень серьезном уродовании референсных платформ от Intel. Что выливается, например, в невозможность настроить VT-D, сменить режим энергосбережения процессора без заходи в BIOS. Также очень любят отключать системные регистры и приводить в негодность тулзы класса PCM.

По части шедулинга прерываний — Birq понимает, когда сокет удаленный и с учетом этого распределяет прерывания.
Оу, для меня это первая полезная статья на хабре за многие годы ) Спасибо тебе автор )
Рад стараться =)
Очень интересно, сам AF_PACKET конечно не новость, но оптимизация и анализ просто замечательны.
Кстати у меня ядро 3.13, однако PACKET_FANOUT присутствует в хедерах, хотя в манах не упоминается. Тем не менее все сбилдилось и работает. Спасибо.
есть возможность задать буфер приема, SO_RCVBUF, но на моем тест-стенде это не дало никаких результатов.


А вы не проверяли код возврата setsockopt? Ибо на той же 7ой центоси этот буффер по умолчанию ограничен 512kb, когда как на практике для подобных процессоров знающие люди рекомендуют от 4mb.
Буквально неделю назад общался с Мареком по немного смежной теме — по захвату с netmap/pf_ring.

Но у нас все же разные статьи, Марек обрабатывает данные и пропускает их глубоко в стек. Я же наоборот отключаю как можно больше прикладной обработки пакетов ядром, чтобы сделать это в user space.

Но в ряде случаев (UDP сервисы там), мой подход можно использовать как замену подхода Марека на очень больших скоростях.
Извините — может глупый вопрос: «Картинка в статье имеет отношение к статье или нет?»
Я почему спрашиваю — вроде бы на картинке в сервер заходит несколько сетевых кабелей (5?), но прием пакетов происходит из нескольких интерфейсов или только из одного eth6?
Картинка просто для привлечения внимания =) Там всего 4 1GE линка в LACP. Тест стенд не такой фотогеничный, ибо собран из десктоп оборудования, это оказалось самым гибким вариантом по части полного доступа ко всем фичам Intel платформы.
Раз пошла такая пьянка: нижний сервер стоит «задом наперед» или это модель, в которую диски с 2х сторон ставятся?
Supermicro это :)
это то понятно. Но почему он в сторону горячего коридора повернут дисками(если мы считаем что верхний сервер стоит «правильно»)?
Павел,
Спасибо за статью.

>>Моя статья расскажет Вам как принять 10 миллионов пакетов в секунду
>>без использования таких библиотек как Netmap, PF_RING, DPDK

Но ведь DPDK делает это на одном ядре.
Да, PF_RING/NETMAP/DPDK могут реально принять ~10-14mpps на одном ядре этого же процессора. Но это все же очень специфичный софт, имеющий ряд ограничений (архитектуры — arm/powerpc/avrXX, версии ядер, сам факт сборки модулей ядра часто нудоступен, PF_RING/NETMAP поддерживаются только на Intel сетевых), а AF_PACKET — это технология которая доступна на всем многообразии железа и платформ, где только работает Линукс, пусть и медленнее, но универсальность зашкаливает.
Согласен, что как универсальное решение ваш подход интересен.
Особенно при разработке виртуальных сетевых функций,
без акселерации на уровне гипервизора и не требующий использования
специализированных SDK.

По поводу PF_RING/NETMAP/DPDK я читал интересный отчет по анализу
http://www.net.in.tum.de/fileadmin/bibtex/publications/theses/2014-gallenmueller-high-speed-packet-processing.pdf.
Много думал. Для себя мы выбрали DPDK.
Блин, он на немецком =) Да, DPDK хорош если речь идет про новый проект. Если есть унаследованный код — тут PF_RING/Netmap впереди, ибо работают в рамках обычного окружения Linux API, а не строят свой абсолютно чуждый Nix программисту API.
А что за проект Вы делаете на DPDK, если не секрет? Я use case коллекционирую :)
Не не не, там только первые страницы на немецком =).
Начиная с 10-го там английским по белому написано.

На DPDK делаем коммутатор с интерфейсами в lxc/kvm.
Замену для openvswitch в openstack.
Да, форвардеры/роутеры одно удовольствие писать на DPDK — там почти все для этого есть и ворох примеров :)
А инициатива по классификаторам OF в tc вам не кажется более привлекательной? Менять овс можно либо на чистый юзерспейс, либо на них, в общем-то (как кажется). И какие в первую очередь задачи преследуете?
>>А инициатива по классификаторам OF в tc вам не кажется более привлекательной?
Мне кажется просто привлекательной, с точки зрения рекламы OpenFlow,
как SDN протокола. Ну и вообще мне нравится идея держать это в одном месте.
А так, насколько мне известно, все кому не лень пользуют классификатор из kernel модуля OVS.

>>Менять овс можно либо на чистый юзерспейс, либо на них, в общем-то (как кажется).
Мы на SP рынке, ни чистый userspace, ни kernel тупо не вытягивают нагрузку.
DPDK развивается и обещает чтение на wire speed на 40Gb/s.
У нас пока нечем такой траффик сгенерить, но мы работаем над этим ©.

OVS не подходит, так как нет поддержки chained groups, и впилить
архитектурно трудновыполнимо. Также там некоторые проблемы с meters,
select algs и т.п. Не реализованы dpdk bond и т.д.

В общем после анализа, определили свои фишки, посмотрели на опыт BigSwitch
который форкнул OVS в IVS, потом родил свой SwitchLight,
и начали делать.

А задачи у нас простые, нужен виртуальный свитч под наши решения,
конкретно под NFV ферму на x86 серверах.
Ну и попутно framework для наших партнеров по коммутаторам
(без dpdk, а со своим datapath).

И чтобы WindRiver с его виртуальным свитчем без поддержки OpenFlow
и 12mpps на 2 ядра (в составе его Titanium Server) оказался менее
производительным.

>> Ну и попутно framework для наших партнеров по коммутаторам

Не совсем понятно — у коммутаторов обычно софтверный датапэс как класс отсутствует, я даже сходу не могу привести обратный пример. Или все же речь про общий апи?
скажем так, ofagent с openflow 1.3/1.4/1.5 и с api, для прогрузки правил в hardware pipeline через SDK вендора асиков.
Просто опять же из плотной работы выяснилось, что и у pica8, и у Intel ONS есть ньюансы (оба на базе ovs).
Ну ONS насколько мне известно забил на софтовую платформу и делает упор на апи матрицы, а овс, когда я там его тестировал, был очень глючным. Вообще идея втыкать of-правила в матрицу, казавшаяся очень привлекательной два года назад, продолжает разбиваться о фактические возможности недорогих матриц — затруднена цепочечная группировка правил в нужных масштабах, много чего в железе просто не поддерживается и тд. Кумулюс года полтора назад казался достаточно провальным с его тогдашними объемами поддержки железа, а сейчас напротив — ONIE сертифицировался на большом числе железок, на него же подтянулась pica и будущее кажется довольно неплохим, плюс ребята реально много делают плюшек в хост-стеке. Pica же, напротив, с текущими тенденциями развития матриц не имеет, на мой взгляд, прорывного будущего. Втыкать полную спецификацию OF в обычный ToR-девайс очень сложно и избыточно, поскольку ему надо правил порядка 1..10 * число портов как максимум, а процессинг трафика лучше распределять по софтсвичам на вычнодах. На абсолютную истину мои высказывания не претендуют, просто развитие матриц все сильнее корректирует взгляды на то, как это все должно (будет) работать.
Друзья, позвольте вклинится. А есть свичевые матрицы с достаточно широким каналом к контрол плейну и мощным железом на контрол плейне (хотя бы i7/e3)?

Уж очень хочется идею выявления атак силами github.com/FastVPSEestiOu/fastnetmon затащить даже в свич.
Это задача SUME-подобных вещей, если даже свич твоим требованиям удовлетворит (тот же ONS), на все порты его просто не хватит.
Я могу сказать за SDN свитчи (openflow/cumulus). В основном у всех control plane на atom (если x86). Максимум что я видел — это i3, на Intel ONP FM6K (etegro 410i). Зимой в 2014 говорили, что будет FM10K, с мощным control plane, но на NFV воркшопе Intel Network Builders в мае никто в курсе не был. Но вообще у нас как раз и задача вынести control plane со свитчей, поэтому особо эту тему не копал.
Все порты, понятно, не засунуть. А вот хотя бы гигабит завернут на контрол плейн да там внимательно посмотреть его и проанализировать было бы хорошо.
Ну вон pica если нормализовать не может в hardware table, заворачивает на CPU. Выглядит на атоме это очень грустно :(
Я бы посмотрел в сторону коробок от A10, мультипроцессорные системы с большим количеством портов. Они на них CGNAT выпускают. По сути обычные серваки.
А что за сетевки они ставят?
Это хороший вопрос, мне идентифицировать не удалось. Видел в чужой лабе, там было 16x10GB/s SFP+.
Судя по дата шитам, внутри есть PCI-E expansions.
Ого, 16 штук, это круто! :)
да, похоже
of старше 1.1 на старых асиках с линейным pipeline (типа firebolt, trident, trident+) на мой взляд утопия. Но в принципе tomahawk уже разрабатывался с оглядкой на openflow. Мы реализуем подход похожий на описанный вами, потому что как не крути, tcam лимитирован. Только у нас траффик доводится на вычноду и там отдаётся в вычсервис, в зависимости от типа траффика, клиента, потребителя и т.п.
Приятно видеть живого человека, имеющего компетенции в OVS и OpenFlow, да еще и пишущего на русском :)

OVS не подходит, так как нет поддержки chained groups, и впилить архитектурно трудновыполнимо

Так вроде у них были реализованы группы, но они в итоге их отломали их относительно недавно из-за некоторого бага с дедлоками. Трудновыполним именно фикс этого бага?

Также там некоторые проблемы с meters, select algs и т.п. Не реализованы dpdk bond и т.д.

А что за проблемы с meters и select algs? Год назад когда смотрел на meters — они вообще не были реализованы.
>> Приятно видеть живого человека, имеющего компетенции в OVS и OpenFlow, да еще и пишущего на русском :)
Я кстати тоже приятно удивлен, что есть с кем поговорить :-)

>> Так вроде у них были реализованы группы
Да, у них был chaining, но они выпилили его году в 2014, объяснив
потенциальным readlock.

>> Трудновыполним именно фикс этого бага?
Скажу так, костыли есть, можно обойтись, но вот именно фикс,
гарантирующий работу group chaining в рамках архитектуры OVS — нетривиален.
В рамках координат моих знаний о его архитектуре, естественно.

Но если честно, то на мой взгляд OVS достаточно громоздок для baremetal
коммутаторов, и унификация под разные реализации datapath для x86
платформ тоже имеет свои минусы.

У него отличное сильное комьюнити, его используют множество проектов, но
конкретно нам надо решать наши сугубо прикладные задачи, и быстро :-(
Текущие реализации OVS и его портов типа Pica8 требуют костылей со стороны
контроллера, что повышает трудозатраты (разработчиков и инженеров) и
прозрачность кода.

>>А что за проблемы с meters и select algs?

meters — там есть pull реквест (годичной давности), когда проверял в феврале
— в develop ветке его не было.

select algs — там для select групп, раньше можно было делать select по dl_dst only.
Сейчас вроде еще есть 5-tuple hash, и он даже работал для tcp, udp flow,
но нет никакого механизма включить его для select группы в dpdk режиме.
Только правками в коде.

Оценив количество костылей, решили делать свой.
Потому что даже в случае выкладывания в открытый доступ ядра, мы останемся
владельцами проекта, в плане решений, что мерджить, а что нет.

Ну и изначально ориентируемся на легкий framework для baremetal с версией для x86
серверов. А не на виртуальный свитч, который портируют на baremetal.
> Текущие реализации OVS и его портов типа Pica8 требуют костылей со стороны
контроллера, что повышает трудозатраты (разработчиков и инженеров) и
прозрачность кода.

Гм, там вроде нет особенных костылей, если не говорить именно про пику.

> Ну и изначально ориентируемся на легкий framework для baremetal с версией для x86
серверов.

Тем не менее, фоллбекать процессинг на менеджмент энжин, при отсутствии возможности сделать требуемое матрицей, вы будете или вместо этого ограничите возможности стандарта per-матрица?

BTW выше вы с Игорем имеете в виду разные чейнинги.
>>Гм, там вроде нет особенных костылей

Я имею ввиду костыли, которые приходится писать в сетевых приложениях для контроллера.
Вот пишешь ты допустим обработку mpls. Все правила, которые генерит контроллер
OpenFlow 1.3 spec conforming.

На практике:

OVS — три метки максимум (меняется в коде), push mpls метки на пакет с vlan вызывает drop.
push трех меток в userspace отрабатывал, в kernel нет (пофиксили в upstream).

Pica — если dl_dst не указал и vlan — процессинг на CPU
(а если в разных таблицах actions, то вообще drop) и две метки, Intel FM — одна метка,
так как старый билд OVS.

Ну и подобные мелочи…

>>Тем не менее, фоллбекать процессинг на менеджмент энжин…
Будем фоллбекать, но не совсем так как Pica.
Сейчас просто делаем research на эту тему, нужно подтверждение наших
идей, ждем ответ вендора матриц.
Блин, он на немецком =)

Хм, а читается как английский :)

drwatson32, спасибо, весьма занимательное чтиво
Рад что пригодилось.
ЕМНИП netmap легко дает 14.88 Mpps на 10G — info.iet.unipi.it/~luigi/netmap
netmap can forward packets at line rate (14.88 Mpps) at 1.7 GHz without touching data, and slightly slower with full data copies. As a comparison, native packet forwarding using the in-kernel bridge does about 700Kpps on the same hardware. Though the comparison is a bit unfair because our bridge and testpcap don't do address lookups; however we have some real forwarding code (a modified version of openvswitch) that does almost 3Mpps using netmap.
Так и есть — он очень быстр. Но 1) Он на Linux не стандартный и планов по его интеграции нету 2) требует патчинга драйверов
Раз уж стоит вопрос заниматься этим профессионально и на пределе возможностей, почему бы не взять FreeBSD? Нужно же подходящие инструменты использовать под задачи. Я про целесообразность погружения статьи. Статья отличная и я проплюсовал как мог, но все же :)
Задача — захват трафика на максимальной скорости, она очень универсальна и требовать от пользователя смены ОС для работы приложения — довольно странно.

AF_PACKET гибче netmap, если про то зашла речь, аналогов ему в FreeBSD нету. Netmap полностью отключает сетевую от стека, что в ряде случае недопустимо и трафик нужно смотреть на живом интерфейсе, подключенном к сетевому стеку ОС.

divert сокеты медленны и подобных AF_PACKET'у скоростей не дадут.
А где можно узнать про аппаратный (или программно-аппаратный) захват трафика? Я представляю, например, такую схему: имеется ПЛИС с 10GE- и PCIe-модулем, а прошивка реализует захват ethernet-трафика и его передачу в системную память посредством PCI Exporess. Кажется, такой подход позволит поднять скорость до полной утилизации пропускной способности 10GE.
Полной скорости добиться можно без таких сложностей, PF_RING/NETMAP/DPDK дают полные 14.6 MPPS на захват. А вот 40GE — это уже иной мир, там все сложнее :)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории