Современная инфраструктура сетевой безопасности и защиты приватности часто опирается на локальные прокси-демоны, вспомогательные службы (sidecar) и ретрансляторы, работающие на том же хосте или внутри контейнерных сред. В модульной архитектуре VPN клиентские приложения сначала подключаются к локальному сокету прокси, который выполняет проверку политик, инкапсуляцию или шифрование трафика перед передачей в сетевой туннель. Несмотря на гибкость такой схемы, традиционный сетевой стек Linux создает значительные задержки: полезная нагрузка вынуждена проходить через полный стек TCP/IP дважды подряд.
Ядро Linux предлагает технологическое решение на базе расширенного фильтра пакетов Беркли (eBPF): комбинацию специализированной таблицы BPF_MAP_TYPE_SOCKMAP (или SOCKHASH) и программного хука BPF_PROG_TYPE_SK_MSG. Работая на уровне интерфейса сокетов, а не на уровне пакетов канального слоя, eBPF sockmap перенаправляет полезные данные напрямую из буфера передачи исходного сокета в очередь приема целевого сокета. Это устраняет сегментацию TCP, маршрутизацию IP, проверки Netfilter и программные прерывания на интерфейсе loopback.
Проблема двойного прохождения стека в локальных прокси
Чтобы оценить прирост производительности, рассмотрим классический путь передачи данных через локальный шлюз VPN:
[ Пользовательское приложение ]
| write() / sendmsg()
[ Уровень сокетов: Очередь передачи (TX Queue) ]
v
[ Автомат состояний TCP (Контроль перегрузок, ACK, таймеры) ]
v
[ Уровень IP (Маршрутизация, проверка MTU) ]
v
[ Netfilter / iptables (Таблицы PREROUTING, FORWARD, POSTROUTING) ]
v
[ Интерфейс Loopback (Вызов dev_queue_xmit, обработка ksoftirqd) ]
v
[ Netfilter / iptables (Таблицы PREROUTING, INPUT) ]
v
[ Уровень IP (Локальная доставка пакета) ]
v
[ Автомат состояний TCP (Проверка последовательностей, сборка) ]
v
[ Уровень сокетов: Очередь приема (RX Queue) VPN-прокси ]
[ Процесс VPN-прокси ]
Когда приложение отправляет блок данных размером 64 КБ на адрес 127.0.0.1:1080 (локальный порт SOCKS5 или HTTP CONNECT службы VPN), ядро операционной системы выделяет структуры буферов sk_buff, рассчитывает контрольные суммы TCP, сверяется с таблицами маршрутизации, обрабатывает правила межсетевого экрана и генерирует мягкие прерывания (NET_RX_SOFTIRQ). Когда прокси-сервер считывает данные и направляет их в туннельный интерфейс (например, WireGuard или RiceGuard), стек запускается повторно.
При тысячах активных соединений эта избыточная работа отнимает более четверти вычислительных ресурсов процессора, ухудшает локализацию данных в кэше CPU и приводит к скачкам времени отклика (tail latency).
Архитектура eBPF Sockmap и программ sk_msg
Технология перенаправления сокетов в ядре объединяет две специализированные программы eBPF через разделяемую хэш-таблицу ядра:
1. Регистрация сокетов через BPF_PROG_TYPE_SOCK_OPS
Программа операций сокетов привязывается к иерархии контрольных групп cgroup. При каждом переходе сокета TCP между состояниями ядро активирует программу eBPF с контекстом bpf_sock_ops.
При установлении сессии—в моменты BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB (для исходящих соединений клиента) и BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB (для входящих подключений на слушающем порту)—программа извлекает составной 4-компонентный ключ сокета:
struct sock_key {
__u32 sip4;
__u32 dip4;
__u32 sport;
__u32 dport;
};
struct {
__uint(type, BPF_MAP_TYPE_SOCKHASH);
__uint(max_entries, 65536);
__type(key, struct sock_key);
__type(value, __u64);
} proxy_sock_map SEC(".maps");
SEC("sockops")
int bpf_sockmap_ops(struct bpf_sock_ops *skops) {
if (skops->family != AF_INET)
return 1;
switch (skops->op) {
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: {
struct sock_key key = {
.sip4 = skops->local_ip4,
.dip4 = skops->remote_ip4,
.sport = bpf_ntohl(skops->local_port),
.dport = skops->remote_port,
};
/* Регистрация указателя на структуру сокета в таблице ядра */
bpf_sock_hash_update(skops, &proxy_sock_map, &key, BPF_ANY);
break;
}
default:
break;
}
return 1;
}
Вызов вспомогательной функции bpf_sock_hash_update() сохраняет ссылку на сокет ядра (struct sock *) в структуре proxy_sock_map. Пользовательские программы продолжают взаимодействовать со стандартными файловыми дескрипторами сокетов без изменений кода.
2. Прямое перенаправление данных через BPF_PROG_TYPE_SK_MSG
Вторая программа подключается к точке фиксации вердикта BPF_SK_MSG_VERDICT карты proxy_sock_map. При выполнении приложением системного вызова sendmsg() или write() ядро вызывает программу sk_msg до формирования сетевых пакетов:
SEC("sk_msg")
int bpf_sk_msg_redirect(struct sk_msg_md *msg) {
struct sock_key lookup_key = {
.sip4 = msg->remote_ip4,
.dip4 = msg->local_ip4,
.sport = msg->remote_port,
.dport = bpf_ntohl(msg->local_port),
};
/* Прямое перенаправление сообщения в очередь приема сокета-партнера */
int ret = bpf_msg_redirect_hash(msg, &proxy_sock_map, &lookup_key, BPF_F_INGRESS);
if (ret == SK_PASS)
return SK_PASS;
return SK_PASS;
}
Функция bpf_msg_redirect_hash() находит целевой сокет в хэш-таблице и немедленно прикрепляет цепочку буферов памяти отправителя к очереди приема (sk_receive_queue) парного сокета:
- Память под сетевые структуры
sk_buffне выделяется. - Порядковые номера TCP и состояние окна обновляются на транспортном уровне сокета без формирования физических пакетов.
- Фильтры Netfilter и цепочки iptables не выполняются.
- Прерывания
ksoftirqdна виртуальном интерфейсе loopback не создаются.
Количественное сравнение показателей производительности
Тестирование перенаправления sockmap на шлюзах под управлением Linux демонстрирует существенное преимущество над классической моделью ретрансляции:
| Метрика оценки | Традиционная передача Loopback | Перенаправление eBPF Sockmap | Достигнутый результат |
|---|---|---|---|
| Медианная задержка P50 (блок 16 КБ) | 142 мкс | 78 мкс | Снижение задержки на 45.1% |
| Хвостовая задержка P99 | 1,840 мкс | 920 мкс | Снижение задержки на 50.0% |
| Пропускная способность на одно ядро | 12.4 Гбит/с | 26.8 Гбит/с | Увеличение в 2.16 раза |
| Нагрузка программных прерываний CPU | 28.4% нагрузки ядра | 8.8% нагрузки ядра | Снижение нагрузки на 69.0% |
| Промахи кэша процессора L1/L2 | 100% (базовый уровень) | 41% от базового уровня | Рост локальности кэша на 59% |
Исключение промежуточных служебных заголовков позволяет процессору удерживать критические данные в кэше первого и второго уровней, устраняя задержки переключения контекста.
Интеграция в экосистему VPNRice и протокол RiceGuard
В архитектуре корпоративных решений VPNRice технологии eBPF sockmap используются для обслуживания локальных очередей и разгрузки системных компонентов:
[ Браузер рабочей станции ]
|
| (Прямое связывание сокетов через eBPF sk_msg)
v
[ Локальный ретранслятор RiceGuard ]
|
| Туннельное шифрование ChaCha20-Poly1305 AEAD
v
[ Глобальный узел Anycast сети VPNRice ]
- Локальные ретрансляторы RiceGuard: Трафик клиентских приложений направляется через локальный шлюз. Использование sockmap обеспечивает передачу данных между процессами со скоростью разделяемой памяти, позволяя криптографическому модулю RiceGuard полностью загружать скоростные каналы связи без перегрузки процессора.
- Предотвращение утечек DNS: Модуль обработки DNS перехватывает локальные запросы к порту 53 и транслирует их через шифрованные протоколы DoH/DoT. Ускорение сокетов устраняет задержки при межсетевой пересылке пакетов между сетевыми пространствами имен.
- Изолированные контейнерные среды: На пограничных шлюзах с высокой плотностью контейнеров sockmap связывает сетевые сокеты напрямую, исключая избыточные виртуальные мосты (veth bridge).
Системные требования и диагностика в Linux
Для работы механизма eBPF sockmap ядро Linux (версии 5.10 или выше) должно быть скомпилировано с поддержкой синтаксического анализатора потоков:
# Проверка конфигурации текущего ядра Linux
zcat /proc/config.gz | grep -E "CONFIG_BPF_STREAM_PARSER|CONFIG_NET_SOCK_MSG"
# Требуемый вывод:
# CONFIG_BPF_STREAM_PARSER = y
# CONFIG_NET_SOCK_MSG = y
После загрузки программ в ядро администраторы могут просматривать зарегистрированные структуры сокетов и отслеживать показатели маршрутизации:
# Просмотр активных таблиц сокетов
sudo bpftool map list type sockhash
# Чтение диагностических сообщений ядра в реальном времени
sudo bpftool prog tracelog
Интеграция программируемого взаимодействия сокетов ядра и компактных туннельных протоколов формирует новый стандарт скорости для защищенных сетевых систем.



