Проєктування сучасних шлюзів віртуальних приватних мереж для обробки мультигігабітних потоків вимагає гнучкого програмованого контролю як вхідних (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      │  <--- Обробка сирого xdp_md до виділення skb
└──────┬───────┘       (Скидання/перенаправлення, лише прийом, немає точки виходу)
      │
      ▼
 alloc_skb()
      │
      ▼
┌──────────────────┐
│ eBPF TC Ingress  │  <--- Робота зі структурою __sk_buff (clsact qdisc)
└──────┬───────────┘       (Аналіз L3/L4, таблиці BPF, спрямування в тунель)
      │
      ▼
 Мережевий стек (IP/TCP/UDP) ── Рівень сокетів
      │
      ▼ (Зашифрований вихідний трафік Egress)
┌──────────────────┐
│ eBPF TC Egress   │  <--- Робота зі структурою __sk_buff перед кільцем NIC
└──────┬───────────┘       (Таймінги 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

У розгалужених багатокористувацьких VPN-інфраструктурах передавання пакетів між фізичним адаптером і віртуальним інтерфейсом тунелю зазвичай вимагає подвійного проходження стека 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 та усунення мікровсплесків

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

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

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

  1. Програма eBPF egress обчислює мінімальний інтервал між пакетами згідно з призначеним профілем швидкості користувача.
  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 відбуваються миттєво, без втрат пакетів та без потреби перезапуску інтерфейсу.

Застосування в глобальній мережі Anycast платформи RiceGuard

У межах розподіленої мережі RiceGuard підсистема eBPF TC забезпечує передбачувану якість обслуговування:

  • Сесійний контроль швидкості: Підключення фільтрів eBPF egress до кожного клієнтського інтерфейсу дозволяє ізолювати сесії індивідуальними годинниками EDT, оберігаючи чутливий до затримок трафік від фонових завантажень.
  • Потокова телеметрія у BPF Map: Класифікатори ingress надсилають виміряні метрики потоків (лічильники пакетів, швидкість передачі, аномальні заголовки) у кільцеві буфери ядра для аналізу загроз без витратних опитувань із простору користувача.
  • Апаратна інтеграція зі SmartNIC: У центрах обробки даних зі спеціалізованими адаптерами правила eBPF TC транслюються безпосередньо у логіку мережевої карти, підтримуючи швидкість 100 Гбіт/с із субмікросекундною затримкою.

Поєднання програмованої комутації eBPF TC та безблокувального таймінгу EDT дає змогу WireGuard та RiceGuard підтримувати високу щільність трафіку й надійну ізоляцію клієнтських з'єднань.