Високопродуктивні VPN-шлюзи та прикордонні вузли безпеки одночасно обслуговують два принципово різні потоки мережевого трафіку: трафік локальної термінації (зашифровані тунелі WireGuard чи IPsec, призначені для локальних сокетів хоста) і транзитний трафік пересилання (декапсульовані пакети користувачів, які скеровуються через мережеві інтерфейси у зовнішню мережу WAN). У мережевому стеку Linux оптимізація шляху між первинною класифікацією пакетів та опитуванням таблиці маршрутизації безпосередньо визначає масштабованість вузла.

Механізм IP Early Demux (ip_early_demux) було інтегровано в ядро Linux для усунення повторних звернень до дерева маршрутизації FIB (Forwarding Information Base) для встановлених сесій. Проте на системах, які поєднують функції криптографічного завершення тунелів та високошвидкісного маршрутизатора пакетів, конфігурація Early Demux потребує чіткого розуміння архітектурних компромісів.


Дилема маршрутизації: Пошук у дереві FIB Trie проти Кешування сокетів

Щоб оцінити призначення IP Early Demux, розглянемо стандартний шлях вхідного пакета IPv4 після отримання від драйвера мережевого адаптера (NIC):

  1. Прийом пакета: Пакет надходить до мережевого рівня через функцію ip_rcv() і спрямовується до ip_rcv_finish_core().
  2. Стандартний пошук маршруту: За замовчуванням ядро відразу викликає ip_route_input_noref() для опитування дерева FIB trie. Цей процес аналізує IP-адресу призначення, джерело, мітки політик маршрутизації (fwmark) та вхідний інтерфейс, з'ясовуючи, призначений пакет локальному хосту (RTN_LOCAL) чи підлягає пересиланню (RTN_UNICAST).
  3. Демультиплексування до сокету: Якщо маршрут визначено як RTN_LOCAL, обробка передається функції ip_local_deliver(). Транспортний протокол (TCP або UDP) виконує пошук у хеш-таблиці сокетів (inet_lookup_established або таблиці портів UDP) для знаходження відповідної структури struct sock.
[ Вхідний пакет IPv4 ]
          │
          ▼
[ Стандартний шлях: ip_rcv_finish_core() ]
          │
          ▼
[ Пошук у дереві FIB Trie: ip_route_input_noref() ] ──► (Витрата процесорних тактів)
          │
          ├─► Якщо локальний (RTN_LOCAL) ──► ip_local_deliver() ──► Хеш-таблиця сокетів
          └─► Якщо пересилається (RTN_UNICAST) ──► ip_forward()

На шлюзі, що обробляє 5 мільйонів пакетів щосекунди крізь тисячі відкритих тунелів VPN, сканування дерева FIB trie для кожного пакета створює серйозне навантаження на підсистему оперативної пам'яті та призводить до очищення кешу процесора рівнів L2/L3.


Архітектурний механізм: Прямий перехід Early Demux без звернення до FIB

IP Early Demux змінює послідовність дій для активних з'єднань. До виклику підсистеми маршрутизації FIB ядро виконує попередній пошук сокету безпосередньо з контексту ip_rcv_finish_core().

[ Вхідний пакет IPv4 ]
          │
          ▼
[ Early Demux: ip_rcv_finish_core() ]
          │
          ▼
[ Попередній пошук сокету: tcp_v4_early_demux() / udp_v4_early_demux() ]
          │
          ├─► Збіг знайдено (sk->sk_rx_dst валідний):
          │         │
          │         ├─► Призначення маршруту з кешу (skb_dst_set_noref)
          │         └─► Перехід відразу до ip_local_deliver() [Без опитування FIB trie]
          │
          └─► Немає локального збігу:
                    │
                    └─► Повернення до класичного пошуку через ip_route_input_noref()

У вихідному коді ядра (net/ipv4/ip_input.c) логіка Early Demux викликає відповідні протокольні функції:

/* Концептуальна схема обробки у net/ipv4/ip_input.c */
static int ip_rcv_finish_core(struct net *net, struct sock *sk,
                             struct sk_buff *skb, struct net_device *dev,
                             const struct sk_buff *hint)
{
    const struct iphdr *iph = ip_hdr(skb);

    if (net->ipv4.sysctl_ip_early_demux && !skb_dst(skb)) {
        const struct net_protocol *ipprot;
        int protocol = iph->protocol;

        ipprot = rcu_dereference(inet_protos[protocol]);
        if (ipprot && ipprot->early_demux)
            ipprot->early_demux(skb);
    }

    /* Якщо запис маршруту додано через early_demux, пошук у FIB пропускається */
    if (!skb_dst(skb)) {
        int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
                                      iph->tos, dev);
        if (unlikely(err))
            goto drop;
    }
    return NET_RX_SUCCESS;
}

Принцип кешування адресата (sk_rx_dst)

Під час встановлення з'єднання TCP або за умови роботи сокетів UDP у підключеному режимі (після виклику connect()), ядро фіксує маршрут призначення у полі sk->sk_rx_dst.

Під час обробки у tcp_v4_early_demux() ядро витягує ідентифікаційні параметри з заголовків IP та TCP, знаходить зареєстрований сокет і перевіряє стан sk->sk_rx_dst. Якщо запис актуальний, посилання закріплюється за skb->_skb_refdst за допомогою skb_dst_set_noref(skb, dst). Пакет миттєво спрямовується до локального адресата, не затримуючись на етапі аналізу маршрутної інформації.


Експлуатаційний компроміс: Завершення тунелів проти Транзитного пересилання

Попри те що Early Demux суттєво підвищує швидкість обробки для локального трафіку, на гібридних шлюзах VPN виникають специфічні фактори:

  • Марні операції на транзитному трафіку: Коли шлюз пересилає декапсульований трафік користувачів до зовнішніх мереж, ці пакети не призначені внутрішнім сокетам. Якщо Early Demux увімкнено, ядро здійснює невдалу спробу пошуку в хеш-таблиці сокетів для кожного транзитного пакета, після чого змушене переходити до дерева FIB.
  • Ризик застарівання маршруту за асиметричних умов: У разі динамічної зміни маршрутів ECMP кешований запис sk_rx_dst може спрямовувати пакети за старими параметрами до моменту чергової валідації сокетом.
+-------------------------------------------------------------------------+
|                  Сценарії використання на шлюзах VPN                    |
+-------------------------------------------------------------------------+
| Сценарій А: Сервер завершення тунелів (Вузли RiceGuard / WireGuard)    |
| -> Пакети прямують до локальних сокетів (інкапсуляція UDP)             |
| -> Оптимізація: Увімкнути ip_early_demux та udp_early_demux            |
|                                                                         |
| Сценарій Б: Виділений маршрутизатор транзитного пересилання             |
| -> Пакети переміщуються між WAN та внутрішніми мережами                |
| -> Оптимізація: Вимкнути ip_early_demux задля усунення зайвих пошуків   |
+-------------------------------------------------------------------------+

Налаштування та оптимізація Early Demux у Linux

Операційна система Linux пропонує набір параметрів sysctl для гнучкого регулювання поведінки Early Demux.

1. Перевірка активних параметрів

З'ясуйте значення конфігурації на поточному сервері:

# Перевірка параметрів Early Demux
sysctl net.ipv4.ip_early_demux
sysctl net.ipv4.tcp_early_demux
sysctl net.ipv4.udp_early_demux

2. Конфігурація для серверів термінації тунелів

На шлюзах, де основним завданням є прийом та дешифрування тунельного трафіку (наприклад, вузли RiceGuard з мільйонами пакетів UDP), активуйте упереджене демультиплексування:

# Увімкнення загального Early Demux
sysctl -w net.ipv4.ip_early_demux = 1

# Увімкнення для TCP (веб-інтерфейси та сервіси керування)
sysctl -w net.ipv4.tcp_early_demux = 1

# Увімкнення для UDP (підключені тунельні сокети)
sysctl -w net.ipv4.udp_early_demux = 1

Щоб параметри зберігалися після перезавантаження системи, збережіть їх у /etc/sysctl.d/99-early-demux.conf:

cat << 'EOF' > /etc/sysctl.d/99-early-demux.conf
net.ipv4.ip_early_demux = 1
net.ipv4.tcp_early_demux = 1
net.ipv4.udp_early_demux = 1
EOF
sysctl --system

3. Оптимізація для транзитних вузлів та маршрутизаторів NAT

Якщо сервер призначений переважно для пересилання декапсульованого трафіку без локального завершення тунелів, вимкнення Early Demux зберігає ресурси процесора:

# Вимкнення на транзитних маршрутизаторах
sysctl -w net.ipv4.ip_early_demux = 0

4. Моніторинг кешу за допомогою nstat

Аналізуйте показники влучання упередженого пошуку та статистику мережевого стека через nstat:

# Перегляд статистики успішних збігів Early Demux та обсягів пересилання
nstat -az | grep -E "IpEarlyDemux|IpInReceives|IpForwDatagrams"

Порівняння архітектурних режимів Early Demux

Вплив увімкнення Early Demux залежить від спрямованості мережевих потоків:

Критерій оцінки Early Demux увімкнено (Локальні сокети) Early Demux вимкнено (Стандартний шлях) Early Demux на транзитному маршрутизаторі
Навантаження на FIB Trie Оминається для встановлених з'єднань Виконується для кожного пакета Пошук у FIB плюс невдала перевірка хешу
Використання кешу L2/L3 Сфокусовано у хеш-таблиці сокетів Розподілено по гілках дерева FIB Вище через подвійне звернення до пам'яті
Швидкість обробки PPS 7.8 Mpps стабільно на одне ядро 5.9 Mpps стабільно на одне ядро Незначне зниження пропускної здатності (~4%)
Затримка пересилання Звичайна (звернення до FIB) Передбачувана та мінімальна Додаткові 3-5% затримки на малих пакетах
Ризик застарівання маршруту Мінімальний (валідація запису dst) Відсутній (завжди новий розрахунок) Відсутній (немає прив'язки до сокету)

Архітектурний підхід у платформі RiceGuard

У мережевій структурі VPNRice сервери RiceGuard розділяють завдання прийому трафіку та його подальшої маршрутизації:

  1. Зовнішні інтерфейси прийому: Точки входу, що приймають зашифровані UDP-пакети клієнтів, працюють із net.ipv4.ip_early_demux = 1 та net.ipv4.udp_early_demux = 1. Кадри WireGuard відразу узгоджуються з відкритими сокетами й надходять до криптографічних конвеєрів RiceGuard без затримок у таблиці маршрутизації.
  2. Внутрішні інтерфейси транзиту: Простори імен мережі (network namespaces), які скеровують декапсульований трафік користувачів, використовують net.ipv4.ip_early_demux = 0. Це гарантує безперешкодне сканування дерева FIB trie без зайвих промахів у таблицях сокетів.
  3. Відокремлення служб керування та DNS: Служби адміністрування TCP та захищені резолвери DNS функціонують в окремих сегментах з tcp_early_demux = 1, забезпечуючи блискавичне опрацювання запитів та надійний захист від витоків DNS.