Протокол защиты транспортного уровня TLS 1.3 является фундаментом криптографической безопасности современного интернета. В архитектурах виртуальных частных сетей (VPN), граничных ретрансляторов и инкапсулированных защитных туннелей (таких как DNS-over-HTTPS или потоковые мультиплексоры) шифрование предотвращает перехват и модификацию трафика.

Однако классические библиотеки TLS пространства пользователя (userspace), включая OpenSSL, BoringSSL и Rustls, сталкиваются с фундаментальными ограничениями производительности на высоконагруженных сетевых узлах. В традиционной схеме каждый пакет, проходящий через защищенный узел, вынужден многократно копироваться между буферами сокетов ядра и памятью пользовательского процесса, требуя постоянных переключений контекста.

При переходе на интерфейсы со скоростями 40GbE, 100GbE и плотным потоком пакетов подобные операции истощают процессорный кэш, перегружают системную шину памяти и снижают общую пропускную способность.

Чтобы исключить лишнее дублирование данных в оперативной памяти и перенести симметричные криптографические операции непосредственно на сетевые адаптеры, в ядро Linux был внедрен механизм Kernel TLS (kTLS, CONFIG_TLS) в сочетании с аппаратной разгрузкой SmartNIC.

+--------------------------------------------------------------------------+
|          СРАВНЕНИЕ ПЕРЕДАЧИ ДАННЫХ: USERSPACE TLS И LINUX KTLS           |
+--------------------------------------------------------------------------+
|                                                                          |
|  [ КЛАССИЧЕСКАЯ РЕТРАНСЛЯЦИЯ TLS В ПРОСТРАНСТВЕ ПОЛЬЗОВАТЕЛЯ ]           |
|                                                                          |
|      Вход с NIC ──> sk_buff ──(Копирование 1)──> Память Userspace        |
|                                                     │                    |
|                                           CPU дешифрует и шифрует        |
|                                           (Сброс кэшей L1/L2)            |
|                                                     v                    |
|      Выход в NIC <── sk_buff <──(Копирование 2)── Память Userspace       |
|      (Требует 4 копирования и 2 переключения контекста на цикл)          |
|                                                                          |
|  ----------------------------------------------------------------------  |
|                                                                          |
|  [ РЕТРАНСЛЯЦИЯ LINUX KTLS ZERO-COPY С АППАРАТНОЙ РАЗГРУЗКОЙ ]           |
|                                                                          |
|      Вход с NIC (DMA)                                                    |
|           │                                                              |
|           v (Буфер страниц ядра / sk_buff)                               |
|      +-------------------------------------------------------------+     |
|      |                  СЕТЕВОЙ СТЕК ЯДРА LINUX                    |     |
|      |                                                             |     |
|      |   Модуль kTLS (CONFIG_TLS) / Маршрутизация eBPF Sockmap     |     |
|      |   - Полное отсутствие копирования в Userspace               |     |
|      |   - Прямая пересылка внутри ядра через splice()             |     |
|      +-------------------------------------------------------------+     |
|           │                                                              |
|           v (Передача Zero-Copy через DMA)                               |
|      Аппаратный криптопроцессор на сетевой карте (Mellanox / Intel)      |
|           │                                                              |
|           v                                                              |
|      Зашифрованный трафик на оптической линии (Скорость 100GbE)          |
|                                                                          |
+--------------------------------------------------------------------------+

Узкие места обработки трафика в пространстве пользователя

В архитектуре стандартных сетевых демонов ретрансляция защищенного трафика создает существенные вычислительные издержки:

  1. Частота системных вызовов: постоянное выполнение функций read() или SSL_read() провоцирует непрерывные переходы процессора между пространством пользователя и ядром.
  2. Двойное копирование памяти: входящие сетевые фреймы из буферов ядра передаются в оперативную память процесса. После логической обработки вызовы write() или SSL_write() копируют данные обратно в буферы сокетов ядра.
  3. Вытеснение процессорного кэша: интенсивное выполнение симметричного блочного шифрования (AES-GCM или ChaCha20-Poly1305) переполняет кэш-память уровней L1 и L2, вызывая задержки при обработке миллионов пакетов в секунду.

На серверах с портами 100GbE это архитектурное ограничение сдерживает производительность одного процессорного ядра на уровне 18–25 Гбит/с независимо от аппаратных ресурсов платформы.

Архитектура Linux kTLS: разделение фаз рукопожатия и передачи

Подсистема Kernel TLS устраняет указанные недостатки путем строгого разграничения задач:

  • Плоскость управления в Userspace: сложные протокольные сценарии с управлением состоянием (валидация цепочек сертификатов X.509, согласование ALPN и асимметричный обмен ключами ECDHE или постквантовыми алгоритмами) по-прежнему исполняются библиотеками пространства пользователя (OpenSSL, GnuTLS).
  • Плоскость данных в Ядре: как только сессионные симметричные ключи согласованы, пользовательское приложение передает их в сокетный уровень ядра с помощью вызова setsockopt().

С этого момента ядро Linux берет на себя формирование записей TLS, контроль порядковых номеров (sequence numbers), а также шифрование и проверку целостности данных непосредственно при вызовах tcp_sendmsg и tcp_recvmsg.

+--------------------------------------------------------------------------+
|                  СХЕМА ИНИЦИАЛИЗАЦИИ KTLS В ЯДРЕ LINUX                   |
+--------------------------------------------------------------------------+
|                                                                          |
|    1. Установление соединения TCP (Стандартный сокет)                    |
|                                                                          |
|    2. Рукопожатие TLS 1.3 на уровне Userspace (OpenSSL / BoringSSL)      |
|       - Выбор шифра: TLS_AES_256_GCM_SHA384                              |
|       - Вычисление симметричных ключей и векторов инициализации (IV)     |
|                                                                          |
|    3. Активация kTLS через системный вызов:                              |
|       setsockopt(fd, SOL_TCP, TCP_ULP, "tls", sizeof("tls"));            |
|                                                                          |
|    4. Передача криптографических параметров в ядро:                      |
|       setsockopt(fd, SOL_TLS, TLS_TX, &crypto_info, sizeof(info));       |
|       setsockopt(fd, SOL_TLS, TLS_RX, &crypto_info, sizeof(info));       |
|                                                                          |
|    5. Прямой ввод-вывод на уровне ядра:                                  |
|       Ядро самостоятельно упаковывает и шифрует фреймы                   |
|       Приложения используют splice(), sendfile() и MSG_ZEROCOPY          |
|                                                                          |
+--------------------------------------------------------------------------+

Настройка kTLS в приложениях на C / Rust

Пример инициализации kTLS через библиотеку OpenSSL 3.0+:

#include <sys/socket.h>
#include <netinet/tcp.h>
#include <linux/tls.h>
#include <openssl/ssl.h>

int enable_ktls_session(SSL *ssl, int socket_fd) {
    /* Шаг 1: Активация поддержки kTLS в OpenSSL */
    SSL_set_options(ssl, SSL_OP_ENABLE_KTLS);

    /* Шаг 2: Завершение стандартного рукопожатия TLS 1.3 */
    if (SSL_accept(ssl) <= 0) {
        return -1;
    }

    /* Шаг 3: Проверка успешного включения kTLS на отправку и прием */
    if (BIO_get_ktls_send(SSL_get_wbio(ssl))) {
        /* Ядро отвечает за формирование записей и шифрование исходящих данных */
    }
    if (BIO_get_ktls_recv(SSL_get_rbio(ssl))) {
        /* Ядро берет на себя проверку подлинности и дешифрацию входящих данных */
    }

    return 0;
}

Пересылка данных Zero-Copy через splice() и sendfile()

Главное эксплуатационное достоинство kTLS заключается в поддержке примитивов zero-copy для зашифрованных сетевых соединений.

В стандартном TLS вызов splice() не применим, так как открытые данные должны быть зашифрованы перед отправкой в стек. Благодаря kTLS трафик пересылается между двумя сокетами без транзита в память процесса:

/* Пересылка zero-copy между входящим сокетом и исходящим туннелем */
int pipefd[2];
pipe(pipefd);

while (1) {
    /* Перемещение расшифрованных данных из сокета в кольцевой буфер ядра */
    ssize_t bytes = splice(inbound_fd, NULL, pipefd[1], NULL, 65536, SPLICE_F_MOVE);
    if (bytes <= 0) break;

    /* Пересылка данных в исходящий сокет с шифрованием kTLS */
    splice(pipefd[0], NULL, outbound_fd, NULL, bytes, SPLICE_F_MOVE);
}

Данная схема полностью освобождает шину оперативной памяти хоста от повторной передачи байтов.

Аппаратная разгрузка: делегирование криптографии SmartNIC

Помимо программной реализации kTLS на процессоре, сетевые контроллеры корпоративного класса (NVIDIA Mellanox ConnectX-6 Dx / BlueField и Intel Ethernet 800) поддерживают аппаратный режим kTLS Device Offload.

Сравнение режимов работы kTLS

Ядро Linux поддерживает три уровня интеграции kTLS:

+--------------------------------------------------------------------------+
|                  РЕЖИМЫ РАБОТЫ ТЕХНОЛОГИИ KTLS                           |
+--------------------------------------------------------------------------+
| Режим               | Исполнительный блок   | Нагрузка CPU | Поток 100GbE|
+---------------------+-----------------------+--------------+-------------+
| Программный Userspace| Процессор (Процесс)  | 100%         | Ограничен   |
| Программный kTLS    | Процессор (Ядро ASM)  | 35-50%       | Достижим    |
| kTLS Device Offload | Аппаратный ASIC карты | <2%          | Полная линия|
+--------------------------------------------------------------------------+

При включении аппаратной разгрузки:

  1. Ядро связывает дескриптор sk_buff с криптографическим контекстом без изменения содержимого пакета.
  2. Контроллер DMA сетевой карты считывает открытый текст из памяти хоста.
  3. Аппаратный блок AES на сетевом адаптере выполняет шифрование непосредственно перед модуляцией сигнала в оптическом кабеле.

Проверка и включение аппаратной разгрузки:

# Проверка возможностей сетевого интерфейса
ethtool -k eth0 | grep tls-hw

# Включение аппаратного шифрования и дешифрации
ethtool -K eth0 tls-hw-tx-offload on
ethtool -K eth0 tls-hw-rx-offload on

Интеграция с eBPF Sockmap для маршрутизации внутри ядра

Для максимальной производительности kTLS объединяется с технологией eBPF sockmap (BPF_MAP_TYPE_SOCKMAP и BPF_PROG_TYPE_SK_MSG).

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

Входной NIC ──> kTLS Дешифрация ──> Фильтр eBPF sk_msg ──> kTLS Шифрование ──> Выходной NIC
               [ Полная обработка внутри ядра без переключения контекста ]

Практические преимущества для RiceGuard и VPNRice

В инфраструктуре сервиса VPNRice связка kTLS и аппаратной криптографии обеспечивает ключевые технологические преимущества:

  1. Масштабируемость пограничных узлов: серверы, обслуживающие протоколы DoH и DoT, обрабатывают сотни тысяч параллельных запросов на один узел с задержкой менее одной миллисекунды.
  2. Энергоэффективность и снижение тепловыделения: перенос операций AES-GCM на аппаратные контроллеры снижает нагрузку на центральные процессоры до 80%, предотвращая троттлинг на плотных серверных стойках.
  3. Безопасность пользовательских данных: расшифрованный трафик циркулирует исключительно в очередях ядра, не сохраняясь в динамической памяти пользовательских процессов.

Эксплуатационные требования

При проектировании промышленных систем с поддержкой kTLS рекомендуется:

  • Версия ядра: использовать Linux 5.15 LTS и новее, а для аппаратной разгрузки SmartNIC — ядра 6.1+.
  • Привилегии процессов: предоставить права на установку сокетных протоколов ULP или назначить возможность CAP_NET_ADMIN.
  • Наборы шифров: отдавать предпочтение алгоритмам TLS_AES_128_GCM_SHA256 и TLS_AES_256_GCM_SHA384, так как аппаратные блоки NIC оптимизированы под AES-GCM.

Объединение Linux kTLS, передачи zero-copy и аппаратных возможностей SmartNIC формирует надежный стандарт для современных защищенных шлюзов VPN.