Remove stale pacing and handshake descriptions from ETCP protocol specification
This commit is contained in:
1 parent
964f6cb045
commit
9459b43211
2 files changed
+39
-24
No files matched your search
+38
-23
@@ -30,7 +30,9 @@ HELLO/CHALLENGE/CONFIRM и keepalive передаются своим незав
|
||||
- последовательно сканируем секции и отдаём их на обработку нужным обработчикам:
|
||||
- ack (0x01): пара seq+attempt_no подтверждает физическую попытку только принявшего ACK линка;
|
||||
seq отдельно подтверждает логическую доставку. till освобождает логические записи, без BBR/снятия чужого flight.
|
||||
- timestamp (0x06): обновляем last RTT, пересчитываем RTT avg10 и jitter по каждому линку; обновляем recv_dt_avg для раздельного измерения времени передачи/приёма
|
||||
- timestamp (0x06): обновляем RTT и EWMA |ΔRTT| пути; recv_dt_avg хранит поправки времени TX/RX в Q16.
|
||||
У conn rtt_last — среднее RTT линков, rtt_avg_10 — их максимум (имя историческое).
|
||||
Для BBR/RTO используются отдельные DATA RTT/jitter, измеренные по ACK физических попыток.
|
||||
- [0x00] payload: сохраняем фрагмент в recv_q либо распознаём уже сохранённый/доставленный seq.
|
||||
Только после этого ставим ACK с seq+attempt_no в ack_q принявшего линка; OOM до сохранения не подтверждается.
|
||||
после сканирования проверяем появились ли в linked-list собранные данные которые можно переместить в выходную очередь. и перемещаем если есть.
|
||||
@@ -115,8 +117,9 @@ pending/частичный буфер normalizer; ограничение окн
|
||||
**** Формат кодограмм для etcp.c/h ****
|
||||
Каждая кодограмма состоит из обязательного заголовка и опциональных секций:
|
||||
1. Обязательный заголовок всего пакета (3 байта) добавляется при передаче, есть во всех пакетах:
|
||||
[Timestamp high][Timestamp low][flags/flag_up]
|
||||
- Timestamp: uint16_t, время отправки в единицах 0.1 мс (циклическое). при переповторах время обновляется
|
||||
[Timestamp:2][flags/flag_up:1]
|
||||
- Timestamp: uint16_t, время отправки в единицах 0.1 мс (циклическое), при повторе обновляется.
|
||||
Копируется из памяти в порядке байтов хоста; на используемых little-endian платформах передаётся LE.
|
||||
- flag_up: uint8_t, bit0 = up/down (recv_keepalive) — признак живой приёмной стороны
|
||||
|
||||
шифруется строго ВСЁ включая ВСЕ заголовки (кроме обфусцированного publickey в INIT-пакетах).
|
||||
@@ -171,20 +174,22 @@ pending/частичный буфер normalizer; ограничение окн
|
||||
При прохождении трафика через этот модуль подсчитываются метрики (для каждого канала отдельно):
|
||||
- RTT последнего пакета и время отправки последнего пакета
|
||||
- общее количество отправленных/принятых байт
|
||||
- количество перезапросов (по каждому каналу отдельно) в пакетах. когда etcp модуль принял запрос ретрансмиссии он смотрит с какого интерфейса был отправлен этот пакет (это запоминается при отправке) и увеличивает счетчики.
|
||||
- число фактических повторных отправок; потери для BBR учитываются отдельно при истечении физических попыток.
|
||||
- обновление текущего количества неподтвержденных данных inflight (в пакетах и байтах) - сколько по этому каналу отправленных и неподтвержденных на текущий момент
|
||||
Планировщик/балансир каналов (etcp_loadbalancer.c/h):
|
||||
для каждого канала ограничение полосы с тестированием отклика:
|
||||
- временную нагрузку 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. (при неактивности обновляем время)
|
||||
счетчик нужен для контроля можно ли передавать следующий пакет ("расчётное время передачи");
|
||||
когда etcp пытается отправить пакет через линк, то если в линке не инициализировано подключение и он клиент - то он отбрасывает этот пакет и запускает процесс установки соединения.
|
||||
также процесс установки соединения инициируется при добавлении канала (в etcp_link_new) если это client.
|
||||
Окно и pacing проверяются до извлечения DATA из очереди; без допустимого UP-линка фрагмент остаётся в очереди.
|
||||
У каждого линка собственный shaper по точной bbr_pacing_rate в байтах/с:
|
||||
- После успешного send списываются только payload_len DATA-байтов, без заголовков и ACK.
|
||||
- Основной счётчик времени имеет единицу 0.1 мс; sub-разряды — 0.1 нс, модуль 1000000.
|
||||
Прибавка равна ceil(payload_len * 10000000000 / bbr_pacing_rate), вычисляется целочисленно в uint64.
|
||||
- Накопленный кредит после простоя ограничивается 1 мс. При смене ставки пересчитывается оставшийся долг;
|
||||
таймер снимается и при необходимости назначается заново. При нулевой ставке pacing не ограничивает отправку.
|
||||
- ACK/control и измерительная burst-серия не списывают DATA-pacing. Окно DATA проверяется и в burst.
|
||||
Конструктор исходящего UDP-линка запускает INIT сразу; TCP использует отдельный STCP connect/handshake.
|
||||
процесс установки соединения (рукопожатие):
|
||||
- клиент отправляет init запрос и выставляет таймаут
|
||||
- по таймауту повторяет init запросы с экспоненциально растущим интервалом (начальный 500ms, макс 50s, +25% каждые 10 попыток), ведется счетчик запросов init.
|
||||
- по таймауту повторяет INIT: начальный интервал 500 TB = 50 мс, максимум 50000 TB = 5 с;
|
||||
каждые 10 отправок интервал увеличивается на interval/4 + 1 TB. TB здесь равен 0.1 мс.
|
||||
- когда клиент получает init подтверждение — снимает таймаут, выставляет link_state=connected, запускает keepalive.
|
||||
- каждая сторона сообщает в INIT собственный reset_id (64 бита, BE); ответ не повторяет эпоху запроса.
|
||||
- первая неизвестная эпоха пира сохраняется из handshake. Смена уже известной эпохи запускает challenge;
|
||||
@@ -225,18 +230,18 @@ link_state: 0=just init, 1=handshake, 2=reconnect, 3=connected.
|
||||
- mtu: uint16_t локальный MTU
|
||||
- keepalive: uint16_t интервал keepalive в мс
|
||||
- recovery: uint16_t = link->recovery_interval/100 (исходная единица — 0.1 мс)
|
||||
- link_id: uint8_t (0-255), назначается отправителем
|
||||
- link_id: uint8_t (1-255), назначается отправителем; ноль запрещён
|
||||
- socket_id: uint8_t, уникальный ID сокета отправителя
|
||||
- only_local: uint8_t, флаг "только локальные подключения"
|
||||
- type: uint8_t, тип сокета (CFG_SERVER_TYPE_UNKNOWN=0/PUBLIC=1/NAT=2/PRIVATE=3/LOCAL=4)
|
||||
- type: uint8_t, конфигурационный тип сокета CFG_SERVER_TYPE_*
|
||||
- src_ipv4/src_port: адрес интерфейса отправителя; для IPv6 src_ipv4=0, порт сохраняется;
|
||||
при SOCKS5 оба поля нулевые. Это заявленный адрес, а не наблюдение внешнего NAT-адреса.
|
||||
- collision: 1 — отправитель разрешает встречное подключение, заявляя роль master для линка
|
||||
- ed25519_pubkey: публичный ключ Ed25519 отправителя, 32 байта
|
||||
- device_type: CLIENT_TYPE_SERVER/DESKTOP/MOBILE
|
||||
- random_padding: случайные байты до размера handshake (защита от fingerprinting)
|
||||
- salt + obfuscated_pubkey: публичный ключ с обфускацией (не шифруется AES,
|
||||
но скрывается XOR с salt и собственным публичным ключом). Передаётся в конце пакета
|
||||
- salt + obfuscated_pubkey: ключ отправителя XOR SHA256(salt || публичный ключ получателя).
|
||||
Этот блок не шифруется AES, передаётся в конце пакета
|
||||
в noencrypt_len байтах (не попадает под AES-шифрование)
|
||||
INIT устанавливает UDP-линк: создаёт или переиспользует общий ETCP_CONN к пиру.
|
||||
Между узлами одно ETCP-соединение с несколькими линками; TCP-линк использует STCP handshake.
|
||||
@@ -250,7 +255,7 @@ link_state: 0=just init, 1=handshake, 2=reconnect, 3=connected.
|
||||
- node_id: 64 бита, big-endian
|
||||
- reset_id: uint64_t, собственная локальная эпоха потока сервера (big-endian), не эхо запроса
|
||||
- mtu: uint16_t локальный MTU сервера
|
||||
- link_id: uint8_t идентификатор канала сервера
|
||||
- link_id: uint8_t идентификатор канала сервера, 1..255
|
||||
- remote_socket_id: uint8_t (ID серверного сокета; ID клиентского сокета известен серверу из запроса)
|
||||
- only_local: uint8_t флаг сервера
|
||||
- type: uint8_t тип сокета сервера
|
||||
@@ -279,7 +284,10 @@ link_state: 0=just init, 1=handshake, 2=reconnect, 3=connected.
|
||||
ADDRESS_CHECK (0x08) — аутентифицированное входящее испытание endpoint.
|
||||
Модель NAT и алгоритм владельца: doc/nat_detection.txt.
|
||||
|
||||
При получении INIT получатель создаёт или переиспользует ETCP_CONN и подходящий линк;
|
||||
До изменения транспорта INIT_REQUEST проверяет полное 67-байтовое тело и соответствие node_id
|
||||
аутентифицированному X25519-ключу. Оба направления INIT отклоняют link_id=0 и MTU меньше
|
||||
ETCP_MIN_UDP_MTU(family): 191 байт для IPv4, 211 для IPv6. MTU больше 1500 ограничивается сверху.
|
||||
Получатель создаёт или переиспользует ETCP_CONN и подходящий линк;
|
||||
для существующего соединения проверяет совпадение ключа пира. Эпоха из INIT учитывается
|
||||
через etcp_conn_apply_peer_reset_id, без немедленного сброса потока при смене известной эпохи.
|
||||
Принятый INIT обновляет recv_keepalive, время последнего RX и входной remote_keepalive=flag_up
|
||||
@@ -291,13 +299,19 @@ link_state: 0=just init, 1=handshake, 2=reconnect, 3=connected.
|
||||
INIT_RESPONSE должен содержать полное обязательное тело (64 байта без timestamp/flag_up)
|
||||
и node_id, соответствующий аутентифицированному ключу пира. Усечённый ответ или чужой node_id
|
||||
отклоняются до изменения живости, эпохи и параметров линка. Дополнительный padding разрешён.
|
||||
приём кодограмм из UDP выглядит так:
|
||||
uasync select -> etcp_connection.c etcp_connections_read_callback: memory_pool_alloc, decrypt (or init) -> etcp_conn_input (etcp.c/h - сам tcp механизм) -> ETCP_CONN output_queue
|
||||
Входящий conn с известным ключом индексируется по node_id до callback создания линка.
|
||||
Отмена входящего линка из callback не закрывает conn, если у него появился другой линк или NCD-владелец.
|
||||
Close немедленно отключает линк и ресурсы, но память удерживается active_users до выхода внутренних стеков RX/переходов/callbacks.
|
||||
Приём UDP: uasync -> etcp_connections_read_callback_socket -> etcp_process_packet ->
|
||||
etcp_packet_decrypted (проверки и снятие SESSION_DATA) -> etcp_conn_input -> normalizer/output_queue.
|
||||
|
||||
Keepalive: [0x08] [flags(1)] [period_ms(2 LE)] [sleep_fields?:4] [padding...].
|
||||
При KA_FLAG_SLEEP добавляются active_remaining(2 LE) и sleep_duration(2 LE), оба в единицах 0.1 с.
|
||||
Полезное тело занимает 4 байта без SLEEP или 8 байт с SLEEP; остальные байты — padding.
|
||||
keepalive пакеты шлют и клиент и сервер с заданным интервалом если нет полезного трафика.
|
||||
Обычные keepalive периодически шлют обе стороны; полезный TX сбрасывает адаптивный период к базовому.
|
||||
Сон одной из сторон останавливает обычный таймер. UDP-анонс сна подтверждается флагом SLEEP_ACK,
|
||||
повторяется до ACK/исчерпания попыток; TCP отдельного подтверждения сна не требует.
|
||||
RX watchdog работает отдельно в normal и standby и не продлевается собственными отправками.
|
||||
Сервер прекращает слать keepalive если линк потерян (ждёт keepalive от клиента).
|
||||
flag_up в заголовке устанавливается в значение recv_keepalive (признак что локальная сторона принимает пакеты).
|
||||
|
||||
@@ -311,7 +325,8 @@ Padding keepalive, PING/PONG и STCP-handshake:
|
||||
user_data_len/resp_data_len обозначают только полезные данные. PONG не копирует padding запроса.
|
||||
- STCP: salt(8)+pubkey(32) открыты, затем 51 байт зашифрованного handshake и padding_size байт
|
||||
нулевого padding, зашифрованного продолжением того же CTR. Приём продвигает CTR через padding;
|
||||
padding_size=0 сразу завершает handshake. Параметры берутся у ETCP_CONN, иначе у instance.
|
||||
padding_size=0 сразу завершает handshake. При ETCP_CONN клиент читает актуальный диапазон при отправке;
|
||||
без ETCP_CONN сохраняет выбранную длину при запуске. Сервер выбирает длину независимо по пиру/instance.
|
||||
- Каждый STCP-поток использует соль своего отправителя: клиентскую для client→server, серверную для server→client.
|
||||
CTR IV = первые 12 байт SHA256(session_key || stream_id_LE || salt(8) || "uTun3-stream") + четыре нулевых байта.
|
||||
Отправка инициализируется после генерации своей соли, приём — после чтения соли пира, до расшифровки тела.
|
||||
|
||||
@@ -40,7 +40,7 @@ ACK_REQUEST и PAYLOAD (размер, seq, attempt_no). Направление:
|
||||
Пакет не изменяется; это диагностика после снятия защиты, а не проверка подлинности.
|
||||
Длинная строка безопасно усекается, неизвестная секция завершает разбор. Пример:
|
||||
```
|
||||
ETCP PKT: SEND ts=12345 ack=100/0 dup=0 ch_ts=200/150 payload=100(seq=101,try=1)
|
||||
ETCP PKT: SEND ts=12345 ack=100/0 dup=0 ch_ts=200/150 payload=100(seq=101 attempt=1)
|
||||
```
|
||||
|
||||
### Зависимости
|
||||
|
||||
Reference in new issue
Block a user