Наша почта:
В Москве:
РФ (звонок бесплатный):
Зеркала не врут: Зеркальное средство трафика в OVN
{Инженер-разработчик в K2 Cloud }
Александра Рукомойникова

Видео доклада

Презентация спикера

Зеркала не врут: Зеркальное средство трафика в OVN
как в публичном облаке был реализован сервис зеркалирования трафика на базе open source-решений OVN и Open vSwitch. Александра пишет на C уже два года, преимущественно в open source-проекты — OVN, Open vSwitch, FRR, — и в прошлом году закончила бакалавриат МФТИ.
K2 Cloud — B2B-облако, работающее с 2009 года и являющееся первым в России публичным облаком собственной разработки. VMware и OpenStack в нём не используются: облачная платформа написана с нуля. Инфраструктура развёрнута в двух регионах: Москва с тремя зонами доступности и зона доступности в Санкт-Петербурге. В качестве SDN применяется open source-решение OVN, на которое команда ранее перешла с NSX.
Зеркалирование трафика в K2 Cloud — первый подобный сервис на российском облачном рынке. Всё, на чём он построен, — open source: OVN, Open vSwitch, а для пользователей Kubernetes существует CNI-плагин OVN-Kubernetes.

Open vSwitch и OVN: как устроена обработка трафика

Open vSwitch — это L2/L3-коммутатор для Linux. Он работает на базе старого протокола OpenFlow и поддерживает два режима работы: с ядром и с DPDK в user space.

Open vSwitch выполняет обработку трафика на основе OpenFlow-правил, но сами эти правила нужно чем-то генерировать. Эту задачу решает SDN-контроллер поверх него — OVN. Архитектура OVN достаточно сложная, но в общих чертах выглядит так:
  • Северная база данных (Northbound DB)
    хранит понятную пользователю информацию: логическую топологию, виртуальные коммутаторы и роутеры, правила балансировки и так далее.
  • Центральный контроллер ovn-northd
    транслирует высокоуровневую информацию из северной базы в низкоуровневую — в южную базу данных (Southbound DB).
  • Локальные агенты на нодах
    подключаются к южной базе данных. На каждой ноде работает ovn-controller: он получает из южной базы информацию об обработке пакетов и передаёт её через Unix-сокет в Open vSwitch. В user space Open vSwitch представлен демоном ovs-vswitchd, который на основе сгенерированных OVN OpenFlow-правил и обрабатывает пакеты.
В логических правилах OVN обработка пакетов выглядит вполне человекочитаемо, а вот на уровне OpenFlow — уже нет. Каждое OpenFlow-правило состоит из четырёх частей: номера таблицы (каждая таблица — отдельный этап обработки пакета), приоритета (в каждой таблице выигрывает правило с наибольшим приоритетом), а также match и action.

Путь пакета и инкапсуляция Geneve

Наглядный пример: две виртуальные машины развёрнуты на разных гипервизорах, и с первой на вторую идёт обычный пинг. При обработке на первом гипервизоре к оверлейному пакету добавляются underlay-заголовки: в случае IP-фабрики это Ethernet-фрейм, IP-заголовок и UDP для инкапсуляции в Geneve. Трафик идёт по underlay-сети до второго гипервизора, там распаковывается, и оригинальный оверлейный пакет уходит внутрь второй виртуальной машины.
Чем Geneve лучше VXLAN? Во-первых, в Geneve, как и в VXLAN, есть VNI — идентификатор сети. OVN использует VNI, чтобы передавать информацию о том, в рамках какой логической подсети должен обрабатываться трафик. Во-вторых, в Geneve есть поле TLV, в котором можно передавать произвольную кастомную информацию. OVN использует его для передачи inport и outport — портов, из которых трафик пришёл и в которые его нужно отправить.

Зеркалирование трафика: сущности и сценарии

На физических сетях зеркалирование давно не новость, для него существует множество протоколов. В K2 Cloud выделено несколько сущностей зеркалирования:
  • источник трафика — виртуальная машина, с которой копируется трафик;
  • приёмник трафика;
  • сессия зеркалирования — сам процесс копирования и передачи скопированного трафика;
  • фильтры зеркалирования — механизм, идею которого команда позаимствовала у облака Amazon.
Основные сценарии использования лежат в области информационной безопасности: трафик можно зеркалировать на системы IDS и обнаруживать атаки, а также использовать зеркалирование в целях мониторинга.

Нативное зеркалирование в OVN: почему оно не подошло

Поскольку OVN — open source, была надежда, что делать ничего не придётся: поддержка зеркалирования там уже есть, причём сразу в трёх видах — SPAN, RSPAN и ERSPAN/GRE. Однако запустить сервис на этом не удалось.
Пример:
две виртуальные машины в одной подсети и третья, выступающая приёмником трафика. На первой машине настраивается RSPAN-зеркалирование, с неё же запускается пинг на вторую. На приёмнике трафика при этом не оказывается ничего.
Разбор underlay-сети показал, что Open vSwitch действительно отправляет оверлейный пакет с добавленным ERSPAN/GRE-заголовком — всё как задумано, — но в IP-заголовке в качестве IP destination стоит виртуальный адрес третьей виртуальной машины. В физической underlay-сети такого адреса нет.

Причина в том, что Open vSwitch в нативном режиме работает с зеркалированием в underlay: копировать можно только на какой-то физический порт. Облаку такой вариант не подходит — требуется, чтобы всё было виртуальным. Реализовывать зеркалирование пришлось самостоятельно.

Когда копировать: до или после фаервола

Целевая картина такова: на приёмнике трафика должно быть видно ровно то же самое, что и на источнике, — ни больше ни меньше. Поэтому для исходящего трафика было решено сначала его зеркалировать, а уже потом фильтровать по правилам фаервола. Для входящего трафика — ровно наоборот: сначала фильтрация, потом зеркалирование. В итоге трафик, отброшенный правилами фаервола, до виртуальной машины всё равно не дойдёт, а значит, копировать его нет смысла.

Базовый случай: одна подсеть

Самый простой случай — когда источник и приёмник находятся в одной подсети. Виртуальная машина отправляет трафик, а на уровне OpenFlow генерируются правила, позволяющие скопировать пакет. Исходный пакет как шёл, так и продолжает идти на порт VM2, куда он и собирался. Для зеркалированного пакета выставляется порт приёмника — VM3, — и пакет отправляется туда. По сути, это обычный SPAN, как в физической сети: указывается порт, и всё работает.

На уровне Logical Flow в OVN правило простое: если inport трафика — это порт VM1, для него выполняется action mirror. На уровне OpenFlow сложнее: матчится metadata — тот самый VNI, который передавался в Geneve-заголовке и означает подсеть, в которой обрабатывается трафик, — и регистр 14, соответствующий порту виртуальной машины. Никаких inport и outport на уровне OpenFlow уже нет, есть только регистры.

С action оказалось сложнее: пакет нужно скопировать. Здесь Open vSwitch всё сделал заранее — у него есть действие clone, создающее копию пакета. Внутри копии меняются порты, и она отправляется на порт третьей виртуальной машины.

Разные подсети: контейнерные порты

Когда источник и приёмник находятся в разных подсетях, всё работает так же, как в обычных физических сетях: пакет идёт на роутер, там происходит маршрутизация и меняются MAC-адреса. Но копировать трафик нужно ровно таким, каким он был, то есть маршрутизировать его и вообще обрабатывать по логической топологии нельзя — требуется сразу отправить копию на VM3.

В физических сетях эта задача решается инкапсуляцией в GRE-туннель. Делать двойную инкапсуляцию внутри оверлейной сети не хотелось: пришлось бы патчить не только user space и Open vSwitch, но и ядерный модуль, а кроме того, появлялись потенциальные проблемы с hardware offloading. От этой идеи отказались.

Решение нашлось в документации OVN — контейнерные порты. Эта функциональность позволяет заводить трафик внутрь контейнеров, расположенных внутри виртуальных машин. Схема такая: внутри виртуальной машины развёрнуты два контейнера, разделённые на сетевом уровне VLAN'ами. В OVN создаётся абстрактный порт контейнера. Пинг приходит на этот порт, OVN при обработке добавляет VLAN-тег, и пакет сразу оказывается на нужной виртуальной машине, вообще не проходя по логической топологии. Дальше виртуальная машина всё делает сама: снимает VLAN-тег и отправляет пакет в контейнер.

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

В итоге схема выглядит так: есть роутер и две подсети, VM1 — источник трафика, VM3 — приёмник. Копия пакета уже создана. В той же подсети, где находится источник, создаётся специальный контейнерный порт для зеркалирования, и весь скопированный трафик отправляется ему — так же, как это делалось раньше, простой сменой порта назначения. Дальше OVN делает всё сам, и пакет сразу оказывается на третьей виртуальной машине, никак не изменившись.

Фильтры зеркалирования

Работающего зеркалирования оказалось мало — сервису хотелось добавить изюминки. K2 Cloud поддерживает API Amazon, а у Amazon есть функциональность фильтров для зеркалируемого трафика. Для пользователей OVN сделать это крайне просто — достаточно дописать дополнительные правила в match для пакета.

Плюсы такого решения:
  • Снижение нагрузки на сенсор. Клиенты могут сами выбирать, какой именно трафик они хотят зеркалировать. Например, если настроены бэкапы с большими elephant flow, такой трафик можно не зеркалировать и не скармливать анализатору.
  • Распределение трафика по нескольким приёмникам.
  • Экономия ресурсов сети. Зеркалирование даёт x2 по сети — трафик клиента фактически умножается на два. Если клиент зеркалирует только то, что ему нужно, выигрывает и облако.
Реализация оказалась несложной, и фильтры доступны в облачной консоли.

Пакеты из будущего: история одного реордеринга

Код работал, но обнаружился нюанс. От клиентов пришёл скриншот дампа, на котором сначала шёл ICMP reply, а потом ICMP request. Это противоречит всем сетевым законам. Разработчику хотелось бы думать, что это фича — копирование пакетов из будущего раньше пакетов из прошлого, — но клиенты так не считали.

Разбирательство началось с физики. На физическом порту всё было правильно: сначала request, потом reply. Значит, дело в Open vSwitch.

Чтобы понять, как это возможно, нужно вспомнить, как Open vSwitch работает с трафиком. Есть два режима: kernel space и user space datapath. На примере kernel space: пакет приходит на сетевую карту и попадает в ядерный модуль Open vSwitch, который отдаёт его в user space — делает upcall. Open vSwitch в user space обращается к OpenFlow-правилам, понимает, что нужно сделать с таким пакетом, и возвращает решение в kernel space. Kernel space сохраняет эту информацию у себя, и все следующие пакеты того же потока данных проходят напрямую через ядерный модуль, уже не совершая дорогостоящего upcall в user space.

Связка user space и kernel space устроена на протоколе netlink, и с каждым netlink-сокетом в kernel space работает отдельный поток в user space — поток ovs-vswitchd, демона, который занимается обработкой трафика.

Логи демона показали следующее. Сначала всё честно: приходит request, его подхватывает handler-поток в user space. Затем приходит ICMP reply — и его подхватывает уже совсем другой handler. Open vSwitch обрабатывает reply, тот заканчивает обработку и улетает на виртуальную машину, в то время как request всё ещё обрабатывается. В этом и была проблема.

Продакшн и upstream

После решения этой проблемы сервис был запущен в продакшн в обоих регионах — в Москве и Санкт-Петербурге. Клиенты им пользуются.

Оставалась последняя часть — отправить код в upstream. Команда K2 Cloud — активный контрибьютор OVN, так что и с этим проблем не возникло. Забавный факт: сообщество OVN, как и сообщество Open vSwitch, до сих пор живёт в mail-листах. GitHub используется только для уже принятых в upstream коммитов, а вся разработка идёт в patchwork и мейл-листах — привыкнуть к этому непросто.
Финал
Зеркалирование трафика в облаке нельзя просто взять из коробки: нативные механизмы Open vSwitch рассчитаны на underlay и работу с физическими портами, а облаку нужна полностью виртуальная схема. Решение было построено на уже существующих в OVN примитивах — действии clone и контейнерных портах, — что позволило обойтись без двойной инкапсуляции, без правки ядерного модуля и без проблем с hardware offloading, а заодно резко сократить объём нового кода.
Дополнительного overhead на обработку зеркалирования при этом не возникает: для SDN зеркалированные пакеты — это обычные пакеты, upcall в user space совершает только первый из них, а дальше поток идёт по общему пути. Фильтры зеркалирования снижают нагрузку и на клиентские сенсоры, и на сеть облака.
Отдельный урок этой истории — про реордеринг пакетов: кастомный ядерный модуль, казавшийся безобидным, приводил к обработке пакетов одного потока в разных handler'ах и к нарушению их порядка. Отказ от него вернул корректную последовательность.
Сервис работает в продакшне в обоих регионах, а доработки отправлены в upstream — и остаются доступны всему сообществу.
В Москве:
РФ (звонок бесплатный):
Наша почта:
GetNet
Остались вопросы?
Группа компаний VoxLink © 2011-2026