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

За звичайного життєвого циклу сокетів Linux виклик системної функції close() або завершення процесу змушує ядро надіслати клієнту пакет FIN або RST. Для підключених користувачів таке раптове переривання означає збій передавання файлів, розрив відеоконференцій та хвилю повторних спроб авторизації. Одночасне звернення тисяч клієнтів створює ефект навали (thundering herd), перевантажуючи сервери автентифікації та процесорні ядра шлюзу.

Традиційне розв'язання полягає у поступовому виведенні з'єднань: нові сесії спрямовуються до свіжого процесу, а старий працює до природного закриття існуючих тунелів. Проте робочі тунелі VPN мають тривалий життєвий цикл і вкрай рідко закриваються самі. На практиці таке очікування може тривати годинами або днями, затримуючи розгортання критичних виправлень. Справжнім технічним вирішенням проблеми є пряме передавання діючих сокетів між процесами.


Проблема скидання з'єднань: Оновлення демонів та переривання сесій VPN

Чутливість транспортних каналів під час оновлення програмного забезпечення зумовлена збереженням стану в протоколі TCP. На відміну від маршрутизації UDP без фіксації стану, кожен діючий сокет TCP утримує розгалужену систему параметрів усередині мережевого стека ядра Linux:

  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 є недостатнім, якщо втрачено стан вищих рівнів. Параметри сесій TLS та ключі WireGuard поверх TCP слід передавати синхронно разом із дескриптором.

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

VPNRice усуває розриви сесій користувачів на граничних серверах завдяки глибокій інтеграції описаних рішень у мережеве ядро RiceGuard. Замість примусового перепідключення під час технічного обслуговування, вузли RiceGuard здійснюють автоматичну міграцію сокетів на рівні ядра операційної системи.

Завдяки поєднанню маршрутизації eBPF із технологією TCP_REPAIR, сервери RiceGuard плавно передають активні з'єднання між незалежними потоками без втрати пакетів або сигналів скидання TCP. Сесії користувачів працюють стабільно, забезпечуючи високу надійність зв'язку та сталий захист цифрової конфіденційності.