You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

489 lines
16 KiB

// etcp.c - ETCP Protocol Implementation
#include "../lib/u_async.h"
#include "utun_instance.h"
#include "etcp.h"
#include "../lib/debug_config.h"
#include "../lib/ll_queue.h"
#include "crc32.h"
#include "secure_channel.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
// Service packet headers (from protocol, but reset/init handled elsewhere)
#define ETCP_ACK_HEADER 0x01
#define ETCP_RETRANS_HEADER_BASE 0x10 // 0x10 to 0x2F for retrans requests
#define ETCP_PAYLOAD_HEADER 0x00
#define ETCP_INIT_RESPONSE 0x03
// Forward declarations of internal functions (adapted from etcp_master)
static void retransmit_check(struct ETCP_CONN* etcp);
static void update_metrics(struct ETCP_CONN* etcp, uint16_t rtt);
static uint16_t timestamp_diff(uint16_t t1, uint16_t t2);
static int id_compare(uint16_t id1, uint16_t id2);
static void retransmit_packet(struct ETCP_CONN* etcp, uint16_t id);
// Timer callbacks
static void tx_timer_callback(void* arg);
static void retransmit_timer_callback(void* arg);
// Internal queue callbacks
static void tx_queue_callback(struct ll_queue* q, struct ll_entry* entry, void* arg);
static void schedule_ack_timer(struct ETCP_CONN* etcp);
static void request_retransmission_for_gaps(struct ETCP_CONN* etcp);
static void queue_clear(struct ll_queue* q) {
struct ll_entry* entry;
while ((entry = queue_entry_get(q)) != NULL) {
queue_entry_free(entry);
}
}
static void etcp_update_window(struct ETCP_CONN* etcp) {
etcp->window_size = 65536; // Initial window size
etcp->retrans_timer_period = 20; // Default retransmit period
}
// Reset connection (adapted from etcp_reset in master, without packet sending)
void etcp_conn_reset(struct ETCP_CONN* etcp) {
if (!etcp) return;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_conn_reset: resetting instance");
// Cancel timers
if (etcp->next_tx_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->next_tx_timer);
etcp->next_tx_timer = NULL;
}
if (etcp->retransmit_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->retransmit_timer);
etcp->retransmit_timer = NULL;
}
// Clear queues (but keep them)
queue_clear(etcp->input_queue);
queue_clear(etcp->output_queue);
// Clear lists
rx_packet_t* rx = etcp->rx_list;
while (rx) {
rx_packet_t* next = rx->next;
if (rx->data) free(rx->data);
free(rx);
rx = next;
}
etcp->rx_list = NULL;
sent_packet_t* sent = etcp->sent_list;
while (sent) {
sent_packet_t* next = sent->next;
if (sent->data) free(sent->data);
free(sent);
sent = next;
}
etcp->sent_list = NULL;
// Reset metrics and stats
etcp->rtt_last = 0;
etcp->rtt_avg_10 = 0;
etcp->rtt_avg_100 = 0;
etcp->jitter = 0;
etcp->bytes_sent_total = 0;
etcp->retransmissions_count = 0;
etcp->ack_packets_count = 0;
etcp->control_packets_count = 0;
etcp->total_packets_sent = 0;
etcp->unique_packets_sent = 0;
etcp->bytes_received_total = 0;
// Reset IDs
etcp->next_tx_id = 1;
etcp->last_sent_id = 0;
etcp->last_rx_id = 0;
etcp->last_delivered_id = 0;
// Reset history
etcp->rtt_history_idx = 0;
etcp->rtt_history_count = 0;
// Reset pending
etcp->pending_ack_count = 0;
etcp->pending_retransmit_count = 0;
// Reset window
etcp->unacked_bytes = 0;
etcp->last_acked_id = 0;
etcp->last_rx_ack_id = 0;
etcp->next_retrans_time = 0;
etcp->window_blocked = 0;
// Reset forward progress
etcp->oldest_missing_id = 0;
etcp->missing_since_time = 0;
etcp_update_window(etcp);
}
// Creating ETCP instance
// после создания надо добавить peer bublic key.
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance) {
if (!instance) return NULL;
struct ETCP_CONN* etcp = calloc(1, sizeof(struct ETCP_CONN));
if (!etcp) return NULL;
etcp->mtu = 1500; // Default MTU
etcp->instance = instance;
// Initialize crypto context
if (sc_init_ctx(&etcp->crypto_ctx, &etcp->instance->my_keys) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connection_create: failed to initialize crypto context for node %llu", (unsigned long long)instance->node_id);
free(etcp);
return NULL;
}
// Initialize queues (tx_queue is input_queue)
etcp->input_queue = queue_new(instance->ua,NULL);
if (!etcp->input_queue) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "etcp_connection_create: failed to create input queue for node %llu", (unsigned long long)instance->node_id);
free(etcp);
return NULL;
}
etcp->output_queue = queue_new(instance->ua,NULL);
if (!etcp->output_queue) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "etcp_connection_create: failed to create output queue for node %llu", (unsigned long long)instance->node_id);
queue_free(etcp->input_queue);
free(etcp);
return NULL;
}
// Set callback for input_queue (tx_queue)
queue_set_callback(etcp->input_queue, tx_queue_callback, etcp);
// Initialize state (from master)
etcp->bandwidth = 10000; // Default: 10000 bytes per timebase (0.1us)
etcp->last_sent_timestamp = get_current_timestamp();
etcp->bytes_allowed = 0;
// Reset all other fields to initial state
etcp_conn_reset(etcp);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_connection_create: created instance with node_id=%llu, mtu=%d",
(unsigned long long)etcp->instance->node_id, etcp->mtu);
etcp->next=instance->connections;
instance->connections=etcp;
instance->connections_count++;
return etcp;
}
// Destroying ETCP instance
void etcp_connection_close(struct ETCP_CONN* etcp) {
if (!etcp) return;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_connection_close: destroying instance");
// Cancel timers
if (etcp->next_tx_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->next_tx_timer);
etcp->next_tx_timer = NULL;
}
if (etcp->retransmit_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->retransmit_timer);
etcp->retransmit_timer = NULL;
}
// Free queues
if (etcp->input_queue) queue_free(etcp->input_queue);
if (etcp->output_queue) queue_free(etcp->output_queue);
// Free rx_list
rx_packet_t* rx = etcp->rx_list;
while (rx) {
rx_packet_t* next = rx->next;
if (rx->data) free(rx->data);
free(rx);
rx = next;
}
// Free sent_list
sent_packet_t* sent = etcp->sent_list;
while (sent) {
sent_packet_t* next = sent->next;
if (sent->data) free(sent->data);
free(sent);
sent = next;
}
free(etcp);
}
// Process incoming packet (partial, truncated in original)
void etcp_conn_input(struct ETCP_DGRAM* pkt) {
if (!pkt) return;
struct ETCP_CONN* etcp=pkt->link->etcp;
if (!etcp) return;
uint8_t* data = pkt->data;
size_t len = pkt->data_len;
if (len < 4) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_conn_input: packet too short (%zu bytes) from node %llu", len, (unsigned long long)etcp->instance->node_id);
return; // Min header
}
uint16_t id = (data[0] << 8) | data[1];
uint16_t timestamp = (data[2] << 8) | data[3];
// Check for INIT_RESPONSE packet (special control packet)
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[ETCP DEBUG] etcp_conn_input: id=%u, len=%zu, first bytes: %02x %02x %02x %02x %02x",
id, len, data[0], data[1], data[2], data[3], data[4]);
data += 4; len -= 4; // Skip header for regular packet processing
int has_payload = 0;
uint8_t* payload_data = NULL;
size_t payload_len = 0;
int is_duplicate = 0;
while (len > 0) {
uint8_t section_header = *data++;
len--;
if (section_header == ETCP_ACK_HEADER) {
// ACK section
uint8_t count = *data++;
len--;
for (uint8_t i = 0; i < count; i++) {
uint16_t ack_id = (data[0] << 8) | data[1];
uint16_t ack_ts = (data[2] << 8) | data[3];
data += 4; len -= 4;
// Process ACK: remove from sent_list, update window
sent_packet_t** ptr = &etcp->sent_list;
while (*ptr) {
if ((*ptr)->id == ack_id) {
sent_packet_t* to_free = *ptr;
*ptr = to_free->next;
etcp->unacked_bytes -= to_free->payload_len;
update_metrics(etcp, timestamp_diff(get_current_timestamp(), ack_ts));
free(to_free->data);
free(to_free);
break;
}
ptr = &(*ptr)->next;
}
}
// last_delivered and last_rx
uint16_t last_delivered = (data[0] << 8) | data[1];
uint16_t last_rx = (data[2] << 8) | data[3];
data += 4; len -= 4;
etcp->last_rx_ack_id = last_rx;
} else if ((section_header & 0xF0) == ETCP_RETRANS_HEADER_BASE) {
// Retrans request
uint8_t count = (section_header & 0x0F) + 1;
for (uint8_t i = 0; i < count; i++) {
uint16_t retrans_id = (data[0] << 8) | data[1];
data += 2;
len -= 2;
retransmit_packet(etcp, retrans_id);
}
// last_delivered and last_rx
uint16_t last_delivered = (data[0] << 8) | data[1];
uint16_t last_rx = (data[2] << 8) | data[3];
data += 4; len -= 4;
} else if (section_header == ETCP_PAYLOAD_HEADER) {
// Payload section
has_payload = 1;
payload_data = data;
payload_len = len;
break; // Payload is last
}
}
// Check for duplicate
rx_packet_t* rx = etcp->rx_list;
while (rx) {
if (rx->id == id) {
is_duplicate = 1;
break;
}
rx = rx->next;
}
if (is_duplicate) {
// Add to pending ACKs
if (etcp->pending_ack_count < 32) {
etcp->pending_ack_ids[etcp->pending_ack_count] = id;
etcp->pending_ack_timestamps[etcp->pending_ack_count] = timestamp;
etcp->pending_ack_count++;
}
schedule_ack_timer(etcp);
return;
}
// Insert into rx_list (sorted)
rx_packet_t* new_rx = calloc(1, sizeof(rx_packet_t));
if (!new_rx) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "etcp_conn_input: failed to allocate memory for rx packet from node %llu", (unsigned long long)etcp->instance->node_id);
return;
}
new_rx->id = id;
new_rx->timestamp = timestamp;
new_rx->has_payload = has_payload;
if (has_payload) {
new_rx->data = malloc(payload_len);
if (!new_rx->data) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "etcp_conn_input: failed to allocate %zu bytes for payload data from node %llu", payload_len, (unsigned long long)etcp->instance->node_id);
free(new_rx);
return;
}
memcpy(new_rx->data, payload_data, payload_len);
new_rx->data_len = payload_len;
etcp->bytes_received_total += payload_len;
}
// Insert sorted
rx_packet_t** ptr = &etcp->rx_list;
while (*ptr && id_compare((*ptr)->id, id) < 0) {
ptr = &(*ptr)->next;
}
new_rx->next = *ptr;
*ptr = new_rx;
// Update last_rx_id
if (id_compare(id, etcp->last_rx_id) > 0) {
etcp->last_rx_id = id;
}
// Add to pending ACKs
if (etcp->pending_ack_count < 32) {
etcp->pending_ack_ids[etcp->pending_ack_count] = id;
etcp->pending_ack_timestamps[etcp->pending_ack_count] = timestamp;
etcp->pending_ack_count++;
}
// Request retrans for gaps and deliver contiguous
request_retransmission_for_gaps(etcp);
schedule_ack_timer(etcp);
}
// Getting statistics (extended with master stats)
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 (!etcp) return;
if (packets_sent) *packets_sent = etcp->total_packets_sent;
if (packets_recv) *packets_recv = etcp->bytes_received_total / 100; // Approximate
if (pool_allocs || pool_reuse) {
memory_pool_get_stats(etcp->instance->pkt_pool, pool_allocs, pool_reuse);
}
}
// ... (rest of functions from master, adapted to ETCP_CONN and using etcp_link_send for tx)
// For tx: assume primary channel (etcp->channels), or iterate if multi-path
static void send_etcp_packet(struct ETCP_CONN* etcp, uint8_t* pkt, uint16_t len) {
if (!etcp->links) {
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "send_etcp_packet: no links available for node %llu", (unsigned long long)etcp->instance->node_id);
return; // No links
}
// Send to primary link (or iterate for multi-path)
char dg_mem[1600];
struct ETCP_DGRAM* dg=(struct ETCP_DGRAM*)&dg_mem;
dg->data_len=len;
dg->noencrypt_len=0;
dg->link=etcp->links;// first link TODO: добавить отправку по нескольким линкам
etcp_encrypt_send(dg);
}
static void update_metrics(struct ETCP_CONN* etcp, uint16_t rtt) {
etcp->rtt_last = rtt;
// Update averages, jitter, etc.
// Placeholder: etcp->rtt_avg_10 = (etcp->rtt_avg_10 * 9 + rtt) / 10;
// etcp->rtt_avg_100 = (etcp->rtt_avg_100 * 99 + rtt) / 100;
}
uint64_t get_current_time_units() {
struct timeval tv;
gettimeofday(&tv, NULL);
return ((uint64_t)tv.tv_sec * 10000ULL) + (tv.tv_usec / 100);
}
uint16_t get_current_timestamp() {
return (uint16_t)get_current_time_units();
}
static uint16_t timestamp_diff(uint16_t t1, uint16_t t2) {
if (t1 >= t2) return t1 - t2;
return (0xFFFF - t2) + t1 + 1; // Wrap around
}
static int id_compare(uint16_t id1, uint16_t id2) {
int16_t diff = id1 - id2;
if (diff == 0) return 0;
return (diff > 0) ? 1 : -1; // Simple for now, add wrap logic if needed
}
static void retransmit_packet(struct ETCP_CONN* etcp, uint16_t id) {
sent_packet_t* sent = etcp->sent_list;
while (sent) {
if (sent->id == id) {
send_etcp_packet(etcp, sent->data, sent->data_len);
etcp->retransmissions_count++;
break;
}
sent = sent->next;
}
}
static void tx_queue_callback(struct ll_queue* q, struct ll_entry* entry, void* arg) {
(void)q;
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
// Process entry to send packet
// Placeholder: uint8_t* data = ll_entry_data(entry);
// size_t len = ll_entry_size(entry);
// // Build packet and send
// send_etcp_packet(etcp, data, len);
queue_resume_callback(q);
}
static void schedule_ack_timer(struct ETCP_CONN* etcp) {
// Placeholder: set timeout to send ACKs
}
static void request_retransmission_for_gaps(struct ETCP_CONN* etcp) {
// Detect gaps in rx_list and add to pending_retransmit
}
static void retransmit_check(struct ETCP_CONN* etcp) {
// Check sent_list for timeouts
}
static void tx_timer_callback(void* arg) {
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
// Handle next tx
}
static void retransmit_timer_callback(void* arg) {
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
retransmit_check(etcp);
}