Моя статья расскажет Вам как принять 10 миллионов пакетов в секунду без использования таких библиотек как Netmap, PF_RING, DPDK и прочие. Делать мы это будем силами обычного Линукс ядра версии 3.16 и некоторого количества кода на С и С++.



Сначала я хотел бы поделиться парой слов о том, как работает pcap — общеизвестный способ захвата пакетов. Он используется в таких популярных утилитах как iftop, tcpdump, arpwatch. Кроме этого, он отличается очень высокой нагрузкой на процессор.

Итак, Вы открыли им интерфейс и ждете пакетов от него используя обычный подход — bind/recv. Ядро в свою очередь получает данные из сетевой карты и сохраняет в пространстве ядра, после этого оно обнаруживает, что пользователь хочет получить его в юзер спейсе и передает через аргумент команды recv, адрес буфера куда эти данные положить. Ядро покорно копирует данные (уже второй раз!). Выходит довольно сложно, но это не все проблемы pcap.

Кроме этого, вспомним, что recv — это системный вызов и вызываем мы его на каждый пакет приходящий на интерфейс, системные вызовы обычно очень быстры, но скорости современных 10GE интерфейсов (до 14.6 миллионов вызовов секунду) приводят к тому, что даже легкий вызов становится очень затратным для системы исключительно по причине частоты вызовов.

Также стоит отметить, что у нас на сервере обычно более 2х логических ядер. И данные могут прилететь на любое их них! А приложение, которое принимает данные силами pcap использует одно ядро. Вот тут у нас включаются блокировки на стороне ядра и кардинально замедляют процесс захвата — теперь мы занимаемся не только копированием памяти/обработкой пакетов, а ждем освобождения блокировок, занятых другими ядрами. Поверьте, на блокировки может зачастую уйти до 90% процессорных ресурсов всего сервера.

Хороший списочек проблем? Итак, мы их все геройски попробуем решить!

Итак, для определенности зафиксируем, что мы работаем на зеркальных портах (что означает, что откуда-то извне сети нам приходит копия всего трафика определенного сервера). На них в свою очередь идет трафик — SYN флуд пакетами минимального размера на скорости 14.6 mpps/7.6GE.

Сетевые ixgbe, драйверы с SourceForge 4.1.1, Debian 8 Jessie. Конфигурация модуля: modprobe ixgbe RSS=8,8 (это важно!). Процессор у меня i7 3820, с 8ю логическими ядрами. Поэтому везде, где я использую 8 (в том числе в коде) Вы должны использовать то число ядер, которое есть у Вас.

Распределим прерывания по имеющимся ядрам


Обращаю внимание, что нам на порт прилетают пакеты, целевые MAC адреса которых не совпадают с MAC адресом нашей сетевой карты. В противном случае включится в работу TCP/IP стек Linux и машина подавится трафиком. Этот момент очень важен, мы сейчас обсуждаем только захват чужого трафика, а не обработку трафика, который предназначается данной машине (хотя для этого мой метод подходит с легкостью).

Теперь проверим, сколько трафика мы можем принять, если начнем слушать весь трафик.

Включаем promisc режим на сетевой карте:
ifconfig eth6 promisc


После этого в htop мы увидим очень неприятную картину — полную перегрузку одного из ядер:

1  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]     
2  [                                                            0.0%]     
3  [                                                            0.0%]     
4  [                                                            0.0%]     
5  [                                                            0.0%]
6  [                                                            0.0%]
7  [                                                            0.0%]
8  [                                                            0.0%]


Для определения скорости на интерфейсе воспользуемся спец-скриптом pps.sh: gist.github.com/pavel-odintsov/bc287860335e872db9a5

Скорость на интерфейсе при этом довольно маленькая — 4 миллиона пакетов секунду:
bash /root/pps.sh eth6
TX eth6: 0 pkts/s RX eth6: 3882721 pkts/s
TX eth6: 0 pkts/s RX eth6: 3745027 pkts/s


Чтобы решить эту проблему и распределить нагрузку по всем логическим ядрам (у меня их 8) нужно запустить следующей скрипт: gist.github.com/pavel-odintsov/9b065f96900da40c5301 который распределит прерывания от всех 8 очередей сетевой карты по всем имеющимся логическим ядрам.

Прекрасно, скорость сразу вылетела до 12mpps (но это не захват, это лишь показатель того, что мы можем прочесть трафик на такой скорости из сети):
 bash /root/pps.sh eth6
TX eth6: 0 pkts/s RX eth6: 12528942 pkts/s
TX eth6: 0 pkts/s RX eth6: 12491898 pkts/s
TX eth6: 0 pkts/s RX eth6: 12554312 pkts/s


А нагрузка на ядра стабилизировалась:
1  [|||||                                                       7.4%]     
2  [|||||||                                                     9.7%]     
3  [||||||                                                      8.9%]    
4  [||                                                          2.8%]     
5  [|||                                                         4.1%]
6  [|||                                                         3.9%]
7  [|||                                                         4.1%]
8  [|||||                                                       7.8%]


Сразу обращаю внимание, что в тексте будут использоваться два примера кода, вот они:
AF_PACKET, AF_PACKET + FANOUT: gist.github.com/pavel-odintsov/c2154f7799325aed46ae
AF_PACKET RX_RING, AF_PACKET + RX_RING + FANOUT: gist.github.com/pavel-odintsov/15b7435e484134650f20

Это законченные приложения с максимальным уровнем оптимизаций. Промежуточные (заведомо более медленные версии кода) я не привожу — но все флажки для управления всеми оптимизациями выделены и объявлены в коде как bool — можете легко повторить мой путь у себя.

Первая попытка запуска AF_PACKET захвата без оптимизаций


Итак, запускаем приложение по захвату трафика силами AF_PACKET:
We process: 222048 pps
We process: 186315 pps


И нагрузка в потолок:
1  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||   86.1%]     
2  [||||||||||||||||||||||||||||||||||||||||||||||||||||||     84.1%]     
3  [||||||||||||||||||||||||||||||||||||||||||||||||||||       79.8%]     
4  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||  88.3%]     
5  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||    83.7%]
6  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||  86.7%]
7  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 89.8%]
8  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||90.9%]


Причина тому, что ядро утонуло в блокировках, на которые тратит все процессорное время:
Samples: 303K of event 'cpu-clock', Event count (approx.): 53015222600
  59.57%  [kernel]        [k] _raw_spin_lock
   9.13%  [kernel]        [k] packet_rcv
   7.23%  [ixgbe]         [k] ixgbe_clean_rx_irq
   3.35%  [kernel]        [k] pvclock_clocksource_read
   2.76%  [kernel]        [k] __netif_receive_skb_core
   2.00%  [kernel]        [k] dev_gro_receive
   1.98%  [kernel]        [k] consume_skb
   1.94%  [kernel]        [k] build_skb
   1.42%  [kernel]        [k] kmem_cache_alloc
   1.39%  [kernel]        [k] kmem_cache_free
   0.93%  [kernel]        [k] inet_gro_receive
   0.89%  [kernel]        [k] __netdev_alloc_frag
   0.79%  [kernel]        [k] tcp_gro_receive


Оптимизация AF_PACKET захвата с помощью FANOUT


Итак, что же делать? Немного подумать :) Блокировки возникают тогда, когда несколько процессоров пытаются использовать один ресурс. В нашем случае это происходит из-за того, что у нас один сокет и его обслуживает одно приложение, что вынуждает остальные 8 логических процессоров стоять в постоянном ожидании.

Тут нам на помощь придет отличная функция — FANOUT, а если по-русски — разветвления. Для AF_PACKET мы можем запустить несколько (разумеется, наиболее оптимальным в нашем случае будет число процессов равное числу логических ядер). Кроме этого, мы можем задать алгоритм по которому данные будут распределяться по этим сокетам. Я выбрал режим PACKET_FANOUT_CPU, так как в моем случае данные очень равномерно распределяются по очередям сетевой карты и это, на мой взгляд, наименее ресурсоемкий вариант балансировки (хотя тут не ручаюсь — рекомендую посмотреть в коде ядра).

Корректируем в примере кода bool use_multiple_fanout_processes = true;

И снова запускаем приложение.

О чудо! 10 кратное ускорение:
We process: 2250709 pps
We process: 2234301 pps
We process: 2266138 pps

Процессоры, конечно, по прежнему загружены по полной:
1  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||92.6%]     
2  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.1%]     
3  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.2%]     
4  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.3%]     
5  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.1%]
6  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.7%]
7  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.7%]
8  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||93.2%]


Но карта perf top выглядит уже совершенно иначе — никаких локов больше нет:
Samples: 1M of event 'cpu-clock', Event count (approx.): 110166379815
  17.22%  [ixgbe]         [k] ixgbe_clean_rx_irq      
   7.07%  [kernel]        [k] pvclock_clocksource_read          
   6.04%  [kernel]        [k] __netif_receive_skb_core    
   4.88%  [kernel]        [k] build_skb     
   4.76%  [kernel]        [k] dev_gro_receive    
   4.28%  [kernel]        [k] kmem_cache_free 
   3.95%  [kernel]        [k] kmem_cache_alloc 
   3.04%  [kernel]        [k] packet_rcv 
   2.47%  [kernel]        [k] __netdev_alloc_frag 
   2.39%  [kernel]        [k] inet_gro_receive
   2.29%  [kernel]        [k] copy_user_generic_string
   2.11%  [kernel]        [k] tcp_gro_receive
   2.03%  [kernel]        [k] _raw_spin_unlock_irqrestore


Кроме этого, у сокетов (хотя я не уверен про AF_PACKET) есть возможность задать буфер приема, SO_RCVBUF, но на моем тест-стенде это не дало никаких результатов.

Оптимизация AF_PACKET захвата с помощью RX_RING — кольцевого буфера


Что же делать и почему все равно медленно? Ответ тут в функции build_skb, она означает, что внутри ядра по-прежнему производится два копирования памяти!

Теперь попробуем разобраться с выделением памяти за счет использования RX_RING.

И ура 4 MPPS вершина взята!!!
We process: 3582498 pps
We process: 3757254 pps
We process: 3669876 pps
We process: 3757254 pps
We process: 3815506 pps
We process: 3873758 pps


Такой прирост скорости был обеспечен тем, что копирование памяти из буфера сетевой карты теперь производится лишь однажды. И при передачей из пространства ядра в пространство пользователя повторное копирование не производится. Это обеспечивается за счет общего буфера выделенного в ядре и пропущенного в user space.

Также меняется подход с работой — нам теперь нельзя висеть и слушать когда придет пакет (помните — это оверхед!), теперь с помощью вызова poll мы можем ожидать сигнала, когда будет заполнен целый блок! И после этого начинать его обработку.

Оптимизация AF_PACKET захвата с помощью RX_RING силами FANOUT


Но все равно у нас проблемы с блокировками! Как же их победить? Старым методом — включить FANOUT и выделить блок памяти для каждого потока-обработчика!
Samples: 778K of event 'cpu-clock', Event count (approx.): 87039903833
  74.26%  [kernel]       [k] _raw_spin_lock
   4.55%  [ixgbe]        [k] ixgbe_clean_rx_irq
   3.18%  [kernel]       [k] tpacket_rcv
   2.50%  [kernel]       [k] pvclock_clocksource_read
   1.78%  [kernel]       [k] __netif_receive_skb_core
   1.55%  [kernel]       [k] sock_def_readable
   1.20%  [kernel]       [k] build_skb
   1.19%  [kernel]       [k] dev_gro_receive
   0.95%  [kernel]       [k] kmem_cache_free
   0.93%  [kernel]       [k] kmem_cache_alloc
   0.60%  [kernel]       [k] inet_gro_receive
   0.57%  [kernel]       [k] kfree_skb
   0.52%  [kernel]       [k] tcp_gro_receive
   0.52%  [kernel]       [k] __netdev_alloc_frag


Итак, подключаем FANOUT режим для RX_RING версии!

УРА! РЕКОРД!!! 9 MPPS!!!
We process: 9611580 pps
We process: 8912556 pps
We process: 8941682 pps
We process: 8854304 pps
We process: 8912556 pps
We process: 8941682 pps
We process: 8883430 pps
We process: 8825178 pps


perf top:
Samples: 224K of event 'cpu-clock', Event count (approx.): 42501395417
  21.79%  [ixgbe]              [k] ixgbe_clean_rx_irq
   9.96%  [kernel]             [k] tpacket_rcv
   6.58%  [kernel]             [k] pvclock_clocksource_read
   5.88%  [kernel]             [k] __netif_receive_skb_core
   4.99%  [kernel]             [k] memcpy
   4.91%  [kernel]             [k] dev_gro_receive
   4.55%  [kernel]             [k] build_skb
   3.10%  [kernel]             [k] kmem_cache_alloc
   3.09%  [kernel]             [k] kmem_cache_free
   2.63%  [kernel]             [k] prb_fill_curr_block.isra.57


К слову, справедливости ради обновление на ядро 4.0.0 ветки особенного ускорения не дало. Скорость держалась в тех же пределах. Но нагрузка на ядра упала значительно!
1  [|||||||||||||||||||||||||||||||||||||                       55.1%]     
2  [|||||||||||||||||||||||||||||||||||                         52.5%]     
3  [||||||||||||||||||||||||||||||||||||||||||                  62.5%]     
4  [||||||||||||||||||||||||||||||||||||||||||                  62.5%]     
5  [|||||||||||||||||||||||||||||||||||||||                     57.7%]
6  [||||||||||||||||||||||||||||||||                            47.7%]
7  [|||||||||||||||||||||||||||||||||||||||                     55.9%]
8  [|||||||||||||||||||||||||||||||||||||||||                   61.4%]


В заключение хотелось бы добавить, что Linux является просто потрясающей платформой для анализа трафика даже в окружении, где нельзя собрать какой-либо специализированный модуль ядра. Это очень и очень радует. Есть надежда, что в уже ближайших версиях ядра можно будет обрабатывать 10GE на полноценной wire-speed в 14.6 миллионов/пакетов секунды используя процессор на 1800 мегагерц :)

Рекомендуемые к прочтению материалы:
www.kernel.org/doc/Documentation/networking/packet_mmap.txt
man7.org/linux/man-pages/man7/packet.7.html

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


  1. YourChief
    25.06.2015 16:33
    +4

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

    Возникли вопросы:
    Для балансирования обработчиков прерываний от очередей сетевой карты не разумнее ли воспользоваться irqbalance?
    Не пробовали ли вы тюнить сетевую карту и настраивать coalescing ( ethtool -C ) чтобы снизить частоту срабатывания обработчика прерывания?


    1. neolink
      25.06.2015 17:43
      +8

      разница между go и c в 60! раз, подсказывает мне что код был не совсем одинаковый


      1. pavelodintsov Автор
        25.06.2015 17:46

        Кстати, для Go есть отличная библиотека для захвата пакетов — github.com/google/gopacket


      1. YourChief
        25.06.2015 18:04
        +1

        на си я пользовался libmaxminddb напрямую и имел возможность не делать многократное преобразование запрашиваемого адреса и запрашивал только нужную информацию вместо полной структуры. тем не менее в Go я разочарован: во-первых, разница не настолько большая по сравнению с питоном, чтобы стоило морочиться со статической типизацией и сравнительно небольшим набором языковых инструментов; во-вторых, сборка программы компилятором Go 1.4.2 вместо 1.2.1 сделало её медленнее еще на 30%. это ужасно


        1. pavelodintsov Автор
          25.06.2015 18:06

          Кстати, такой эффект может давать преобразования бинарщины в текстовый вид, вот даже народ умные либы для оптимизации пишет: github.com/h2o/qrintf


          1. YourChief
            25.06.2015 18:50

            вообще да. либу я бы не потащил, но на последовательный вывод из нескольких буферов заменил бы


        1. neolink
          25.06.2015 18:17

          как-то у меня не вяжется первая часть со второй. вы вроде признаете что постоянно что-то преобразовывали хотя это не нужно было, но потом делаете вывод о производительности.
          про 30% это видимо время на GC какойнить map[string] *GeoData


          1. YourChief
            25.06.2015 18:37

            geoip2-golang принимает запрашиваемый адрес только в строковом виде, который у меня берётся из счётчика, который просто long. затем сам запрос внутрях libmaxminddb из строкового вида преобразуется в число. это преобразования, которые диктует окружение, увы.

            мапов у меня нет нигде, это дефект новой версии golang. вот так вот замечательно развивается язык.


            1. DLag
              25.06.2015 22:50

              Так а в чем конкретно дефект?
              И не совсем ясно почему не расширили структуру библиотеки для того чтобы она принимала long…
              Делается это очень просто.


              1. DLag
                25.06.2015 22:54

                1. Используйте maxminddb-golang, вместо непонятной кривой надстройки
                2. Посмотрите в вызов Lookup, и увидите там в первых строках ipV4Address := ipAddress.To4()
                3. Допишите свою функцию без преобразований
                4. profit!


                1. YourChief
                  26.06.2015 00:38

                  Для каждого языка я пользовался тем, что предлагает maxmind на сайте в разделе API.


                  1. DLag
                    26.06.2015 00:43
                    +1

                    Ну не стоит же рассматривать их как последнюю инстанцию в вопросах софта, который они не тестировали…
                    А на странице пакета, который вы использовали, во втором абзаце написано следующее:

                    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.

                    Т.е. про конкретно ваш случай и пишут.


              1. YourChief
                26.06.2015 00:34

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

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


                1. DLag
                  26.06.2015 00:37
                  +1

                  На своем софте я заметил только ускорение с переходом на новую версию.
                  Сборщик отлично работать начал.

                  Посмотрите профайлинг, зафайлите баг разработчикам со статистикой.
                  Там адекватные и отзывчивые люди.


        1. andyN
          25.06.2015 22:52

          Посмотрите на Nim — околопитонячий синтаксис плюс производительность Си.


          1. DLag
            25.06.2015 22:55

            Нет там производительности Си, и не может быть по определению.
            Он транслируется в C, но не более.


            1. andyN
              25.06.2015 23:21

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

              2) Не всегда программист имеет квалификацию/время на написание производительного Си-кода руками. В некоторых (скорее всего редких, но все же) случаях, как мне кажется, Nim создаст даже более производительный код, чем низкоквалифицированный программист.


              1. DLag
                25.06.2015 23:47

                Слишком много допущений в ваших словах и ниодного пруфа.


      1. Xazzzi
        25.06.2015 23:14
        +2

        Медленность Go vs C таки не удивительна (сборщик мусора же), а вот разницу C c Rust было бы интересно оценить, ведь последний именно как системный язык позиционируется.


      1. Latobco
        26.06.2015 10:51
        -1

        Разница неудивительная, ведь Go намного медленнее С. По вашей логике, код на Python должен содержать специальные sleep и тоже не быть таким же, как C!


        1. neolink
          26.06.2015 12:46
          +1

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


    1. Viacheslav01
      26.06.2015 13:51

      А можно код ради интереса покопаюсь?


      1. YourChief
        26.06.2015 17:10

        Да, пожалуйста:
        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}"
        


  1. pavelodintsov Автор
    25.06.2015 16:41
    +3

    irqbalance штука довольно cложная и временами делает только хуже. Но есть отличная реализация грамотного подхода к задаче балансировки прерываний, это проект Birq: libcode.org/projects/birq от Serj Kalichev. Я его довольно активно использую там, где нагрузка не распределяется так красиво.

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


  1. amarao
    25.06.2015 17:23
    +2

    Спасибо.


    1. pavelodintsov Автор
      25.06.2015 17:25

      Рад, что мои изыскания полезны :)


  1. DustCn
    25.06.2015 17:29
    +2

    Когда переедете на двухсокетный сервер у вас будет ближний и дальний (по отношению к сетевухе) сокет. Это тоже надо будет учитывать…


    1. pavelodintsov Автор
      25.06.2015 17:36
      +4

      Ага, поэтому предпочитаю односокетные машинки с очень мощными ядрами класса.

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

      По части шедулинга прерываний — Birq понимает, когда сокет удаленный и с учетом этого распределяет прерывания.


  1. invented
    25.06.2015 18:03
    +9

    Оу, для меня это первая полезная статья на хабре за многие годы ) Спасибо тебе автор )


    1. pavelodintsov Автор
      25.06.2015 18:05
      +1

      Рад стараться =)


  1. degs
    25.06.2015 22:36

    Очень интересно, сам AF_PACKET конечно не новость, но оптимизация и анализ просто замечательны.
    Кстати у меня ядро 3.13, однако PACKET_FANOUT присутствует в хедерах, хотя в манах не упоминается. Тем не менее все сбилдилось и работает. Спасибо.


    1. pavelodintsov Автор
      25.06.2015 22:39

      3.13 думаю тоже без проблем подходит. А вот младше 3.6 все плохо — home.regit.org/2012/12/af-packet-oops git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c0de08d04215031d68fa13af36f347a6cfa252ca


  1. maydjin
    25.06.2015 23:52

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


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


  1. SergeyT
    26.06.2015 05:34
    +4

    Название статьи показалось до боли знакомым.
    Не вот эта ли статья была источником вдохновения: How to receive a million packets per second?


    1. pavelodintsov Автор
      26.06.2015 10:05
      +1

      Буквально неделю назад общался с Мареком по немного смежной теме — по захвату с netmap/pf_ring.

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

      Но в ряде случаев (UDP сервисы там), мой подход можно использовать как замену подхода Марека на очень больших скоростях.


  1. nckma
    26.06.2015 11:21
    +1

    Извините — может глупый вопрос: «Картинка в статье имеет отношение к статье или нет?»
    Я почему спрашиваю — вроде бы на картинке в сервер заходит несколько сетевых кабелей (5?), но прием пакетов происходит из нескольких интерфейсов или только из одного eth6?


    1. pavelodintsov Автор
      26.06.2015 11:36

      Картинка просто для привлечения внимания =) Там всего 4 1GE линка в LACP. Тест стенд не такой фотогеничный, ибо собран из десктоп оборудования, это оказалось самым гибким вариантом по части полного доступа ко всем фичам Intel платформы.


      1. TaHKucT
        26.06.2015 18:25

        Раз пошла такая пьянка: нижний сервер стоит «задом наперед» или это модель, в которую диски с 2х сторон ставятся?


        1. pavelodintsov Автор
          26.06.2015 18:27

          Supermicro это :)


          1. TaHKucT
            26.06.2015 23:50

            это то понятно. Но почему он в сторону горячего коридора повернут дисками(если мы считаем что верхний сервер стоит «правильно»)?



  1. drwatson32
    26.06.2015 14:07
    +2

    Павел,
    Спасибо за статью.

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

    Но ведь DPDK делает это на одном ядре.


    1. pavelodintsov Автор
      26.06.2015 14:11

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


      1. drwatson32
        26.06.2015 14:39

        Согласен, что как универсальное решение ваш подход интересен.
        Особенно при разработке виртуальных сетевых функций,
        без акселерации на уровне гипервизора и не требующий использования
        специализированных SDK.

        По поводу PF_RING/NETMAP/DPDK я читал интересный отчет по анализу
        http://www.net.in.tum.de/fileadmin/bibtex/publications/theses/2014-gallenmueller-high-speed-packet-processing.pdf.
        Много думал. Для себя мы выбрали DPDK.


        1. pavelodintsov Автор
          26.06.2015 14:46

          Блин, он на немецком =) Да, DPDK хорош если речь идет про новый проект. Если есть унаследованный код — тут PF_RING/Netmap впереди, ибо работают в рамках обычного окружения Linux API, а не строят свой абсолютно чуждый Nix программисту API.


          1. pavelodintsov Автор
            26.06.2015 15:04

            А что за проект Вы делаете на DPDK, если не секрет? Я use case коллекционирую :)


            1. drwatson32
              26.06.2015 15:53
              +1

              Не не не, там только первые страницы на немецком =).
              Начиная с 10-го там английским по белому написано.

              На DPDK делаем коммутатор с интерфейсами в lxc/kvm.
              Замену для openvswitch в openstack.


              1. pavelodintsov Автор
                26.06.2015 16:09

                Да, форвардеры/роутеры одно удовольствие писать на DPDK — там почти все для этого есть и ворох примеров :)


              1. xdeller
                26.06.2015 18:07

                А инициатива по классификаторам OF в tc вам не кажется более привлекательной? Менять овс можно либо на чистый юзерспейс, либо на них, в общем-то (как кажется). И какие в первую очередь задачи преследуете?


                1. drwatson32
                  28.06.2015 17:45

                  >>А инициатива по классификаторам 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) оказался менее
                  производительным.


                  1. xdeller
                    28.06.2015 18:23

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

                    Не совсем понятно — у коммутаторов обычно софтверный датапэс как класс отсутствует, я даже сходу не могу привести обратный пример. Или все же речь про общий апи?


                    1. drwatson32
                      28.06.2015 19:15

                      скажем так, ofagent с openflow 1.3/1.4/1.5 и с api, для прогрузки правил в hardware pipeline через SDK вендора асиков.
                      Просто опять же из плотной работы выяснилось, что и у pica8, и у Intel ONS есть ньюансы (оба на базе ovs).


                      1. xdeller
                        28.06.2015 20:22

                        Ну ONS насколько мне известно забил на софтовую платформу и делает упор на апи матрицы, а овс, когда я там его тестировал, был очень глючным. Вообще идея втыкать of-правила в матрицу, казавшаяся очень привлекательной два года назад, продолжает разбиваться о фактические возможности недорогих матриц — затруднена цепочечная группировка правил в нужных масштабах, много чего в железе просто не поддерживается и тд. Кумулюс года полтора назад казался достаточно провальным с его тогдашними объемами поддержки железа, а сейчас напротив — ONIE сертифицировался на большом числе железок, на него же подтянулась pica и будущее кажется довольно неплохим, плюс ребята реально много делают плюшек в хост-стеке. Pica же, напротив, с текущими тенденциями развития матриц не имеет, на мой взгляд, прорывного будущего. Втыкать полную спецификацию OF в обычный ToR-девайс очень сложно и избыточно, поскольку ему надо правил порядка 1..10 * число портов как максимум, а процессинг трафика лучше распределять по софтсвичам на вычнодах. На абсолютную истину мои высказывания не претендуют, просто развитие матриц все сильнее корректирует взгляды на то, как это все должно (будет) работать.


                        1. pavelodintsov Автор
                          28.06.2015 20:24

                          Друзья, позвольте вклинится. А есть свичевые матрицы с достаточно широким каналом к контрол плейну и мощным железом на контрол плейне (хотя бы i7/e3)?

                          Уж очень хочется идею выявления атак силами github.com/FastVPSEestiOu/fastnetmon затащить даже в свич.


                          1. xdeller
                            28.06.2015 22:04

                            Это задача SUME-подобных вещей, если даже свич твоим требованиям удовлетворит (тот же ONS), на все порты его просто не хватит.


                          1. drwatson32
                            28.06.2015 22:36

                            Я могу сказать за SDN свитчи (openflow/cumulus). В основном у всех control plane на atom (если x86). Максимум что я видел — это i3, на Intel ONP FM6K (etegro 410i). Зимой в 2014 говорили, что будет FM10K, с мощным control plane, но на NFV воркшопе Intel Network Builders в мае никто в курсе не был. Но вообще у нас как раз и задача вынести control plane со свитчей, поэтому особо эту тему не копал.


                            1. pavelodintsov Автор
                              28.06.2015 22:57

                              Все порты, понятно, не засунуть. А вот хотя бы гигабит завернут на контрол плейн да там внимательно посмотреть его и проанализировать было бы хорошо.


                              1. drwatson32
                                28.06.2015 23:08

                                Ну вон pica если нормализовать не может в hardware table, заворачивает на CPU. Выглядит на атоме это очень грустно :(
                                Я бы посмотрел в сторону коробок от A10, мультипроцессорные системы с большим количеством портов. Они на них CGNAT выпускают. По сути обычные серваки.


                                1. pavelodintsov Автор
                                  28.06.2015 23:12

                                  А что за сетевки они ставят?


                                  1. drwatson32
                                    29.06.2015 00:25

                                    Это хороший вопрос, мне идентифицировать не удалось. Видел в чужой лабе, там было 16x10GB/s SFP+.
                                    Судя по дата шитам, внутри есть PCI-E expansions.


                                    1. pavelodintsov Автор
                                      29.06.2015 09:53

                                      Ого, 16 штук, это круто! :)


                                      1. pavelodintsov Автор
                                        29.06.2015 09:55

                                        1. pavelodintsov Автор
                                          29.06.2015 09:58

                                          Похоже, оно и есть www.sarsen.net/csi/426377216/f/pdf/pcieexpansionconfigurations.pdf в ТТХ заявлено 20GB/s между хост-машиной и этим экспандером, как раз 160 Gpbs, 16*10GE =)


                                          1. drwatson32
                                            29.06.2015 12:08

                                            да, похоже


                        1. drwatson32
                          28.06.2015 23:04

                          of старше 1.1 на старых асиках с линейным pipeline (типа firebolt, trident, trident+) на мой взляд утопия. Но в принципе tomahawk уже разрабатывался с оглядкой на openflow. Мы реализуем подход похожий на описанный вами, потому что как не крути, tcam лимитирован. Только у нас траффик доводится на вычноду и там отдаётся в вычсервис, в зависимости от типа траффика, клиента, потребителя и т.п.


                  1. ilukyanov
                    29.06.2015 13:41

                    Приятно видеть живого человека, имеющего компетенции в OVS и OpenFlow, да еще и пишущего на русском :)

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

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

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

                    А что за проблемы с meters и select algs? Год назад когда смотрел на meters — они вообще не были реализованы.


                    1. drwatson32
                      29.06.2015 16:50

                      >> Приятно видеть живого человека, имеющего компетенции в 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.


                      1. xdeller
                        29.06.2015 17:52

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

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

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

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

                        BTW выше вы с Игорем имеете в виду разные чейнинги.


                        1. drwatson32
                          29.06.2015 19:29

                          >>Гм, там вроде нет особенных костылей

                          Я имею ввиду костыли, которые приходится писать в сетевых приложениях для контроллера.
                          Вот пишешь ты допустим обработку 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 на эту тему, нужно подтверждение наших
                          идей, ждем ответ вендора матриц.


          1. simpleadmin
            26.06.2015 16:08

            Блин, он на немецком =)

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

            drwatson32, спасибо, весьма занимательное чтиво


            1. drwatson32
              28.06.2015 17:46

              Рад что пригодилось.


      1. k06a
        01.07.2015 17:58

        ЕМНИП 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. pavelodintsov Автор
          01.07.2015 18:00

          Так и есть — он очень быстр. Но 1) Он на Linux не стандартный и планов по его интеграции нету 2) требует патчинга драйверов


          1. k06a
            01.07.2015 18:05

            Раз уж стоит вопрос заниматься этим профессионально и на пределе возможностей, почему бы не взять FreeBSD? Нужно же подходящие инструменты использовать под задачи. Я про целесообразность погружения статьи. Статья отличная и я проплюсовал как мог, но все же :)


            1. pavelodintsov Автор
              01.07.2015 18:08

              Задача — захват трафика на максимальной скорости, она очень универсальна и требовать от пользователя смены ОС для работы приложения — довольно странно.

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

              divert сокеты медленны и подобных AF_PACKET'у скоростей не дадут.


  1. janatem
    30.06.2015 17:02

    А где можно узнать про аппаратный (или программно-аппаратный) захват трафика? Я представляю, например, такую схему: имеется ПЛИС с 10GE- и PCIe-модулем, а прошивка реализует захват ethernet-трафика и его передачу в системную память посредством PCI Exporess. Кажется, такой подход позволит поднять скорость до полной утилизации пропускной способности 10GE.


    1. pavelodintsov Автор
      30.06.2015 17:06
      +1

      Полной скорости добиться можно без таких сложностей, PF_RING/NETMAP/DPDK дают полные 14.6 MPPS на захват. А вот 40GE — это уже иной мир, там все сложнее :)