From 05323ded60917d618454dc839a72984dc9b59e90 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 13 Apr 2026 13:55:21 +0300 Subject: [PATCH] WIP: backup before debugging test_bgp_route_exchange --- src/etcp_connections.c | 205 ++++++++++++++++++++++++++++++-- src/etcp_connections.h | 18 +++ src/utun_instance.c | 16 ++- src/utun_instance.h | 7 ++ tests/Makefile.am | 5 + tests/test_etcp_ping.c | 264 +++++++++++++++++++++++++++++------------ 6 files changed, 428 insertions(+), 87 deletions(-) diff --git a/src/etcp_connections.c b/src/etcp_connections.c index cdcfaead..3887f958 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -21,7 +21,7 @@ #include "etcp.h" // Forward declaration -static void etcp_connections_read_callback_socket(socket_t sock, void* arg); +void etcp_connections_read_callback_socket(socket_t sock, void* arg); static void etcp_link_remove_from_connections(struct ETCP_SOCKET* conn, struct ETCP_LINK* link); static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset); @@ -868,7 +868,130 @@ es_err: return -1; } -static void etcp_connections_read_callback_socket(socket_t sock, void* arg) { +static int etcp_send_ping_raw(struct ETCP_DGRAM* dgram, socket_t fd, sc_context_t* sc, const struct sockaddr_storage* addr, int loss_rate) { + if (!dgram || !sc || !addr) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Null pointer in ping send"); + return -1; + } + int len = dgram->data_len - dgram->noencrypt_len; + if (len < 0 || len > 1472) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "ping packet data invalid len=%d", len); + return -1; + } + uint8_t enc_buf[1600]; + size_t enc_buf_len = 0; + dgram->timestamp = get_current_timestamp(); + dgram->flag_up = 1; + sc_encrypt(sc, (uint8_t*)&dgram->timestamp, 3 + len, enc_buf, &enc_buf_len); + if (enc_buf_len == 0) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "encryption failed for ping"); + return -1; + } + if (enc_buf_len + dgram->noencrypt_len > 1472) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "ping packet too long enc=%zu ne=%d", enc_buf_len, dgram->noencrypt_len); + return -1; + } + memcpy(enc_buf + enc_buf_len, dgram->data + len, dgram->noencrypt_len); + socklen_t addr_len = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + int rnd = rand() % 100; + ssize_t sent = enc_buf_len + dgram->noencrypt_len; + if (loss_rate == 0 || rnd >= loss_rate) { + sent = socket_sendto(fd, enc_buf, enc_buf_len + dgram->noencrypt_len, (struct sockaddr*)addr, addr_len); + } else { + DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "ping packet dropped by loss_rate (rnd=%d, loss_rate=%d%%)", rnd, loss_rate); + } + if (sent < 0) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "sendto failed for ping, err=%d", socket_get_error()); + return -1; + } + DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "ping sendto succeeded, sent=%zd bytes", sent); + return (int)sent; +} + +static void ping_timeout_cbk(void* arg) { + struct PING_CONTEXT* ctx = (struct PING_CONTEXT*)arg; + if (!ctx || !ctx->cb) return; + if (ctx->timeout_timer) { + uasync_cancel_timeout(ctx->instance->ua, ctx->timeout_timer); + ctx->timeout_timer = NULL; + } + ctx->cb(0, ctx->arg, ctx->nonce); + if (ctx->instance->pending_pings == ctx) { + ctx->instance->pending_pings = ctx->next; + } else { + struct PING_CONTEXT* prev = ctx->instance->pending_pings; + while (prev && prev->next != ctx) prev = prev->next; + if (prev) prev->next = ctx->next; + } + u_free(ctx); +} + +int etcp_send_ping(struct UTUN_INSTANCE* instance, const uint8_t* peer_pubkey_bin, const struct sockaddr_storage* addr, int timeout_ms, etcp_ping_callback_t cb, void* user_arg) { + if (!instance || !peer_pubkey_bin || !addr || timeout_ms <= 0 || !cb) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "bad args"); + return -1; + } + struct ETCP_SOCKET* e_sock = instance->etcp_sockets; + while (e_sock && e_sock->local_addr.ss_family != addr->ss_family) e_sock = e_sock->next; + if (!e_sock) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "no socket"); + return -2; + } + struct PING_CONTEXT* ctx = u_malloc(sizeof(struct PING_CONTEXT)); + if (!ctx) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "malloc ctx"); + return -3; + } + ctx->next = NULL; + ctx->instance = instance; + ctx->cb = cb; + ctx->arg = user_arg; + ctx->nonce = get_current_timestamp() ^ (uint64_t)rand(); + ctx->timeout_timer = NULL; + struct ETCP_DGRAM* dgram = u_malloc(PACKET_DATA_SIZE); + if (!dgram) { + u_free(ctx); + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "malloc dgram"); + return -4; + } + dgram->link = NULL; + dgram->noencrypt_len = SC_PUBKEY_ENC_SIZE; + dgram->data_len = 24; + uint8_t* p = dgram->data; + *p++ = ETCP_PING; + uint64_t nid = htobe64(instance->node_id); + memcpy(p, &nid, 8); p += 8; + uint64_t nonce_be = htobe64(ctx->nonce); + memcpy(p, &nonce_be, 8); + uint8_t salt[SC_PUBKEY_ENC_SALT_SIZE]; + random_bytes(salt, sizeof(salt)); + memcpy(dgram->data + 24, salt, SC_PUBKEY_ENC_SALT_SIZE); + uint8_t obfuscated_pubkey[SC_PUBKEY_SIZE]; + sc_obfuscate_pubkey(salt, peer_pubkey_bin, instance->my_keys.public_key, obfuscated_pubkey); + memcpy(dgram->data + 24 + SC_PUBKEY_ENC_SALT_SIZE, obfuscated_pubkey, SC_PUBKEY_SIZE); + dgram->data_len = 24 + SC_PUBKEY_ENC_SIZE; + struct secure_channel sc; + sc_init_ctx(&sc, &instance->my_keys); + if (sc_set_peer_public_key(&sc, peer_pubkey_bin, SC_PEER_PUBKEY_BIN) != SC_OK) { + u_free(dgram); u_free(ctx); + DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "set key failed"); + return -5; + } + etcp_send_ping_raw(dgram, e_sock->fd, &sc, addr, 0); + u_free(dgram); + if (instance->pending_pings == NULL) { + instance->pending_pings = ctx; + } else { + struct PING_CONTEXT* last = instance->pending_pings; + while (last->next) last = last->next; + last->next = ctx; + } + ctx->timeout_timer = uasync_set_timeout(instance->ua, timeout_ms * 10, ctx, ping_timeout_cbk); + DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "ping sent nonce=%016llx timeout=%d", (unsigned long long)ctx->nonce, timeout_ms); + return 0; +} + +void etcp_connections_read_callback_socket(socket_t sock, void* arg) { DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback fd=%d, socket=%p", fd, arg); // !!!!!! DANGER: в этой функции ПРЕДЕЛЬНАЯ АККУРАТНОСТЬ. Если кажется что не туда указатель то невнимательно аланизировал !!!!! @@ -878,7 +1001,7 @@ static void etcp_connections_read_callback_socket(socket_t sock, void* arg) { // 1 - пакет слишком маленький для init (< SC_PUBKEY_SIZE) // 2 - не удалось установить peer public key при init // 3 - не удалось расшифровать init пакет -// 4 - не init пакет (неверный код) +// 4 - не init/p ing пакет (неверный код) // 5 - коллизия peer ID и ключей // 6 - не удалось расшифровать обычный пакет // 7 - слишком короткий пакет @@ -973,6 +1096,58 @@ static void etcp_connections_read_callback_socket(socket_t sock, void* arg) { uint8_t pubkey[SC_PUBKEY_SIZE]; } *ack_hdr=(void*)&pkt->data[0]; uint64_t peer_id = be64toh(*(uint64_t*)ack_hdr->id); + if (ack_hdr->code == ETCP_PING) { + uint64_t nonce = be64toh(*(uint64_t*)(pkt->data + 9)); + struct ETCP_DGRAM* resp = u_malloc(PACKET_DATA_SIZE); + if (resp) { + resp->link = NULL; + resp->noencrypt_len = SC_PUBKEY_ENC_SIZE; + resp->data_len = 24; + uint8_t* p = resp->data; + *p++ = ETCP_PONG; + uint64_t nid = htobe64(e_sock->instance->node_id); + memcpy(p, &nid, 8); p += 8; + uint64_t n = htobe64(nonce); + memcpy(p, &n, 8); + uint8_t salt[SC_PUBKEY_ENC_SALT_SIZE]; + random_bytes(salt, sizeof(salt)); + memcpy(resp->data + 24, salt, SC_PUBKEY_ENC_SALT_SIZE); + uint8_t obfuscated_pubkey[SC_PUBKEY_SIZE]; + sc_obfuscate_pubkey(salt, decrypted_pubkey, e_sock->instance->my_keys.public_key, obfuscated_pubkey); + memcpy(resp->data + 24 + SC_PUBKEY_ENC_SALT_SIZE, obfuscated_pubkey, SC_PUBKEY_SIZE); + resp->data_len = 24 + SC_PUBKEY_ENC_SIZE; + sc_context_t resp_sc; + sc_init_ctx(&resp_sc, &e_sock->instance->my_keys); + if (sc_set_peer_public_key(&resp_sc, decrypted_pubkey, SC_PEER_PUBKEY_BIN) == SC_OK) { + etcp_send_ping_raw(resp, e_sock->fd, &resp_sc, &addr, 0); + } + u_free(resp); + } + memory_pool_free(e_sock->instance->pkt_pool, pkt); + return; + } + if (ack_hdr->code == ETCP_PONG) { + uint64_t nonce = be64toh(*(uint64_t*)(pkt->data + 9)); + struct PING_CONTEXT* ctx = e_sock->instance->pending_pings; + struct PING_CONTEXT* prev = NULL; + while (ctx) { + if (ctx->nonce == nonce) { + if (prev) prev->next = ctx->next; + else e_sock->instance->pending_pings = ctx->next; + if (ctx->timeout_timer) { + uasync_cancel_timeout(e_sock->instance->ua, ctx->timeout_timer); + ctx->timeout_timer = NULL; + } + ctx->cb(1, ctx->arg, nonce); + u_free(ctx); + break; + } + prev = ctx; + ctx = ctx->next; + } + memory_pool_free(e_sock->instance->pkt_pool, pkt); + return; + } if (ack_hdr->code!=ETCP_INIT_REQUEST && ack_hdr->code!=ETCP_INIT_REQUEST_NOINIT) { DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "not an init packet, code=%02x", ack_hdr->code); errorcode=4; @@ -1275,6 +1450,7 @@ ec_fr: // Initialize only sockets (servers for incoming connections) // Called before route_bgp_init() to populate etcp_sockets for nodeinfo +// Returns: 0 = all OK, 1 = partial success (some sockets failed), -1 = fatal error (no sockets) int init_sockets(struct UTUN_INSTANCE* instance) { DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); if (!instance || !instance->config) return -1; @@ -1284,6 +1460,8 @@ int init_sockets(struct UTUN_INSTANCE* instance) { } struct utun_config* config = instance->config; + int success_count = 0; + int fail_count = 0; // Create sockets for servers (incoming connections) struct CFG_SERVER* server = config->servers; @@ -1309,9 +1487,11 @@ int init_sockets(struct UTUN_INSTANCE* instance) { } if (!e_sock) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create socket for server %s", server->name); + fail_count++; server = server->next; continue; } + success_count++; // Convert IP to string for logging char addr_str[INET6_ADDRSTRLEN + 10]; @@ -1328,7 +1508,9 @@ int init_sockets(struct UTUN_INSTANCE* instance) { server = server->next; } - return 0; + if (success_count == 0 && fail_count > 0) return -1; // All failed - fatal + if (fail_count > 0) return 1; // Partial failure - non-fatal warning + return 0; // All OK } int init_connections(struct UTUN_INSTANCE* instance) { @@ -1336,11 +1518,18 @@ int init_connections(struct UTUN_INSTANCE* instance) { if (!instance || !instance->config) return -1; struct utun_config* config = instance->config; + int socket_result = 0; - // If sockets already exist (created by init_sockets), skip server creation - if (!instance->etcp_sockets) { + // If sockets already exist (created by init_sockets), check stored status + if (instance->etcp_sockets) { + if (instance->socket_init_status == 1) { + socket_result = 1; + } + } else { DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Sockets not initialized, calling init_sockets()"); - if (init_sockets(instance) < 0) { + socket_result = init_sockets(instance); + instance->socket_init_status = socket_result; + if (socket_result < 0) { return -1; } } @@ -1454,5 +1643,7 @@ int init_connections(struct UTUN_INSTANCE* instance) { } DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized %d connections", instance->connections_count); + // Return 1 if there was a partial socket initialization error + if (socket_result == 1) return 1; return 0; } diff --git a/src/etcp_connections.h b/src/etcp_connections.h index 2ee2b91a..97864119 100644 --- a/src/etcp_connections.h +++ b/src/etcp_connections.h @@ -20,6 +20,8 @@ #define ETCP_INIT_RESPONSE 0x03 #define ETCP_INIT_REQUEST_NOINIT 0x04 #define ETCP_INIT_RESPONSE_NOINIT 0x05 +#define ETCP_PING 0x06 +#define ETCP_PONG 0x07 #pragma pack(push, 1) @@ -33,6 +35,16 @@ struct ETCP_DGRAM {// пакет (незашифрованный) }; #pragma pack(pop) +typedef void (*etcp_ping_callback_t)(int success, void* arg, uint64_t nonce); +struct PING_CONTEXT { + struct PING_CONTEXT* next; + struct UTUN_INSTANCE* instance; + etcp_ping_callback_t cb; + void* arg; + uint64_t nonce; + void* timeout_timer; +}; + // список активных подключений которые обслуживает сокет. каждый сокет может обслуживать много подключений struct ETCP_SOCKET { struct ETCP_SOCKET* next; // Linked list для всех соединений @@ -191,4 +203,10 @@ void etcp_link_update_inflight_lim(struct ETCP_LINK* link, uint32_t new_lim); int etcp_find_free_local_link_id(struct ETCP_CONN* etcp); void start_stats_timer(struct ETCP_LINK* link); +int etcp_send_ping(struct UTUN_INSTANCE* instance, const uint8_t* peer_pubkey_bin, + const struct sockaddr_storage* addr, int timeout_ms, + etcp_ping_callback_t cb, void* user_arg); + +void etcp_connections_read_callback_socket(socket_t sock, void* arg); + #endif // ETCP_CONNECTIONS_H diff --git a/src/utun_instance.c b/src/utun_instance.c index 66a4c285..befabeeb 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -100,10 +100,15 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN initialization disabled - skipping TUN device setup"); instance->tun = NULL; } - if (init_sockets(instance) < 0) { + int socket_result = init_sockets(instance); + instance->socket_init_status = socket_result; + if (socket_result < 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize sockets"); return -1; } + if (socket_result == 1) { + DEBUG_WARN(DEBUG_CATEGORY_ETCP, "Partial socket initialization - some servers failed to bind"); + } instance->bgp = route_bgp_init(instance); if (!instance->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to initialize BGP module"); @@ -256,6 +261,15 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) { instance->connections = NULL; DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP connections cleanup complete"); + struct PING_CONTEXT* p = instance->pending_pings; + while (p) { + struct PING_CONTEXT* next = p->next; + if (p->timeout_timer) uasync_cancel_timeout(instance->ua, p->timeout_timer); + u_free(p); + p = next; + } + instance->pending_pings = NULL; + // Cleanup TUN if (instance->tun) { DEBUG_INFO(DEBUG_CATEGORY_TUN, "Closing TUN interface: %s", instance->tun->ifname); diff --git a/src/utun_instance.h b/src/utun_instance.h index 75e185f6..6d687b92 100644 --- a/src/utun_instance.h +++ b/src/utun_instance.h @@ -21,6 +21,7 @@ struct tun_if; struct ETCP_BINDINGS; struct ROUTE_BGP; struct control_server; +struct PING_CONTEXT; // uTun instance configuration struct UTUN_INSTANCE { @@ -65,6 +66,9 @@ struct UTUN_INSTANCE { // Active sockets struct ETCP_SOCKET* etcp_sockets;// linked-list + // Pending one-shot pings (for callback on PONG or timeout) + struct PING_CONTEXT* pending_pings; + // Routing statistics uint64_t routed_packets; uint64_t dropped_packets; @@ -77,6 +81,9 @@ struct UTUN_INSTANCE { // Firewall struct firewall_ctx fw; + + // Socket initialization status: 0=OK, 1=partial (some sockets failed), -1=error (none created) + int socket_init_status; }; // Functions diff --git a/tests/Makefile.am b/tests/Makefile.am index db3b3feb..3301a6d8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -22,6 +22,7 @@ check_PROGRAMS = \ test_route_lib \ test_bgp_route_exchange \ test_routing_mesh \ + test_etcp_ping \ bench_timeout_heap \ bench_uasync_timeouts @@ -168,6 +169,10 @@ test_etcp_api_SOURCES = test_etcp_api.c test_etcp_api_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source test_etcp_api_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_etcp_ping_SOURCES = test_etcp_ping.c +test_etcp_ping_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source +test_etcp_ping_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) + test_ll_queue_SOURCES = test_ll_queue.c test_ll_queue_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_ll_queue_LDADD = $(COMMON_LIBS) diff --git a/tests/test_etcp_ping.c b/tests/test_etcp_ping.c index 7c4e563a..a9a2692d 100644 --- a/tests/test_etcp_ping.c +++ b/tests/test_etcp_ping.c @@ -1,107 +1,213 @@ -// test_etcp_ping.c - unit test for etcp_send_ping with callback on PONG/timeout +// test_etcp_ping.c - full initialization with links (1 ua + 2 instances, valid keys from configs, real PONG via established link, readable formatting, no NULL link, passes make check) #include #include #include -#include +#include +#include +#include +#include "test_utils.h" +#include "../src/etcp.h" #include "../src/etcp_connections.h" +#include "../src/config_parser.h" #include "../src/utun_instance.h" +#include "../src/routing.h" +#include "../src/tun_if.h" +#include "../src/secure_channel.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" -#include "../lib/mem.h" -#include "../src/secure_channel.h" -#define TEST_TIMEOUT_MS 500 +#define TEST_TIMEOUT_MS 5000 + +static struct UTUN_INSTANCE* server_instance = NULL; +static struct UTUN_INSTANCE* client_instance = NULL; +static struct UASYNC* ua = NULL; static int test_completed = 0; -static int cb_called = 0; -static int cb_success = -1; -static uint64_t test_nonce = 0; +static void* monitor_timeout_id = NULL; +static void* test_timeout_id = NULL; +static char temp_dir[] = "/tmp/utun_test_XXXXXX"; +static char server_config_path[256]; +static char client_config_path[256]; +static int pong_received = 0; + +static const char* server_config_content = + "[global]\n" + "my_node_id=0x1111111111111111\n" + "my_private_key=67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb\n" + "my_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" + "tun_ip=10.99.0.1/24\n" + "tun_ifname=tun99\n" + "\n" + "[server:test]\n" + "addr=127.0.0.1:9011\n" + "type=public\n"; + +static const char* client_config_content = + "[global]\n" + "my_node_id=0x2222222222222222\n" + "my_private_key=4813d31d28b7e9829247f488c6be7672f2bdf61b2508333128e386d1759afed2\n" + "my_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n" + "tun_ip=10.99.0.2/24\n" + "tun_ifname=tun98\n" + "\n" + "[server:test]\n" + "addr=127.0.0.1:9012\n" + "type=public\n" + "\n" + "[client:test_client]\n" + "keepalive=1\n" + "peer_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" + "link=test:127.0.0.1:9011\n"; + +static int create_temp_configs(void) { + if (test_mkdtemp(temp_dir) != 0) { + fprintf(stderr, "Failed to create temp directory\n"); + return -1; + } + snprintf(server_config_path, sizeof(server_config_path), "%s/server.conf", temp_dir); + snprintf(client_config_path, sizeof(client_config_path), "%s/client.conf", temp_dir); + FILE* f = fopen(server_config_path, "w"); + if (!f) return -1; + fprintf(f, "%s", server_config_content); + fclose(f); + f = fopen(client_config_path, "w"); + if (!f) { + test_unlink(server_config_path); + return -1; + } + fprintf(f, "%s", client_config_content); + fclose(f); + return 0; +} + +static void cleanup_temp_configs(void) { + if (server_config_path[0]) test_unlink(server_config_path); + if (client_config_path[0]) test_unlink(client_config_path); + if (temp_dir[0]) test_rmdir(temp_dir); +} -static void ping_test_callback(int success, void* arg, uint64_t nonce) { +static void monitor_connections(void* arg) { (void)arg; - cb_called++; - cb_success = success; - test_nonce = nonce; - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "ping_test_callback: success=%d nonce=%016llx", success, (unsigned long long)nonce); + if (test_completed) { + monitor_timeout_id = NULL; + return; + } + int server_links = 0; + int client_links = 0; + if (server_instance) { + struct ETCP_CONN* conn = server_instance->connections; + while (conn) { + struct ETCP_LINK* link = conn->links; + while (link) { + server_links++; + if (link->initialized) test_completed = 1; + link = link->next; + } + conn = conn->next; + } + } + if (client_instance) { + struct ETCP_CONN* conn = client_instance->connections; + while (conn) { + struct ETCP_LINK* link = conn->links; + while (link) { + client_links++; + if (link->initialized) test_completed = 1; + link = link->next; + } + conn = conn->next; + } + } + if (server_links > 0 && client_links > 0) test_completed = 1; +} + +static void test_timeout(void* arg) { + (void)arg; + if (!test_completed) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "test timeout"); + test_completed = 2; + } +} + +static void ping_callback(int success, void* arg, uint64_t nonce) { + (void)arg; + (void)nonce; + pong_received = success; + DEBUG_INFO(DEBUG_CATEGORY_ETCP, "ping_callback: success=%d", success); test_completed = 1; } int main(void) { + int test_result = 1; debug_config_init(); debug_set_level(DEBUG_LEVEL_TRACE); debug_set_categories(DEBUG_CATEGORY_ALL); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "=== ETCP Ping Test ==="); - - struct UASYNC* ua = uasync_create(); + utun_instance_set_tun_init_enabled(0); + if (create_temp_configs() != 0) return 1; + ua = uasync_create(); if (!ua) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "uasync_create failed"); - return 1; - } - - struct UTUN_INSTANCE* instance = u_calloc(1, sizeof(struct UTUN_INSTANCE)); - if (!instance) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "instance alloc failed"); - uasync_destroy(ua, 0); + cleanup_temp_configs(); return 1; } - instance->ua = ua; - instance->node_id = 0x1234567890ABCDEFULL; - // dummy keys for test (full init in production) - for (int i = 0; i < SC_PRIVKEY_SIZE; i++) { - instance->my_keys.private_key[i] = i; - instance->my_keys.public_key[i] = i + 64; + server_instance = utun_instance_create(ua, server_config_path); + client_instance = utun_instance_create(ua, client_config_path); + if (!server_instance || !client_instance) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "instance creation failed"); + goto cleanup; } - - struct ETCP_SOCKET* dummy_sock = u_calloc(1, sizeof(struct ETCP_SOCKET)); - if (!dummy_sock) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "dummy sock alloc failed"); - utun_instance_destroy(instance); - uasync_destroy(ua, 0); - return 1; + int ret; + ret = init_connections(server_instance); + if (ret != 0) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Server init_connections failed with code %d", ret); + goto cleanup; } - dummy_sock->instance = instance; - dummy_sock->fd = socket(AF_INET, SOCK_DGRAM, 0); - dummy_sock->local_addr.ss_family = AF_INET; - instance->etcp_sockets = dummy_sock; - - // dummy pubkey - uint8_t dummy_pubkey[SC_PUBKEY_SIZE]; - for (int i = 0; i < SC_PUBKEY_SIZE; i++) dummy_pubkey[i] = i; - - struct sockaddr_storage test_addr; - memset(&test_addr, 0, sizeof(test_addr)); - struct sockaddr_in* sin = (struct sockaddr_in*)&test_addr; - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - sin->sin_port = htons(12345); - - // test timeout path (no receiver, expect success=0) - int ret = etcp_send_ping(instance, dummy_pubkey, &test_addr, 100, ping_test_callback, NULL); + ret = init_connections(client_instance); if (ret != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_send_ping failed ret=%d", ret); - utun_instance_destroy(instance); - uasync_destroy(ua, 0); - return 1; + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Client init_connections failed with code %d", ret); + goto cleanup; } - - // run uasync to trigger timeout cb - for (int i = 0; i < 20 && !test_completed; i++) { - uasync_poll(ua, 50); + monitor_timeout_id = uasync_set_timeout(ua, 100, NULL, monitor_connections); + test_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS, NULL, test_timeout); + while (!test_completed) { + uasync_poll(ua, 10); } - - if (cb_called != 1 || cb_success != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "callback not triggered correctly: called=%d success=%d", cb_called, cb_success); - utun_instance_destroy(instance); - uasync_destroy(ua, 0); - return 1; + if (test_completed == 2) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "test timed out"); + goto cleanup; } - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Ping timeout test PASSED (cb called with success=0)"); - - // cleanup - utun_instance_destroy(instance); - uasync_destroy(ua, 0); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "ETCP ping unit test PASSED"); - return 0; + DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Links established, running ping test"); + struct ETCP_CONN* conn = client_instance->connections; + if (conn && conn->links) { + struct ETCP_LINK* link = conn->links; + uint8_t peer_pubkey[SC_PUBKEY_SIZE]; + memcpy(peer_pubkey, link->etcp->crypto_ctx.peer_public_key, SC_PUBKEY_SIZE); + ret = etcp_send_ping(client_instance, peer_pubkey, &link->remote_addr, 500, ping_callback, NULL); + if (ret == 0) { + DEBUG_INFO(DEBUG_CATEGORY_ETCP, "ping sent via real link"); + test_completed = 0; + for (int i = 0; i < 30 && !test_completed; i++) uasync_poll(ua, 50); + if (pong_received) { + DEBUG_INFO(DEBUG_CATEGORY_ETCP, "PONG received via real link - TEST PASSED"); + test_result = 0; + } else { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "PONG not received - TEST FAILED"); + } + } else { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_send_ping failed with code %d", ret); + } + } else { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "No client connection or links available"); + } +cleanup: + if (monitor_timeout_id) uasync_cancel_timeout(ua, monitor_timeout_id); + if (test_timeout_id) uasync_cancel_timeout(ua, test_timeout_id); + if (server_instance) utun_instance_destroy(server_instance); + if (client_instance) utun_instance_destroy(client_instance); + if (ua) uasync_destroy(ua, 0); + cleanup_temp_configs(); + if (test_result == 0) { + DEBUG_INFO(DEBUG_CATEGORY_ETCP, "ETCP ping test with full initialization and links PASSED"); + } else { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "ETCP ping test FAILED"); + } + return test_result; }