Document ETCP window ownership and completed connection fixes
This commit is contained in:
1 parent
bc27352710
commit
11aa760d47
2 files changed
+57
-45
No files matched your search
+39
-27
@@ -1,21 +1,25 @@
|
||||
Формат кодограммы:
|
||||
------------------
|
||||
пакеты идут по следующему пути:
|
||||
пакет кем-то помещается в входную очередь (ETCP input_queue).
|
||||
Когда load balancer готов отправить следующий пакет он делает вызов etcp_request_pkt в etcp.c/h и etcp либо формирует следующую кодограмму или встаёт в состояние async ожидания (возвращая null):
|
||||
- либо wait ETCP input_queue (если нечего передавать)
|
||||
- либо wait SACK + wait timeout (если sack очередь полная и нет необслуженных запросов ретрансмиссий). после истечения wait timeout он помечает используя round-robin очередной пакет как "нужна ретрансмиссия".
|
||||
из async ожидания он может вызвать etcp_loadbalancer input_from_etcp и передать пакет на отправку.
|
||||
как формируется пакет:
|
||||
1. выбираем канал передачи (etcp_loadbalancer_select_link)
|
||||
2. если есть место в rwin (объём inflight данных) то берем очередной пакет из ETCP input_queue и его отправляем
|
||||
3. если пакет найден или есть неподтверждённые ACK:
|
||||
1. вызываем функцию формирования опциональных секций (ACK, channel timestamp) - они записываются в начало
|
||||
2. в конец добавляем секцию 0x00 с данными пакета
|
||||
при отправке пакета помечаем в inflight списке с какого интерфейса он отправлен
|
||||
Функция прикрепления опциональных секций:
|
||||
- запрашивает выбор канала передачи.
|
||||
- добавляет накопившиеся ACK
|
||||
Кодограмма сервиса через etcp_send попадает в normalizer; его фрагменты поступают в
|
||||
input_queue, затем в indexed input_send_q с назначенным seq.
|
||||
Очереди, ACK/RTO, изменение окна и готовность линка вызывают etcp_conn_wake_sender.
|
||||
Один call_soon обслуживает накопленные события без рекурсивного входа и задержки таймером.
|
||||
При OOM выделения сетевого пакета очередь сохраняется; retry назначается через 10ms.
|
||||
close/reset отменяет ожидающий запуск. Не подтверждённая сессия не передаёт DATA/ACK;
|
||||
HELLO/CHALLENGE/CONFIRM и keepalive передаются своим независимым путём.
|
||||
|
||||
Формирование пакета:
|
||||
1. Посмотреть следующий DATA и его длину, не извлекая его из input_send_q.
|
||||
2. Выбрать инициализированный UP-линк с готовым shaper и местом для целого DATA:
|
||||
inflight_bytes + длина DATA <= inflight_lim_bytes. Проверка выполняется без переполнения.
|
||||
Среди допустимых выбирается минимум inflight_bytes, при равенстве — round-robin.
|
||||
3. Если DATA не помещается, он остаётся в очереди; для накопленных ACK выбирается
|
||||
рабочий линк без ограничения DATA-cwnd, с учётом shaper.
|
||||
4. Выделить ETCP_DGRAM до удаления DATA из очереди и изменения счётчиков.
|
||||
Отправляемый DATA занимает окно выбранного линка и переходит в input_wait_ack.
|
||||
Пакет содержит ACK, опциональные timestamp/измерительные секции и последнюю секцию PAYLOAD.
|
||||
5. В активной ограниченной burst-серии pacing обходится; DATA всё равно должен
|
||||
помещаться в окно, иначе серия отправляет FILLER. ACK/FILLER окно DATA не занимают.
|
||||
При приёме пакета (от etcp_connections):
|
||||
- последовательно сканируем секции и отдаём их на обработку нужным обработчикам:
|
||||
- ack (0x01): помечаем в inflight пакеты как подтверждённые и проставляем время подтверждения. также снимаем блокировку отправки
|
||||
@@ -25,7 +29,10 @@
|
||||
|
||||
пересканирование inflight:
|
||||
- запускается при добавлении пакета в очередь ожидания ACK (wait_ack_cb). устанавливается таймер на первый неистекший пакет.
|
||||
- если время последней отправки > RTT_avg10*K1+jitter*K2 (коэффициенты K1=32, K2=32, деление на 16) то пакет заново отправляется
|
||||
- возраст последней отправки сравнивается с (RTT_avg10*32+jitter*32)/16,
|
||||
ограниченным диапазоном 50..10000 таймбаз (5ms..1s).
|
||||
- истёкший пакет освобождает окно прежнего линка и переходит в input_send_q;
|
||||
его новая попытка проходит обычный допуск по окну и pacing выбранного линка.
|
||||
- очередь FIFO (сортирована по timestamp добавления = времени отправки), сканирование прекращается на первом неистекшем пакете
|
||||
- по истечению таймера (ack_timeout_cb) снова вызывается проверка очереди
|
||||
|
||||
@@ -39,21 +46,26 @@ inflight - две очереди (ll_queue):
|
||||
- timestamp последней отправки (last_timestamp)
|
||||
- число отправок (send_count)
|
||||
- send_hist[8] - история через какие линки отправлялся (индекс линка)
|
||||
- inflight_bytes/inflight_packets per-link отслеживается в ETCP_LINK
|
||||
Размер буфера inflight лимитируем как сумму по всем активным линкам optimal_inflight.
|
||||
- inflight_bytes/inflight_packets линка равны сумме длин/числу WAIT_ACK с этим last_link.
|
||||
WAIT_SEND окно не занимает, в том числе после RTO. last_link сохраняет историю попытки.
|
||||
Поздний ACK удаляет WAIT_SEND без повторного вычитания; duplicate ACK учёт не меняет.
|
||||
Удаление линка обнуляет last_link в обеих очередях до освобождения его памяти.
|
||||
Общий optimal_inflight равен сумме лимитов всех линков, с насыщением UINT32_MAX;
|
||||
это дополнительный backpressure подачи новых фрагментов, не замена индивидуальных окон.
|
||||
Отдельно ограничиваем размах sequence относительно кумулятивного ACK: новый seq выдаётся
|
||||
только если (uint32_t)(next_tx_id - rx_ack_till) <= MAX_INFLIGHT_SIZE (16384).
|
||||
Selective ACK освобождает отдельную inflight-запись, но не продвигает эту границу:
|
||||
приёмник пока не может освободить пакеты за дыркой. Кумулятивный ACK возобновляет
|
||||
input_queue даже если подтверждённые записи уже удалены по selective ACK.
|
||||
ACK ограничивается последним назначенным seq (next_tx_id - 1); сравнения учитывают uint32 wrap.
|
||||
optimal_inflight расчитывается из RTT и bandwidth линка.
|
||||
|
||||
bandwidth по каждому линку адаптивно подстраиваем:
|
||||
Периодически в stats_timer_cb собираем статистику окна (rtt_avg10, ретрансмиссии, переданные пакеты).
|
||||
BW рассчитывается как: inflight_lim_bytes * 8 / rtt_min (в секундах) * 8 / 1024 * 1.3 (Kbits/sec).
|
||||
Размер inflight_lim_bytes плавно подстраивается вверх/вниз в зависимости от того, растёт ли RTT.
|
||||
Burst-измерения bandwidth (0x07, 0x08) зарезервированы, но на данный момент не реализованы.
|
||||
BBR каждого линка получает ACK/loss samples и возвращает cwnd и pacing rate.
|
||||
Лимит DATA — не менее 8192 байт и не более bbr_max_cwnd (по умолчанию 1048576).
|
||||
Отказ из-за нехватки окна сохраняет cwnd_limited до следующего ACK: вычитание ACK
|
||||
не должно скрывать этот факт от BBR. app_limited учитывает ожидающие очереди и
|
||||
pending/частичный буфер normalizer; ограничение окном не означает отсутствие данных.
|
||||
Потеря для BBR учитывается при RTO, фактическая повторная передача — при новой попытке.
|
||||
При уменьшении окна уже отправленные пакеты остаются в учёте; новые DATA ждут ACK/RTO.
|
||||
Измерительные секции burst 0x07/0x08 и FILLER поддерживаются реализацией.
|
||||
|
||||
|
||||
**** Формат кодограмм для etcp.c/h ****
|
||||
@@ -107,7 +119,7 @@ Burst-измерения bandwidth (0x07, 0x08) зарезервированы,
|
||||
- обновление текущего количества неподтвержденных данных inflight (в пакетах и байтах) - сколько по этому каналу отправленных и неподтвержденных на текущий момент
|
||||
Планировщик/балансир каналов (etcp_loadbalancer.c/h):
|
||||
для каждого канала ограничение полосы с тестированием отклика:
|
||||
- полосу ограничиваем по объему данных - полному числу байт ethernet пакта (добавляем размер заголовков)
|
||||
- временную нагрузку shaper считаем по data_len + 2 байта на пакет (текущая реализация).
|
||||
Ограничение реализуем следующим образом:
|
||||
- в структуре есть счётчик времени (квантизация стандартная 0.1ms) по которое канал загружен данными. И суб-разряды (nanotime) этого счетчика 0.1ns - 0.1ms (считает по модулю 1000000 и переполнения добавляет в основной счетчик) чтобы при высоких скоростях не было потери точности.
|
||||
Этим счётчиком контролируется полоса: при передаче к нему добавляется расчётное время передачи текущего ethernet пакета (используя текущий bandwidth считаем время передачи одного байта (float) и умножаем на число байт, переводим в таймбазу nanotime). А если при передаче время меньше current_time - delta_time (delta_time - константа в define, x0.1ms) то он устанавливается как current time-delta_time. (при неактивности обновляем время)
|
||||
|
||||
+18
-18
@@ -4,25 +4,26 @@
|
||||
|
||||
## Текущие задачи
|
||||
|
||||
[ ] **ETCP: etcp_connect выдаёт готовность индексированного DOWN/reset-соединения** — воспроизведено 22.10.2026:
|
||||
Ветка conn && !ctx в etcp_connect.c:281 проверяет только state==0; state==1 принимается за готовность.
|
||||
Стенд на реальных очередях/реестре вызывает EARLY и LATE при links_up=0, initialized=1;
|
||||
также при links_up=1, initialized=0, reinit_pending=1, session_required=1. Возврат 0 в обоих случаях.
|
||||
Это противоречит контракту готовности в etcp_connect.h/etcp_api.h; state отражает индексирование, не UP сессии.
|
||||
Проверить реальные вызывающие NCD/CM, определить условие готовности и ожидание восстановления/подтверждения;
|
||||
отдельно учесть close_requested и повторную подписку после уже выданного EARLY. Комментариями проблему не маскировать.
|
||||
Стенд вне репозитория /tmp/protocol-header-audit.c; лог /tmp/protocol-header-audit.log.
|
||||
[+] **ETCP: единый установщик прямого транспорта через NCD** — исправлено 22.10.2026:
|
||||
Удалён неиспользуемый рабочим кодом etcp_connect с EARLY/LATE, отдельными линками и таймерами;
|
||||
три тестовых файла переведены на NCD handles с начала подключения. Сохранены проверки независимых владельцев,
|
||||
отмены отложенного UP, TIMEOUT, выхода из UTUN с сохранением транспорта и shutdown с pending LEAVE.
|
||||
Состояние реестра, физический UP, готовность ETCP-сессии и READY группы разделены в контрактах.
|
||||
Начальный отправитель не создаёт преждевременный UP. Merkle-фикстура ждёт готовности обоих потоков.
|
||||
|
||||
[ ] **ETCP: проверить распределение cwnd/inflight между линками** — воспроизведено 22.10.2026:
|
||||
etcp_loadbalancer.h обещает учёт cwnd/inflight в select_link; реализация фильтрует initialized/link_status/shaper
|
||||
и выбирает минимум inflight_bytes, но inflight_lim_bytes не проверяет. При штатных лимитах стенд выбирает
|
||||
полный линк 8192/8192 вместо свободного 16384/65536; сессия готова, общее окно свободно, shaper/burst отсутствуют.
|
||||
Вызов etcp_request_pkt после выбора добавляет inflight выбранному линку; общий допуск данных ограничивается отдельно.
|
||||
Определить границы ответственности общего окна/BBR/выбора линка, отдельно ACK/control/retrans/burst;
|
||||
проверить, где требуется ограничение отдельного линка, и согласовать реализацию и контракт. Не добавлять слепой запрет,
|
||||
способный заблокировать ACK или retrans. Стенд/лог: /tmp/protocol-header-audit.c, /tmp/protocol-header-audit.log.
|
||||
[+] **ETCP: индивидуальные окна DATA, ACK и корректный учёт повторов** — исправлено 22.10.2026:
|
||||
Выбор линка проверяет место для целого DATA, ACK не блокируется DATA-cwnd; burst без места использует FILLER.
|
||||
WAIT_ACK занимает окно, RTO освобождает его до постановки в WAIT_SEND; поздний ACK не вычитает байты повторно.
|
||||
BBR получает факт ограничения окном до вычитания ACK; app_limited учитывает подготовленные данные и normalizer.
|
||||
Один call_soon объединяет пробуждения без таймерной задержки/рекурсии; OOM retry через 10ms, reset/close отменяет token.
|
||||
Невозможность запланировать отправку закрывает транспорт отложенно, сохраняя вызывающий стек.
|
||||
Проверяются размер max_cwnd, переполнение суммы окон; ошибки [global] теперь отклоняют конфигурацию.
|
||||
Новый test_etcp_windows: 12 сценариев настоящих очередей/отправителя, сверка счётчиков и внедрение отказов.
|
||||
STCP-тест проверяет каждый байт двустороннего 1MiB трафика при окнах 8KiB и независимых тестовых командах.
|
||||
ASAN/UBSan с detect_leaks=1: windows/session/transport/connect/stcp_traffic/NCD, 6/6;
|
||||
результаты /tmp/etcp-asan-final-results.log, логи /tmp/etcp-asan-final-logs/.
|
||||
|
||||
[ ] **Аудит .h ETCP/STCP: исправить подтверждённые расхождения контрактов** — проверено 22.10.2026, исходники не менялись:
|
||||
[ ] **Аудит .h ETCP/STCP: оставшиеся расхождения контрактов** — проверено 22.10.2026:
|
||||
stcp.h: do_close уже очищает оба sc_stream и планирует free для allocated/free_on_close; комментарий утверждает обратное.
|
||||
Разделить контракты finish (drain/запрет новых данных/до 1с) и do_close (немедленное закрытие/потеря очередей/callback).
|
||||
Описать владение внешними rx_queue/tx_queue, преусловия размера frame и callback/reentrancy/lifetime.
|
||||
@@ -34,7 +35,6 @@
|
||||
etcp.h: ref_count удерживает объект/phase-2, но close уже освобождает normalizer и линки в phase-1;
|
||||
reset_done не блокирует reinit (в реализации только запись/диагностика); last_rx_id не обновляется при RX.
|
||||
etcp_connection_ready объявлена без реализации/вызовов; рабочая функция — etcp_conn_ready.
|
||||
etcp_loadbalancer.h ссылается на отсутствующее packet_request_fn; фактически вызывается link_ready_for_send_fn.
|
||||
etcp_connections.h проверен частично: у data_len исключены все 3 байта timestamp/flag, не только timestamp;
|
||||
send_hook уступает SOCKS UDP; etcp_send_tcp_ping при отказе запуска возвращает отрицательный код без callback.
|
||||
etcp_session.h и etcp_padding.h сверены с реализацией без подтверждённых смысловых расхождений.
|
||||
|
||||
Reference in new issue
Block a user