diff --git a/src/transport_layer/etcp.h b/src/transport_layer/etcp.h index 69045222..a4e9dd2a 100644 --- a/src/transport_layer/etcp.h +++ b/src/transport_layer/etcp.h @@ -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 @@ -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); diff --git a/src/transport_layer/etcp_connections.h b/src/transport_layer/etcp_connections.h index 72f91acc..a9655741 100644 --- a/src/transport_layer/etcp_connections.h +++ b/src/transport_layer/etcp_connections.h @@ -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) diff --git a/src/transport_layer/etcp_tx.h b/src/transport_layer/etcp_tx.h new file mode 100644 index 00000000..11e8f4d8 --- /dev/null +++ b/src/transport_layer/etcp_tx.h @@ -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 +#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