Define independent logical fragments and per-path transmission attempts

This commit is contained in:
evgeny committed 2026-10-23 10:09:44 +02:00
1 parent 3c44c3d7bc
commit b68e0ac3db
3 files changed
+79 -19

No files matched your search

+17 -15
View File
@@ -9,6 +9,7 @@
#include "etcp_connections.h"
#include "secure_channel.h"
#include "etcp_padding.h"
#include "etcp_tx.h"
#include "../lib/ll_queue.h"
#include <stdint.h>
@@ -36,6 +37,7 @@ uint16_t get_current_timestamp(void);
#define ETCP_SECTION_MEAS_TS 0x07 // Measurement timestamp for bandwidth (burst packet)
#define ETCP_SECTION_MEAS_RESP 0x08 // Measurement response (burst result)
#define ETCP_SECTION_FILLER 0x09 // Filler/dummy data (discarded by receiver)
#define ETCP_SECTION_ACK_REQUEST 0x0a // запрос актуальной логической границы, без DATA/BBR sample
// Burst measurement constants
#define BURST_PACKET_COUNT 12 // число пакетов в burst
@@ -73,17 +75,14 @@ uint16_t get_current_timestamp(void);
struct INFLIGHT_PACKET {// выделяется из etcp->inflight_pool
struct ll_entry ll;
uint32_t seq; // packet seq (ID по документации)
struct ETCP_LINK* last_link; // Последний линк попытки; занимает его окно только в WAIT_ACK
struct ETCP_LINK* last_link; // Заимствованный курсор выбора следующего пути; очищается перед free линка
uint64_t last_timestamp; // Last send timestamp
uint8_t send_count; // Number of sends
uint8_t retrans_req_count; // Number of retrans requests
uint8_t state; // WAIT_ACK занимает окно last_link; WAIT_SEND ожидает первую/повторную отправку
uint64_t last_attempt_id; // Последняя принятая транспортом попытка; ненулевой ID отличает retry
uint32_t send_count; // Статистика отправок с насыщением, без wrap
uint32_t retrans_req_count; // Число запросов повтора с насыщением
uint32_t active_attempts; // Попытки этого seq, ещё занимающие окна каналов
uint8_t state; // WAIT_SEND — готов к отправке; WAIT_ACK — логически ожидает доставки
uint8_t send_hist[8]; // через какие каналы передавался пакет (NEW). send_count - head ptr
uint64_t delivered_at_send; // link->delivered_bytes на момент отправки (для BBR prior_delivered)
uint64_t delivered_time_at_send_tb; // время последней доставки при отправке; начало ACK-интервала BBR
uint64_t first_tx_at_send_tb; // начало интервала отправок BBR (оба интервала — в 0.1мс)
uint32_t inflight_at_send; // link->inflight_bytes на момент отправки (для BBR tx_in_flight)
uint8_t is_app_limited; // данные ограничены приложением на момент отправки
};
// Список пакетов для сборки. собирается в ll_queue (используем быстрый поиск с хешем)
@@ -95,9 +94,9 @@ struct ETCP_FRAGMENT {// выделяется из пула etcp->rx_pool
struct ACK_PACKET {
struct ll_entry ll;
uint32_t seq;// sequence number
uint16_t pkt_timestamp;// timestamp пакета (часы уладенной стороны)
uint32_t recv_timestamp;// время приема (локальное)
uint64_t attempt_id; // Индекс очереди конкретного линка
uint32_t seq; // Логический фрагмент
uint64_t received_at_us; // Полное локальное время, без modulo-wrap задержки
};
#define ETCP_TX_STATE_DATA_WAIT 1
@@ -143,11 +142,11 @@ struct ETCP_CONN {
// Inflight очереди (2 шт) - пока пакет в статусе inflight - к нему прикрепляется struct INFLIGHT_PACKET
struct memory_pool* inflight_pool; // память для inflight очередей
struct memory_pool* attempt_pool; // метаданные физических попыток; payload принадлежат inflight_pool
struct memory_pool* io_pool; // память для rx очередей
struct ll_queue* input_send_q; // очередь на отправку (inflight_pool -> INFLIGHT_PACKET)
struct ll_queue* input_wait_ack; // очередь ожидающих подтверждение (inflight_pool -> struct INFLIGHT_PACKET)
struct ll_queue* ack_q; // неотправленные подтверждения приема пакетов (instance.ack_pool -> struct ACK_PACKET) + index [+0 SEQ]
struct ll_queue* recv_q; // очередь на сборку фрагментированных пакетов(rx_pool -> struct ETCP_FRAGMENT) + index [+0 SEQ]
@@ -163,6 +162,7 @@ struct ETCP_CONN {
// IDs and state
uint32_t next_tx_id; // ID для добавления в очередь отправки (с этим id будет добавлен следующий пакет)
uint64_t next_attempt_id; // Не переиспользуется при reset/пересоздании линков; исчерпание закрывает соединение
uint32_t last_rx_id; // Last received ID
uint32_t last_delivered_id; // Last delivered to output_queue
@@ -190,8 +190,10 @@ struct ETCP_CONN {
uint32_t optimal_inflight; // Sum over links
// Timers
void* retrans_timer; // Общий RTO: 2*(rtt_avg_10+jitter), ограничен 10мс..1с; callback проверяет FIFO
void* ack_resp_timer; // ACK send timer
void* ack_window_timer; // запрос потерянного till, только при заполненном приёмном окне
uint8_t ack_till_pending; // нужно передать текущую логическую границу по рабочему пути
uint8_t ack_request_pending; // нужно запросить границу у пира
void* down_timer; // удаление после непрерывного DOWN, включая первоначальное ожидание
uint64_t down_deadline_tb; // сохраняется при наличии NCD handles после истечения таймера
@@ -330,7 +332,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp);
void etcp_conn_wake_sender(struct ETCP_CONN* conn);
// Process ACK receipt - remove acknowledged packet from inflight queues
void etcp_ack_recv(struct ETCP_CONN* etcp, uint32_t seq, uint16_t ts, uint16_t dts);
void etcp_ack_recv(struct ETCP_CONN* etcp, uint32_t seq);
// Recalculate optimal_inflight and resume input queue after link inflight_lim change
void etcp_conn_on_inflight_lim_changed(struct ETCP_CONN* etcp);
+13 -4
View File
@@ -37,7 +37,9 @@ extern "C" {
// Wire-MTU кап: пакет на проводе не должен превышать 1500 (согласовано с TUN_MAX_PACKET_SIZE).
// PACKET_DATA_SIZE (2048) — это внутренний буфер аллокации/приёма, а не wire-MTU.
#define PACKET_DATA_MAX_MTU 1500
#define ETCP_MAX_PAYLOAD_SIZE (PACKET_DATA_SIZE - ETCP_ACK_BASE_SIZE - 5) /* 2035 */
#define ETCP_PAYLOAD_HEADER_SIZE 13 // type + seq:u32 + attempt_id:u64
#define ETCP_ACK_ENTRY_SIZE 16 // seq:u32 + attempt_id:u64 + receiver_delay_us:u32
#define ETCP_MAX_PAYLOAD_SIZE (PACKET_DATA_SIZE - ETCP_ACK_BASE_SIZE - ETCP_PAYLOAD_HEADER_SIZE)
#define ETCP_RFC791_MIN_MTU 576
// Типы кодограмм протокола
@@ -68,6 +70,8 @@ struct ETCP_DGRAM {// пакет (незашифрованный)
uint16_t data_len;// общий размер пакета не включая timestamp
uint16_t noencrypt_len;// число байт (с конца) которые не надо шифровать. для передачи pubkey
uint16_t payload_len; // локальные метаданные request_pkt: DATA-байты; 0 — только ACK/control/FILLER, вне DATA-pacing
struct ETCP_TX_ATTEMPT* attempt; // локальный резерв окна; complete снимает/фиксирует его после send
uint8_t burst_packet; // Пакет создан измерительной серией; не зависит от последующей смены burst_active
uint16_t timestamp;// timestamp отправляющего узла при отправке пакета
uint8_t flag_up:1;// bit0 = up/down (recv_keepalive)
uint8_t data[];// данные пакета (без timestamp)
@@ -245,8 +249,13 @@ struct ETCP_LINK {
uint64_t shaper_sub_nanotime; // sub 0-999999 для 0.1 нс - 0.1 мс
uint8_t shaper_state;
void* shaper_timer;
struct ll_queue* attempts; // Физические попытки, индекс attempt_id:u64; ll.len учитывается в окне
struct ll_queue* ack_q; // ACK только DATA этого линка, индекс attempt_id:u64
void* retrans_timer; // Срок старейшей принятой транспортом попытки этого канала
uint32_t data_rtt_us; // Сглаженный RTT DATA->ACK этого пути; 0 до первого измерения
uint32_t data_jitter_us; // Сглаженное абсолютное отклонение собственного DATA RTT
uint32_t inflight_bytes; // байты WAIT_ACK с last_link == этому линку; WAIT_SEND окно не занимает
uint32_t inflight_bytes; // DATA-байты физических попыток этого линка, включая синхронный резерв send
uint32_t inflight_packets; // число таких пакетов
uint32_t inflight_lim_bytes; // индивидуальный лимит DATA в байтах, включая целиком следующий фрагмент
uint8_t cwnd_limited; // DATA не поместился в окно; сохраняется до передачи этого факта BBR по ACK
@@ -326,8 +335,8 @@ struct ETCP_LINK {
// === BBR congestion control ===
struct bbr* bbr; // BBR state (per-link, создаётся в etcp_link_new)
uint64_t delivered_bytes; // кумулятивно delivered bytes на линке (для round detection)
uint64_t last_ack_time_tb; // время последней доставки/начала первого flight, 0.1мс
uint64_t first_tx_time_tb; // начало интервала отправок, продвигается подтверждёнными попытками
uint64_t last_ack_time_us; // время последней доставки/начала первого flight в микросекундах
uint64_t first_tx_time_us; // начало интервала отправок, продвигается подтверждёнными попытками
uint32_t bbr_pacing_rate; // Точный pacing B/s; менять через set_pacing_rate для пересчёта долга/таймера
uint32_t bbr_loss_since_ack; // счётчик потерь на линке с последнего ACK (для rs->lost)
+49
View File
@@ -0,0 +1,49 @@
/* etcp_tx — учёт физических попыток DATA и ACK одного канала.
* Логический seq/очереди доставки принадлежат ETCP_CONN, попытка — ETCP_LINK.
* prepare резервирует окно; complete фиксирует принятую транспортом отправку либо откатывает резерв.
* ACK возвращается своим путём, обновляет только его модель и отдельно подтверждает логический seq.
* Все вызовы выполняются в uasync-потоке; DATA/ACK этого формата требуют согласованной сессии. */
#ifndef ETCP_TX_H
#define ETCP_TX_H
#include <stdint.h>
#include "../lib/ll_queue.h"
struct ETCP_CONN;
struct ETCP_LINK;
struct ETCP_DGRAM;
struct INFLIGHT_PACKET;
/* Запись из attempt_pool соединения; индекс очереди линка — attempt_id, payload здесь не хранится. */
struct ETCP_TX_ATTEMPT {
struct ll_entry ll; // len — DATA-байты, dgram=NULL; одна очередь, владелец — линк
uint64_t attempt_id; // уникален в пределах жизни соединения, 0 не используется
uint32_t seq; // логический фрагмент; может быть уже освобождён после доставки
uint64_t sent_at_us; // время принятия транспортом; 0 — пока зарезервирован
uint64_t delivered_at_send; // доставленные DATA-байты этого канала на момент отправки
uint64_t delivered_time_us; // начало ACK-интервала оценки скорости
uint64_t first_tx_us; // начало интервала отправки
uint32_t inflight_at_send; // окно после включения этой попытки
uint8_t is_app_limited; // источник не имел ожидающих данных либо идёт PROBE_RTT
};
/* Инициализировать очереди attempts/ACK линка. 0 — успех, -1 — OOM (с логом); при ошибке очереди очищены. */
int etcp_tx_link_init(struct ETCP_LINK* link);
/* Снять попытки/таймер/ACK линка перед reset или free. retry=1 возвращает недоставленные DATA на отправку.
* reset не меняет физический UP; destroy дополнительно освобождает очереди. */
void etcp_tx_link_reset(struct ETCP_LINK* link, int retry);
void etcp_tx_link_destroy(struct ETCP_LINK* link);
/* Расчётный RTO в микросекундах: 2*(собственный RTT+jitter), 10мс..1с; до DATA ACK использует KA RTT/1с. */
uint32_t etcp_tx_rto_us(const struct ETCP_LINK* link);
/* Резервировать окно для packet; NULL — ошибка с логом, packet остаётся в очереди отправки. */
struct ETCP_TX_ATTEMPT* etcp_tx_prepare(struct ETCP_LINK* link, struct INFLIGHT_PACKET* packet);
/* Завершить синхронный send: success=1 принят транспортом, 0 — снять резерв без сетевой потери.
* Вызвать ровно один раз до освобождения dgram и до следующего poll; dgram->attempt обнуляется. */
void etcp_tx_complete(struct ETCP_DGRAM* dgram, int success);
/* Принятый ACK: seq подтверждает логическую доставку; attempt_id обновляет окно/BBR только своего линка.
* Неизвестный старый ID не создаёт измерение и не вычитает окно. UINT32_MAX delay — RTT недостоверен. */
void etcp_tx_ack(struct ETCP_LINK* link, uint32_t seq, uint64_t attempt_id, uint32_t receiver_delay_us);
/* Поставить ACK уже сохранённого/доставленного DATA в очередь этого линка; 0/-1, ошибка логируется. */
int etcp_tx_queue_ack(struct ETCP_LINK* link, uint32_t seq, uint64_t attempt_id);
#endif