Проектирование современных шлюзов виртуальных частных сетей с пропускной способностью в десятки гигабит требует гибкого программируемого контроля входящих (ingress) и исходящих (egress) потоков пакетов. Хотя технология eXpress Data Path (XDP) зарекомендовала себя как мощный инструмент низкоуровневой фильтрации на уровне драйвера сетевой карты, она имеет принципиальное ограничение: XDP работает исключительно на приеме до того, как ядро Linux выделит метаданные буфера пакета (struct sk_buff). Это не позволяет XDP анализировать исходящий трафик, управлять очередями отправки или производить глубокую модификацию пакетов после прохождения сокетов.

Для полноценного двунаправленного управления сетевые инженеры Linux применяют подсистему Traffic Control (TC) в сочетании с классификаторами eBPF (BPF_PROG_TYPE_SCHED_CLS). Подключаемые через современную дисциплину очередей clsact (qdisc), программы eBPF TC исполняются непосредственно в сетевом стеке ядра с полным доступом к структуре sk_buff, обеспечивая динамическую маршрутизацию L4/L7, перенаправление пакетов между интерфейсами и регулирование темпа отправки (departure-time pacing) без переключения контекста в пространство пользователя.

Сравнение точек исполнения: eBPF TC и XDP в сетевом стеке ядра

Чтобы наглядно представить место eBPF TC в архитектуре защищенных туннелей, рассмотрим точки его подключения относительно XDP.

Входящий пакет (Ingress)
      │
      ▼
┌──────────────┐
│ XDP Hook     │  <--- Работает с «сырым» xdp_md до выделения skb
└──────┬───────┘       (Сброс/перенаправление, только прием, нет точки egress)
      │
      ▼
 alloc_skb()
      │
      ▼
┌──────────────────┐
│ eBPF TC Ingress  │  <--- Работает со структурой __sk_buff (clsact qdisc)
└──────┬───────────┘       (Анализ L3/L4, таблицы BPF, перенаправление в туннель)
      │
      ▼
 Сетевой стек (IP/TCP/UDP) ── Уровень сокетов
      │
      ▼ (Зашифрованный исходящий трафик Egress)
┌──────────────────┐
│ eBPF TC Egress   │  <--- Работает со структурой __sk_buff перед кольцом адаптера
└──────┬───────────┘       (Тайминги EDT, модификация DSCP, шейпинг полосы)
      │
      ▼
Очередь драйвера физической сетевой карты

Исполнение логики на уровне TC предоставляет ключевые технические преимущества:

  • Двунаправленная обработка: Точки подключения TC активны как на входящем, так и на исходящем пути физических адаптеров (eth0) и виртуальных устройств туннеля (wg0).
  • Полный доступ к sk_buff: Программы TC могут считывать и модифицировать заголовки, контрольные суммы, временные метки пакетов (skb->tstamp) и метки фильтрации (skb->mark).
  • Прямое исполнение действий: Благодаря флагу direct-action (da), классификатор BPF сразу возвращает вердикт (TC_ACT_OK, TC_ACT_SHOT, TC_ACT_REDIRECT), избавляя от нагромождения цепочек фильтров.

Перенаправление пакетов без копирования через bpf_redirect

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

С помощью eBPF TC сетевой инженер настраивает прямую межсетевую пересылку в памяти ядра через функцию bpf_redirect():

#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 65536);
    __type(key, __u32);   /* Виртуальный IPv4 клиента */
    __type(value, __u32); /* Индекс целевого интерфейса */
} route_map SEC(".maps");

SEC("tc")
int tc_ingress_router(struct __sk_buff *skb)
{
    void *data = (void *)(long)skb->data;
    void *data_end = (void *)(long)skb->data_end;
    
    /* Проверка границ: заголовки Ethernet + IPv4 */
    if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end)
        return TC_ACT_OK;
        
    struct iphdr *iph = data + sizeof(struct ethhdr);
    __u32 dst_ip = iph->daddr;
    
    /* Быстрый поиск интерфейса в таблице BPF */
    __u32 *ifindex = bpf_map_lookup_elem(&route_map, &dst_ip);
    if (ifindex) {
        /* Прямое перенаправление пакета мимо стандартного стека IP */
        return bpf_redirect(*ifindex, 0);
    }
    
    return TC_ACT_OK;
}

Подключенный к физическому интерфейсу, классификатор анализирует входящий трафик, сопоставляет IP-адрес с целевым туннельным устройством по таблице BPF и перенаправляет пакет прямо во входную очередь виртуального адаптера. Операция не требует копирования памяти, снижая затраты тактов процессора на пакет более чем на 40%.

Ритмичная отправка EDT и предотвращение микровсплесков

Серьезной проблемой в высокоскоростных туннелях является взрывной характер передачи данных (microbursts). При одновременной активности множества потоков передачи файлов передатчики TCP генерируют плотные пачки пакетов. Традиционные алгоритмы ограничения скорости (TBF или HTB) задерживают избыточные пакеты в программных очередях, защищенных глобальными спинлоками (spinlocks). При высоких нагрузках конкуренция блокировок резко ограничивает масштабируемость системы на многоядерных процессорах.

Современное ядро Linux устраняет проблему избыточной буферизации (bufferbloat) с помощью планирования по времени ранней отправки (Early Departure Time — EDT). Вместо удержания пакетов в блокирующих очередях система назначает каждому пакету расчетную временную метку отправки (skb->tstamp).

Используя eBPF TC на исходящем потоке (egress), инженеры реализуют безблокировочный шейпинг полосы:

  1. Исходящая программа eBPF вычисляет минимальный интервал между пакетами согласно назначенному тарифу пропускной способности клиента.
  2. Программа обновляет skb->tstamp = max(current_time, last_departure_time + packet_len / rate).
  3. Базовая дисциплина справедливой очереди (sch_fq) направляет пакеты в сетевой адаптер строго по их временным меткам, исключая задержки из-за взаимных блокировок.

Настройка и загрузка программ eBPF TC через clsact

Управление подсистемой eBPF TC в Linux выполняется с помощью стандартных утилит iproute2:

# 1. Добавление дисциплины clsact на физический интерфейс WAN
tc qdisc add dev eth0 clsact

# 2. Загрузка классификатора BPF в точку Ingress с флагом direct-action
tc filter add dev eth0 ingress bpf da obj vpn_router.o sec tc_ingress

# 3. Загрузка регулятора скорости EDT в точку Egress
tc filter add dev eth0 egress bpf da obj vpn_shaper.o sec tc_egress

# 4. Проверка активных фильтров и статистики выполнения
tc filter show dev eth0 ingress
tc filter show dev eth0 egress

Благодаря независимой структуре точек подключения clsact, загрузка и бесшовное обновление программ BPF выполняются на лету, не вызывая потерь пакетов и простоев интерфейса.

Применение в архитектуре глобальной сети RiceGuard

В распределенной сети узлов Anycast платформы RiceGuard подсистема eBPF TC применяется для обеспечения гарантированного качества сервиса:

  • Индивидуальный контроль полосы сессий: Подключение фильтров eBPF egress непосредственно к туннельным интерфейсам позволяет изолировать потоки каждого клиента персональными таймерами EDT, защищая интерактивный трафик от всплесков фоновых загрузок.
  • Потоковая телеметрия в BPF Map: Входящие классификаторы передают агрегированные метрики (счетчики пакетов, скорость передачи, аномальные заголовки) в кольцевые буферы ядра для мгновенного анализа угроз без опросов из пользовательского пространства.
  • Аппаратная разгрузка SmartNIC: В дата-центрах со специализированными сетевыми картами правила eBPF TC транслируются прямо в аппаратные таблицы коммутации, обеспечивая обработку на скорости 100 Гбит/с с микросекундной задержкой.

Связка программируемой диспетчеризации eBPF TC и безблокировочного шейпинга EDT позволяет WireGuard и RiceGuard достигать непревзойденной плотности трафика и высокой стабильности передачи данных.