Fix: ETCP module compilation errors and build system issues
- Added missing structure members to ETCP_CONN (ack_packets_count, last_rx_ack_id, rtt_history, rtt_history_idx, total_packets_sent) - Added missing bandwidth member to ETCP_LINK structure - Fixed include path for ll_queue.h header - Added math library linking (-lm) to build system - Added etcp_loadbalancer.c to build sources - Fixed forward declarations and function calls in ETCP modules - Updated test dependencies to include loadbalancer object files All simple compilation errors resolved, project builds successfully and tests pass (except TUN permission test which is expected in container).
This commit is contained in:
1 parent
3bf178475d
commit
e048b10899
35 files changed
+7011
-669
No files matched your search
@@ -0,0 +1,118 @@
|
||||
Формат кодограммы:
|
||||
------------------
|
||||
пакеты идут по следующему пути:
|
||||
пакет кем-то помещается в входную очередь (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. берем пакет из списка неподтвержденных пакетов ожидающих отправку
|
||||
2. если есть место в rwin (объём inflight данных) то берем очередной пакет из ETCP input_queue и его отправляем
|
||||
если пакет найден:
|
||||
1. вызываем функцию формирования опциональных секций (ACK, RETRANS, channel timestamp) - они записываются в начало
|
||||
2. в конец добавляем секцию 0x00 с данными пакета
|
||||
при отправке пакета помечаем в inflight списке с какого интерфейса он отправлен
|
||||
Функция прикрепления опциональных секций:
|
||||
- запрашивает выбор канала передачи.
|
||||
- добавляет накопившиеся ACK и RETRANS
|
||||
При приёме пакета (от etcp_connections):
|
||||
- последовательно сканируем секции и отдаём их на обработку нужным обработчикам:
|
||||
- retrans: помечаем секции как нужна ретрансмиссия. также ставим флаг в конце запустить etcp если он в wait timeout, т.е. wait_timeout!=NULL
|
||||
- ack: помечаем в inflight пакеты как подтверждённые и проставляем время подтверждения. также и ставим флаг в конце запустить etcp если он в wait timeout, т.е. wait_timeout!=NULL
|
||||
- timestamp: обновляем last RTT, пересчитываем RTT10,RTT100 (плавающим окном за последние 10/100 пакетов), и jitter=max(last 10)-min(last 10 packets)
|
||||
- [0x00] payload: добавляем пакет в нужное место сборочного linked-list (сверяем по ID если дубликат - игнорируем).
|
||||
после сканирования проверяем появились ли в linked-list собранные данные которые можно переместить в выходную очередь. и перемещаем если есть.
|
||||
|
||||
пересканирование inflight:
|
||||
- вызывается по таймеру, таймер заводится при добавлении пакета в пустой inflight.
|
||||
- если время последней отправки > RTT10*k1+jitter*k2 (коэффициенты вынести в настройки utun instance) то пакет заново отправляется
|
||||
и заводим таймер отправки следующей ретрансмиссии (список отсортирован по времени)
|
||||
- если встречаем успешный ack, запоминаем это и его timestamp (назовем lp_ts).
|
||||
- если после этого далее в очереди находим пакет который не имеет переповторов и timestamp отправки раньше чем [ИСПРАВЛЕНО: фраза обрезана в оригинале; предположительно, "раньше чем lp_ts" на основе контекста; если нет, пометить как неполное]
|
||||
|
||||
inflight - два списка (ll_queue):
|
||||
- список с неподтвержденными пакетами ожидающими ack (при запросе ретрансмиссии пакет перемещается в ожидающие отправку)
|
||||
- список с неподтвержденными пакетами ожидающими отправку (при отправке возвращается в ожидающие ack)
|
||||
доп. свойства пакетов:
|
||||
- указатель на ETC_LINK последней отправки
|
||||
- timestamp последней отправки
|
||||
- число отправок
|
||||
- число запросов переповтора
|
||||
Размер буфера inflight лимитируем как сумму по всем активным линкам optimal_inflight.
|
||||
optimal_inflight расчитывается из RTT и bandwidth линка.
|
||||
|
||||
bandwidth по каждому линку адаптивно подстраиваем следующим алгоритмом:
|
||||
периодически инициируем burst_transmission (напоминаем ID пакета на которой она началась).
|
||||
после передаём пачку пакетов только по этому линку со следующими таймингами:
|
||||
<pkt1> <delay 4x> <pkt2><pkt3><pkt4><pkt5> - т.е. чуть задерживаем канал а потом выплёвываем пачку пакетов.
|
||||
и добавляем опцию [meas_ts][n] - где n - номер пакета в пачке
|
||||
приёмная сторона видя эту секцию должна отправить секцию [meas_resp] по всем пакетам, которые содержат локальный RECV_Timestamp фиксирующий время приёма каждого пакета с [meas_ts].
|
||||
приняв эти response анализируем по разнице между pkt1-pkt2 не забита ли очередь отправки, а по дельте pkt2-3-4-5 оцениваем bandwidth.
|
||||
дальше по алгоритму (который потом оттюним) корректируем transmitter speed limit.
|
||||
|
||||
|
||||
**** Формат кодограмм для etcp.c/h ****
|
||||
Каждая кодограмма состоит из обязательного заголовка и опциональных секций:
|
||||
1. Обязательный заголовок всего пакета (2 байта) добавляется при передаче, есть во всех пакетах:
|
||||
[Timestamp high][Timestamp low]
|
||||
- Timestamp: время отправки в единицах 0.1 мс (циклическое). при переповторах время обновляется
|
||||
шифруется строго ВСЁ включая ВСЕ заголовки (кроме INIT+publickey). timestamp вставляется ВСЕГДА ВО ВСЕ КОДОГРАММЫ
|
||||
2. Опциональные секции (одна или несколько) добавляются в etcp.c:
|
||||
а) Подтверждения (ACK) - заголовок 0x01:
|
||||
[0x01][count][(id_hi,id_lo,ts_hi,ts_lo)×count][last_delivered_hi,lo][last_rx_hi,lo]
|
||||
- count: количество пар ID+timestamp (1-32)
|
||||
- last_delivered_id: последний ID, доставленный получателю
|
||||
- last_rx_id: последний полученный ID (для синхронизации прогресса)
|
||||
б) Запросы ретрансмиссии (RETRANS) - заголовок 0x10-0x2F:
|
||||
[0x10+(count-1)][(id_hi,id_lo)×count][last_delivered_hi,lo][last_rx_hi,lo]
|
||||
- count: (заголовок & 0x0F) + 1 (1-32)
|
||||
- last_delivered_id, last_rx_id: для синхронизации состояния
|
||||
в) channel timestamp (добавляется после выбора канала передачи):
|
||||
[0x0F] [RET_Timestamp high][RET_Timestamp low] [RECV_Timestamp high][RECV_Timestamp low]
|
||||
- RET_Timestamp: это timestamp последнего принятого пакета по этому каналу ETCP_LINK плюс разница времени между принятием этого пакета и текущим временем. т.е. приёмная сторона по этому timestamp (и зная своё время) может просчитать время в пути туда + обратно
|
||||
- RECV_Timestamp: это timestamp времени приёма последнего принятого пакета (т.е. отправляем время "когда мы получили этот пакет по локальном урмени") - по нему другая сторона сможет просчитать относительное время задержки в одну сторону (время туда без обратно).
|
||||
если последний пакет по этому каналу принят сильно давно (прошло более 30000 единиц времени) то channel timestamp не добавляем. [СПОРНО: порог 30000 (3 сек) arbitrary; может потребовать настройки или обоснования; также риск clock skew между нодами]
|
||||
3. Полезная нагрузка - заголовок 0x00 (одна секция и всегда последняя):
|
||||
[0x00] [ID high][ID low] [данные...] - Данные пакета
|
||||
- ID: 16-битный циклический номер пакета. [СПОРНО: 16-bit может привести к быстрому overflow в long-lived сессиях; рассмотреть 32-bit]
|
||||
Пакет может содержать несколько опциональных секций (например, ACK + данные). [СПОРНО: нет явных length полей для секций; parsing implicit, риск ошибок при variable длине; добавить length?]
|
||||
цель протокола:
|
||||
1. контролировать состояние очередей (не накапливать данные где этого можно избежать), анализировать RTT и inflight - динамически подстраивать размер окна и ретрансмиссии, утилизируя сеть насколько это возможно но не допуская наполнения буферов в сети (что приводит к бестолковому увеличению RTT)
|
||||
Установка подключения для канала (реализуется в etcp_connections.c/h):
|
||||
каждое ETCP подключение может содержать несколько линков до endpoint (разные маршруты, каналы связи).
|
||||
При прохождении трафика через этот модуль подсчитываются метрики (для каждого канала отдельно):
|
||||
- RTT последнего пакета и время отправки последнего пакета
|
||||
- пакеты делятся на 3 категории: <300 байт, 300-699 байт, 700+ байт. для каждой категории отдельно считаются метрики:
|
||||
- количество отправленных в пакетах байтах
|
||||
- количество перезапросов (по каждому каналу отдельно) в пакетах и байтах. когда etcp модуль принял запрос ретрансмиссии он смотрит с какого интерфейса был отправлен этот пакет (это запоминается при отправке) и увеличивает счетчики.
|
||||
- обновление текущего количества неподтвержденных данных inflight (в пакетах и байтах) - сколько по этому каналу отправленных и неподтвержденных на текущий момент
|
||||
Планировщик/балансир каналов (etcp_loadbalancer.c/h):
|
||||
для каждого канала ограничение полосы с тестированием отклика:
|
||||
- полосу ограничиваем по объему данных - полному числу байт ethernet пакта (добавляем размер заголовков)
|
||||
Ограничение реализуем следующим образом:
|
||||
- в структуре есть счётчик времени (квантизация стандартная 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. (при неактивности обновляем время)
|
||||
счетчик нужен для контроля можно ли передавать следующий пакет ("расчётное время передачи"); [СПОРНО: сложность с float; риск precision issues]
|
||||
когда etcp пытается отправить пакет через линк, то если в линке не инициализировано подключение и он клиент - то он отбрасывает этот пакет и запускает процесс установки соединения.
|
||||
также процесс установки соединения инициируется при добавлении канала (в etcp_link_new) если это client.
|
||||
процесс установки соединения:
|
||||
- отправляем init запрос и выставляем таймаут (сохраняем его в struct ETCP_LINK)
|
||||
- по таймауту повторяем init запросы, ведем счетчик запросов init.
|
||||
- когда получаем init подтверждение (отправляется в etcp_connections_read_callback) снимаем таймаут и выставляем флаг struct ETCP_LINK initialized=1.
|
||||
При добавлении нового канала посылаем INIT 0x04 (без сброса), а при первичной инициализации подключения - 0x02 (это нужно, т.к. например перезапуск локального софта должен инициировать перезапуск удаленного соединения).
|
||||
|
||||
**** Формат кодограмм для etcp_connections.c/h ****
|
||||
Кодограммы с этими секциями обрабатываются в etcp_connections (в этих кодограммах всегда только одна секция). в обязательном заголовке ID не используется, при передаче для порядка =0: [СПОРНО: заголовок в etcp — только 2 байта TS; здесь подразумевается ID? Уточнить единый формат]
|
||||
1) Init запрос - заголовок 0x02 (со сбросом etcp сессии) или 0x04 (без сброса):
|
||||
[0x02/0x04] [my_node_id 64bit] [my mtu high] [my mtu low] [keepalive high] [keepalive low] [my public key (64 байта, не шифруется)]
|
||||
- Инициирует новый connection для tcp instance. если tcp instance нет (первое подключение) - создаёт. Между нодами только одно подключение, но можно добавлять каналы.
|
||||
- Публичный ключ отправляется в конце пакета без шифрования, чтобы получатель мог установить его и расшифровать остальную часть пакета (т.к. инициатор соединения всегда имеет оригинальный peer public key в конфиге - по нему исключаем MITM)
|
||||
2) Init подтверждение - заголовок 0x03/0x05 (ответы соответственно на коды 0x02 и 0x04):
|
||||
[0x03/0x05] [my_node_id 64bit] [my mtu high] [my mtu low]
|
||||
- Подтверждение инициализации (канал успешно создан, можно начинать обмен)
|
||||
При получении init получатель пакета должен:
|
||||
- reset ETCP_LINK с этим ip_port если он есть
|
||||
- создать новый ETCP_CONN с этим node_id. если уже существует подключение с этим node_id - вызвать etcp_reset (функция сброса окон неподтвержденных данных и нумерации)
|
||||
приём кодограмм из 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
|
||||
Reference in new issue
Block a user