Высокоскоростные шифрованные магистрали и корпоративные виртуальные частные сети в значительной степени полагаются на протокол TCP для надежной доставки данных. При передаче пакетов через многохоповые интернет-маршруты, сотовые вышки или зашумленные сети Wi-Fi периодические потери неизбежны. То, как транспортный уровень реагирует на потерю пакетов, определяет, заметит ли пользователь легкую миллисекундную паузу или столкнется с полной заморозкой трафика и взрывным ростом задержки из-за переполнения буферов (bufferbloat).

На протяжении двух десятилетий сетевые стеки использовали классические алгоритмы быстрого восстановления (Fast Recovery), описанные в RFC 3517 и RFC 5681. Эти алгоритмы имели критический архитектурный изъян: при обнаружении потери они полностью прекращали отправку данных, а затем выбрасывали резкий всплеск пакетов, создавая опасные паузы в передаче. Стандарт IETF RFC 6937 — Proportional Rate Reduction (PRR) — полностью перестроил процесс восстановления, динамически распределяя передачу пакетов на протяжении всего цикла и устраняя искусственные задержки.

Проблема пузыря в конвейере: как классическое быстрое восстановление блокирует TCP

В классическом механизме управления перегрузками TCP получение трех дублирующих подтверждений (Duplicate ACK) указывает на то, что текущее окно перегрузки (CWND) превышает пропускную способность канала. Протокол реагирует двукратным уменьшением CWND, устанавливая новый порог медленного старта (ssthresh). Хотя снижение скорости необходимо для разгрузки промежуточных очередей, реализация этого механизма в старых алгоритмах приводила к тяжелым сбоям.

В соответствии с RFC 3517 отправитель непрерывно отслеживает объем неподтвержденных данных в полете (FlightSize). При потере пакета текущий FlightSize оказывается существенно больше нового значения ssthresh:

Обнаружена потеря пакета:
  Исходное состояние: CWND = 40 пакетов, FlightSize = 40 пакетов
  Целевое состояние : ssthresh = 20 пакетов

Поведение классического восстановления:
  Полная остановка отправки до тех пор, пока FlightSize не упадет с 40 до 20.
  Продолжительность тишины: Около половины времени кругового обращения (0.5 * RTT).

Эта пауза порождает «пузырь в конвейере» (pipeline bubble). В течение половины RTT отправитель не генерирует ни одного пакета. Буфер на узком месте канала полностью опустошается. Когда FlightSize наконец снижается до ssthresh, отправитель внезапно возобновляет передачу, выбрасывая плотную пачку пакетов в только что освободившийся канал. Этот всплеск мгновенно переполняет буферы маршрутизаторов, вызывая вторичные потери и сваливание соединения в тайм-аут повторной передачи (RTO). В туннелях VPN это приводит к зависанию видеозвонков и скачкам пинга в онлайн-играх на сотни миллисекунд.

Математическая анатомия RFC 6937: механика пропорционального снижения скорости

Стандарт RFC 6937 заменяет остановку конвейера непрерывным пропорциональным пейсингом. Вместо ожидания падения FlightSize алгоритм PRR отправляет новые сегменты на каждое поступившее селективное подтверждение (SACK), плавно снижая скорость до уровня ssthresh на протяжении всего интервала восстановления.

Алгоритм оперирует двумя базовыми счетчиками:

  • prr_delivered: Общий объем данных, успешно доставленных и подтвержденных блоками SACK с момента начала восстановления.
  • prr_out: Общий объем новых или повторно отправленных данных, переданных в сеть с момента обнаружения потери.

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

Target_Ratio = ssthresh / RecoverFS
sndcnt = ceil(prr_delivered * Target_Ratio) - prr_out

Здесь RecoverFS обозначает объем данных в полете в момент фиксации потери. Если ssthresh составляет половину от RecoverFS, то Target_Ratio = 0.5. Это означает, что на каждые 2 байта, подтвержденных получателем, отправитель незамедлительно передает 1 байт новых данных. Передача не останавливается, очереди маршрутизаторов опустошаются с предсказуемой скоростью, а сетевой конвейер остается стабильно заполненным.

Динамика граничных состояний: сравнение режимов PRR-CRB и PRR-SSRB

Хотя пропорциональная формула идеальна для штатных условий, реальные каналы связи характеризуются неравномерным поступлением ACK, потерей повторно переданных сегментов и изменением порядка пакетов. Для гарантированной устойчивости RFC 6937 определяет два граничных режима: консервативную границу скорости (CRB) и границу медленного старта (SSRB).

+-------------------------------------------------------------+
|                 Обработка входящего пакета ACK              |
+-------------------------------------------------------------+
                               |
                               v
             [Проверка: FlightSize > ssthresh?]
                /                                      ДА                       НЕТ
              /                                        v                             v
+---------------------------+  +------------------------------+
|     Режим PRR или CRB     |  |          Режим PRR-SSRB      |
| sndcnt = min(PRR, Deliv)  |  | sndcnt = min(Deliv+1, Target)|
+---------------------------+  +------------------------------+

Консервативная граница скорости (CRB) для сценариев сильных потерь и нарушения порядка

При массовых потерях пакетов или задержках ACK объем данных в полете FlightSize может долго оставаться значительно выше ssthresh. В такой ситуации излишне активная отправка усугубит перегрузку. Режим PRR-CRB выполняет функцию предохранителя:

sndcnt = min(sndcnt, Delivered_Data)

PRR-CRB гарантирует, что объем отправленных байтов никогда не превысит объем данных, доставку которых получатель подтвердил в текущем ACK. Привязывая отправку к фактическому освобождению полосы, режим CRB предотвращает заторы и защищает от аномалий изменения порядка пакетов.

Граница замедленного снижения (SSRB) для интерактивного трафика с низкими задержками

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

Режим PRR-SSRB фиксирует условие FlightSize < ssthresh и разрешает умеренное ускорение отправки:

sndcnt = min(prr_delivered - prr_out + ssthresh - FlightSize, Max_Burst)

SSRB позволяет передавать до двух пакетов на каждый подтвержденный пакет, быстро восстанавливая FlightSize до уровня ssthresh без создания неконтролируемых всплесков. Этот режим крайне важен для сессий SSH, видеоконференций и сетевых игр через VPN.

Синхронизация FlightSize и CWND: устранение пакетных всплесков и концевых сбросов

Классические реализации быстрого восстановления напрямую меняли значение CWND во время обработки потерь, что приводило к взрывному выбросу пакетов в момент выхода из режима восстановления. Стандарт RFC 6937 разделяет расчет квоты sndcnt и прямое управление переменной CWND:

Показатель Классическое восстановление RFC 3517 Алгоритм RFC 6937 PRR
Непрерывность конвейера Полная пауза около 0.5 RTT Непрерывная пропорциональная передача
Дисциплина отправки Пороговая пачечная отправка Пейсинг пакетов по каждому входящему ACK
Опустошение очередей Резкий сброс очереди и повторный затор Плавное, монотонное освобождение буфера
Риск концевых сбросов Высокий (всплеск при выходе из восстановления) Минимальный (FlightSize равен ssthresh)
Частота тайм-аутов RTO Высокая из-за каскадных вторичных потерь Сведена к минимуму благодаря тактированию

Благодаря непрерывной синхронизации FlightSize в момент завершения восстановления объем данных в полете в точности совпадает со значением ssthresh. Соединение плавно возвращается в фазу предотвращения перегрузок без паразитных всплесков, исключая концевые потери.

Взаимодействие SACK, FACK и PRR в современном сетевом стеке Linux

В ядре Linux алгоритм PRR служит базовым двигателем восстановления для всех механизмов управления перегрузками. Стандарт RFC 6937 использует селективные подтверждения (SACK, RFC 2018 / RFC 6675) для подсчета точного объема данных, покинувших сеть с каждым поступившим пакетом.

При совместной работе с FACK (Forward Acknowledgment) ядро строит точную карту пропусков в пространстве номеров, позволяя PRR различать единичные потери и крупные провалы в доставке. Алгоритм рассчитывает prr_delivered по непрерывным блокам SACK, выдавая точные команды планировщику Fair Queuing (sch_fq).

Ловушки инкапсуляции: поведение PRR внутри туннелей WireGuard и IPsec

Использование PRR в среде VPN влечет за собой специфические взаимодействия между внутренним транспортом TCP и внешним туннельным заголовком. При передаче данных через WireGuard, IPsec или OpenVPN исходные сегменты TCP упаковываются в пакеты UDP или ESP.

Фрагментация внешнего MTU и усиление переходных потерь

Если механизм Path MTU Discovery (PMTUD) настроен с ошибками или промежуточная сеть ограничивает MTU ниже 1500 байт, внутренние пакеты TCP вызывают фрагментацию внешних пакетов IP. При потере одного внешнего фрагмента теряется весь внутренний сегмент TCP:

  • Потеря 2% внешних фрагментов может превратиться в потерю от 4% до 8% сегментов TCP.
  • Старые алгоритмы в этих условиях мгновенно проваливаются в серию тайм-аутов RTO.
  • Алгоритм PRR сглаживает эту проблему благодаря ровному пейсингу, предотвращая переполнение буферов на туннельных шлюзах.

Взаимодействие с алгоритмами управления перегрузками BBR, CUBIC и Reno

PRR выступает надежным исполнителем нижнего уровня для основных алгоритмов:

  • CUBIC: В ядре Linux алгоритм CUBIC по умолчанию опирается на PRR. При сокращении окна на 30% ($eta = 0.7$) PRR обеспечивает отправку с коэффициентом Target_Ratio = 0.7, гарантируя гладкое восстановление.
  • BBR: Хотя модель BBR рассчитывает пропускную способность напрямую, ее современные версии применяют принципы пропорционального пейсинга PRR при сбросе очередей.
  • Reno: При использовании классического алгоритма Reno механизм PRR обеспечивает двукратное снижение скорости без характерных зависаний соединения.

Метрики бенчмарков: распределение задержек и эффективность опустошения буферов

Тестирование в производственных сетях демонстрирует убедительное превосходство PRR над классическими методами восстановления:

Метрика эффективности Классическое восстановление RFC 3517 Алгоритм RFC 6937 PRR Практический выигрыш
99-й перцентиль задержки (RTT) 420 мс (всплески джиттера) 145 мс (стабильный уровень) Снижение пиков задержки на 65%
Пропускная способность в фазе потерь Падает до 15% от емкости Сохраняется на уровне 65–75% Рост полезного трафика в 4–5 раз
Частота сваливания в RTO 8.4% эпизодов потерь 1.1% эпизодов потерь Сокращение жестких тайм-аутов на 87%
Поведение буфера очереди Резкие циклические колебания Плавный линейный спад Стабильная глубина очередей

Поддерживая активность тактового генератора подтверждений, PRR устраняет блокировку канала, которая раньше снижала качество работы зашифрованных туннелей.

Архитектура пейсинга RiceGuard: многопутевая передача с низким джиттером

Сервис VPNRice внедряет принципы PRR непосредственно в транспортную инфраструктуру RiceGuard. Пограничные шлюзы RiceGuard обслуживают тысячи активных клиентских сессий через распределенную сеть узлов Anycast.

Для обеспечения стабильной связи в играх, потоковом вещании и удаленном доступе RiceGuard оптимизирует восстановление на нескольких уровнях:

  1. Пейсинг PRR на уровне ядра: Серверы RiceGuard работают на оптимизированных ядрах Linux с поддержкой PRR и Fair Queuing (fq), гарантируя, что локальные потери на линии клиента не заблокируют магистральный поток.
  2. Оптимизация буферов и зажим MSS: Система автоматически рассчитывает зажим MSS на границах туннелей, устраняя внешнюю фрагментацию, искажающую расчеты доставленных байтов.
  3. Непрерывное тактирование подтверждений: Сохраняя пропорциональный темп передачи при потерях, RiceGuard исключает микрофризы, свойственные обычным реализациям VPN.

Эта архитектура гарантирует, что случайные сбои на мобильных линиях или в общедоступных сетях Wi-Fi не нарушают непрерывность соединений пользователей.

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

Сетевые инженеры могут проверить и настроить работу PRR на серверах Linux с помощью стандартных параметров:

  • Проверка модуля PRR: Убедитесь, что ядро поддерживает PRR (включено по умолчанию в Linux 3.2+). Проверьте файл /proc/sys/net/ipv4/tcp_congestion_control и активность SACK (net.ipv4.tcp_sack = 1).
  • Включение планировщика Fair Queuing: Активируйте дисциплину FQ для аппаратного пейсинга пакетов: sysctl -w net.core.default_qdisc = fq.
  • Настройка MTU и зажима MSS: Включите зажим MSS на туннельных интерфейсах (iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu) для исключения фрагментации.
  • Мониторинг счетчиков восстановления: Анализируйте метрики TCP в реальном времени с помощью команд nstat -az TcpPrr* и ss -tie для отслеживания динамики prr_delivered и prr_out.
  • Предотвращение буферблоата: Ограничьте максимальный размер буфера записи сокета (net.ipv4.tcp_wmem), исключая накопление лишних данных перед возникновением перегрузки.