Document compact attempt wire layout and epoch reuse protection

This commit is contained in:
evgeny committed 2026-10-23 19:25:20 +02:00
1 parent 9a9517deab
commit 77a737fd71
2 files changed
+21 -12

No files matched your search

+18 -11
View File
@@ -20,7 +20,7 @@ HELLO/CHALLENGE/CONFIRM и keepalive передаются своим незав
Кумулятивный till можно отправить по любому рабочему пути. ACK-only не увеличивает нагрузку DATA-shaper;
физическая очередь канала остаётся общей, её накопление ограничивают DATA-cwnd/pacing BBR.
4. Выделить ETCP_DGRAM до удаления DATA из очереди и изменения счётчиков.
Выделить отдельную физическую попытку с новым attempt_id и зарезервировать окно выбранного линка.
Выделить физическую попытку с новым attempt_no для этого seq и зарезервировать окно выбранного линка.
Только успешное принятие транспортом фиксирует время, last_link и перенос DATA в input_wait_ack.
Ошибка send снимает резерв; логический DATA остаётся готовым без сетевого loss sample.
Пакет содержит ACK, опциональные timestamp/измерительные секции и последнюю секцию PAYLOAD.
@@ -28,11 +28,11 @@ HELLO/CHALLENGE/CONFIRM и keepalive передаются своим незав
помещаться в окно, иначе серия отправляет FILLER. ACK/FILLER окно DATA не занимают.
При приёме пакета (от etcp_connections):
- последовательно сканируем секции и отдаём их на обработку нужным обработчикам:
- ack (0x01): attempt_id подтверждает физическую попытку только принявшего ACK линка;
- ack (0x01): пара seq+attempt_no подтверждает физическую попытку только принявшего ACK линка;
seq отдельно подтверждает логическую доставку. till освобождает логические записи, без BBR/снятия чужого flight.
- timestamp (0x06): обновляем last RTT, пересчитываем RTT avg10 и jitter по каждому линку; обновляем recv_dt_avg для раздельного измерения времени передачи/приёма
- [0x00] payload: сохраняем фрагмент в recv_q либо распознаём уже сохранённый/доставленный seq.
Только после этого ставим ACK с seq+attempt_id в ack_q принявшего линка; OOM до сохранения не подтверждается.
Только после этого ставим ACK с seq+attempt_no в ack_q принявшего линка; OOM до сохранения не подтверждается.
после сканирования проверяем появились ли в linked-list собранные данные которые можно переместить в выходную очередь. и перемещаем если есть.
Таймер попыток каждого линка (etcp_tx.c):
@@ -57,7 +57,9 @@ inflight - две очереди (ll_queue):
- timestamp последней отправки (last_timestamp)
- число отправок (send_count)
- send_hist[8] — local_link_id последних попыток, только диагностика
- last_attempt_id и active_attempts — последняя отправка и число ещё учитываемых физических копий
- last_attempt_id — внутренний составной ключ (seq<<16)|attempt_no последней принятой отправки
- attempt_count — выданные номера попыток данного seq, общие для всех каналов, включая неудачные send
- active_attempts — число ещё учитываемых физических копий
- Payload принадлежит только логической записи. Попытка ETCP_TX_ATTEMPT хранит seq и неизменные снимки, без payload.
inflight_bytes/inflight_packets линка равны сумме DATA-длин/числу записей в его attempts, включая резерв send.
ACK/till может уже удалить логический объект, пока другая физическая копия продолжает занимать своё окно.
@@ -126,17 +128,17 @@ pending/частичный буфер normalizer; ограничение окн
Формат и подтверждение эпох описаны в разделе «Подтверждение транспортной сессии» ниже.
2. Опциональные секции (одна или несколько) добавляются в etcp.c внутри DATA/ACK после заголовка сессии:
а) Подтверждения (ACK) - заголовок 0x01:
[0x01][count][last_delivered_id(4 байта LE)][rx_dup_count(2 байта LE)][[seq(4 LE)][attempt_id(8 LE)][delay_us(4 LE)] × count]
[0x01][count][last_delivered_id(4 байта LE)][rx_dup_count(2 байта LE)][[seq(4 LE)][attempt_no(2 LE)][delay_us(4 LE)] × count]
- count: количество ACK записей (1 байт; до 255, дополнительно ограничено MTU и оставшимся местом)
- last_delivered_id: uint32_t — последний ID, доставленный получателю (по этот ID всё собрано подряд)
- rx_dup_count: uint16_t — счётчик принятых дубликатов (локальный, для вычисления tx_dup_count на удалённой стороне)
- seq: uint32_t — номер подтверждаемого пакета
- attempt_id: uint64_t — ID конкретной физической попытки DATA; возвращается только тем же путём
- attempt_no: uint16_t — номер физической попытки данного seq, 1..65535; возвращается только тем же путём
- delay_us: uint32_t — задержка между приёмом DATA и формированием ACK, мкс; UINT32_MAX исключает RTT sample
При count>0 после всех записей обязательно добавляется [rx_data_bytes(8 LE)][rx_data_time_us(8 LE)].
Это точка приёма всего пути на момент формирования ACK, а не отдельной подтверждаемой попытки.
Время — монотонные микросекунды получателя при последнем положительном приросте DATA-байтов; 0/0 — точки нет.
Каждая запись = 16 байт, база ACK = 8 байт; полный размер = 8+16*count+(count>0 ? 16 : 0).
Каждая запись = 10 байт, база ACK = 8 байт; полный размер = 8+10*count+(count>0 ? 16 : 0).
count=0 допустим для передачи till, точки приёма не содержит. До обработки проверяется вся длина ACK.
ACK_REQUEST: [0x0a], одна секция длиной 1 байт, запрос повторной отправки актуального till.
в) channel timestamp (добавляется после выбора канала передачи, если есть свежие данные):
@@ -148,10 +150,15 @@ pending/частичный буфер normalizer; ограничение окн
Всего 5 байт. Добавляется только если last_recv_updated=1 (был принят пакет с момента последней отправки)
и если прошло менее 100с (dt < 1000000 таймбаз). Секция не является источником DATA RTT для BBR/RTO.
3. Полезная нагрузка - заголовок 0x00 (одна секция и всегда последняя):
[0x00] [ID 4 байта LE] [attempt_id 8 байт LE] [данные...]
- ID: uint32_t, 32-битный циклический номер пакета
- attempt_id: uint64_t, монотонный ненулевой ID; не сбрасывается при reset или пересоздании линка.
Переполнение закрывает соединение. Заголовок PAYLOAD занимает 13 байт; при повторе attempt_id новый, seq прежний.
[0x00] [ID 4 байта LE] [attempt_no 2 байта LE] [данные...]
- ID: uint32_t, номер фрагмента 1..UINT32_MAX в пределах одной эпохи потока.
- attempt_no: uint16_t, номер 1..65535, общий для всех каналов одного ID.
Номер выдаётся при создании попытки и не возвращается при отказе send; исходный seq при повторе сохраняется.
После номера65535 следующая попытка аварийно меняет эпоху через REINIT; счётчик не оборачивается.
Перед выдачей seq=0 после UINT32_MAX поток также меняет эпоху: пара seq/attempt_no не повторяется внутри эпохи.
Смена эпохи отбрасывает прежний поток, владельцы восстанавливают свои операции по REINIT, как при фатальном OOM.
Старые DATA/ACK отклоняются заголовком сессии до разбора. Заголовок PAYLOAD занимает7 байт.
Внутренние хеш-очереди сохраняют64-битный ключ (seq<<16)|attempt_no; он не передаётся по сети целиком.
Пакет может содержать несколько опциональных секций (например, ACK + channel_timestamp + данные),
Порядок: ACK, возможный ACK_REQUEST, измерительные/временные секции и FILLER, затем payload последним.
Разбор секций — последовательное сканирование с известными размерами (нет явного поля длины секции).
+3 -1
View File
@@ -41,7 +41,9 @@ bbr_tx_start(&bbr_state); // вызывать при возобновлени
### Интеграция с ETCP_LINK
BBR создаётся в `etcp_link_new()` как `link->bbr`. `etcp_tx_ack()` передаёт sample только известной попытки своего линка.
Sample использует неизменные снимки delivered и времени доставки/отправки из `ETCP_TX_ATTEMPT`.
DATA несёт `seq+attempt_id`, ACK возвращает их и задержку формирования ACK тем же каналом.
DATA несёт `seq:u32+attempt_no:u16`, ACK возвращает их и задержку формирования ACK тем же каналом.
Номер 1..65535 общий для всех каналов одного seq; внутренняя очередь индексируется ключом `(seq<<16)|attempt_no`.
Исчерпание номера или seq меняет эпоху потока до повторного использования пары; старые DATA/ACK отсекаются сессией.
RTT попытки равен времени до ACK минус эта задержка. Неизвестный/истёкший ID не обновляет BBR/RTT или физическое окно.
`etcp_ack_recv()` освобождает только логическую запись; кумулятивный till не создаёт BBR samples.
Интервал — максимум времени отправки и подтверждения; ACK одной пачки не создают искусственную сверхскорость.