Browse Source

WIP: backup before debugging test_bgp_route_exchange

congestion
Evgeny 6 months ago
parent
commit
05323ded60
  1. 205
      src/etcp_connections.c
  2. 18
      src/etcp_connections.h
  3. 16
      src/utun_instance.c
  4. 7
      src/utun_instance.h
  5. 5
      tests/Makefile.am
  6. 264
      tests/test_etcp_ping.c

205
src/etcp_connections.c

@ -21,7 +21,7 @@
#include "etcp.h" #include "etcp.h"
// Forward declaration // 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_remove_from_connections(struct ETCP_SOCKET* conn, struct ETCP_LINK* link);
static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset); static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset);
@ -868,7 +868,130 @@ es_err:
return -1; 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_TRACE(DEBUG_CATEGORY_CONNECTION, "");
// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback fd=%d, socket=%p", fd, arg); // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback fd=%d, socket=%p", fd, arg);
// !!!!!! DANGER: в этой функции ПРЕДЕЛЬНАЯ АККУРАТНОСТЬ. Если кажется что не туда указатель то невнимательно аланизировал !!!!! // !!!!!! DANGER: в этой функции ПРЕДЕЛЬНАЯ АККУРАТНОСТЬ. Если кажется что не туда указатель то невнимательно аланизировал !!!!!
@ -878,7 +1001,7 @@ static void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
// 1 - пакет слишком маленький для init (< SC_PUBKEY_SIZE) // 1 - пакет слишком маленький для init (< SC_PUBKEY_SIZE)
// 2 - не удалось установить peer public key при init // 2 - не удалось установить peer public key при init
// 3 - не удалось расшифровать init пакет // 3 - не удалось расшифровать init пакет
// 4 - не init пакет (неверный код) // 4 - не init/p ing пакет (неверный код)
// 5 - коллизия peer ID и ключей // 5 - коллизия peer ID и ключей
// 6 - не удалось расшифровать обычный пакет // 6 - не удалось расшифровать обычный пакет
// 7 - слишком короткий пакет // 7 - слишком короткий пакет
@ -973,6 +1096,58 @@ static void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
uint8_t pubkey[SC_PUBKEY_SIZE]; uint8_t pubkey[SC_PUBKEY_SIZE];
} *ack_hdr=(void*)&pkt->data[0]; } *ack_hdr=(void*)&pkt->data[0];
uint64_t peer_id = be64toh(*(uint64_t*)ack_hdr->id); 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) { 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); DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "not an init packet, code=%02x", ack_hdr->code);
errorcode=4; errorcode=4;
@ -1275,6 +1450,7 @@ ec_fr:
// Initialize only sockets (servers for incoming connections) // Initialize only sockets (servers for incoming connections)
// Called before route_bgp_init() to populate etcp_sockets for nodeinfo // 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) { int init_sockets(struct UTUN_INSTANCE* instance) {
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "");
if (!instance || !instance->config) return -1; if (!instance || !instance->config) return -1;
@ -1284,6 +1460,8 @@ int init_sockets(struct UTUN_INSTANCE* instance) {
} }
struct utun_config* config = instance->config; struct utun_config* config = instance->config;
int success_count = 0;
int fail_count = 0;
// Create sockets for servers (incoming connections) // Create sockets for servers (incoming connections)
struct CFG_SERVER* server = config->servers; struct CFG_SERVER* server = config->servers;
@ -1309,9 +1487,11 @@ int init_sockets(struct UTUN_INSTANCE* instance) {
} }
if (!e_sock) { if (!e_sock) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create socket for server %s", server->name); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create socket for server %s", server->name);
fail_count++;
server = server->next; server = server->next;
continue; continue;
} }
success_count++;
// Convert IP to string for logging // Convert IP to string for logging
char addr_str[INET6_ADDRSTRLEN + 10]; char addr_str[INET6_ADDRSTRLEN + 10];
@ -1328,7 +1508,9 @@ int init_sockets(struct UTUN_INSTANCE* instance) {
server = server->next; 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) { int init_connections(struct UTUN_INSTANCE* instance) {
@ -1336,11 +1518,18 @@ int init_connections(struct UTUN_INSTANCE* instance) {
if (!instance || !instance->config) return -1; if (!instance || !instance->config) return -1;
struct utun_config* config = instance->config; struct utun_config* config = instance->config;
int socket_result = 0;
// If sockets already exist (created by init_sockets), skip server creation // If sockets already exist (created by init_sockets), check stored status
if (!instance->etcp_sockets) { 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()"); 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; return -1;
} }
} }
@ -1454,5 +1643,7 @@ int init_connections(struct UTUN_INSTANCE* instance) {
} }
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized %d connections", instance->connections_count); 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; return 0;
} }

18
src/etcp_connections.h

@ -20,6 +20,8 @@
#define ETCP_INIT_RESPONSE 0x03 #define ETCP_INIT_RESPONSE 0x03
#define ETCP_INIT_REQUEST_NOINIT 0x04 #define ETCP_INIT_REQUEST_NOINIT 0x04
#define ETCP_INIT_RESPONSE_NOINIT 0x05 #define ETCP_INIT_RESPONSE_NOINIT 0x05
#define ETCP_PING 0x06
#define ETCP_PONG 0x07
#pragma pack(push, 1) #pragma pack(push, 1)
@ -33,6 +35,16 @@ struct ETCP_DGRAM {// пакет (незашифрованный)
}; };
#pragma pack(pop) #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 {
struct ETCP_SOCKET* next; // Linked list для всех соединений 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); int etcp_find_free_local_link_id(struct ETCP_CONN* etcp);
void start_stats_timer(struct ETCP_LINK* link); 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 #endif // ETCP_CONNECTIONS_H

16
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"); DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN initialization disabled - skipping TUN device setup");
instance->tun = NULL; 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"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize sockets");
return -1; 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); instance->bgp = route_bgp_init(instance);
if (!instance->bgp) { if (!instance->bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to initialize BGP module"); 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; instance->connections = NULL;
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP connections cleanup complete"); 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 // Cleanup TUN
if (instance->tun) { if (instance->tun) {
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Closing TUN interface: %s", instance->tun->ifname); DEBUG_INFO(DEBUG_CATEGORY_TUN, "Closing TUN interface: %s", instance->tun->ifname);

7
src/utun_instance.h

@ -21,6 +21,7 @@ struct tun_if;
struct ETCP_BINDINGS; struct ETCP_BINDINGS;
struct ROUTE_BGP; struct ROUTE_BGP;
struct control_server; struct control_server;
struct PING_CONTEXT;
// uTun instance configuration // uTun instance configuration
struct UTUN_INSTANCE { struct UTUN_INSTANCE {
@ -65,6 +66,9 @@ struct UTUN_INSTANCE {
// Active sockets // Active sockets
struct ETCP_SOCKET* etcp_sockets;// linked-list struct ETCP_SOCKET* etcp_sockets;// linked-list
// Pending one-shot pings (for callback on PONG or timeout)
struct PING_CONTEXT* pending_pings;
// Routing statistics // Routing statistics
uint64_t routed_packets; uint64_t routed_packets;
uint64_t dropped_packets; uint64_t dropped_packets;
@ -77,6 +81,9 @@ struct UTUN_INSTANCE {
// Firewall // Firewall
struct firewall_ctx fw; struct firewall_ctx fw;
// Socket initialization status: 0=OK, 1=partial (some sockets failed), -1=error (none created)
int socket_init_status;
}; };
// Functions // Functions

5
tests/Makefile.am

@ -22,6 +22,7 @@ check_PROGRAMS = \
test_route_lib \ test_route_lib \
test_bgp_route_exchange \ test_bgp_route_exchange \
test_routing_mesh \ test_routing_mesh \
test_etcp_ping \
bench_timeout_heap \ bench_timeout_heap \
bench_uasync_timeouts 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_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_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_SOURCES = test_ll_queue.c
test_ll_queue_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_ll_queue_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_ll_queue_LDADD = $(COMMON_LIBS) test_ll_queue_LDADD = $(COMMON_LIBS)

264
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 <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <time.h>
#include <sys/stat.h>
#include <unistd.h>
#include "test_utils.h"
#include "../src/etcp.h"
#include "../src/etcp_connections.h" #include "../src/etcp_connections.h"
#include "../src/config_parser.h"
#include "../src/utun_instance.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/u_async.h"
#include "../lib/debug_config.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 test_completed = 0;
static int cb_called = 0; static void* monitor_timeout_id = NULL;
static int cb_success = -1; static void* test_timeout_id = NULL;
static uint64_t test_nonce = 0; 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 void ping_test_callback(int success, void* arg, uint64_t nonce) { 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 monitor_connections(void* arg) {
(void)arg; (void)arg;
cb_called++; if (test_completed) {
cb_success = success; monitor_timeout_id = NULL;
test_nonce = nonce; return;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "ping_test_callback: success=%d nonce=%016llx", success, (unsigned long long)nonce); }
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; test_completed = 1;
} }
int main(void) { int main(void) {
int test_result = 1;
debug_config_init(); debug_config_init();
debug_set_level(DEBUG_LEVEL_TRACE); debug_set_level(DEBUG_LEVEL_TRACE);
debug_set_categories(DEBUG_CATEGORY_ALL); debug_set_categories(DEBUG_CATEGORY_ALL);
utun_instance_set_tun_init_enabled(0);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "=== ETCP Ping Test ==="); if (create_temp_configs() != 0) return 1;
ua = uasync_create();
struct UASYNC* ua = uasync_create();
if (!ua) { if (!ua) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "uasync_create failed"); cleanup_temp_configs();
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);
return 1; return 1;
} }
instance->ua = ua; server_instance = utun_instance_create(ua, server_config_path);
instance->node_id = 0x1234567890ABCDEFULL; client_instance = utun_instance_create(ua, client_config_path);
// dummy keys for test (full init in production) if (!server_instance || !client_instance) {
for (int i = 0; i < SC_PRIVKEY_SIZE; i++) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "instance creation failed");
instance->my_keys.private_key[i] = i; goto cleanup;
instance->my_keys.public_key[i] = i + 64;
} }
int ret;
struct ETCP_SOCKET* dummy_sock = u_calloc(1, sizeof(struct ETCP_SOCKET)); ret = init_connections(server_instance);
if (!dummy_sock) { if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "dummy sock alloc failed"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Server init_connections failed with code %d", ret);
utun_instance_destroy(instance); goto cleanup;
uasync_destroy(ua, 0);
return 1;
} }
dummy_sock->instance = instance; ret = init_connections(client_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);
if (ret != 0) { if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_send_ping failed ret=%d", ret); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Client init_connections failed with code %d", ret);
utun_instance_destroy(instance); goto cleanup;
uasync_destroy(ua, 0);
return 1;
} }
monitor_timeout_id = uasync_set_timeout(ua, 100, NULL, monitor_connections);
// run uasync to trigger timeout cb test_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS, NULL, test_timeout);
for (int i = 0; i < 20 && !test_completed; i++) { while (!test_completed) {
uasync_poll(ua, 50); uasync_poll(ua, 10);
} }
if (test_completed == 2) {
if (cb_called != 1 || cb_success != 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "test timed out");
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "callback not triggered correctly: called=%d success=%d", cb_called, cb_success); goto cleanup;
utun_instance_destroy(instance);
uasync_destroy(ua, 0);
return 1;
} }
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Links established, running ping test");
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Ping timeout test PASSED (cb called with success=0)"); struct ETCP_CONN* conn = client_instance->connections;
if (conn && conn->links) {
// cleanup struct ETCP_LINK* link = conn->links;
utun_instance_destroy(instance); uint8_t peer_pubkey[SC_PUBKEY_SIZE];
uasync_destroy(ua, 0); 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);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "ETCP ping unit test PASSED"); if (ret == 0) {
return 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;
} }

Loading…
Cancel
Save