@ -9,6 +9,9 @@
# include <sys/time.h>
# include <math.h> // For bandwidth calcs
// Enable comprehensive debug output for ETCP module
# define DEBUG_CATEGORY_ETCP_DETAILED 1
// Constants from spec (adjusted for completeness)
# define MAX_INFLIGHT_BYTES 65536 // Initial window
# define RETRANS_K1 2.0f // RTT multiplier for retrans timeout
@ -25,19 +28,31 @@ static void input_queue_cb(struct ll_queue* q, struct ll_entry* entry, void* arg
// Timestamp diff (with wrap-around)
static uint16_t timestamp_diff ( uint16_t t1 , uint16_t t2 ) {
if ( t1 > = t2 ) return t1 - t2 ;
return ( 0xFFFF - t2 ) + t1 + 1 ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " timestamp_diff: t1=%u, t2=%u " , t1 , t2 ) ;
if ( t1 > = t2 ) {
uint16_t diff = t1 - t2 ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " timestamp_diff: normal case, diff=%u " , diff ) ;
return diff ;
}
uint16_t diff = ( 0xFFFF - t2 ) + t1 + 1 ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " timestamp_diff: wraparound case, diff=%u " , diff ) ;
return diff ;
}
// Get current time in 0.1ms units
uint64_t get_current_time_units ( ) {
struct timeval tv ;
gettimeofday ( & tv , NULL ) ;
return ( ( uint64_t ) tv . tv_sec * 10000ULL ) + ( tv . tv_usec / 100 ) ;
uint64_t time_units = ( ( uint64_t ) tv . tv_sec * 10000ULL ) + ( tv . tv_usec / 100 ) ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " get_current_time_units: tv_sec=%ld, tv_usec=%ld, result=%llu " ,
tv . tv_sec , tv . tv_usec , ( unsigned long long ) time_units ) ;
return time_units ;
}
uint16_t get_current_timestamp ( ) {
return ( uint16_t ) ( get_current_time_units ( ) & 0xFFFF ) ;
uint16_t timestamp = ( uint16_t ) ( get_current_time_units ( ) & 0xFFFF ) ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " get_current_timestamp: result=%u " , timestamp ) ;
return timestamp ;
}
// Internal functions (expanded)
@ -69,8 +84,15 @@ static void update_link_bandwidth(struct ETCP_LINK* link, uint32_t new_bw);
// Create ETCP connection (unchanged)
struct ETCP_CONN * etcp_connection_create ( struct UTUN_INSTANCE * instance ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_create: instance=%p " , instance ) ;
struct ETCP_CONN * etcp = calloc ( 1 , sizeof ( struct ETCP_CONN ) ) ;
if ( ! etcp ) return NULL ;
if ( ! etcp ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " etcp_connection_create: failed to allocate ETCP_CONN " ) ;
return NULL ;
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_create: allocated etcp=%p " , etcp ) ;
etcp - > instance = instance ;
etcp - > input_queue = queue_new ( instance - > ua , instance - > pkt_pool ) ;
@ -79,53 +101,88 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance) {
etcp - > window_size = MAX_INFLIGHT_BYTES ;
etcp - > next_tx_id = 1 ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_create: queues created, mtu=%d, window_size=%u, next_tx_id=%u " ,
etcp - > mtu , etcp - > window_size , etcp - > next_tx_id ) ;
// Init crypto (as per original)
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_create: initializing crypto context " ) ;
if ( sc_init_ctx ( & etcp - > crypto_ctx , & instance - > my_keys ) ! = SC_OK ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " etcp_connection_create: crypto initialization failed " ) ;
etcp_connection_close ( etcp ) ;
return NULL ;
}
DEBUG_INFO ( DEBUG_CATEGORY_ETCP , " etcp_connection_create: successfully created ETCP connection %p " , etcp ) ;
return etcp ;
}
// Close connection (expanded free)
void etcp_connection_close ( struct ETCP_CONN * etcp ) {
if ( ! etcp ) return ;
if ( ! etcp ) {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: called with NULL etcp " ) ;
return ;
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: closing connection %p " , etcp ) ;
// Cancel timers
if ( etcp - > retrans_timer ) uasync_cancel_timeout ( etcp - > instance - > ua , etcp - > retrans_timer ) ;
if ( etcp - > ack_timer ) uasync_cancel_timeout ( etcp - > instance - > ua , etcp - > ack_timer ) ;
if ( etcp - > retrans_timer ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: canceling retrans_timer " ) ;
uasync_cancel_timeout ( etcp - > instance - > ua , etcp - > retrans_timer ) ;
}
if ( etcp - > ack_timer ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: canceling ack_timer " ) ;
uasync_cancel_timeout ( etcp - > instance - > ua , etcp - > ack_timer ) ;
}
// Free inflight
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: freeing wait_ack_list " ) ;
inflight_packet_t * pkt = etcp - > wait_ack_list ;
int wait_ack_count = 0 ;
while ( pkt ) {
inflight_packet_t * next = pkt - > next ;
free_inflight_packet ( pkt ) ;
pkt = next ;
wait_ack_count + + ;
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: freed %d packets from wait_ack_list " , wait_ack_count ) ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: freeing wait_send_list " ) ;
pkt = etcp - > wait_send_list ;
int wait_send_count = 0 ;
while ( pkt ) {
inflight_packet_t * next = pkt - > next ;
free_inflight_packet ( pkt ) ;
pkt = next ;
wait_send_count + + ;
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: freed %d packets from wait_send_list " , wait_send_count ) ;
// Free rx_list
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: freeing rx_list " ) ;
rx_packet_t * rx = etcp - > rx_list ;
int rx_count = 0 ;
while ( rx ) {
rx_packet_t * next = rx - > next ;
free ( rx - > data ) ;
free ( rx ) ;
rx = next ;
rx_count + + ;
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: freed %d packets from rx_list " , rx_count ) ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: freeing queues " ) ;
queue_free ( etcp - > input_queue ) ;
queue_free ( etcp - > output_queue ) ;
DEBUG_INFO ( DEBUG_CATEGORY_ETCP , " etcp_connection_close: connection %p closed successfully " , etcp ) ;
free ( etcp ) ;
}
static void free_inflight_packet ( inflight_packet_t * pkt ) {
if ( pkt ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " free_inflight_packet: freeing packet %p (id=%u, data_len=%u) " ,
pkt , pkt - > id , pkt - > data_len ) ;
free ( pkt - > data ) ;
free ( pkt ) ;
}
@ -133,7 +190,12 @@ static void free_inflight_packet(inflight_packet_t* pkt) {
// Reset connection (unchanged)
void etcp_conn_reset ( struct ETCP_CONN * etcp ) {
if ( ! etcp ) return ;
if ( ! etcp ) {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " etcp_conn_reset: called with NULL etcp " ) ;
return ;
}
DEBUG_INFO ( DEBUG_CATEGORY_ETCP , " etcp_conn_reset: resetting connection %p " , etcp ) ;
// Cancel timers
if ( etcp - > retrans_timer ) uasync_cancel_timeout ( etcp - > instance - > ua , etcp - > retrans_timer ) ;
@ -165,6 +227,7 @@ void etcp_conn_reset(struct ETCP_CONN* etcp) {
etcp - > rx_list = NULL ;
// Reset state
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_conn_reset: clearing packet lists and resetting state " ) ;
etcp - > next_tx_id = 1 ;
etcp - > last_rx_id = 0 ;
etcp - > last_delivered_id = 0 ;
@ -179,22 +242,37 @@ void etcp_conn_reset(struct ETCP_CONN* etcp) {
etcp - > rtt_history_idx = 0 ;
memset ( etcp - > rtt_history , 0 , sizeof ( etcp - > rtt_history ) ) ;
// Queues cleared externally if needed
DEBUG_INFO ( DEBUG_CATEGORY_ETCP , " etcp_conn_reset: connection %p reset completed " , etcp ) ;
}
// Input decrypted packet (unchanged structure, but calls expanded processes)
void etcp_conn_input ( struct ETCP_DGRAM * pkt ) {
if ( ! pkt | | ! pkt - > link | | ! pkt - > link - > etcp ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_conn_input: pkt=%p, link=%p, etcp=%p " ,
pkt , pkt ? pkt - > link : NULL , pkt & & pkt - > link ? pkt - > link - > etcp : NULL ) ;
if ( ! pkt | | ! pkt - > link | | ! pkt - > link - > etcp ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " etcp_conn_input: invalid parameters (pkt=%p, link=%p, etcp=%p) " ,
pkt , pkt ? pkt - > link : NULL , pkt & & pkt - > link ? pkt - > link - > etcp : NULL ) ;
return ;
}
struct ETCP_CONN * etcp = pkt - > link - > etcp ;
uint8_t * data = pkt - > data ;
uint16_t len = pkt - > data_len ;
uint16_t ts = pkt - > timestamp ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_conn_input: processing packet - etcp=%p, data_len=%u, timestamp=%u, link=%p " ,
etcp , len , ts , pkt - > link ) ;
// Update link activity
pkt - > link - > last_activity = get_current_time_units ( ) ;
pkt - > link - > last_recv_local_time = pkt - > link - > last_activity ;
pkt - > link - > last_recv_timestamp = ts ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " etcp_conn_input: updated link activity - last_activity=%llu, last_recv_timestamp=%u " ,
( unsigned long long ) pkt - > link - > last_activity , pkt - > link - > last_recv_timestamp ) ;
// Parse sections
uint16_t pos = 0 ;
while ( pos < len ) {
@ -246,16 +324,34 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
// Request next packet (expanded)
struct ETCP_DGRAM * etcp_request_pkt ( struct ETCP_CONN * etcp ) {
if ( ! etcp ) return NULL ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: etcp=%p " , etcp ) ;
if ( ! etcp ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: called with NULL etcp " ) ;
return NULL ;
}
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: input_queue_count=%d, wait_send_list=%p, unacked_bytes=%u, window_size=%u " ,
queue_entry_count ( etcp - > input_queue ) , etcp - > wait_send_list ,
etcp - > unacked_bytes , etcp - > window_size ) ;
// Check waiting conditions
if ( queue_entry_count ( etcp - > input_queue ) = = 0 & & etcp - > wait_send_list = = NULL ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: no packets available, setting callback and returning NULL " ) ;
// Wait for input or retrans
// Set callback for resume
queue_set_callback ( etcp - > input_queue , input_queue_cb , etcp ) ; // Define cb to resume
return NULL ;
}
if ( etcp - > unacked_bytes > = etcp - > window_size ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: window full (unacked_bytes=%u >= window_size=%u), waiting " ,
etcp - > unacked_bytes , etcp - > window_size ) ;
// Wait ACK or timeout
etcp - > wait_timeout_active = 1 ;
return NULL ;
}
if ( etcp - > unacked_bytes > = etcp - > window_size ) {
// Wait ACK or timeout
etcp - > wait_timeout_active = 1 ;
@ -265,20 +361,32 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
// Get packet (prefer retrans)
inflight_packet_t * ipkt = etcp - > wait_send_list ;
if ( ipkt ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: using retransmission packet id=%u " , ipkt - > id ) ;
remove_inflight ( etcp , ipkt ) ;
} else {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: getting new packet from input queue " ) ;
struct ll_entry * entry = queue_entry_get ( etcp - > input_queue ) ;
if ( ! entry ) return NULL ;
if ( ! entry ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: no entry available from input queue " ) ;
return NULL ;
}
ipkt = calloc ( 1 , sizeof ( inflight_packet_t ) ) ;
if ( ! ipkt ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: failed to allocate inflight_packet_t " ) ;
queue_entry_put_first ( etcp - > input_queue , entry ) ;
return NULL ;
}
ipkt - > id = etcp - > next_tx_id + + ;
ipkt - > data_len = ll_entry_size ( entry ) ;
ipkt - > data = malloc ( ipkt - > data_len ) ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: created new packet id=%u, data_len=%u " ,
ipkt - > id , ipkt - > data_len ) ;
if ( ! ipkt - > data ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: failed to allocate packet data buffer " ) ;
free ( ipkt ) ;
queue_entry_put_first ( etcp - > input_queue , entry ) ;
return NULL ;
@ -289,10 +397,13 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
}
// Select link
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: selecting link via loadbalancer " ) ;
struct ETCP_LINK * link = etcp_loadbalancer_select_link ( etcp ) ;
if ( ! link ) {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: no suitable link available " ) ;
// Put back if new
if ( ipkt - > send_count = = 0 ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: putting new packet back to input queue " ) ;
struct ll_entry * entry = queue_entry_new ( ipkt - > data_len ) ;
memcpy ( ll_entry_data ( entry ) , ipkt - > data , ipkt - > data_len ) ;
queue_entry_put_first ( etcp - > input_queue , entry ) ;
@ -301,14 +412,22 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
return NULL ;
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: selected link %p " , link ) ;
// Build packet buf
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: building packet with optional sections " ) ;
uint8_t buf [ 1600 ] ;
uint16_t len = append_optional_sections ( etcp , buf , link ) ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: optional sections added, len=%u " , len ) ;
// Add payload (assume payload includes ID as first 2 bytes)
uint16_t pay_len = ipkt - > payload_len + 2 ; // ID
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: payload section - pay_len=%u (payload_len=%u + 2 for ID) " ,
pay_len , ipkt - > payload_len ) ;
if ( len + SECTION_HEADER_SIZE + pay_len > sizeof ( buf ) ) {
// Too large
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: packet too large (%u + %u + %u > %u), dropping " ,
len , SECTION_HEADER_SIZE , pay_len , ( unsigned ) sizeof ( buf ) ) ;
free_inflight_packet ( ipkt ) ;
return NULL ;
}
@ -319,10 +438,12 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
buf [ len + + ] = ipkt - > id & 0xFF ;
memcpy ( buf + len , ipkt - > data , ipkt - > payload_len ) ;
len + = ipkt - > payload_len ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: payload added, total len=%u " , len ) ;
// Alloc dgram
struct ETCP_DGRAM * dgram = memory_pool_alloc ( etcp - > instance - > pkt_pool ) ;
if ( ! dgram ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: failed to allocate dgram from memory pool " ) ;
free_inflight_packet ( ipkt ) ;
return NULL ;
}
@ -332,6 +453,9 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
dgram - > timestamp = get_current_timestamp ( ) ;
memcpy ( dgram - > data , buf , len ) ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: allocated dgram %p (link=%p, data_len=%u, timestamp=%u) " ,
dgram , link , len , dgram - > timestamp ) ;
// Update inflight
ipkt - > last_link = link ;
ipkt - > last_timestamp = dgram - > timestamp ;
@ -339,19 +463,30 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
etcp - > unacked_bytes + = ipkt - > payload_len ;
etcp - > bytes_sent_total + = len ; // Approx
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: updated inflight - send_count=%u, unacked_bytes=%u, bytes_sent_total=%u " ,
ipkt - > send_count , etcp - > unacked_bytes , etcp - > bytes_sent_total ) ;
// Move to wait_ack
ipkt - > state = INFLIGHT_STATE_WAIT_ACK ;
ipkt - > next = etcp - > wait_ack_list ;
etcp - > wait_ack_list = ipkt ;
// Add to wait_ack list
ipkt - > next = etcp - > wait_ack_list ;
etcp - > wait_ack_list = ipkt ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: added packet %u to wait_ack_list " , ipkt - > id ) ;
// Start retrans timer if empty before
if ( etcp - > wait_ack_list - > next = = NULL & & ! etcp - > retrans_timer ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: starting retrans_timer (wait_ack_list was empty) " ) ;
etcp - > retrans_timer = uasync_set_timeout ( etcp - > instance - > ua , 20 , etcp , retrans_timer_cb ) ;
}
// Update load balancer after send (call from connections after actual send)
// etcp_loadbalancer_update_after_send(link, len + headers); // Assume called externally
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_request_pkt: successfully created dgram %p for packet id=%u " ,
dgram , ipkt - > id ) ;
return dgram ;
}
@ -364,9 +499,12 @@ static void input_queue_cb(struct ll_queue* q, struct ll_entry* entry, void* arg
queue_resume_callback ( q ) ;
}
// Append optional (expanded: build ACK, RETRANS, TIMESTAMP )
// Append optional (expanded with debug )
static uint16_t append_optional_sections ( struct ETCP_CONN * etcp , uint8_t * buf , struct ETCP_LINK * link ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " append_optional_sections: etcp=%p, link=%p " , etcp , link ) ;
uint16_t len = 0 ;
int sections_added = 0 ;
// Timestamp (assume 2 bytes TS)
uint16_t ts = get_current_timestamp ( ) ;
@ -375,6 +513,8 @@ static uint16_t append_optional_sections(struct ETCP_CONN* etcp, uint8_t* buf, s
buf [ len + + ] = 2 ; // len low
buf [ len + + ] = ( ts > > 8 ) & 0xFF ;
buf [ len + + ] = ts & 0xFF ;
sections_added + + ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " append_optional_sections: added TIMESTAMP section (ts=%u, len=%u) " , ts , len ) ;
// ACK (assume count(1) + [id(2)+ts(2)]*count)
if ( etcp - > pending_ack_count > 0 ) {
@ -383,6 +523,9 @@ static uint16_t append_optional_sections(struct ETCP_CONN* etcp, uint8_t* buf, s
buf [ len + + ] = ( ack_len > > 8 ) & 0xFF ;
buf [ len + + ] = ack_len & 0xFF ;
buf [ len + + ] = etcp - > pending_ack_count ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " append_optional_sections: adding ACK section with %u ACKs " , etcp - > pending_ack_count ) ;
for ( uint8_t i = 0 ; i < etcp - > pending_ack_count ; i + + ) {
uint16_t id = etcp - > pending_ack_ids [ i ] ;
uint16_t ats = etcp - > pending_ack_ts [ i ] ;
@ -390,9 +533,13 @@ static uint16_t append_optional_sections(struct ETCP_CONN* etcp, uint8_t* buf, s
buf [ len + + ] = id & 0xFF ;
buf [ len + + ] = ( ats > > 8 ) & 0xFF ;
buf [ len + + ] = ats & 0xFF ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " append_optional_sections: ACK[%u] - id=%u, ts=%u " , i , id , ats ) ;
}
etcp - > pending_ack_count = 0 ;
etcp - > ack_packets_count + + ;
sections_added + + ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " append_optional_sections: added ACK section (total_len=%u) " , len ) ;
}
// RETRANS (similar: count(1) + id(2)*count)
@ -402,124 +549,227 @@ static uint16_t append_optional_sections(struct ETCP_CONN* etcp, uint8_t* buf, s
buf [ len + + ] = ( ret_len > > 8 ) & 0xFF ;
buf [ len + + ] = ret_len & 0xFF ;
buf [ len + + ] = etcp - > pending_retrans_count ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " append_optional_sections: adding RETRANS section with %u requests " , etcp - > pending_retrans_count ) ;
for ( uint8_t i = 0 ; i < etcp - > pending_retrans_count ; i + + ) {
uint16_t id = etcp - > pending_retrans_ids [ i ] ;
buf [ len + + ] = ( id > > 8 ) & 0xFF ;
buf [ len + + ] = id & 0xFF ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " append_optional_sections: RETRANS[%u] - id=%u " , i , id ) ;
}
etcp - > pending_retrans_count = 0 ;
sections_added + + ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " append_optional_sections: added RETRANS section (total_len=%u) " , len ) ;
}
// MEAS_TS if burst (assume [start_id(2)][n(1)])
if ( etcp - > burst_in_progress ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " append_optional_sections: burst measurement in progress, would add MEAS_TS " ) ;
// Logic for adding to burst packets
// Placeholder
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " append_optional_sections: completed - added %u sections, total_len=%u " ,
sections_added , len ) ;
return len ;
}
// Process ACK (expanded)
static void process_section_ack ( struct ETCP_CONN * etcp , uint8_t * data , uint16_t len ) {
if ( len < 1 ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_ack: etcp=%p, data=%p, len=%u " , etcp , data , len ) ;
if ( len < 1 ) {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " process_section_ack: invalid length %u " , len ) ;
return ;
}
uint8_t count = data [ 0 ] ;
if ( 1 + count * 4 > len ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_ack: count=%u " , count ) ;
if ( 1 + count * 4 > len ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " process_section_ack: invalid count %u for len %u " , count , len ) ;
return ;
}
uint16_t lp_ts = 0 ;
int acked_count = 0 ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_ack: processing %u ACK entries " , count ) ;
for ( uint8_t i = 0 ; i < count ; i + + ) {
uint16_t id = ( data [ 1 + i * 4 ] < < 8 ) | data [ 2 + i * 4 ] ;
uint16_t ts = ( data [ 3 + i * 4 ] < < 8 ) | data [ 4 + i * 4 ] ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " process_section_ack: entry %u - id=%u, ts=%u " , i , id , ts ) ;
inflight_packet_t * pkt = find_inflight ( etcp , id , INFLIGHT_STATE_WAIT_ACK ) ;
if ( pkt ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_ack: found inflight packet %u, removing (payload_len=%u) " ,
id , pkt - > payload_len ) ;
etcp - > unacked_bytes - = pkt - > payload_len ;
remove_inflight ( etcp , pkt ) ;
free_inflight_packet ( pkt ) ;
lp_ts = ts ; // Update last positive ts
acked_count + + ;
} else {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " process_section_ack: no inflight packet found for id=%u " , id ) ;
}
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_ack: successfully ACKed %u/%u packets " , acked_count , count ) ;
// Use lp_ts in retrans_check if needed
etcp - > last_rx_ack_id = ( data [ 1 + ( count - 1 ) * 4 ] < < 8 ) | data [ 2 + ( count - 1 ) * 4 ] ; // Last ACK ID
}
// Process RETRANS (expanded)
static void process_section_retrans ( struct ETCP_CONN * etcp , uint8_t * data , uint16_t len ) {
if ( len < 1 ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_retrans: etcp=%p, data=%p, len=%u " , etcp , data , len ) ;
if ( len < 1 ) {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " process_section_retrans: invalid length %u " , len ) ;
return ;
}
uint8_t count = data [ 0 ] ;
if ( 1 + count * 2 > len ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_retrans: count=%u " , count ) ;
if ( 1 + count * 2 > len ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " process_section_retrans: invalid count %u for len %u " , count , len ) ;
return ;
}
int retrans_count = 0 ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_retrans: processing %u retrans requests " , count ) ;
for ( uint8_t i = 0 ; i < count ; i + + ) {
uint16_t id = ( data [ 1 + i * 2 ] < < 8 ) | data [ 2 + i * 2 ] ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " process_section_retrans: requesting retrans for id=%u " , id ) ;
inflight_packet_t * pkt = find_inflight ( etcp , id , INFLIGHT_STATE_WAIT_ACK ) ;
if ( pkt ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_retrans: found packet %u, marking for retransmission " , id ) ;
pkt - > retrans_req_count + + ;
pkt - > need_retrans = 1 ;
move_to_wait_send ( etcp , pkt ) ;
retrans_count + + ;
} else {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " process_section_retrans: no inflight packet found for id=%u " , id ) ;
}
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_retrans: marked %u/%u packets for retransmission " , retrans_count , count ) ;
}
// Process TIMESTAMP (expanded)
static void process_section_timestamp ( struct ETCP_CONN * etcp , uint8_t * data , uint16_t len ) {
if ( len ! = 2 ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_timestamp: etcp=%p, data=%p, len=%u " , etcp , data , len ) ;
if ( len ! = 2 ) {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " process_section_timestamp: invalid length %u (expected 2) " , len ) ;
return ;
}
uint16_t recv_ts = ( data [ 0 ] < < 8 ) | data [ 1 ] ;
uint16_t rtt = timestamp_diff ( get_current_timestamp ( ) , recv_ts ) ;
uint16_t current_ts = get_current_timestamp ( ) ;
uint16_t rtt = timestamp_diff ( current_ts , recv_ts ) ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_timestamp: recv_ts=%u, current_ts=%u, calculated_rtt=%u " ,
recv_ts , current_ts , rtt ) ;
update_rtt ( etcp , rtt ) ;
}
// Process PAYLOAD (expanded)
static void process_section_payload ( struct ETCP_CONN * etcp , uint8_t * data , uint16_t len ) {
if ( len < 2 ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_payload: etcp=%p, data=%p, len=%u " , etcp , data , len ) ;
if ( len < 2 ) {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " process_section_payload: invalid length %u (minimum 2) " , len ) ;
return ;
}
uint16_t id = ( data [ 0 ] < < 8 ) | data [ 1 ] ;
if ( id < = etcp - > last_rx_id ) return ; // Dup
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_payload: packet id=%u, last_rx_id=%u " , id , etcp - > last_rx_id ) ;
if ( id < = etcp - > last_rx_id ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " process_section_payload: duplicate packet id=%u (last_rx_id=%u), ignoring " ,
id , etcp - > last_rx_id ) ;
return ; // Dup
}
rx_packet_t * new_rx = calloc ( 1 , sizeof ( rx_packet_t ) ) ;
if ( ! new_rx ) return ;
if ( ! new_rx ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " process_section_payload: failed to allocate rx_packet_t " ) ;
return ;
}
new_rx - > id = id ;
new_rx - > timestamp = get_current_timestamp ( ) ;
new_rx - > data_len = len - 2 ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_payload: allocated rx_packet %p (id=%u, data_len=%u, timestamp=%u) " ,
new_rx , new_rx - > id , new_rx - > data_len , new_rx - > timestamp ) ;
new_rx - > data = malloc ( new_rx - > data_len ) ;
if ( ! new_rx - > data ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " process_section_payload: failed to allocate rx data buffer (size=%u) " , new_rx - > data_len ) ;
free ( new_rx ) ;
return ;
}
memcpy ( new_rx - > data , data + 2 , new_rx - > data_len ) ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_payload: copied %u bytes of payload data " , new_rx - > data_len ) ;
// Insert sorted (assume IDs sequential, no wrap for simplicity)
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " process_section_payload: inserting packet into rx_list (id=%u) " , id ) ;
rx_packet_t * * ptr = & etcp - > rx_list ;
while ( * ptr & & ( * ptr ) - > id < id ) ptr = & ( * ptr ) - > next ;
if ( * ptr & & ( * ptr ) - > id = = id ) {
// Dup
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " process_section_payload: duplicate packet id=%u found in rx_list, discarding " , id ) ;
free ( new_rx - > data ) ;
free ( new_rx ) ;
return ;
}
new_rx - > next = * ptr ;
* ptr = new_rx ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_payload: successfully inserted packet id=%u into rx_list " , id ) ;
if ( id > etcp - > last_rx_id ) etcp - > last_rx_id = id ;
if ( id > etcp - > last_rx_id ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " process_section_payload: updating last_rx_id from %u to %u " ,
etcp - > last_rx_id , id ) ;
etcp - > last_rx_id = id ;
}
// Pending ACK
if ( etcp - > pending_ack_count < MAX_PENDING ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " process_section_payload: adding packet id=%u to pending ACKs (count=%u) " ,
id , etcp - > pending_ack_count + 1 ) ;
etcp - > pending_ack_ids [ etcp - > pending_ack_count ] = id ;
etcp - > pending_ack_ts [ etcp - > pending_ack_count + + ] = new_rx - > timestamp ;
} else {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " process_section_payload: pending ACK buffer full (%u), cannot add id=%u " ,
MAX_PENDING , id ) ;
}
// Schedule ACK
if ( ! etcp - > ack_timer ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_payload: scheduling ACK timer (delay=%u tb) " , ACK_DELAY_TB ) ;
etcp - > ack_timer = uasync_set_timeout ( etcp - > instance - > ua , ACK_DELAY_TB , etcp , ack_timer_cb ) ;
}
etcp - > bytes_received_total + = len ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_payload: packet id=%u processed successfully, bytes_received_total=%u " ,
id , etcp - > bytes_received_total ) ;
}
// Process MEAS_TS (expanded: send resp)
static void process_section_meas_ts ( struct ETCP_CONN * etcp , uint8_t * data , uint16_t len ) {
if ( len < 3 ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_meas_ts: etcp=%p, data=%p, len=%u " , etcp , data , len ) ;
if ( len < 3 ) {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " process_section_meas_ts: invalid length %u (minimum 3) " , len ) ;
return ;
}
uint16_t start_id = ( data [ 0 ] < < 8 ) | data [ 1 ] ;
uint8_t n = data [ 2 ] ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_meas_ts: start_id=%u, n=%u " , start_id , n ) ;
// Send MEAS_RESP (add to pending or direct)
// Placeholder: assume added to next packet
// For testing, log
DEBUG_INFO ( DEBUG_CATEGORY_ETCP , " MEAS_TS: start_id=%u n=%u " , start_id , n ) ;
// Build resp [start_id(2)][n(1)][recv_ts(2)]
// But since appended in optional, set state
@ -527,11 +777,17 @@ static void process_section_meas_ts(struct ETCP_CONN* etcp, uint8_t* data, uint1
// Process MEAS_RESP (expanded: calc bw)
static void process_section_meas_resp ( struct ETCP_CONN * etcp , uint8_t * data , uint16_t len ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_meas_resp: etcp=%p, data=%p, len=%u " , etcp , data , len ) ;
// Assume format [start_id(2)][n(1)][recv_ts(2)]
if ( len < 5 ) return ;
if ( len < 5 ) {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " process_section_meas_resp: invalid length %u (minimum 5) " , len ) ;
return ;
}
uint16_t start_id = ( data [ 0 ] < < 8 ) | data [ 1 ] ;
uint8_t n = data [ 2 ] ;
uint16_t recv_ts = ( data [ 3 ] < < 8 ) | data [ 4 ] ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " process_section_meas_resp: start_id=%u, n=%u, recv_ts=%u " , start_id , n , recv_ts ) ;
if ( etcp - > burst_start_id ! = start_id | | ! etcp - > burst_in_progress ) return ;
@ -544,51 +800,111 @@ static void process_section_meas_resp(struct ETCP_CONN* etcp, uint8_t* data, uin
// Detect gaps (expanded)
static void detect_gaps_and_request_retrans ( struct ETCP_CONN * etcp ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " detect_gaps_and_request_retrans: etcp=%p, last_delivered_id=%u " ,
etcp , etcp - > last_delivered_id ) ;
rx_packet_t * rx = etcp - > rx_list ;
uint16_t expected = etcp - > last_delivered_id + 1 ;
int gaps_found = 0 ;
int retrans_requested = 0 ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " detect_gaps_and_request_retrans: starting gap detection from expected_id=%u " , expected ) ;
while ( rx ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " detect_gaps_and_request_retrans: checking rx packet id=%u (expected=%u) " ,
rx - > id , expected ) ;
if ( rx - > id > expected ) {
// Gap: add retrans for expected to rx->id -1
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " detect_gaps_and_request_retrans: gap detected - expected=%u, found=%u " ,
expected , rx - > id ) ;
gaps_found + + ;
for ( uint16_t gid = expected ; gid < rx - > id & & etcp - > pending_retrans_count < MAX_PENDING ; gid + + ) {
etcp - > pending_retrans_ids [ etcp - > pending_retrans_count + + ] = gid ;
retrans_requested + + ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " detect_gaps_and_request_retrans: queued retrans request for id=%u " , gid ) ;
}
expected = rx - > id + 1 ;
} else if ( rx - > id = = expected ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " detect_gaps_and_request_retrans: found expected packet id=%u " , expected ) ;
expected + + ;
} else {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " detect_gaps_and_request_retrans: packet id=%u < expected=%u, already processed " ,
rx - > id , expected ) ;
}
rx = rx - > next ;
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " detect_gaps_and_request_retrans: completed - gaps_found=%d, retrans_requested=%u " ,
gaps_found , retrans_requested ) ;
}
// Deliver contiguous (unchanged)
// Deliver contiguous (expand ed)
static void deliver_contiguous_packets ( struct ETCP_CONN * etcp ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " deliver_contiguous_packets: etcp=%p, last_delivered_id=%u " ,
etcp , etcp - > last_delivered_id ) ;
rx_packet_t * * ptr = & etcp - > rx_list ;
int delivered_count = 0 ;
uint32_t delivered_bytes = 0 ;
while ( * ptr & & ( * ptr ) - > id = = etcp - > last_delivered_id + 1 ) {
rx_packet_t * rx = * ptr ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " deliver_contiguous_packets: delivering packet id=%u (data_len=%u) " ,
rx - > id , rx - > data_len ) ;
struct ll_entry * entry = queue_entry_new ( rx - > data_len ) ;
if ( entry ) {
memcpy ( ll_entry_data ( entry ) , rx - > data , rx - > data_len ) ;
queue_entry_put ( etcp - > output_queue , entry ) ;
delivered_bytes + = rx - > data_len ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " deliver_contiguous_packets: queued packet id=%u to output_queue " , rx - > id ) ;
} else {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " deliver_contiguous_packets: failed to allocate queue entry for packet id=%u " , rx - > id ) ;
}
etcp - > last_delivered_id = rx - > id ;
* ptr = rx - > next ;
free ( rx - > data ) ;
free ( rx ) ;
delivered_count + + ;
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " deliver_contiguous_packets: delivered %u contiguous packets (%u bytes), last_delivered_id=%u " ,
delivered_count , delivered_bytes , etcp - > last_delivered_id ) ;
}
// Retrans check (expanded with lp_ts)
static void retrans_check ( struct ETCP_CONN * etcp ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " retrans_check: etcp=%p, wait_ack_list=%p, rtt_avg_10=%u, jitter=%u " ,
etcp , etcp - > wait_ack_list , etcp - > rtt_avg_10 , etcp - > jitter ) ;
uint64_t now = get_current_time_units ( ) ;
inflight_packet_t * * ptr = & etcp - > wait_ack_list ;
uint16_t lp_ts = 0 ;
int checked_packets = 0 ;
int retrans_packets = 0 ;
uint16_t timeout_calc = ( uint16_t ) ( etcp - > rtt_avg_10 * RETRANS_K1 + etcp - > jitter * RETRANS_K2 ) ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " retrans_check: timeout calculation: rtt_avg_10=%u * %.1f + jitter=%u * %.1f = %u " ,
etcp - > rtt_avg_10 , RETRANS_K1 , etcp - > jitter , RETRANS_K2 , timeout_calc ) ;
while ( * ptr ) {
inflight_packet_t * pkt = * ptr ;
checked_packets + + ;
uint16_t timeout = ( uint16_t ) ( etcp - > rtt_avg_10 * RETRANS_K1 + etcp - > jitter * RETRANS_K2 ) ;
if ( timestamp_diff ( now , pkt - > last_timestamp ) > timeout ) {
uint16_t time_since_send = timestamp_diff ( now , pkt - > last_timestamp ) ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " retrans_check: checking packet id=%u (time_since_send=%u, timeout=%u, need_retrans=%u) " ,
pkt - > id , time_since_send , timeout , pkt - > need_retrans ) ;
if ( time_since_send > timeout ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " retrans_check: packet id=%u timed out, marking for retransmission " , pkt - > id ) ;
pkt - > need_retrans = 1 ;
move_to_wait_send ( etcp , pkt ) ;
etcp - > retransmissions_count + + ;
retrans_packets + + ;
ptr = & etcp - > wait_ack_list ; // Restart scan after move
continue ;
}
@ -597,97 +913,200 @@ static void retrans_check(struct ETCP_CONN* etcp) {
ptr = & pkt - > next ;
}
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " retrans_check: completed - checked %d packets, retransmitted %d packets, total_retransmissions=%u " ,
checked_packets , retrans_packets , etcp - > retransmissions_count ) ;
// Reschedule if list not empty
if ( etcp - > wait_ack_list ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " retrans_check: rescheduling retrans_timer (wait_ack_list not empty) " ) ;
etcp - > retrans_timer = uasync_set_timeout ( etcp - > instance - > ua , 20 , etcp , retrans_timer_cb ) ;
} else {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " retrans_check: clearing retrans_timer (wait_ack_list empty) " ) ;
etcp - > retrans_timer = NULL ;
}
}
// Find inflight (unchang ed)
// Find inflight (expand ed)
static inflight_packet_t * find_inflight ( struct ETCP_CONN * etcp , uint16_t id , uint8_t state ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " find_inflight: etcp=%p, id=%u, state=%u " , etcp , id , state ) ;
inflight_packet_t * list = ( state = = INFLIGHT_STATE_WAIT_ACK ) ? etcp - > wait_ack_list : etcp - > wait_send_list ;
while ( list ) {
if ( list - > id = = id ) return list ;
list = list - > next ;
inflight_packet_t * pkt = list ;
int position = 0 ;
while ( pkt & & pkt - > id ! = id ) {
pkt = pkt - > next ;
position + + ;
}
return NULL ;
if ( pkt ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " find_inflight: found packet %u at position %d in %s list " ,
id , position , ( state = = INFLIGHT_STATE_WAIT_ACK ) ? " wait_ack " : " wait_send " ) ;
} else {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " find_inflight: packet %u not found in %s list " ,
id , ( state = = INFLIGHT_STATE_WAIT_ACK ) ? " wait_ack " : " wait_send " ) ;
}
return pkt ;
return pkt ;
}
// Move to wait_send (unchanged)
// Move to wait_send (expand ed)
static void move_to_wait_send ( struct ETCP_CONN * etcp , inflight_packet_t * pkt ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " move_to_wait_send: etcp=%p, pkt=%p (id=%u, state=%u) " ,
etcp , pkt , pkt - > id , pkt - > state ) ;
remove_inflight ( etcp , pkt ) ;
pkt - > state = INFLIGHT_STATE_WAIT_SEND ;
pkt - > next = etcp - > wait_send_list ;
etcp - > wait_send_list = pkt ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " move_to_wait_send: moved packet %u to wait_send_list " , pkt - > id ) ;
}
// Remove inflight (unchang ed)
// Remove inflight (expand ed)
static void remove_inflight ( struct ETCP_CONN * etcp , inflight_packet_t * pkt ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " remove_inflight: etcp=%p, pkt=%p (id=%u, state=%u) " ,
etcp , pkt , pkt - > id , pkt - > state ) ;
inflight_packet_t * * list = ( pkt - > state = = INFLIGHT_STATE_WAIT_ACK ) ? & etcp - > wait_ack_list : & etcp - > wait_send_list ;
inflight_packet_t * * ptr = list ;
while ( * ptr & & * ptr ! = pkt ) ptr = & ( * ptr ) - > next ;
if ( * ptr ) * ptr = ( * ptr ) - > next ;
int position = 0 ;
while ( * ptr & & * ptr ! = pkt ) {
ptr = & ( * ptr ) - > next ;
position + + ;
}
if ( * ptr ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " remove_inflight: found packet %u at position %d in %s list " ,
pkt - > id , position , ( pkt - > state = = INFLIGHT_STATE_WAIT_ACK ) ? " wait_ack " : " wait_send " ) ;
* ptr = ( * ptr ) - > next ;
} else {
DEBUG_WARN ( DEBUG_CATEGORY_ETCP , " remove_inflight: packet %u not found in %s list " ,
pkt - > id , ( pkt - > state = = INFLIGHT_STATE_WAIT_ACK ) ? " wait_ack " : " wait_send " ) ;
}
}
// Start burst (placeholder expanded)
static void start_burst_measurement ( struct ETCP_CONN * etcp , struct ETCP_LINK * link ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " start_burst_measurement: etcp=%p, link=%p " , etcp , link ) ;
// Periodically call this
etcp - > burst_in_progress = 1 ;
uint16_t old_start_id = etcp - > burst_start_id ;
etcp - > burst_start_id = etcp - > next_tx_id ;
DEBUG_INFO ( DEBUG_CATEGORY_ETCP , " start_burst_measurement: started burst measurement - burst_start_id=%u (was %u) " ,
etcp - > burst_start_id , old_start_id ) ;
// Delay first, then burst 4
// Assume handled in request_pkt by adding meas_ts to next BURST_SIZE pkts
}
// Update link bw (add bandwidth to ETCP_LINK in etcp_connections.h)
/// In ETCP_LINK add: uint32_t bandwidth; // bits/sec
// Update link bw (expanded)
static void update_link_bandwidth ( struct ETCP_LINK * link , uint32_t new_bw ) {
link - > bandwidth = ( link - > bandwidth * 0.9 ) + ( new_bw * 0.1 ) ; // Smooth
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " update_link_bandwidth: link=%p, new_bw=%u bits/sec (current=%u) " ,
link , new_bw , link - > bandwidth ) ;
uint32_t old_bw = link - > bandwidth ;
link - > bandwidth = ( uint32_t ) ( ( link - > bandwidth * 0.9 ) + ( new_bw * 0.1 ) ) ; // Smooth
DEBUG_INFO ( DEBUG_CATEGORY_ETCP , " update_link_bandwidth: updated link bandwidth from %u to %u bits/sec (new_bw=%u) " ,
old_bw , link - > bandwidth , new_bw ) ;
}
// Update RTT (expanded with history)
static void update_rtt ( struct ETCP_CONN * etcp , uint16_t new_rtt ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " update_rtt: etcp=%p, new_rtt=%u (last_rtt=%u, avg_10=%u, avg_100=%u) " ,
etcp , new_rtt , etcp - > rtt_last , etcp - > rtt_avg_10 , etcp - > rtt_avg_100 ) ;
etcp - > rtt_last = new_rtt ;
etcp - > rtt_avg_10 = ( etcp - > rtt_avg_10 * 9 + new_rtt ) / 10 ;
etcp - > rtt_avg_100 = ( etcp - > rtt_avg_100 * 99 + new_rtt ) / 100 ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " update_rtt: updated RTT metrics - last=%u, avg_10=%u, avg_100=%u " ,
etcp - > rtt_last , etcp - > rtt_avg_10 , etcp - > rtt_avg_100 ) ;
// History for jitter
etcp - > rtt_history [ etcp - > rtt_history_idx + + ] = new_rtt ;
if ( etcp - > rtt_history_idx > = RTT_HISTORY_SIZE ) etcp - > rtt_history_idx = 0 ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " update_rtt: stored RTT in history at index %u " ,
( etcp - > rtt_history_idx + RTT_HISTORY_SIZE - 1 ) % RTT_HISTORY_SIZE ) ;
calculate_jitter ( etcp ) ;
}
static void calculate_jitter ( struct ETCP_CONN * etcp ) {
uint16_t min_rtt = UINT16_MAX ;
uint16_t max_rtt = 0 ;
int valid_samples = 0 ;
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " calculate_jitter: analyzing RTT history (history_idx=%u) " , etcp - > rtt_history_idx ) ;
for ( int i = 0 ; i < RTT_HISTORY_SIZE ; i + + ) {
if ( etcp - > rtt_history [ i ] = = 0 ) continue ;
if ( etcp - > rtt_history [ i ] < min_rtt ) min_rtt = etcp - > rtt_history [ i ] ;
if ( etcp - > rtt_history [ i ] > max_rtt ) max_rtt = etcp - > rtt_history [ i ] ;
if ( etcp - > rtt_history [ i ] = = 0 ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " calculate_jitter: history[%d] = 0 (invalid), skipping " , i ) ;
continue ;
}
valid_samples + + ;
if ( etcp - > rtt_history [ i ] < min_rtt ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " calculate_jitter: new min_rtt=%u (was %u) at history[%d] " ,
etcp - > rtt_history [ i ] , min_rtt , i ) ;
min_rtt = etcp - > rtt_history [ i ] ;
}
if ( etcp - > rtt_history [ i ] > max_rtt ) {
DEBUG_TRACE ( DEBUG_CATEGORY_ETCP , " calculate_jitter: new max_rtt=%u (was %u) at history[%d] " ,
etcp - > rtt_history [ i ] , max_rtt , i ) ;
max_rtt = etcp - > rtt_history [ i ] ;
}
}
uint16_t old_jitter = etcp - > jitter ;
etcp - > jitter = max_rtt - min_rtt ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " calculate_jitter: completed - valid_samples=%d, min_rtt=%u, max_rtt=%u, jitter=%u (was %u) " ,
valid_samples , min_rtt , max_rtt , etcp - > jitter , old_jitter ) ;
}
// Timers (unchanged)
// Timers (expanded with debug )
static void retrans_timer_cb ( void * arg ) {
struct ETCP_CONN * etcp = arg ;
struct ETCP_CONN * etcp = ( struct ETCP_CONN * ) arg ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " retrans_timer_cb: etcp=%p, triggered retransmission check " , etcp ) ;
retrans_check ( etcp ) ;
}
static void ack_timer_cb ( void * arg ) {
struct ETCP_CONN * etcp = arg ;
struct ETCP_CONN * etcp = ( struct ETCP_CONN * ) arg ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " ack_timer_cb: etcp=%p, processing pending ACKs " , etcp ) ;
etcp - > ack_timer = NULL ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " ack_timer_cb: processing %u pending ACKs " , etcp - > pending_ack_count ) ;
// Force send ACK if pending (e.g., empty payload pkt)
// For now, assume appended next
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " ack_timer_cb: ACK processing completed " ) ;
}
// Get stats (expanded)
void etcp_get_stats ( struct ETCP_CONN * etcp , size_t * packets_sent , size_t * packets_recv ,
size_t * pool_allocs , size_t * pool_reuse ) {
if ( packets_sent ) * packets_sent = etcp - > total_packets_sent ;
if ( packets_recv ) * packets_recv = etcp - > bytes_received_total / 1400 ; // Approx MTU
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_get_stats: etcp=%p " , etcp ) ;
if ( packets_sent ) {
* packets_sent = etcp - > total_packets_sent ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_get_stats: total_packets_sent=%zu " , * packets_sent ) ;
}
if ( packets_recv ) {
* packets_recv = etcp - > bytes_received_total / 1400 ; // Approx MTU
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_get_stats: bytes_received_total=%u, estimated_packets=%zu " ,
etcp - > bytes_received_total , * packets_recv ) ;
}
if ( pool_allocs | | pool_reuse ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_get_stats: getting memory pool stats " ) ;
memory_pool_get_stats ( etcp - > instance - > pkt_pool , pool_allocs , pool_reuse ) ;
if ( pool_allocs ) DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_get_stats: pool_allocs=%zu " , * pool_allocs ) ;
if ( pool_reuse ) DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " etcp_get_stats: pool_reuse=%zu " , * pool_reuse ) ;
}
}