Qeli 0.8.1 · PACKET_MUX_V1

Recordizer: batching и границы записей

Что PACKET_MUX_V1 делает до шифрования, как согласуется после AUTH и как безопасно настраивать batching, размеры записей, fragmentation и reassembly.

Назначение

Recordizer меняет связь «IP-пакет ↔ запись»

В legacy data plane один внутренний IPv4/IPv6-пакет обычно превращался в одну зашифрованную запись. Это не раскрывает содержимое, но создаёт устойчивую связь размеров и моментов отправки. Согласуемый PACKET_MUX_V1 работает до шифрования:

  • несколько внутренних пакетов могут попасть в одну зашифрованную запись;
  • один внутренний пакет может пересечь границы нескольких записей;
  • целевой размер записи и deadline batching варьируются в заданных пределах;
  • mux-заголовки находятся внутри AEAD и не видны на проводе.
Это не транспорт. Recordizer одинаково работает поверх TCP и UDP carrier и не превращает fake-TLS в TLS, QUIC-shaped режим — в настоящий QUIC/H3, а endpoint — в неизвестный наблюдателю адрес.
Порядок слоёв

Где именно он включён

data plane
внутренний IPv4/IPv6-пакет
  → PACKET_MUX recordizer
  → padding / traffic normalization
  → шифрование PacketCodec
  → carrier (plain / fake-TLS / Reality-H2 / obfs-WS / UDP QUIC shape)
  → carrier/path fragmentation при необходимости

Целевой record size — порог сборки, а не обещание допаддить каждую запись до этого размера. Deadline может отправить меньшую запись. Padding и traffic normalization выполняются после recordizer и могут дополнительно изменить внешний размер.

Согласование

Политикой владеет сервер

Параметры obf.recordizer.* не передаются пользователю и не добавляются в qeli://. Сервер отправляет политику внутри аутентифицированного AUTH-контракта; изменение применяется после переподключения.

ПолитикаПоведениеRollout
offLegacy mapping: один внутренний пакет примерно соответствует одной записи.Откат или диагностика.
preferВключает PACKET_MUX_V1 только если клиент объявил capability; старый клиент остаётся на legacy data plane.Рекомендуемый первый этап и дефолт релизных шаблонов.
requiredНесовместимый клиент отклоняется до выдачи адреса.Только после обновления всего парка.
Серверный профиль

Рекомендуемые значения 0.8.1

server.conf · [profile:main]
obf.recordizer.policy = prefer
obf.recordizer.batch.delay_min_ms = 2
obf.recordizer.batch.delay_max_ms = 8
obf.recordizer.batch.max_packets = 16
obf.recordizer.batch.max_queue_bytes = 262144
obf.recordizer.record.max_payload_bytes = 0
obf.recordizer.record.small_min_ratio = 0.25
obf.recordizer.record.small_max_ratio = 0.875
obf.recordizer.record.full_probability = 0.72
obf.recordizer.fragment.enabled = true
obf.recordizer.fragment.reassembly_timeout_ms = 3000
obf.recordizer.fragment.max_inflight_packets = 64
obf.recordizer.fragment.max_reassembly_bytes = 4194304
obf.recordizer.fragment.max_fragments_per_packet = 64
Параметры

Batching, размер и reassembly

ГруппаКлючиСмысл и ограничения
Deadlinedelay_min_ms
delay_max_ms
Случайное окно flush, запускаемое первым пакетом очереди. 0/0 отправляет без ожидания; max не меньше min.
Очередьmax_packets
max_queue_bytes
Максимум mux-фреймов и жёсткий предел памяти на направление. Queue bytes: 64…4194304.
Payload ceilingmax_payload_bytes0 автоматически выбирает безопасный plaintext по активному carrier/path; явное значение — 64…MAX_TUNNEL_MTU и всё равно ограничивается budget.
Морфологияsmall_min_ratio
small_max_ratio
full_probability
Неполная цель выбирается в диапазоне 0 < min ≤ max ≤ 1; probability находится в 0…1 и определяет шанс полной цели.
Fragmentfragment.enabledРазрешает одному внутреннему пакету пересекать записи. Не отключайте, если не гарантировано помещение пакета целиком.
Reassemblyreassembly_timeout_ms
max_inflight_packets
max_reassembly_bytes
max_fragments_per_packet
Ограничивают время, количество packet ID, общую память и число частей одного пакета; защищают каждое направление от исчерпания ресурсов.
Настройка под сеть

Три безопасные отправные точки

СценарийИзменения от дефолтаКомпромисс
Чувствительный к задержкеdelay=0…2 ms, max_packets=4Меньше batching latency, слабее изменение группировки.
Потеряющий UDPdelay=0…2 ms, max_packets=2, full_probability=0.85Меньше amplification потери одной крупной записи.
Экспериментальная морфологияdelay=3…12 ms, full_probability=0.55Больше вариативность, но выше задержка/overhead; сначала проверяйте на стенде.
Сочетания

Что происходит с другими функциями

  • Работает со всеми TCP/UDP режимами, IPv4/IPv6/dual-stack и TUN/TAP. На TAP Ethernet header снимается и восстанавливается на краю; произвольные L2-фреймы не переносятся.
  • Reality/H2 batching и recordizer delay могут складываться. Изменяйте одно измерение за раз.
  • На UDP слишком крупный batch усиливает эффект потери datagram — используйте короткое окно и малый max_packets.
  • AWG junk работает до handshake; recordizer — только после AUTH. QUIC shape по-прежнему не является настоящим QUIC.
  • obf.fragmentation.* дробит выбранные handshake/carrier writes и не заменяет obf.recordizer.fragment.*.
  • UDP DATA_FRAG/PMTU — внешняя фрагментация уже зашифрованной записи; recordizer fragmentation делит внутренний пакет до PacketCodec.
Data plane · 0.8.1

Системные вызовы пачками — не новый формат записи

После формирования Recordizer-датаграмм Linux и Android отправляют до 32 готовых элементов одним sendmmsg; уже ожидающие входящие датаграммы читаются через recvmmsg. Дополнительного coalescing delay нет, wire format и параметры obf.recordizer.* не меняются.

Same-window A/BДоПослеИзменение
Upload320,8 Мбит/с694,7 Мбит/с+116,5%
Download359,8 Мбит/с697,5 Мбит/с+93,9%
Граница результата: это чередующийся A/B на конкретном двухъядерном лабораторном стенде после внедрения receive batching. Он подтверждает снятие измеренного syscall bottleneck, но не заменяет полный benchmark 0.8.0 на странице «Технологии» и не гарантирует ту же скорость на другом устройстве.
Rollout

Проверка и откат

  1. Обновите сервер, задайте policy=prefer, выполните qeli check-config и перезапустите профиль.
  2. Переподключите клиент и найдите в журнале маркер Packet recordizer: PACKET_MUX_V1 active.
  3. Проверьте двусторонний IPv4/IPv6, DNS, reconnect/roaming, PMTU и длинную нагрузку на каждом используемом transport.
  4. После обновления всего парка при необходимости включите required.
Для отката задайте obf.recordizer.policy = off, перезапустите профиль/службу и переподключите сессии. Изменение не применяется к уже работающей сессии без reconnect.
Первоисточники

Полное руководство recordizer 0.8.1