Encode 16-bit attempt numbers and guard pair reuse by session epochs

This commit is contained in:
evgeny committed 2026-10-23 19:19:54 +02:00
1 parent 6414cb15ef
commit ccbd214c97
3 files changed
+34 -20

No files matched your search

+17 -11
View File
@@ -177,7 +177,6 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
etcp->recv_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, 0, 4, "recv_q"); // Hash for recv_q
etcp->inflight_pool = memory_pool_init(sizeof(struct INFLIGHT_PACKET), "inflight_pool");
etcp->attempt_pool = memory_pool_init(sizeof(struct ETCP_TX_ATTEMPT), "attempt_pool");
etcp->next_attempt_id = 1;
etcp->io_pool = memory_pool_init(sizeof(struct ETCP_FRAGMENT), "io_pool");
etcp->max_inflight = (uint32_t)instance->config->global.bbr_max_cwnd;
if (instance->config->global.bbr_max_cwnd < INFLIGHT_LIM_MIN) {
@@ -826,6 +825,10 @@ void etcp_stats(struct ETCP_CONN* etcp) {
static void input_queue_cb(struct ll_queue* q, void* arg) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "");
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
if (!etcp->next_tx_id) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] sequence numbers exhausted; restarting stream before reuse", etcp->log_name);
etcp_conn_fatal_reinit(etcp, "sequence numbers exhausted"); return;
}
/* Selective ACK освобождает inflight, но не место за дыркой в приёмном окне пира. */
if ((uint32_t)(etcp->next_tx_id - etcp->rx_ack_till) > MAX_INFLIGHT_SIZE) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] peer receive window full: next=%u cumulative_ack=%u queued=%d",
@@ -1068,9 +1071,9 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* conn) {
while (ack_space >= ETCP_ACK_ENTRY_SIZE && data[1] != UINT8_MAX) {
struct ACK_PACKET* ack = (struct ACK_PACKET*)queue_data_get(link->ack_q);
if (!ack) break;
write_le(data + ptr, ack->seq, 4); write_le(data + ptr + 4, ack->attempt_id, 8);
write_le(data + ptr, ack->seq, 4); write_le(data + ptr + 4, (uint16_t)ack->attempt_id, 2);
uint64_t delay = get_time_us() - ack->received_at_us;
write_le(data + ptr + 12, delay >= UINT32_MAX ? UINT32_MAX : delay, 4);
write_le(data + ptr + 6, delay >= UINT32_MAX ? UINT32_MAX : delay, 4);
ptr += ETCP_ACK_ENTRY_SIZE; remaining -= ETCP_ACK_ENTRY_SIZE; ack_space -= ETCP_ACK_ENTRY_SIZE; data[1]++;
queue_entry_free(&ack->ll);
}
@@ -1118,7 +1121,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* conn) {
}
if (packet) {
data[ptr++] = ETCP_SECTION_PAYLOAD; write_le(data + ptr, packet->seq, 4); ptr += 4;
write_le(data + ptr, dgram->attempt->attempt_id, 8); ptr += 8;
write_le(data + ptr, (uint16_t)dgram->attempt->attempt_id, 2); ptr += 2;
memcpy(data + ptr, packet->ll.dgram, packet->ll.len); ptr += packet->ll.len;
}
dgram->data_len = ptr;
@@ -1260,8 +1263,10 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
etcp->tx_dup_count += (int16_t)(duplicates - (uint16_t)etcp->tx_dup_count);
for (unsigned i = 0; i < count; i++) {
const uint8_t* entry = data + ETCP_ACK_BASE_SIZE + i * ETCP_ACK_ENTRY_SIZE;
etcp_tx_ack(pkt->link, (uint32_t)read_le(entry, 4), read_le(entry + 4, 8),
(uint32_t)read_le(entry + 12, 4), rx_bytes, rx_time);
uint32_t seq = (uint32_t)read_le(entry, 4);
uint16_t number = (uint16_t)read_le(entry + 4, 2);
etcp_tx_ack(pkt->link, seq, etcp_tx_attempt_id(seq, number),
(uint32_t)read_le(entry + 6, 4), rx_bytes, rx_time);
if (etcp->close_requested || etcp->reinit_pending) break;
}
uint32_t advanced = 0;
@@ -1343,11 +1348,12 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] short DATA len=%u", etcp->log_name, len); len = 0; break;
}
uint32_t seq = (uint32_t)read_le(data + 1, 4);
uint64_t attempt_id = read_le(data + 5, 8);
uint16_t attempt_no = (uint16_t)read_le(data + 5, 2);
uint64_t attempt_id = etcp_tx_attempt_id(seq, attempt_no);
uint32_t payload_len = len - ETCP_PAYLOAD_HEADER_SIZE;
if (!attempt_id || (!etcp->got_initial_pkt && seq != 1)) {
if (!attempt_no || (!etcp->got_initial_pkt && seq != 1)) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] DATA rejected before initial fragment seq=%u attempt=%llu",
etcp->log_name, seq, (unsigned long long)attempt_id);
etcp->log_name, seq, (unsigned long long)attempt_no);
len = 0; break;
}
int32_t distance = (int32_t)(seq - etcp->last_delivered_id);
@@ -1370,7 +1376,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
if (fragment) queue_entry_free(&fragment->ll);
if (payload) memory_pool_free(etcp->instance->data_pool, payload);
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] DATA admission OOM seq=%u attempt=%llu; no ACK",
etcp->log_name, seq, (unsigned long long)attempt_id);
etcp->log_name, seq, (unsigned long long)attempt_no);
len = 0; break;
}
fragment->seq = seq; fragment->timestamp = pkt->timestamp;
@@ -1387,7 +1393,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
etcp->got_initial_pkt = 1;
if (etcp_tx_queue_ack(pkt->link, seq, attempt_id) == 0) ack_schedule(etcp);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] accepted DATA seq=%u attempt=%llu path=%u bytes=%u duplicate=%d",
etcp->log_name, seq, (unsigned long long)attempt_id, pkt->link->local_link_id, payload_len, duplicate);
etcp->log_name, seq, (unsigned long long)attempt_no, pkt->link->local_link_id, payload_len, duplicate);
if (distance == 1) etcp_output_try_assembly(etcp);
len = 0;
break;
+3 -4
View File
@@ -81,11 +81,10 @@ void etcp_dump_pkt_sections(struct ETCP_DGRAM* pkt, struct ETCP_LINK* link, int
payload_seq = (uint32_t)data[offset + 1] | ((uint32_t)data[offset + 2] << 8) |
((uint32_t)data[offset + 3] << 16) | ((uint32_t)data[offset + 4] << 24);
payload_size = len - offset - ETCP_PAYLOAD_HEADER_SIZE;
uint64_t attempt_id = 0;
for (unsigned i = 0; i < 8; i++) attempt_id |= (uint64_t)data[offset + 5 + i] << (8 * i);
uint16_t attempt_no = data[offset + 5] | ((uint16_t)data[offset + 6] << 8);
has_payload = 1;
pos += snprintf(buf + pos, sizeof(buf) - pos, " payload=%u(seq=%u attempt=%llu)",
payload_size, payload_seq, (unsigned long long)attempt_id);
pos += snprintf(buf + pos, sizeof(buf) - pos, " payload=%u(seq=%u attempt=%u)",
payload_size, payload_seq, attempt_no);
offset = len;
break;
}
+14 -5
View File
@@ -156,13 +156,15 @@ struct ETCP_TX_ATTEMPT* etcp_tx_prepare(struct ETCP_LINK* link, struct INFLIGHT_
conn->log_name, conn->active_attempts, link->local_link_id, link->attempts->count);
return NULL;
}
if (!conn->next_attempt_id || conn->next_attempt_id == UINT64_MAX) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] transmission attempt ID exhausted", conn->log_name);
conn->callbacks_running++; etcp_connection_close(conn); conn->callbacks_running--; return NULL;
if (packet->attempt_count == UINT16_MAX) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] attempt numbers exhausted seq=%u count=%u; restarting stream",
conn->log_name, packet->seq, packet->attempt_count);
etcp_conn_fatal_reinit(conn, "attempt numbers exhausted"); return NULL;
}
struct ETCP_TX_ATTEMPT* attempt = (struct ETCP_TX_ATTEMPT*)queue_entry_new_from_pool(conn->attempt_pool);
if (!attempt) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] cannot allocate transmission attempt", conn->log_name); return NULL; }
attempt->attempt_id = conn->next_attempt_id++; attempt->seq = packet->seq; attempt->ll.len = packet->ll.len;
attempt->attempt_id = etcp_tx_attempt_id(packet->seq, ++packet->attempt_count);
attempt->seq = packet->seq; attempt->ll.len = packet->ll.len;
if (queue_data_put_with_index(link->attempts, &attempt->ll) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] cannot index transmission attempt", conn->log_name); return NULL;
}
@@ -219,6 +221,10 @@ void etcp_tx_complete(struct ETCP_DGRAM* dgram, int success) {
void etcp_tx_ack(struct ETCP_LINK* link, uint32_t seq, uint64_t attempt_id, uint32_t receiver_delay_us,
uint64_t peer_rx_bytes, uint64_t peer_rx_time_us) {
struct ETCP_CONN* conn = link->etcp;
if (!(uint16_t)attempt_id) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] ACK attempt_no=0 rejected seq=%u path=%u", conn->log_name, seq, link->local_link_id);
return;
}
struct ETCP_TX_ATTEMPT* attempt = (struct ETCP_TX_ATTEMPT*)queue_find_data_by_index(link->attempts, &attempt_id);
if (attempt && (attempt->seq != seq || !attempt->sent_at_us)) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] invalid attempt ACK path=%u id=%llu seq=%u expected=%u committed=%d",
@@ -306,7 +312,10 @@ void etcp_tx_ack(struct ETCP_LINK* link, uint32_t seq, uint64_t attempt_id, uint
}
int etcp_tx_queue_ack(struct ETCP_LINK* link, uint32_t seq, uint64_t attempt_id) {
if (!attempt_id) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] DATA attempt_id=0 rejected", link->etcp->log_name); return -1; }
if (!(uint16_t)attempt_id || attempt_id != etcp_tx_attempt_id(seq, (uint16_t)attempt_id)) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] invalid DATA attempt key=%llu seq=%u", link->etcp->log_name,
(unsigned long long)attempt_id, seq); return -1;
}
if (queue_find_data_by_index(link->ack_q, &attempt_id)) return 0;
struct ACK_PACKET* ack = (struct ACK_PACKET*)queue_entry_new_from_pool(link->etcp->instance->ack_pool);
if (!ack) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] cannot allocate path ACK", link->etcp->log_name); return -1; }