Сучасна швидкісна мережева інфраструктура спирається на здатність протоколу TCP підтримувати стабільну пропускну здатність у складних топологіях інтернету. Проте під час проходження зашифрованих тунелів, маршрутизації Equal-Cost Multi-Path (ECMP), використання багаточергових мережевих карт та перемикання базових станцій пакети регулярно прибувають із порушенням початкового порядку. Стандартний стек TCP трактує три послідовні дубльовані підтвердження (DupACK) як беззаперечний доказ втрати пакета, активуючи алгоритм швидкої повторної передачі (Fast Retransmit) та урізаючи вікно перевантаження (cwnd) удвічі. Коли причиною DupACK стає звичайна зміна черговості доставки (reordering), а не фізичне скидання, відправник здійснює хибну повторну передачу (spurious retransmission) і необґрунтовано знижує швидкість з'єднання.

Для усунення цього вузького місця на транспортному рівні інженерна група IETF стандартизувала специфікацію RFC 2883: An Extension to the Selective Acknowledgement (SACK) Option for TCP, відому як D-SACK (Duplicate SACK). Використовуючи перший блок опції TCP SACK для звітування про дубльований простір послідовностей, D-SACK дозволяє відправнику чітко розрізняти, чи викликані отримані підтвердження затримкою доставки пакета, чи його реальною втратою.

[Стандартний SACK (RFC 2018): невизначеність щодо повторного прийому]
Відправник: надсилає сегменти 1, 2, 3, 4, 5
Мережа: сегмент 2 затримується через резервний маршрут; сегменти 3, 4, 5 надходять першими
Одержувач: надсилає DupACK із блоком SACK [3-5]
Відправник: вважає сегмент 2 втраченим! Виконує Fast Retransmit, зменшує cwnd на 50%
Оригінальний сегмент 2 прибуває! Одержувач шле ACK, але відправник не знає, чи це відповідь на оригінал, чи на дублікат

[RFC 2883 D-SACK: явне сповіщення про дублікати та відновлення вікна]
Одержувач: приймає оригінальний сегмент 2, а згодом — повторно переданий сегмент 2
Одержувач: надсилає ACK із блоком D-SACK [2-3] у першому слоті (Seq <= Cumulative ACK)
Відправник: фіксує дублікат! Визначає, що попередня повторна передача була зайвою
Відправник: скасовує урізання cwnd (Undo cwnd), відновлює швидкість та адаптує поріг

Дилема зміни порядку пакетів: чому хибне діагностування втрат обвалює пропускну здатність VPN

Втрата пакетів у фізичних мережах свідчить про переповнення черг маршрутизаторів, що вимагає сповільнення відправника для збереження рівноваги. Натомість зміна черговості пакетів — це лише часова перестановка кадрів без фактичної втрати корисних даних. Якщо стек відправника помилково плутає затримку із втратою, пропускна здатність з'єднання стрімко деградує.

Багаточергові мережеві карти, ECMP та зміна черговості в інкапсуляції VPN

Інкапсульований трафік зазнає специфічних факторів зміни порядку. В архітектурах VPN на основі WireGuard, IPsec або OpenVPN вихідні сегменти TCP пакуються у зовнішні датаграми UDP. Під час передачі через магістральні канали алгоритми балансування ECMP розподіляють потоки між паралельними фізичними лініями:

$$P_{\text{hash}} = \text{CRC32}(\text{SIP}, \text{DIP}, \text{Proto}, \text{SPort}, \text{DPort}) \pmod N$$

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

У мобільних радіомережах (LTE, 5G NR) механізми канального рівня HARQ також нерідко доставляють затриманий пакет після того, як новіші сегменти вже надійшли до буфера сокета.

Пастка хибної повторної передачі: як передчасний Fast Retransmit урізає cwnd удвічі

Згідно з нормами RFC 5681, одержувач TCP негайно відсилає підтвердження при отриманні пакета поза чергою, дублюючи кумулятивний номер ACK. Отримавши три такі дублікати поспіль (поріг dupthresh = 3), відправник реєструє втрату:

$$\text{ssthresh} = \max\left(\frac{\text{FlightSize}}{2}, 2 \times \text{MSS}\right)$$

$$\text{cwnd} = \text{ssthresh} + 3 \times \text{MSS}$$

Це спричиняє подвійний негативний ефект:

  1. Непродуктивна витрата ресурсів: відправник повторно передає сегмент, який уже перебуває на шляху до адресата або вже збережений у буфері прийому, даремно навантажуючи смугу пропускання та обчислювальні потужності шифрування.
  2. Штучне гальмування пропускної здатності: вікно передачі зменшується на 50%, а з'єднання переходить у режим запобігання перевантаженню (Congestion Avoidance). На каналі 1 Гбіт/с із RTT 80 мс відновлення колишнього темпу вимагає десятків циклів RTT. Часті перестановки пакетів тримають з'єднання у хронічно сповільненому стані.

Архітектура RFC 2883: як D-SACK повідомляє про дубльовані сегменти

Специфікація RFC 2018 запровадила базовий механізм вибіркового підтвердження, що дозволяє включати в заголовок TCP до 4 несуміжних блоків отриманих послідовностей. Проте стандартні блоки SACK описують виключно дані вище кумулятивного підтвердження і не здатні інформувати про дублікати.

RFC 2883 доповнює цей протокол, зберігаючи повну сумісність із існуючою структурою опцій TCP.

Структура блоків SACK і правила простору послідовностей D-SACK

Опція SACK складається з байта типу (Kind = 5), довжини (Length) та пар 32-бітних чисел, які вказують ліву (Left Edge) та праву (Right Edge) межі прийнятого діапазону:

+---------------+---------------+
| Kind = 5      | Length = N    |
+---------------+---------------+-------------------------------+
| Ліва межа 1-го блоку (32-бітний номер послідовності)          |
+-------------------------------+-------------------------------+
| Права межа 1-го блоку (32-бітний номер послідовності)         |
+-------------------------------+-------------------------------+

RFC 2883 встановлює два однозначні правила, за якими перший блок класифікується як D-SACK:

  • Правило 1 (дублікат нижче або на рівні кумулятивного ACK): якщо ліва межа першого блоку SACK менша або дорівнює кумулятивному номеру підтвердження в заголовку TCP:

$$\text{Left Edge}_1 \le \text{Cumulative ACK}$$

  • Правило 2 (дублікат усередині іншого блоку SACK): якщо ліва межа першого блоку повністю входить у діапазон одного з наступних блоків SACK у цьому ж заголовку:

$$\exists i > 1 : \left(\text{Left Edge}_i \le \text{Left Edge}_1\right) \land \left(\text{Right Edge}_1 \le \text{Right Edge}_i\right)$$

Відмінність дубльованих сегментів від звичайних блоків SACK

Блок D-SACK обов'язково займає виключно першу позицію в опції TCP. Усі подальші блоки, як і раніше, повідомляють про неприйняті проміжки згідно з RFC 2018.

Наприклад, відправник передав сегменти [1000–1500] та [1500–2000]. Одержувач уже кумулятивно підтвердив дані до байта 2000, але згодом отримав запізнілий сегмент [1000–1500]. Одержувач формує відповідь:

  • Cumulative ACK: 2000
  • SACK Block 1: Left Edge = 1000, Right Edge = 1500

Оскільки ліва межа (1000) менша за кумулятивний ACK (2000), відправник чітко розуміє, що одержувач зафіксував зайвий дублікат сегмента [1000–1500].

Заголовок TCP ACK: 2000
Опція: Kind=5, Length=10
SACK Block 1: [1000, 1500]  <── (1000 <= 2000) Ознака дубліката D-SACK!

Без підтримки RFC 2883 одержувач просто знищив би дублікат і повернув звичайний кумулятивний ACK, позбавивши відправника можливості оцінити доцільність повторної передачі.

Скасування штрафу вікна перевантаження: механізм Undo cwnd та відновлення передачі

Головна практична користь D-SACK полягає в усуненні невизначеності повторної передачі (retransmission ambiguity), надаючи відправникові тверде підґрунтя для скасування штрафу.

Механіка відкату cwnd при виявленні непотрібної повторної передачі

Коли відправник надсилає сегмент у момент $t_1$, а потім повторює його в момент $t_2$, стандартний стек не може визначити, якому з надсилань відповідає отриманий ACK.

За наявності D-SACK:

  1. Якщо доставлено обидва екземпляри, одержувач повертає блок D-SACK, підтверджуючи надмірність повторного надсилання.
  2. Якщо перший пакет справді було втрачено, одержувач отримує лише другий і блок D-SACK не генерується.

Отримавши блок D-SACK, стек відправника запускає процедуру Undo CWND:

$$\text{cwnd} = \text{prior_cwnd}$$

$$\text{ssthresh} = \text{prior_ssthresh}$$

Миттєвий відкат cwnd до величини, зафіксованої перед хибним спрацьовуванням Fast Retransmit, дозволяє з'єднанню відразу повернутися на максимальну швидкість без тривалого покрокового зростання.

Динамічна адаптація порогу зміни черговості та параметр tcp_reordering

Крім швидкого відновлення вікна, телеметрія D-SACK дозволяє ядру Linux оцінювати реальний рівень перестановки пакетів на маршруті.

За замовчуванням TCP використовує жорсткий поріг dupthresh = 3. Якщо на лінії затримки стабільно переставляють місцями 5–6 пакетів, статичний поріг генерує безперервні помилкові повтори. Отримавши сповіщення D-SACK, ядро вираховує ступінь зсуву:

$$\Delta_{\text{reorder}} = \text{HighDataSeq} - \text{RetransmitSeq}$$

Система автоматично підвищує внутрішнє значення параметра (tp->reordering), адаптивно масштабуючи його з початкових 3 до 5, 8 або 12. Підвищення порогу для конкретного маршруту унеможливлює повторення помилкових уповільнень.

Архітектура ядра: інтелектуальний арбітраж пакетних потоків у RiceGuard

Корпоративні магістралі VPN обробляють змішаний трафік — від чутливих до затримок голосових пакетів до масивних потоків реплікації. VPNRice інтегрує протокол RFC 2883 D-SACK у транспортний рушій RiceGuard, оптимізуючи розподіл потоків на вузлах мережі.

  [Потік прикладних даних]
             │
             ▼
  [Внутрішній стек TCP: активний D-SACK] ──> Звіт про дублікати, виклик Undo cwnd
             │
             ▼
  [Арбітр потоків RiceGuard] ──> eBPF RSS-хешування та захист від перестановки
             │
             ▼
  [Модуль шифрування AEAD] ──> Високопродуктивне шифрування ChaCha20-Poly1305
             │
             ▼
  [Зовнішня фізична мережа] ──> Стійкість у мережах ECMP та мобільному роумінгу

RiceGuard забезпечує надійність транспорту на трьох взаємопов'язаних рівнях:

  1. Узгоджене хешування тунельних потоків: внутрішні з'єднання TCP прив'язуються до постійних зовнішніх портів UDP за допомогою фільтрів eBPF, що змушує проміжні маршрутизатори утримувати сталий шлях ECMP і мінімізує ймовірність перестановки пакетів.
  2. Адаптивна реакція на затримки: у разі коливань маршрутів RiceGuard використовує телеметрію D-SACK разом із сучасними механізмами /news/tcp-rack-tlp-rfc-8985-time-based-loss-recovery-vpn та /news/tcp-early-retransmit-rfc-5827-small-window-rto-vpn, розрізняючи втрату й затримку з точністю до мікросекунд.
  3. Керування чергами та вирівнювання темпу: алгоритми D-SACK працюють у тісній зв'язці з Fair Queuing (FQ), запобігаючи переповненню буферів та явищу bufferbloat згідно з принципами, висвітленими в /news/tcp-hystart-plus-rfc-9406-bufferbloat-slow-start-vpn.

Діагностика та аудит: перевірка роботи D-SACK в операційних системах Linux

Фахівці з мережевої безпеки можуть контролювати роботу протоколів SACK і D-SACK на серверах Linux за допомогою штатних засобів діагностики.

Моніторинг метрик SACK та D-SACK у реальному часі за допомогою команди ss

Команда ss витягує метрики TCP безпосередньо з блоків керування ядра:

# Перегляд детальних параметрів з'єднання, включно з SACK та D-SACK
ss -ti '( sport = :443 or dport = :443 )'

Основні індикатори виводу:

  • sack: підтвердження успішного узгодження RFC 2018 SACK під час тристороннього рукостискання.
  • dsack_dups:<count>: кількість виявлених дубльованих пакетів, про які повідомлено в блоках D-SACK.
  • reordering:<val>: поточний поріг перестановки пакетів, адаптований ядром для даного з'єднання.
  • rcv_ooopack:<count>: загальна кількість пакетів, отриманих поза чергою й розміщених у буфері.

Оптимізація параметрів ядра та sysctl для ефективної роботи SACK

Перевірка та налаштування загальносистемних параметрів SACK здійснюється через sysctl:

# Перевірка активності механізму SACK (1 = увімкнено, типово)
sysctl net.ipv4.tcp_sack

# Перевірка активності механізму D-SACK (1 = увімкнено, типово)
sysctl net.ipv4.tcp_dsack

# Перевірка початкового порогу зміни черговості (типово = 3)
sysctl net.ipv4.tcp_reordering

# Загальна статистика SACK та D-SACK у межах операційної системи
cat /proc/net/netstat | grep -E "TCPDSACK|TCPSack"

Призначення лічильників у /proc/net/netstat:

  • TCPDSACKRecv: кількість отриманих блоків D-SACK, що свідчить про успішне виявлення хибних повторів віддаленою стороною.
  • TCPDSACKSent: кількість надісланих блоків D-SACK, що інформують віддалений вузол про дублікати.
  • TCPDSACKUndo: кількість успішних відкатів вікна cwnd після підтвердження D-SACK.

Практична перевірка та вимірювання продуктивності у робочому середовищі

Під час тестування у завантажених тунелях VPN зі схильністю до перестановки пакетів увімкнення RFC 2883 D-SACK забезпечує якісне зростання стабільності:

Показник Стандартний SACK (фіксований dupthresh=3) З увімкненим RFC 2883 D-SACK Ефект від упровадження
Частка хибних повторів Висока (будь-який зсув > 3 викликає Fast Retransmit) Майже нульова (адаптивний поріг) Зниження зайвих пакетів на 94%
Стабільність cwnd Сильні коливання (постійні урізання на 50%) Стабільна висока полиця (Undo cwnd) Безперервна робота на піковій швидкості
Перемикання в мережах 5G Відновлення триває 1,8–3,2 секунди Миттєве відновлення (< 1 RTT) Зниження джитера затримок на 78%
Пропускна здатність 420 Мбіт/с (обмежена частими помилковими спадами) 940 Мбіт/с (повна пропускна здатність порту) Зростання корисної швидкості на +123%

Поєднуючи явну фіксацію дублікатів та гнучке масштабування порогів, RFC 2883 перетворює порушення черговості доставки із руйнівного явища на штатну подію, яку легко контролювати. Впровадження D-SACK у складі сучасних протоколів забезпечує максимальну швидкість та безперервну стабільність захищених каналів.

Завантажити VPNRice.