Эволюция сетевой подсистемы базовой станции: как мы пришли от программного маршрутизатора к аппаратному ускорению

Привет, Хабр! Меня зовут Вадим Винник, я старший инженер-программист в YADRO. Наша компания развивает телеком-направление с 2022 года: за это время мы прошли путь от прототипирования на отладочных платах до серийного производства и эксплуатации нашего оборудования в коммерческих сетях. О разработке базовых станций — от первых идей до инженерных решений, работающих в реальной сети, — мы рассказываем в цикле статей.
Сегодня обсудим сетевую подсистему: именно от нее зависит скорость передачи данных, стабильность соединения и емкость сети. Сначала наша сеть работала на стандартном Linux, но для огромного трафика это было накладно. Мы кое-что оптимизировали за счет DPDK, но и это был не предел. Следующим шагом стало использование аппаратного коммутатора, доступного на некоторых аппаратных платформах. Что из этого получилось — расскажу ниже.
Прочитать другие статьи цикла
Виды трафика и задача сетевой подсистемы
Базовая станция постоянно пропускает сквозь себя интенсивные потоки данных. Этот трафик принято делить на несколько типов:
User Plane (Плоскость пользователя) — голос и данные абонентов. Требования здесь жесткие: высочайшая пропускная способность (десятки Гбит/с) и минимальная задержка. Это наш самый критичный и «тяжелый» трафик.
Control Plane (Плоскость управления). К этому типу относятся сигнальные сообщения, с помощью которых устанавливаются и поддерживаются соединения между абонентами. Трафика в этой плоскости немного, но он критичен для работы всей системы.
Management Plane (Плоскость управления и администрирования) — трафик для мониторинга, настройки и управления оборудованием. Он должен быть надежно изолирован, чтобы даже при пиковых нагрузках можно было получить доступ к станции.
Synchronization Plane (Плоскость синхронизации). Критически важный уровень в архитектуре современных сетей (особенно в Open RAN / 5G), который отвечает за передачу трафика синхронизации (частоты, фазы и времени) между узлами сети. Поскольку малейший сдвиг во времени (даже на уровне микросекунд или наносекунд) может привести к обрыву соединения, интерференции и падению скорости, к S-Plane предъявляются самые жесткие требования по задержке (Latency) и джиттеру (Jitter).
Эти потоки данных должны сосуществовать на одном физическом оборудовании, но при этом не мешать друг другу. Это примерно так же сложно, как обеспечить одновременное движение по железной дороге скорых пассажирских поездов, товарных составов и автомотрис с ремонтными бригадами.
Задача сетевой подсистемы — не просто соединить между собой все установленные в шасси платы, радиочастотные модули (RU) и опорную сеть (Core Network). Она должна настроить соединения таким образом, чтобы каждый тип трафика передавался по физически и логически отделенным каналам с гарантиями скорости передачи, задержек, пропускной способности и отказоустойчивости.
Сложное инженерное решение — результат длительной эволюции, проходящей через ряд прототипов. Каждая стадия эволюции решения — это попытка выжать максимум полезных характеристик из определенной аппаратуры и программных средств, зачастую — изобрести хитрый и неочевидный способ использования этих имеющихся в наличии средств. Именно таким оказался путь к сетевой подсистеме базовой станции.
Если опустить второстепенные детали (они интересные, но тут понадобился бы свой отдельный цикл), этот путь состоит из трех основных вех:
Стандартный стек Linux. Было просто, гибко и понятно, но годилось лишь для первого прототипа: чтобы базовая станция начала подавать признаки разумной жизни. Конечно, такое решение упирается в непреодолимые ограничения по производительности и предсказуемости задержек.
Ускорение на основе DPDK. Довольно популярный способ ускорения приложений, работающих с интенсивным сетевым трафиком. Он стал хорошим шагом вперед по сравнению с первым вариантом и мог бы вполне выйти в финал, если бы не еще одна идея.
Аппаратный коммутатор — встроенный, доступный прямо на плате. Если им управлять программно, все остальное будет делаться как бы само, без участия ПО и без нагрузки на CPU.
На каждом из этих этапов встретились свои увлекательные подзадачи, требовавшие как широты архитектурного мышления, так и внимания к отдельным машинным инструкциям и тактам процессора. О них расскажу дальше.
Мы ищем инженеров в нашу команду. Вы тоже могли бы работать над аппаратной платформой и программным обеспечением для телеком-оборудования и рассказывать об этом на Хабре. Открытые вакансии можно посмотреть на карьерном сайте — будем рады вашим откликам!
Горящие вакансии:
— инженер по разработке встраиваемого ПО;
— инженер по тестированию цифровых модемов;
— инженер по полевым испытаниям.
Как реализовать подсистему: Linux
На начальном этапе разработки для реализации сетевой подсистемы использовались стандартные механизмы Linux. Такой подход обеспечивал быстроту разработки и переносимость — это когда продукт запускается на различном оборудовании и различных аппаратных архитектурах (x86, arm64, risc-v). Все эти инструменты общедоступны и хорошо документированы.
Для изоляции различных типов трафика или различных участков внутренней сети на ум сами собой приходят сетевые пространства имен (network namespaces) — этот механизм ядра позволяет создавать изолированные экземпляры сетевого стека, каждый со своими интерфейсами, таблицами маршрутизации и правилами фильтрации. Такая изоляция была особенно важна на раннем прототипе — он представлял собой одну физическую машину, выполнявшую все функции базовой станции. Впоследствии управляющая функциональность и обработка базовой полосы были разделены между физически отдельными машинами Control Board (CB) и Baseband Board (BB). В контексте базовой станции естественно выделить отдельные пространства имен для плоскостей пользователя, управления и администрирования или для связи плат.
Таким образом в конфигурационном файле базовой станции нужно описать каждое пространство имен: задать его имя и расположение, то есть на какой из нескольких машин, входящих в состав базовой станции оно располагается. Сетевой конфигуратор базовой станции обрабатывает эти описания, выполняя команды ip netns add (и ip netns del при выключении). Чтобы включить пересылку пакетов, конфигуратор из контекста этого пространства устанавливает соответствующий флажок:
ip netns exec <ns_name> bash -c "echo 1 > /proc/sys/net/ipv4/ip_forward"Для дальнейшей работы с пространством имен открывается дескриптор специального файла /var/run/netns/<ns_name>. Чтобы переключиться в пространство, а затем гарантированно вернуться из него обратно, удобным оказался класс-обертка над функцией setns, работающий по принципу RAII.
В том же конфигурационном файле базовой станции содержатся описания сетевых устройств. Они включают в себя описания маршрутов (routes), виртуальных локальных сетей (vlan), мостов (bridge), виртуальных кабелей (veth), там же можно указывать и имя ранее сконфигурированного пространства имен. Разберем эти элементы чуть подробнее.
Для организации связи между пространствами имен использовались VETH-пары (Virtual Ethernet). Этот механизм создает виртуальный кабель, соединяющий два пространства: каждый интерфейс пары привязывается к своему пространству, формируя прямой канал передачи данных. Прочитав описание VETH-пары, конфигуратор создает ее и перемещает интерфейсы в соответствующие пространства имен командами:
ip link add <vethA> type veth peer name <vethB>
ip link set <veth> netns <ns_name>После перемещения каждому интерфейсу назначаются IP-адреса и устанавливается состояние UP.
Для объединения нескольких интерфейсов в одну логическую сеть внутри пространства имен применялись штатные средства управления мостами. Так, создание моста и добавление интерфейса в мост выполняются командами:
ip link add name <bridge> type bridge
ip link set <port> master <bridge>Для гибкого разделения трафика на канальном уровне хорошо подходят виртуальные локальные сети (VLAN) на физических портах или виртуальных функциях. По описанию из файла конфигуратор выполняет команды наподобие:
ip link add link <parent> name <vlan> type vlan id <vlan_id> <дополнительные параметры>
Полезные дополнения
Обертки над командой ip для настройки сетевых интерфейсов, маршрутов и прочего, как описано выше, уже позволяет в целом сконфигурировать сеть базовой станции и запустить трафик. Но штатные средства Linux позволяют добиться еще большего.
В Linux имеются средства для фильтрации и классификации трафика — например, фреймворки iptables и более новый nftables. В нашей разработке выбрали второй из-за большего быстродействия: документация обещает поиск правила за O(1) вместо O(n), поддержку множества действий в одном правиле, атомарное изменение правил и множество других преимуществ.
Нелишним было обратить внимание на механизмы GRO и LRO, которые объединяют несколько пакетов в один перед передачей в сетевой стек. Представим себе отправку десяти мелких пакетов по 100 байт и сравним это с отправкой одного объединенного пакета из 1000 байт. При том же объеме полезных данных количество прерываний и проходов по стеку сокращается в десять раз, снижается загрузка линии заголовками нижних уровней, транзакций между памятью и сетевой картой становится меньше, снижается нагрузка на CPU. Казалось бы, одна сплошная выгода. Но плата за нее непомерна для задач сотовой связи: такая оптимизация увеличивает задержки, ведь мелкие пакеты нужно сперва накопить. Поэтому по крайней мере для пользовательской плоскости от эти оптимизации могут обернуться пессимизацией.
Теперь поговорим о настройке параметров прерываний. Параметр rx-usec утилиты ethtool задает максимальное время в микросекундах между получением пакета сетевой картой и генерацией прерывания для его обработки, то есть влияет на прием данных. Параметр tx-usecs, напротив, влияет на передачу: это максимальное время в микросекундах перед генерированием прерывания о завершении передачи пакета. Если поставить этим параметрам низкие значения (в пределе 0), прерывание будет генерироваться сразу после получения или приема каждого пакета. Задержка в этом случае минимальна, а нагрузка на процессор, наоборот, максимальна. Если же увеличить значения этих параметров, прерывание будет генерироваться один раз на несколько пакетов, которые успели пройти через устройство в пределах установленной задержки, то есть увеличение задержки снижает нагрузку на процессор. Эти параметры можно применять для интерфейсов backhaul.
Еще стоит упомянуть об управлении качеством обслуживания: Linux предоставляет для этого все необходимое в виде tc (Traffic Сontrol). С его помощью можно, например, определять классы с гарантированной скоростью и обеспечивать справедливое распределение пропускной способности между потоками.
Что получилось
Если опустить не слишком сложные, но многочисленные подробности реализации, результат получается таким. Linux (как ядро с модулями, так и набор доступных из коробки утилит) содержит все необходимое для такой тонкой и сложной задачи, как конфигурирование внутренней сети базовой станции мобильной связи. Сеть можно разбивать на изолированные логические участки, связывать эти участки между собой, задавать правила фильтрации и маршрутизации и делать много других полезных настроек. Для разработки решения на базе штатных средств Linux не нужно ничего, кроме документации — которая имеется в изобилии. В частности, не нужно ни писать своими руками, ни модифицировать драйверы, ни заботиться о совместимости своего решения с аппаратурой — всю эту работу уже сделали разработчики системного ПО. Значит, на этом пути можно быстро получить работающий прототип базовой станции и приступить к собственно телекоммуникационным задачам.
Но, несмотря на эти безусловные преимущества, это решение все же не годится для окончательного изделия, которое может выдерживать высокие нагрузки при LTE или 5G трафике. Каждый сетевой пакет проходит через сложную цепочку обработки, начиная с аппаратного прерывания, неизбежного переключения в режим ядра и заканчивая копированием данных пакета. Все это создает нагрузку на центральный процессор. Для настольной рабочей машины, трафик которой обычно ограничен потребностью посмотреть видео в онлайне или (изредка) скачать огромные файлы, такие накладные расходы совершенно приемлемы. Тогда как гигантский трафик базовой станции (особенно в пользовательской плоскости) можно было бы обработать лишь исключительно мощным и дорогим многоядерным процессором, да еще требующим особого охлаждения. К тому же более серьезная проблема — это даже не величина задержек, а их непредсказуемость: планировщик ядра может в любой момент прервать обработку пакета для выполнения другой задачи. Поэтому нужно искать пути к оптимизации.
Использование DPDK
Ограничения стандартного стека Linux, в первую очередь связанные с нестабильностью и высокими задержками, потребовали перехода к более производительным решениям. Таким решением стал DPDK (Data Plane Development Kit) — набор библиотек и драйверов, позволяющий обрабатывать сетевые пакеты в пользовательском пространстве в обход ядра операционной системы.
Разберем принцип работы DPDK. Традиционный путь пакета в Linux включает несколько этапов:
Пакет поступает на сетевую карту.
Генерируется аппаратное прерывание.
Пакет проходит через сетевой стек ядра (уровни IP, TCP/UDP).
Пакет передается пользовательскому приложению через системный вызов.
Этот путь включает переключения контекста, копирования данных и обработку прерываний, что создает непредсказуемые задержки и загружает центральный процессор.
DPDK предлагает альтернативный подход:
Сетевой интерфейс переключается в режим, который позволяет работать с устройством из пользовательского пространства.
Вместо ожидания прерываний приложение опрашивает сетевую карту (polling).
Пакеты передаются непосредственно в пользовательское приложение без промежуточного копирования (zero-copy).
При этом одни интерфейсы могут работать через DPDK, а другие — оставаться под управлением ядра. Более того, технология SR-IOV (виртуализация шины) позволяет разделить один физический порт на несколько виртуальных функций (VF), каждая из которых ведет себя как отдельное сетевое устройство. Это дает возможность распределять нагрузку. Например, одну VF можно передать под управление DPDK для обработки критичного по производительности трафика (например, плоскость пользователя), а другую VF того же физического порта оставить под управлением ядра, где можно воспользоваться всеми преимуществами простой реализации из предыдущего раздела (например, для управляющего и служебного трафика плоскости управления и администрирования).
Само по себе использование DPDK снижает задержку, но для телекоммуникационного оборудования требуется не просто низкая, но и предсказуемая задержка порядка микросекунд. Для этого нужны более тонкие настройки. Дальше — о них.
Привязка потоков к ядрам (CPU affinity). Критичные потоки нужно жестко привязать к выделенным специально для них ядрам процессора. Это гарантирует, что потоки не будут мигрировать между ядрами и всегда будут работать с локальным кешем.

Изоляция ядер (CPU isolation). Планировщику Linux запрещается запускать на выделенных ядрах какие-либо другие процессы, в том числе системные. Дополнительно на этих ядрах запрещается обработки прерываний от сетевых карт и таймеров. Это делает работу критичных потоков полностью детерминированной — они никогда не прерываются, и переход в режим ядра не происходит.
Настройка кольцевых буферов: увеличение размера (примерно на порядок по сравнению с размером по умолчанию) позволяет без потерь выдерживать всплески трафика.
Open vSwitch с поддержкой DPDK — еще одно усовершенствование. Когда трафик нужно направить сразу с CB на BBLo, но связь есть только через BBHI, самым лучшим программным решением оказался OVS. В отличие от стандартного Linux Bridge он имеет меньшую задержку, что очень важно в наших задачах.
Переход на DPDK в сочетании с тонкой настройкой системы позволил значительно улучшить характеристики сетевой подсистемы. Задержка обработки пакетов снизилась до микросекунд, нагрузка на центральный процессор значительно уменьшилась благодаря отсутствию прерываний и переключений контекста, а производительность стала предсказуемой и перестала зависеть от планировщика ядра.
Эта выгода, конечно, досталась ценой ненулевых усилий. Так, на открытый код DPDK пришлось наложить несколько своих модификаций, да и общий объем кода на C++ возрос.
Но использование DPDK — не предел. Обработка трафика на скоростях реальной сотовой связи все еще требует значительных вычислительных ресурсов CPU. Следующим шагом эволюции стало использование аппаратного коммутатора, который позволяет полностью разгрузить центральный процессор, переложив задачи коммутации на специализированный ASIC.
Решение на базе аппаратного коммутатора
Прорыва удалось добиться благодаря встроенному Ethernet-коммутатору на некоторых платформах. Он может полностью взять на себя коммутацию и маршрутизацию пакетов, разгрузив тем самым центральный процессор.
Идея в том, чтобы ПО базовой станции, а именно сетевой конфигуратор, только программировал этот аппаратный коммутатор и следил за его работоспособностью, предоставив ему всю черновую работу. То есть разработчикам достаточно просто взять библиотеку, поставляемую вместе с аппаратным устройством, и реализовать абстракции прикладного уровня, через которые задается конфигурация сети. В коде это выглядит как инициализация коммутатора при старте системы и последующая настройка на нем портов, VLAN и маршрутов в соответствии с конфигурацией из файла.
У аппаратного коммутатора есть много полезных разнообразных функций. Об этом — дальше.
Функции аппаратного коммутатора
Коммутация на уровне L2. Коммутатор обеспечивает аппаратную коммутацию кадров между портами. Это превосходно подходит для основной задачи: организовать связь между платами внутри шасси. VLAN-изоляция трафика разных плоскостей также выполняется аппаратно: пакеты из одного VLAN не попадают в другой (а если это для чего-то нужно — всегда можно настроить маршрутизацию явно).
Маршрутизация на уровне L3. Коммутатор на аппаратном уровне поддерживает маршрутизацию IP-пакетов между различными VLAN. Это означает, что даже относительно сложные перенаправления трафика не загружают CPU. Для каждого виртуального маршрутизатора таблица маршрутизации своя, что позволяет изолировать маршруты разных плоскостей.
Балансировка трафика. Коммутатор поддерживает механизм Equal-Cost Multipath (ECMP), позволяющий распределять трафик по нескольким маршрутам с одинаковой стоимостью. Это увеличивает пропускную способность и обеспечивает отказоустойчивость.
Аппаратная фильтрация. Аппаратный коммутатор позволяет настраивать правила фильтрации, которые в дальнейшем выполняются на ASIC, то есть отбрасывать или перенаправлять пакеты можно, опять-таки, без участия CPU. Это особенно важно для защиты от нежелательного трафика и изоляции критичных потоков. Например, весь PTP-трафик (синхронизация часов — чрезвычайно важное условие работы сотовой связи) может быть автоматически направлен на специальный порт.
Обучение MAC-адресам. Коммутатор ведет аппаратную таблицу MAC-адресов, отслеживая, на каком порту находится каждое устройство. Это позволяет принимать решения о коммутации без участия CPU. При появлении нового устройства таблица обновляется автоматически: коммутатор достаточно умен, чтобы делать это самостоятельно. При этом высокоуровневое ПО вполне может получить у коммутатора построенную им MAC-таблицу.
Механизм триггеров. Коммутатор позволяет настроить действия, выполняемые при наступлении определенных событий. Например, при появлении нового MAC-адреса на порту можно автоматически создать маршрут до устройства.
ARP Spoofing. Коммутатор может перехватывать ARP-запросы и отвечать на них самостоятельно, используя заранее известные соответствия IP-адресов и MAC-адресов.
Преимущества подхода
Центральному процессору в базовой станции всегда есть чем заняться, и переложить обработку огромного числа пакетов на отдельное устройство — однозначно полезное дело. К тому же аппаратная коммутация выполняется с предсказуемой задержкой, не зависящей от загрузки процессора. Это критично для телекоммуникационного оборудования, где нужно уложиться в крайне жесткие ограничения по предельным задержкам — иначе телефонный разговор станет кусочно-рваным. Коммутатор обрабатывает трафик в реальном времени, на «родной» для линии скорости, да еще без потери производительности при росте нагрузки. Наконец, аппаратный коммутатор — это узкоспециализированное устройство со всеми вытекающими последствиями: его можно создать предельно оптимизированным для выполнения одной своей функции. Поэтому одну и ту же работу отдельный коммутатор выполнит с гораздо меньшим потреблением энергии (а значит — выделением тепла), чем центральный процессор (то есть устройство вынужденно универсальное).
Конечно же, за всякую выгоду необходимо чем-то платить. Во-первых, в аппаратуре базовой станции появляется еще одно устройство. На первый взгляд, это должно даже повысить стоимость платы, но в действительности оказывается наоборот: аппаратный коммутатор стоит меньше, чем удается сэкономить за счет менее мощного CPU.
Чем действительно приходится расплачиваться — так это написанием узкоспециализированного кода, предназначенного для одного определенного устройства. Налицо резкий контраст с первым решением: оно было универсальным, работало на совершенно любой аппаратуре, контейнере или виртуальной машине, если только там запускался Linux в стандартной, «коробочной», комплектации. Но ведь и базовая станция мобильной связи — это не то же самое, что настольный компьютер для массовых повседневных задач пользователя: специфическая предметная область естественно требует и особой аппаратуры, и специализированных программных решений.
К выводам
Какой можно сделать вывод, если взглянуть на все описанное с высоты птичьего полета?
Во-первых, это очередное подтверждение давно известной истины: никакая достаточно сложная система не возникает на пустом месте — она строится в ходе эволюции через ряд более простых прототипов. Первый из них может вообще не удовлетворять требованиям к окончательному промышленному образцу и нужен в качестве отправной точки для по-настоящему серьезных модификаций.
Во-вторых, не бывает решений, идеальных по всем критериям. В нашем случае сознательно выбран размен производительности на универсальность решения. Если делать ПО так, чтобы оно запускалось на любом тостере или наручных часах, не получится пользоваться выгодами специализированного устройства.
Наконец, действительно хорошие решения достигаются не какой-то одной технологией, пусть даже и самой передовой, а их грамотным сочетанием и тонкой настройкой системы в целом.
Путь поиска оптимального решения привел нас к полному циклу разработки и производства базовых станций: сейчас производственные мощности YADRO позволяют выпускать до 50 тысяч базовых станций в год. Наше телеком-оборудование работает в коммерческих сетях «Билайна» и «МегаФона» — в небольших населенных пунктах и нагруженных сетях крупных городов. В мае 2026 года первая отечественная базовая станция начала работу в городе-миллионнике — Нижнем Новгороде, до конца 2026 года мы планируем подключить еще несколько тысяч базовых станций в более чем 30 регионах страны (и параллельно обновлять станции прошлого поколения, конечно). Так, с учетом новых запусков коммерческая эксплуатация базовых станций YADRO сегодня охватывает уже 37 регионов России. Цели на будущее амбициозные — чувствую, тем для новых статей еще будет много.
Еще статьи из цикла про разработку базовых станций в YADRO:
— «Почему антенны работают на физике XIX века, но требуют технологий XXI».
— «Как базовая станция превращает данные в радиосигнал: разбираем цифровой тракт радиомодуля».
— «Какие этапы разработки и тестирования проходит радиомодуль в YADRO».
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.