В критически важной инфраструктуре виртуальных частных сетей непрерывная доступность узлов является ключевым требованием. Корпоративные шлюзы, резервные прокси WireGuard поверх TCP и граничные ретрансляторы одновременно обрабатывают десятки тысяч активных сессий TCP. Когда системные разработчики выпускают обновления безопасности или устраняют утечки памяти, инженеры сталкиваются с дилеммой: стандартный перезапуск процесса демона немедленно обрывает пользовательские туннели.

При штатном жизненном цикле сокетов Linux вызов системного вызова close() или завершение процесса-владельца заставляет ядро отправить удаленному клиенту пакет FIN или RST. Для подключенных пользователей такой обрыв означает прерывание передачи данных, зависание видеозвонков и лавину повторных попыток авторизации. Одновременное обращение тысяч клиентов порождает эффект громоподобного стада (thundering herd), перегружая аутентификационные серверы и процессорные мощности.

Традиционный подход заключается в плавном дренаже соединений: новые клиенты направляются к تازه запущенному процессу, пока старый продолжает работу до завершения существующих сессий. Однако VPN-сессии отличаются высокой продолжительностью и редко закрываются спонтанно. В реальных условиях дренаж может затягиваться на часы или дни, препятствуя своевременной установке критических исправлений. Оптимальным техническим решением является прямая миграция открытых сокетов между процессами.


Проблема сброса соединений: Обновление демонов и разрыв сессий VPN

Уязвимость сетевых соединений при обновлении программного обеспечения обусловлена состоянием, которое сохраняет протокол TCP. В отличие от маршрутизации UDP без сохранения состояния, каждый работающий сокет TCP хранит обширный комплекс параметров в сетевом стеке ядра:

  1. Счетчики скользящего окна: Номера последовательностей (snd_una, snd_nxt, rcv_nxt), регулирующие корректную доставку байтового потока.
  2. Динамические оценки: Сглаженное время оборота пакета (srtt), вариация сетевой задержки (rttvar) и параметры окна перегрузки (snd_cwnd, snd_ssthresh).
  3. Буферы передаваемых данных: Системные структуры sk_buff, удерживающие отправленные пакеты до подтверждения и принятые байты до вызова функции чтения.
Последствия стандартного перезапуска процессов для клиентов VPN:

[ Приложение клиента ] ──► [ Активный туннель ] ──► [ Старый демон (PID 101) ]
                                                            │
                                                     (Команда остановки)
                                                            │
                                                            ▼
[ Приложение клиента ] ◄── [ Получен TCP RST ] ◄── [ Ядро освобождает сокет ]
       │
 (Обрыв связи)
       │
       ▼
 [ Лавина повторных подключений: Перегрузка нового демона рукопожатиями ]

Если шлюз останавливает устаревший демон без переноса состояния, сетевой стек ядра незамедлительно разрушает структуры сокета. Отправка пакетов Reset (RST) удаленным узлам разрушает туннель шифрования. Для сотрудников на удаленной работе и серверных систем это приводит к резкому обрыву сетевого обмена.


Архитектура Linux TCP_REPAIR: Механика заморозки сетевых сокетов

Для решения задач создания контрольных точек и миграции процессов в контейнерах (проект CRIU) ядро Linux предоставляет параметр сокета TCP_REPAIR на уровне SOL_TCP. Этот механизм позволяет привилегированному процессу перевести активный сокет TCP в режим управляемого анабиоза.

В обычном режиме операционная система запрещает пространству пользователя произвольно менять порядковые номера или таймеры, предотвращая логические ошибки и атаки. При включении TCP_REPAIR ядро временно приостанавливает автоматический контроль протокола, предоставляя приложению возможность считывать и корректировать внутренние переменные.

Жизненный цикл сокета TCP при миграции через режим восстановления:

[ Нормальная работа ] ──► setsockopt(fd, SOL_TCP, TCP_REPAIR, TCP_REPAIR_ON)
                                  │
                                  ▼
                         [ РЕЖИМ ВОССТАНОВЛЕНИЯ ]
                                  │
         ┌────────────────────────┴────────────────────────┐
         ▼                                                 ▼
Чтение / запись номеров серий             Остановка таймеров (RTO, Keepalive)
Выгрузка очередей приема и отправки       Подавление автоматической отправки RST
         │                                                 │
         └────────────────────────┬────────────────────────┘
                                  │
                                  ▼
[ Новый демон принимает FD ] ──► setsockopt(fd, SOL_TCP, TCP_REPAIR, TCP_REPAIR_OFF)
                                  │
                                  ▼
                       [ Возобновление работы ]

1. Остановка системных таймеров и перевод в режим восстановления

Для активации режима восстановления процессу требуются права сетевого администратора (CAP_NET_ADMIN). Управляющая программа переводит сокет в приостановленное состояние через вызов setsockopt():

int repair_mode = 1;
if (setsockopt(sock_fd, SOL_TCP, TCP_REPAIR, &repair_mode, sizeof(repair_mode)) < 0) {
    perror("Ошибка включения режима TCP_REPAIR");
    return -1;
}

После этого ядро останавливает все внутренние таймеры сокета. Таймер ожидания повторной передачи (RTO) прекращает отсчет, зонды поддержания связи (Keepalive) не отправляются, а таймеры нулевого окна замораживаются. Поступающие от клиентов пакеты задерживаются во входных очередях без отправки сбросов RST.

2. Фиксация и восстановление очередей и номеров последовательностей TCP

В режиме восстановления системный координатор извлекает значения номеров последовательности для очередей передачи и приема с помощью опций TCP_REPAIR_QUEUE и TCP_QUEUE_SEQ:

uint32_t current_seq;
socklen_t seq_len = sizeof(current_seq);
int queue_target = TCP_SEND_QUEUE;

/* Выбор очереди отправки для чтения номера последовательности */
setsockopt(sock_fd, SOL_TCP, TCP_REPAIR_QUEUE, &queue_target, sizeof(queue_target));
getsockopt(sock_fd, SOL_TCP, TCP_QUEUE_SEQ, &current_seq, &seq_len);

Этот программный интерфейс выполняет две критические функции:

  • Фиксация состояния: Точная регистрация байтов, подтвержденных удаленной стороной.
  • Внедрение параметров: При создании сокета в изолированном пространстве имен принимающий демон может записать корректные счетчики до возобновления трафика.

Механизмы передачи сокетов: SCM_RIGHTS против контрольных точек CRIU

Передача открытого сетевого сокета от завершающегося демона к تازه запущенному процессу реализуется с использованием двух проверенных моделей архитектуры.

Схема передачи сокета между процессами на одном хосте:

[ Старый демон (Версия v1) ]
       │  1. setsockopt(TCP_REPAIR, TCP_REPAIR_ON)
       │  2. Сохранение криптографического контекста и серийных номеров
       │  3. Отправка дескриптора через sendmsg() с SCM_RIGHTS
       ▼
 [ Локальный канал IPC UNIX Domain Socket ]
       │
       ▼  4. Прием дескриптора сокета через recvmsg()
[ Новый демон (Версия v2) ]
       │  5. Проверка согласованности счетчиков и регистрация в epoll
       │  6. setsockopt(TCP_REPAIR, TCP_REPAIR_OFF)
       ▼
[ Бесперебойное продолжение передачи сетевого потока ]

1. Передача дескрипторов файлов через локальные сокеты UNIX

При обновлении службы в пределах одного сервера отправка дескриптора через сокет домена UNIX с применением управляющего сообщения SCM_RIGHTS обеспечивает максимальную скорость и минимальные накладные расходы. Вместо ресурсоемкой сериализации структуры ядро напрямую копирует файловую ссылку между таблицами дескрипторов процессов.

Старый демон формирует управляющее сообщение struct msghdr с флагом SCM_RIGHTS. Новый процесс считывает его через recvmsg() и мгновенно получает рабочий дескриптор, указывающий на существующую структуру сокета в ядре без копирования буферов памяти.

2. Конвейер восстановления состояния и выход из режима починки

После получения дескриптора новый демон привязывает его к криптографическому состоянию сессии, подключает к циклу обработки событий (epoll или io_uring) и отключает режим восстановления:

int repair_disable = 0;
if (setsockopt(imported_fd, SOL_TCP, TCP_REPAIR, &repair_disable, sizeof(repair_disable)) < 0) {
    perror("Ошибка выключения режима TCP_REPAIR");
    return -1;
}

При отключении опции ядро рассчитывает новые интервалы таймеров исходя из текущего времени и высвобождает пакетные очереди. Накопленные пакеты немедленно отправляются в физический интерфейс, а входящий трафик поступает новому процессу без потери байтов.


Практический регламент: Реализация миграции сокетов без потерь

Надежный процесс обновления демонов на узлах распределения трафика включает четыре последовательных шага:

  1. Запуск и проверка: Инициализация нового процесса рядом со старым, считывание конфигурации и криптографических ключей до вмешательства в активные сессии.
  2. Контролируемая заморозка: Перевод сокетов клиентов в режим TCP_REPAIR_ON калиброванными группами для предотвращения скачков задержки.
  3. Межпроцессная передача: Отправка дескрипторов и криптографических состояний через локальный сокет UNIX.
  4. Разморозка и завершение: Вызов TCP_REPAIR_OFF в новом процессе, активация сетевых очередей и корректная остановка старого демона.
Временная шкала бесперебойной передачи шлюза:

Старый процесс          Состояние TCP в ядре            Новый процесс
      │                            │                           │
      ├─ TCP_REPAIR_ON ──────────► │ (Таймеры остановлены)     │
      │                            │                           │
      ├─ SCM_RIGHTS fd ──────────────────────────────────────► │ (Прием дескриптора)
      │                            │                           │
      │                            │ ◄── TCP_REPAIR_OFF ───────┤ (Таймеры запущены)
      │                            │                           │
      ├─ (Штатный выход)           │ (Поток на полной скорости)├─ (Обработка пакетов)

Компромиссы высокой доступности и нейтрализация сбоев

Применение TCP_REPAIR требует внимательного учета нескольких технических аспектов:

  • Привилегии безопасности: Управление опцией требует прав CAP_NET_ADMIN. Демоны должны сбрасывать эти привилегии сразу после завершения фазы миграции.
  • Ограничения таймеров клиента: Пока таймеры сервера остановлены, клиентские таймеры повторной передачи продолжают идти. Весь цикл передачи дескриптора должен укладываться в 200–500 миллисекунд, чтобы не спровоцировать тайм-аут RTO на стороне клиента.
  • Синхронизация прикладного криптоконтекста: Сохранение байтового потока TCP не имеет смысла без переноса ключей шифрования. Сессии WireGuard поверх TCP и параметры сессий TLS должны передаваться строго синхронно с дескриптором.

Архитектура RiceGuard: Бесшовные граничные шлюзы и стабильность сессий

VPNRice исключает разрывы пользовательских сессий на граничных узлах за счет глубокой интеграции этих механизмов в сетевое ядро RiceGuard. Вместо принудительного разрыва соединений во время планового обновления ПО, RiceGuard выполняет автоматическую миграцию сокетов на уровне ядра Linux.

Комбинируя маршрутизацию сокетов eBPF с возможностями TCP_REPAIR, узлы RiceGuard перемещают активные туннели между изолированными потоками без потери пакетов и сбросов TCP. Соединения пользователей сохраняют непрерывную пропускную способность, обеспечивая высокую надежность и устойчивую цифровую конфиденциальность.