diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 diff --git a/src/config_parser.c b/src/config_parser.c index 0c29b134..54aa00a4 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -374,6 +374,14 @@ static int parse_global(const char *key, const char *value, struct global_config global->keepalive_interval = atoi(value); return 0; } + if (strcmp(key, "inflight_min_bytes") == 0) { + global->inflight_min_bytes = atoi(value); + return 0; + } + if (strcmp(key, "inflight_max_bytes") == 0) { + global->inflight_max_bytes = atoi(value); + return 0; + } if (strcmp(key, "debug_level") == 0) { return assign_string(global->debug_level, sizeof(global->debug_level), value); } @@ -646,6 +654,9 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename) // Set default values cfg->global.name[0] = '\0'; cfg->global.keepalive_timeout = 2000; // Default 2 seconds + cfg->global.keepalive_interval = 200; // Default 0.2 s + cfg->global.inflight_min_bytes = 2000; // Default 2KB + cfg->global.inflight_max_bytes = 100000; // Default 100KB cfg->global.firewall_rules = NULL; cfg->global.firewall_rule_count = 0; cfg->global.firewall_bypass_all = 0; diff --git a/src/config_parser.h b/src/config_parser.h index 20c76404..1c1f25e2 100644 --- a/src/config_parser.h +++ b/src/config_parser.h @@ -111,6 +111,8 @@ struct global_config { int tun_test_mode; // test mode: 1 = don't open real TUN, queues only int keepalive_timeout; // keepalive timeout in ms (default: 2000) int keepalive_interval; // keepalive interval in ms (default: 200) + int inflight_min_bytes; // inflight window starting size (default: 2000) + int inflight_max_bytes; // inflight window max cap (default: 100000) // Firewall configuration struct CFG_FIREWALL_RULE *firewall_rules; diff --git a/src/etcp.c b/src/etcp.c index 0973dd19..5ee876c2 100644 --- a/src/etcp.c +++ b/src/etcp.c @@ -59,6 +59,112 @@ static void drain_and_free_queue(struct ll_queue** q) { *q = NULL; } +static int inflight_seq_cmp(const void* a, const void* b) { + const struct INFLIGHT_PACKET* pa = *(const struct INFLIGHT_PACKET**)a; + const struct INFLIGHT_PACKET* pb = *(const struct INFLIGHT_PACKET**)b; + if (pa->seq < pb->seq) return -1; + if (pa->seq > pb->seq) return 1; + return 0; +} + +static void feed_dgram_to_asm(struct ETCP_CONN* etcp, struct PKTNORM* pn, + uint8_t* dgram, uint16_t dgram_len, + uint8_t** asm_buf, uint16_t* asm_len, uint16_t* asm_cap, + uint32_t* returned) { + uint16_t need = *asm_len + dgram_len; + if (need > *asm_cap) { + uint16_t new_cap = *asm_cap ? *asm_cap : 256; + while (new_cap < need) new_cap *= 2; + uint8_t* new_buf = u_realloc(*asm_buf, new_cap); + if (!new_buf) return; + *asm_buf = new_buf; + *asm_cap = new_cap; + } + memcpy(*asm_buf + *asm_len, dgram, dgram_len); + *asm_len = need; + + while (*asm_len >= 2) { + uint16_t pkt_len = (*asm_buf)[0] | ((*asm_buf)[1] << 8); + if (pkt_len == 0 || pkt_len > 16384 || *asm_len < 2 + pkt_len) break; + + const uint8_t* pkt_data = *asm_buf + 2; + if (pkt_data[0] != ETCP_ID_ROUTE_ENTRY) { + struct ll_entry* e = ll_alloc_lldgram(pkt_len); + if (e) { + memcpy(e->dgram, pkt_data, pkt_len); + e->len = pkt_len; + queue_data_put(pn->input, e); + (*returned)++; + } + } + + uint16_t consumed = 2 + pkt_len; + *asm_len -= consumed; + if (*asm_len > 0) memmove(*asm_buf, *asm_buf + consumed, *asm_len); + } +} + +static void etcp_return_inflight_to_normalizer(struct ETCP_CONN* etcp) { + struct PKTNORM* pn = etcp->normalizer; + if (!pn) return; + + struct INFLIGHT_PACKET** inflight = NULL; + int inflight_count = 0; + struct ll_entry** input_frags = NULL; + int input_count = 0; + struct ll_entry* entry; + + while ((entry = queue_data_get(etcp->input_wait_ack))) { + inflight = u_realloc(inflight, (inflight_count + 1) * sizeof(*inflight)); + inflight[inflight_count++] = (struct INFLIGHT_PACKET*)entry; + } + while ((entry = queue_data_get(etcp->input_send_q))) { + inflight = u_realloc(inflight, (inflight_count + 1) * sizeof(*inflight)); + inflight[inflight_count++] = (struct INFLIGHT_PACKET*)entry; + } + while ((entry = queue_data_get(etcp->input_queue))) { + input_frags = u_realloc(input_frags, (input_count + 1) * sizeof(*input_frags)); + input_frags[input_count++] = entry; + } + + if (inflight_count + input_count == 0 && (!pn->data || pn->data_ptr == 0)) return; + + if (inflight_count > 0) qsort(inflight, inflight_count, sizeof(*inflight), inflight_seq_cmp); + + uint8_t* asm_buf = NULL; + uint16_t asm_len = 0; + uint16_t asm_cap = 0; + uint32_t returned_packets = 0; + + for (int i = 0; i < inflight_count; i++) { + feed_dgram_to_asm(etcp, pn, inflight[i]->ll.dgram, inflight[i]->ll.len, + &asm_buf, &asm_len, &asm_cap, &returned_packets); + memory_pool_free(etcp->instance->data_pool, inflight[i]->ll.dgram); + memory_pool_free(etcp->inflight_pool, inflight[i]); + } + u_free(inflight); + + for (int i = 0; i < input_count; i++) { + feed_dgram_to_asm(etcp, pn, input_frags[i]->dgram, input_frags[i]->len, + &asm_buf, &asm_len, &asm_cap, &returned_packets); + memory_pool_free(etcp->instance->data_pool, input_frags[i]->dgram); + memory_pool_free(etcp->io_pool, input_frags[i]); + } + u_free(input_frags); + + if (pn->data && pn->data_ptr > 0) { + feed_dgram_to_asm(etcp, pn, pn->data, pn->data_ptr, + &asm_buf, &asm_len, &asm_cap, &returned_packets); + } + + if (asm_buf) { u_free(asm_buf); } + + if (returned_packets > 0) { + DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] returned %u packets to normalizer input during reinit", + etcp->log_name, returned_packets); + } +} + uint16_t get_current_timestamp() { return (uint16_t)get_time_tb(); } @@ -268,6 +374,9 @@ void etcp_conn_reset(struct ETCP_CONN* etcp) { etcp->rtt_history_idx = 0; etcp->last_rr_link = NULL; + // Return unconfirmed inflight data to normalizer input (до нормалайзера) + if (etcp->normalizer) etcp_return_inflight_to_normalizer(etcp); + // Clear queues (keep queue structures) clear_queue(etcp->input_queue); clear_queue(etcp->output_queue); diff --git a/src/etcp_connections.c b/src/etcp_connections.c index 11c8158d..e9c3dd40 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -52,7 +52,6 @@ void etcp_link_update_inflight_lim(struct ETCP_LINK* link, uint32_t new_lim) { if (link->etcp) { uint32_t sum = 0; for (struct ETCP_LINK* l = link->etcp->links; l; l = l->next) sum += l->inflight_lim_bytes; - if (sum < 100000) sum = 100000; link->etcp->optimal_inflight = sum; } } @@ -799,7 +798,9 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn if (link->keepalive_interval < 10) link->keepalive_interval = 10; link->keepalive_sent_count = 0; link->keepalive_recv_count = 0; - link->inflight_lim_bytes = 30000; + link->inflight_lim_bytes = etcp->instance->config + ? (uint32_t)etcp->instance->config->global.inflight_min_bytes : 2000; + if (link->inflight_lim_bytes < link->mtu * 2) link->inflight_lim_bytes = link->mtu * 2; link->inflight_phase = INFLIGHT_PHASE_SLOW_START; link->slow_start_threshold = 1000000; // будет обновлён после первого burst-замера link->last_window_update_tb = 0; @@ -856,7 +857,6 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn // пересчитать connection-level optimal_inflight { uint32_t sum = 0; for (struct ETCP_LINK* tl = etcp->links; tl; tl = tl->next) sum += tl->inflight_lim_bytes; - if (sum < 100000) sum = 100000; etcp->optimal_inflight = sum; } DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "NEW link initialized on etcp=[%s] link=%p socket=%s id=%d is_server=%d mtu=%d", etcp->log_name, link, conn->name, link->local_link_id, link->is_server, link->mtu); @@ -1020,7 +1020,11 @@ static void link_stats_timer_cb(void* arg) { // Clamp if (new_lim < (int)link->mtu * 2) new_lim = link->mtu * 2; - if (new_lim > 1000000) new_lim = 1000000; + { + int max_lim = link->etcp->instance->config + ? link->etcp->instance->config->global.inflight_max_bytes : 100000; + if (new_lim > max_lim) new_lim = max_lim; + } // Burst target cap (если есть burst_target_bdp — не превышаем 1.1×) if (link->burst_target_bdp > 0 && new_lim > (int)(link->burst_target_bdp * 11 / 10)) diff --git a/src/pkt_normalizer.c b/src/pkt_normalizer.c index 59e04c9d..e617488c 100644 --- a/src/pkt_normalizer.c +++ b/src/pkt_normalizer.c @@ -169,16 +169,6 @@ void pn_reset(struct PKTNORM* pn) { // Reset unpacker state pn_unpacker_reset_state(pn); - // Drain input and output queues to discard pending data and maintain consistency - struct ll_entry* entry; - while ((entry = queue_data_get(pn->input)) != NULL) { - queue_dgram_free(entry); - queue_entry_free(entry); - } - while ((entry = queue_data_get(pn->output)) != NULL) { - queue_dgram_free(entry); - queue_entry_free(entry); - } queue_resume_callback(pn->input); queue_resume_callback(pn->output); } diff --git a/tests/Makefile.am b/tests/Makefile.am index 12e97ad9..a0194452 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -30,6 +30,7 @@ check_PROGRAMS = \ test_nat_engine \ test_nat_transport \ test_nat_stress \ + test_etcp_reinit_inflight \ bench_timeout_heap \ bench_uasync_timeouts @@ -172,6 +173,10 @@ test_etcp_congestion_SOURCES = test_etcp_congestion.c test_etcp_congestion_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source test_etcp_congestion_LDADD = $(top_builddir)/src/utun-dummynet.o $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_etcp_reinit_inflight_SOURCES = test_etcp_reinit_inflight.c +test_etcp_reinit_inflight_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source +test_etcp_reinit_inflight_LDADD = $(top_builddir)/src/utun-dummynet.o $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) + test_etcp_minimal_SOURCES = test_etcp_minimal.c test_etcp_minimal_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source test_etcp_minimal_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) diff --git a/tests/test_etcp_reinit_inflight.c b/tests/test_etcp_reinit_inflight.c new file mode 100644 index 00000000..0edf0c63 --- /dev/null +++ b/tests/test_etcp_reinit_inflight.c @@ -0,0 +1,317 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../lib/u_async.h" +#include "../lib/ll_queue.h" +#include "../lib/memory_pool.h" +#include "../lib/debug_config.h" +#include "../lib/platform_compat.h" +#include "../lib/mem.h" +#include "../src/dummynet.h" +#include "../src/config_parser.h" +#include "../src/utun_instance.h" +#include "../src/etcp.h" +#include "../src/etcp_api.h" +#include "../src/etcp_connections.h" +#include "../src/secure_channel.h" +#include "../src/config_updater.h" +#include "../src/routing.h" + +#define CLI_PORT 31000 +#define DN_PORT 31001 +#define SRV_PORT 31002 + +#define PACKET_SIZE 100 +#define TOTAL_PACKETS 500 +#define POLL_INTERVAL_TB 10 +#define TEST_TIMEOUT_TB 100000 // 10s + +struct test_ctx { + struct UASYNC* ua; + struct UTUN_INSTANCE* sender; + struct UTUN_INSTANCE* receiver; + struct dummynet* dn; + uint8_t received_bitmap[TOTAL_PACKETS / 8 + 1]; + uint16_t packets_received; + uint16_t total_packets_sent; + int test_done; + int phase; + int sender_reinit_detected; +}; + +static struct test_ctx* g_ctx; + +static struct UTUN_INSTANCE* create_instance(struct UASYNC* u, uint64_t node_id, + const char* priv_hex, const char* pub_hex) { + struct UTUN_INSTANCE* inst = u_calloc(1, sizeof(*inst)); + if (!inst) return NULL; + inst->ua = u; + inst->node_id = node_id; + if (sc_init_local_keys(&inst->my_keys, pub_hex, priv_hex) != SC_OK) { u_free(inst); return NULL; } + inst->ack_pool = memory_pool_init(sizeof(struct ACK_PACKET)); + inst->data_pool = memory_pool_init(PACKET_DATA_SIZE); + inst->pkt_pool = memory_pool_init(sizeof(struct ETCP_DGRAM) + PACKET_DATA_SIZE); + if (!inst->ack_pool || !inst->data_pool || !inst->pkt_pool) { u_free(inst); return NULL; } + struct utun_config* cfg = u_calloc(1, sizeof(*cfg)); + if (!cfg) { u_free(inst); return NULL; } + strncpy(cfg->global.my_public_key_hex, pub_hex, MAX_KEY_LEN - 1); + strncpy(cfg->global.my_private_key_hex, priv_hex, MAX_KEY_LEN - 1); + cfg->global.my_node_id = node_id; + cfg->global.mtu = 1400; + cfg->global.keepalive_timeout = 500; + cfg->global.keepalive_interval = 1000; + cfg->global.inflight_min_bytes = 2000; + cfg->global.inflight_max_bytes = 8000; + cfg->global.allowed_keys_allow_all = 1; + inst->config = cfg; + return inst; +} + +static int add_server(struct UTUN_INSTANCE* inst, const char* name, int port) { + struct CFG_SERVER* srv = u_calloc(1, sizeof(*srv)); + if (!srv) return -1; + strncpy(srv->name, name, MAX_CONN_NAME_LEN - 1); + srv->ip.ss_family = AF_INET; + ((struct sockaddr_in*)&srv->ip)->sin_addr.s_addr = inet_addr("127.0.0.1"); + ((struct sockaddr_in*)&srv->ip)->sin_port = htons(port); + srv->type = CFG_SERVER_TYPE_PUBLIC; + srv->next = inst->config->servers; + inst->config->servers = srv; + return 0; +} + +static int add_client(struct UTUN_INSTANCE* inst, const char* peer_pubkey) { + struct CFG_CLIENT* cli = u_calloc(1, sizeof(*cli)); + if (!cli) return -1; + strncpy(cli->name, "peer", MAX_CONN_NAME_LEN - 1); + strncpy(cli->peer_public_key_hex, peer_pubkey, MAX_KEY_LEN - 1); + cli->keepalive = 1; + cli->next = inst->config->clients; + inst->config->clients = cli; + return 0; +} + +static struct CFG_CLIENT_LINK* add_link_to_client(struct CFG_CLIENT* cli, struct CFG_SERVER* srv, int remote_port) { + struct CFG_CLIENT_LINK* link = u_calloc(1, sizeof(*link)); + if (!link) return NULL; + link->remote_addr.ss_family = AF_INET; + ((struct sockaddr_in*)&link->remote_addr)->sin_addr.s_addr = inet_addr("127.0.0.1"); + ((struct sockaddr_in*)&link->remote_addr)->sin_port = htons(remote_port); + link->local_srv = srv; + struct CFG_CLIENT_LINK** tail = &cli->links; + while (*tail) tail = &(*tail)->next; + *tail = link; + return link; +} + +static void dummynet_set_both(struct dummynet* dn, uint32_t loss, int fwd_port, int back_port) { + dummynet_set_direction(dn, DUMMYNET_FORWARD, 0, 0, 0, 200, loss, "127.0.0.1", fwd_port); + dummynet_set_direction(dn, DUMMYNET_BACKWARD, 0, 0, 0, 200, loss, "127.0.0.1", back_port); +} + +static void on_recv(struct ETCP_CONN* conn, struct ll_entry* entry) { + struct test_ctx* ctx = g_ctx; + if (!entry || !ctx) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; } + if (entry->len >= 3 && entry->dgram[0] == 0x00) { + uint16_t seq = (entry->dgram[1] << 8) | entry->dgram[2]; + if (seq < TOTAL_PACKETS) { + if (!(ctx->received_bitmap[seq / 8] & (1 << (seq % 8)))) { + ctx->received_bitmap[seq / 8] |= (1 << (seq % 8)); + ctx->packets_received++; + } + } + } + queue_dgram_free(entry); + queue_entry_free(entry); +} + +static int links_initialized(struct UTUN_INSTANCE* inst) { + struct ETCP_CONN* conn = inst->connections; + while (conn) { + struct ETCP_LINK* link = conn->links; + while (link) { if (link->initialized) return 1; link = link->next; } + conn = conn->next; + } + return 0; +} + +static int buffers_full(struct test_ctx* ctx) { + struct ETCP_CONN* conn = ctx->sender->connections; + if (!conn) return 0; + struct ETCP_LINK* link = conn->links; + return link && link->send_blocked_inflight + && conn->input_wait_ack->count > 5 + && ctx->total_packets_sent >= TOTAL_PACKETS; +} + +static void send_one_packet(struct test_ctx* ctx) { + struct ETCP_CONN* conn = ctx->sender->connections; + if (!conn || !conn->initialized) return; + if (ctx->total_packets_sent >= TOTAL_PACKETS) return; + if (conn->input_queue && queue_entry_count(conn->input_queue) > 200) return; + + struct ll_entry* e = ll_alloc_lldgram(PACKET_SIZE); + if (!e) return; + uint16_t seq = ctx->total_packets_sent; + e->dgram[0] = 0x00; + e->dgram[1] = (seq >> 8) & 0xFF; + e->dgram[2] = seq & 0xFF; + for (int i = 3; i < PACKET_SIZE; i++) e->dgram[i] = (uint8_t)(seq + i); + e->len = PACKET_SIZE; + if (etcp_send(conn, e) == 0) ctx->total_packets_sent++; + else { queue_dgram_free(e); queue_entry_free(e); } +} + +static void send_fill(void* arg) { + struct test_ctx* ctx = (struct test_ctx*)arg; + if (ctx->phase != 2 || ctx->test_done) return; + for (int i = 0; i < 20 && ctx->total_packets_sent < TOTAL_PACKETS; i++) send_one_packet(ctx); + if (ctx->total_packets_sent < TOTAL_PACKETS && ctx->phase == 2) + uasync_set_timeout(ctx->ua, 10, ctx, send_fill, "fill"); +} + +static void monitor(void* arg) { + struct test_ctx* ctx = (struct test_ctx*)arg; + struct ETCP_CONN* conn; + + switch (ctx->phase) { + case 1: // wait init + if (links_initialized(ctx->sender) && links_initialized(ctx->receiver)) { + printf(" links initialized, blocking traffic...\n"); + dummynet_set_both(ctx->dn, 1000, SRV_PORT, CLI_PORT); + ctx->phase = 2; + uasync_set_timeout(ctx->ua, 10, ctx, send_fill, "fill"); + } + break; + case 2: // fill buffers + if (buffers_full(ctx)) { + printf(" buffers full: inflight_blocked=%d wait_ack=%d sent=%d\n", + ctx->sender->connections->links->send_blocked_inflight, + ctx->sender->connections->input_wait_ack->count, + ctx->total_packets_sent); + ctx->phase = 3; + } + break; + case 3: // destroy + recreate receiver + printf(" destroying and recreating receiver...\n"); + ctx->receiver->running = 0; + utun_instance_destroy(ctx->receiver); + ctx->receiver = NULL; + { + const char* s_priv = "67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb"; + const char* s_pub = "1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9"; + ctx->receiver = create_instance(ctx->ua, 0x2222222222222222ULL, s_priv, s_pub); + add_server(ctx->receiver, "srv1", SRV_PORT); + utun_instance_init(ctx->receiver); + etcp_bind(ctx->receiver, ETCP_ID_DATA, on_recv); + } + ctx->phase = 4; + break; + case 4: // unblock + printf(" unblocking traffic...\n"); + dummynet_set_both(ctx->dn, 0, SRV_PORT, CLI_PORT); + ctx->phase = 5; + break; + case 5: // wait delivery + conn = ctx->sender->connections; + if (conn && conn->reinit_count > 0 && !ctx->sender_reinit_detected) { + ctx->sender_reinit_detected = 1; + printf(" reinit detected (count=%u)\n", conn->reinit_count); + } + if (ctx->packets_received >= TOTAL_PACKETS && links_initialized(ctx->sender)) { + printf(" all %u packets received\n", ctx->packets_received); + ctx->phase = 6; + } + break; + case 6: // verify + conn = ctx->sender->connections; + if (conn && conn->reinit_count < 1) { + printf("\n[FAIL] Reinit not triggered (reinit_count=%u)\n", conn->reinit_count); + ctx->test_done = 2; + } else { + int missing = 0; + for (int i = 0; i < TOTAL_PACKETS; i++) + if (!(ctx->received_bitmap[i / 8] & (1 << (i % 8)))) missing++; + if (missing > 0) { + printf("\n[FAIL] %d packets missing, received=%d sent=%d\n", + missing, ctx->packets_received, ctx->total_packets_sent); + ctx->test_done = 2; + } else { + printf("\n[PASS] All %d packets delivered, reinit_count=%u\n", + TOTAL_PACKETS, conn->reinit_count); + ctx->test_done = 1; + } + } + return; + } + + if (!ctx->test_done) uasync_set_timeout(ctx->ua, POLL_INTERVAL_TB * 50, ctx, monitor, "mon"); +} + +static void test_timeout(void* arg) { + struct test_ctx* ctx = (struct test_ctx*)arg; + printf("\n[FAIL] Test timeout (phase=%d sent=%d recv=%d)\n", + ctx->phase, ctx->total_packets_sent, ctx->packets_received); + ctx->test_done = 2; +} + +int main(void) { + printf("=== ETCP Reinit Inflight Preserve Test ===\n\n"); + srand((unsigned)time(NULL)); + debug_config_init(); + debug_set_level(DEBUG_LEVEL_ERROR); + socket_platform_init(); + + struct test_ctx ctx; + memset(&ctx, 0, sizeof(ctx)); + g_ctx = &ctx; + + ctx.ua = uasync_create(); + if (!ctx.ua) { printf("uasync_create failed\n"); return 1; } + + const char* s_priv = "67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb"; + const char* s_pub = "1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9"; + const char* c_priv = "4813d31d28b7e9829247f488c6be7672f2bdf61b2508333128e386d1759afed2"; + const char* c_pub = "c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71"; + + ctx.sender = create_instance(ctx.ua, 0x1111111111111111ULL, c_priv, c_pub); + ctx.receiver = create_instance(ctx.ua, 0x2222222222222222ULL, s_priv, s_pub); + if (!ctx.sender || !ctx.receiver) { printf("create_instance failed\n"); return 1; } + + utun_instance_set_tun_init_enabled(0); + + add_server(ctx.receiver, "srv1", SRV_PORT); + add_server(ctx.sender, "cli1", CLI_PORT); + add_client(ctx.sender, s_pub); + struct CFG_CLIENT* cli = ctx.sender->config->clients; + add_link_to_client(cli, ctx.sender->config->servers, DN_PORT); + + printf("Init instances...\n"); + if (utun_instance_init(ctx.receiver) < 0) { printf("receiver init failed\n"); return 1; } + if (utun_instance_init(ctx.sender) < 0) { printf("sender init failed\n"); return 1; } + + etcp_bind(ctx.receiver, ETCP_ID_DATA, on_recv); + + printf("Creating dummynet...\n"); + ctx.dn = dummynet_create(ctx.ua, "127.0.0.1", DN_PORT); + if (!ctx.dn) { printf("dummynet_create failed\n"); return 1; } + dummynet_set_both(ctx.dn, 0, SRV_PORT, CLI_PORT); + + ctx.phase = 1; + uasync_set_timeout(ctx.ua, POLL_INTERVAL_TB * 50, &ctx, monitor, "mon"); + uasync_set_timeout(ctx.ua, TEST_TIMEOUT_TB, &ctx, test_timeout, "timeout"); + + while (!ctx.test_done) uasync_poll(ctx.ua, POLL_INTERVAL_TB); + + dummynet_destroy(ctx.dn); + if (ctx.receiver) { ctx.receiver->running = 0; utun_instance_destroy(ctx.receiver); } + if (ctx.sender) { ctx.sender->running = 0; utun_instance_destroy(ctx.sender); } + uasync_destroy(ctx.ua, 1); + + return ctx.test_done == 1 ? 0 : 1; +}