Linux Switchdev по-мелланоксовски
Qrator
Это транскрипция выступления прозвучавшего на Yandex NextHop 2020 — видео в конце страницы




Приветствую. Меня зовут Александр Зубков, я хочу рассказать про Linux Switchdev — что это такое и как мы с ним живем в Qrator Labs.



Мы используем Switchdev на коммутаторах Mellanox уже примерно 2-3 года. Коммутаторы Mellanox на чипе Spectrum относятся к категории “white-box”, что значит что вы можете поставить разные операционные системы на данные коммутаторы. Обычно вендор предоставляет для этого некоторый SDK и операционные системы используют этот SDK для того чтобы взаимодействовать с коммутатором. И в случае со свитчами Mellanox есть операционка от самого Mellanox’а, есть Cumulus. Также поддерживается SAI (Switch Abstraction Interface) — это некоторая попытка создать стандартный SDK для разных коммутаторов, который используется уже, в свою очередь, SONiC операционкой. И, естественно, Switchdev поддерживается коммутаторами Mellanox.



Switchdev это такая инфраструктура в ядре Linux’а, которая позволяет построить отображение обычных сетевых настроек самого ядра на датаплейн, на железо вашего коммутатора — это называется offload. На картинке видно, что розовое — это драйвер коммутатора и голубое — это API и утилиты для настройки юзерспейса. Switchdev здесь выступает посредником: для юзерспейса он представляет модель коммутатора, для драйвера он предоставляет инфраструктуру для организации вот этого отображения.



Мы на коммутаторах Mellanox используем довольно стандартный набор функций: маршрутизация, ECMP, в общем ничего необычного. Все это поддерживается с возможностью оффлоада в датаплейн. Единственное чего не хватает — это policy-based routing — в драйвере Mellanox отсутствует поддержка.



Драйвер Mellanox’а находится в ванильном ядре Linux’а с поддержкой Switchdev — то есть не надо никаких патчей или дополнительных бинарных драйверов. Вы можете, практически, брать ядро в вашем любимом дистрибутиве или сами скомпилировать ванильное ядро и пользоваться. Прошивка в свитче обновляется самим драйвером — нужно только соответствующий файл подложить, который обычно содержится в пакете linux-firmware или каком-нибудь подобном.



Для настройки самого свитча используются, естественно, стандартные утилиты Linux’а, в большом количестве. Набор из iproute2, ethtool, LLDP-демон для QoS’а используется в том числе. И sysctl для некоторых параметров.



Для vrf’а в Linux’е есть как сетевые неймспейсы. Но есть и так называемая подсистема vrf — она отличается от сетевых неймспейсов. В этом случае все ваши интерфейсы находятся в одном неймспейсе — при работе с vrf. И для того чтобы управлять маршрутизацией есть специальное правило в ip rule, которое определяет какому vrf’у относится пакет и в соответствии с этим направляет его в определенную таблицу маршрутизации. Чтобы это настроить — vrf в Linux’е — создается специальный интерфейс типа vrf и во время создания к нему привязывается эта таблица. И дальше, если вы хотите какой-то интерфейс добавить в ваш vrf, то соответственно вы с помощью команды ip link устанавливаете вот это специальное устройство в качестве master-интерфейса для вашего интерфейса. И так как все эти интерфейсы находятся в одном пространстве имен то вы можете маршруту указать явно интерфейс из другого vrf’а и таким образом сделать маршруты между интерфейсами.



У нас есть например такая задача, в которой бы помог policy based routing — мы получаем трафик с аплинка и хотим его направлять целиком и безусловно на какие-то фильтрующие узлы. В Cisco или в Arista мы бы сделали policy route map’ы или service policy какие-нибудь, в Linux’е и ip rule можно сделать — но в Linux’е все это, к сожалению, не оффлоадится.



И нам приходится вертеться. Вот мы сделали, например, такую фишку — мы разделили vrf на две части, то есть в одной части — во внешней, находится интерфейс с нашим аплинком, а во внутренней части находятся интерфейсы с нашими фильтрующими узлами.



И примерно так вот выглядит маршрутизация. Во внутреннем vrf’е у нас более менее стандартный набор маршрутов — то есть у нас там внутренние маршруты и default-маршрут через наш аплинк. А уже во внешнем интерфейсе у нас только default-маршрут лежит, но он лежит через наши фильтрующие узлы. Таким образом у нас получился псевдо Policy Based Routing для интерфейсов. Весь трафик который приходит через интерфейс аплинка направляется по другому маршруту.



И в целом когда вы конфигурируете коммутатор на Switchdev’е вам приходится сначала обычно настраивать порты, потом бонд, потом подключаете к бриджу, потом vlan’ы, vrf’ы и уже в конце адреса и маршруты. Это в основном диктуется самой структурой интерфейсов в Linux’е — то как вы все должны настраивать, ну и существуют еще какие-то ограничения не позволяющие вам произвольно менять настройки. То есть это довольно муторная работа, которая в нашей компании изначально выполнялась большим init-скриптом, который все это настраивал. Но, естественно, нам приходится иногда делать изменения в runtime, в продакшене.



Это иногда больно, потому что приходится эту структуру чуть ли не руками перебирать — какие-то интерфейсы разбирать, собирать их заново и это все чревато ошибками, естественно. Когда вы в Cisco работаете — вы поменяли настройки и оболочка обо всем позаботится, а тут какая-то такая низкоуровневая работа проделывается.



Ну, спасибо за то что у нас есть Perl — мы написали скрипт mlxrtr, который принимает такой конфиг и по нему генерирует наборы команд для настройки сети и всего остального. И также поддерживает изменения — если вы какие-то изменения внесете и он прочитает ваш текущий конфиг в Linux’е и посмотрит, что нужно сделать чтобы его привести к состоянию которому вы хотите.



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





Там довольно много команд, но в целом если это у вас в init-скрипте, то это более-менее можно поддерживать.



Например, если вам нужно взять один порт переключить в другой бонд, то вам нужно этот порт отключить от старого бонда, новый бонд отключить от бриджа, потом порт подключить к тому бонду, потом бонд вернуть в бридж, перенастроить на нём vlan’ы — в общем довольно муторная работа и руками это неприятно делать, естественно. Скрипт все это проделывает сам.



Дальше. ACL настраивается… вы можете iptables использовать, но он не оффлоадится — вы можете его только для фильтрации control plane трафика использовать. А если вы хотите в дата-плейне фильтровать, то вам необходимо использовать tc filter в случае Switchdev’а. И тут стоит иметь в виду что tc filter будет уже фильтровать не только маршрутизируемый трафик, но и тот который коммутируется. И также tc filter можно повесить только на физические порты, поэтому если вы работаете с vlan’ами тут нужно уже более сложные конструкции делать. Но там есть интересные фичи, например можете повесить такой блок на несколько интерфейсов и они будут шарить (в смысле делить между собой) общий фильтр. Также есть оператор goto в правилах tc, что тоже довольно прикольно и позволяет делать нелинейные acl’и в отличие от того же Cisco или Arista.



Тут мы тоже для настройки acl имеет у себя утилиту — mlxacl. Мы в основном работаем с vlan’ами на третьем уровне и утилита работает так, что для каждого vlan’а создает отдельную цепочку и в основной цепочки просто матчит vlan’ы и переходит в соответствующую цепочку для этого vlan’а.



Вот тут тоже есть пример такой конфигурации — вот такие команды в результате получаются. Их меньше чем в случае с конфигурацией самого свитча потому что одно правило отображается примерно в одну команду — не так сложно.



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



Но нам нужно для этого сначала посмотреть текущее состояние и так примерно выглядит вывод tc filter — довольно сложно с этим работать.



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



Данные утилиты, про которые я рассказал, мы их выложили в паблик на Гитлабе — можете пользоваться. Они под MIT лицензиями, соответственно свободно доступные.



Естественно без какой-либо гарантии. Это пара скриптов на Perl (предвосхищая ваши вопросы — потому что я знаю Perl и он просто работает), относительно небольшие, почти без зависимостей — там используется пара Perl-модулей которые есть в стандартной поставке Perl’а и утилиты Linux’а, естественно.



Напоследок я хочу, если вы мало с последовательной консолью, с COM-портами, работали, дать несколько советов. Например если кто-то подумал что это способ выхода из Vim’а — вы почти угадали.



Это для некоторых bios’ов эквивалент Ctrl+Alt+Del, они так это воспринимают через последовательный порт. То есть если у вас в загрузчике зависло, например, и вам нужно как-то перезагрузить коммутатор — можете использовать.

Дальше когда дело до ядра доходит — оно естественно перехватывает работу с клавиатурой, поэтому тут вам лучше чтобы ваше ядро SysRq команды принимало — иначе будет сложно свитч перезагрузить. И в случае с SysRq когда вы с клавиатурой работаете и обычным терминалом — там PrintScreen используется, а в случае с последовательной консолью, с COM-портом, вам нужно послать специальный break-сигнал — в minicom’е это Ctrl+F, в screen’е Ctrl+A, Ctrl+B, и дальше уже специальную клавишу SysRq делаете.

И чтобы в bios попасть при загрузке — в bios коммутатора конечно же, потому что фактически там как в обычном компьютере есть bios через который он загружается обычно — Ctrl+B можете нажать.

Вот и все что я хотел кратко рассказать. Если у вас есть вопросы — с удовольствием отвечу.