Files
utun2/tests/test_etcp_simple_traffic.c
T
Evgeny e39979bfac Config: Added [allowed_keys] section for client public key authorization on server
- Added CFG_ALLOWED_KEY struct with binary key storage (64 bytes) for fast memcmp comparison
- Implemented parsing of [allowed_keys] section with allow_all flag and per-key entries
- Added key validation in etcp_connections.c server handler (errorcode=8 for rejected keys)
- Default behavior: deny all if [allowed_keys] section is missing or allow_all=0 with no keys
- Added proper memory cleanup and debug logging for allowed_keys configuration
- Updated all test configs to include [allowed_keys] allow_all=1 for backward compatibility
2026-04-24 00:16:30 +03:00

564 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../lib/platform_compat.h"
#include "test_utils.h"
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <time.h>
#include <sys/stat.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/ll_queue.h"
#include "../lib/debug_config.h"
#define TEST_TIMEOUT_MS 50000 // 5 seconds in uasync timebase (0.1ms units)
#define PACKET_SIZE 100 // Test packet size
static struct UTUN_INSTANCE* server_instance = NULL;
static struct UTUN_INSTANCE* client_instance = NULL;
static struct UASYNC* ua = NULL;
// Temp config file paths
static char temp_dir[] = "/tmp/utun_test_XXXXXX";
static char server_config_path[256];
static char client_config_path[256];
static int server_port = 0;
static int client_port = 0;
// Create temp config files with dynamic ports to avoid conflicts
static int create_temp_configs(void) {
if (test_mkdtemp(temp_dir) != 0) {
fprintf(stderr, "Failed to create temp directory\n");
return -1;
}
// Use PID-based ports to avoid conflicts with parallel tests or leftovers
int base_port = 40000 + (getpid() % 20000);
server_port = base_port;
client_port = base_port + 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) {
fprintf(stderr, "Failed to create server config file\n");
return -1;
}
fprintf(f,
"[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:%d\n"
"type=public\n"
"\n"
"[allowed_keys]\n"
"allow_all=1\n",
server_port);
fclose(f);
f = fopen(client_config_path, "w");
if (!f) {
fprintf(stderr, "Failed to create client config file\n");
test_unlink(server_config_path);
return -1;
}
fprintf(f,
"[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:%d\n"
"type=public\n"
"\n"
"[client: test_client]\n"
"keepalive=1\n"
"peer_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n"
"link=test:127.0.0.1:%d\n",
client_port, server_port);
fclose(f);
return 0;
}
// Cleanup temp config files
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 int test_completed = 0; // 0 = running, 1 = success, 2 = timeout/failure
static void* packet_timeout_id = NULL;
// Test packet data
static uint8_t test_packet_data[PACKET_SIZE];
static int packet_sent = 0;
static int packet_received = 0;
// State variables for monitor_and_send (non-static to avoid issues with multiple test runs in same process)
static int connection_checked = 0;
static int packet_sent_flag = 0;
// Function to check if connection is established - UPDATED WITH DEBUG
static int is_connection_established(struct UTUN_INSTANCE* inst) {
printf("[DEBUG] is_connection_established: Checking instance %p\n", inst);
fflush(stdout);
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "is_connection_established: Checking instance %p", inst);
if (!inst) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "is_connection_established: Instance is NULL");
return 0;
}
printf("[DEBUG] is_connection_established: Instance has %d connections\n", inst->connections_count);
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "is_connection_established: Instance has %d connections", inst->connections_count);
struct ETCP_CONN* conn = inst->connections;
while (conn) {
printf("[DEBUG] is_connection_established: Checking connection %p\n", conn);
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "is_connection_established: Checking connection %p", conn);
struct ETCP_LINK* link = conn->links;
while (link) {
printf("[DEBUG] is_connection_established: Link %p - initialized=%d, is_server=%d\n",
link, link->initialized, link->is_server);
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "is_connection_established: Link %p - initialized=%d, is_server=%d",
link, link->initialized, link->is_server);
if (link->initialized) {
printf("[DEBUG] is_connection_established: FOUND initialized link!\n");
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "is_connection_established: FOUND initialized link!");
return 1;
}
link = link->next;
}
conn = conn->next;
}
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "is_connection_established: No initialized links found");
return 0;
}
// Function to send test packet to client's input queue
static void send_test_packet(void) {
printf("[DEBUG] send_test_packet: ENTERING\n");
fflush(stdout);
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "send_test_packet: ENTERING");
if (!client_instance || packet_sent) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "send_test_packet: SKIPPING (client_instance=%p, packet_sent=%d)", client_instance, packet_sent);
return;
}
struct ETCP_CONN* conn = client_instance->connections;
if (!conn) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "send_test_packet: No ETCP connection found on client");
return;
}
printf("[DEBUG] send_test_packet: Found connection %p\n", conn);
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "send_test_packet: Found connection %p", conn);
printf("[DEBUG] send_test_packet: connection input_queue=%p\n", conn->input_queue);
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "send_test_packet: connection input_queue=%p", conn->input_queue);
printf("[DEBUG] send_test_packet: connection output_queue=%p\n", conn->output_queue);
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "send_test_packet: connection output_queue=%p", conn->output_queue);
printf("[DEBUG] send_test_packet: connection next_tx_id=%u\n", conn->next_tx_id);
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "send_test_packet: connection next_tx_id=%u", conn->next_tx_id);
if (!conn->input_queue) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "send_test_packet: Client input queue is NULL");
return;
}
// Create test packet data
for (int i = 0; i < PACKET_SIZE; i++) {
test_packet_data[i] = (uint8_t)(i % 256);
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "send_test_packet: Created test data, first byte=%02X, last byte=%02X",
test_packet_data[0], test_packet_data[PACKET_SIZE-1]);
// Use etcp_int_send function
printf("[DEBUG] send_test_packet: Calling etcp_int_send with len=%d\n", PACKET_SIZE);
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "send_test_packet: Calling etcp_int_send with len=%d", PACKET_SIZE);
int result = etcp_int_send(conn, test_packet_data, PACKET_SIZE);
printf("[DEBUG] send_test_packet: etcp_int_send returned %d\n", result);
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "send_test_packet: etcp_int_send returned %d", result);
if (result != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "send_test_packet: Failed to send packet via etcp_int_send");
return;
}
packet_sent = 1;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "send_test_packet: SUCCESS - Test packet sent via etcp_int_send (%d bytes)", PACKET_SIZE);
}
// Function to check if packet received in server's output queue
static void check_packet_received(void) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "check_packet_received: ENTERING");
if (!server_instance || packet_received) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "check_packet_received: SKIPPING (server_instance=%p, packet_received=%d)", server_instance, packet_received);
return;
}
struct ETCP_CONN* conn = server_instance->connections;
if (!conn) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "check_packet_received: No connection on server");
return;
}
if (!conn->output_queue) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "check_packet_received: No output_queue on server connection");
return;
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "check_packet_received: Checking server connection %p", conn);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "check_packet_received: Server output_queue count: %d", queue_entry_count(conn->output_queue));
// Disable routing callback to keep packets in output_queue for test verification
queue_set_callback(conn->output_queue, NULL, NULL);
// Check if there's any packet in output queue
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_data_get(conn->output_queue);
if (pkt) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "check_packet_received: Found packet in output queue");
// ETCP_FRAGMENT содержит ll_entry в начале, данные в pkt_data
size_t size = pkt->ll.len;
uint8_t* data = pkt->ll.dgram;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "check_packet_received: Packet size=%zu, expected=%d", size, PACKET_SIZE);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "check_packet_received: First byte received=%02X, expected=%02X",
data[0], test_packet_data[0]);
// Проверяем только данные (первые PACKET_SIZE байт), размер может включать ETCP заголовки
if (size >= PACKET_SIZE && memcmp(data, test_packet_data, PACKET_SIZE) == 0) {
packet_received = 1;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "check_packet_received: SUCCESS - Packet received in server output queue (%zu bytes), data matches", size);
} else {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "check_packet_received: Packet received but data differs (expected %d bytes, got %zu)", PACKET_SIZE, size);
}
// Освобождаем ETCP_FRAGMENT и данные
if (pkt->ll.dgram) {
memory_pool_free(conn->instance->data_pool, pkt->ll.dgram);
}
queue_entry_free((struct ll_entry*)pkt);
} else {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "check_packet_received: No packet found in output queue");
}
}
// Monitor connection and send packet when ready
static void monitor_and_send(void* arg) {
printf("[DEBUG] monitor_and_send: ENTERING (arg=%p)\n", arg);
fflush(stdout);
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "monitor_and_send: ENTERING (arg=%p)", arg);
(void)arg;
if (test_completed) {
packet_timeout_id = NULL;
return;
}
// Check connection
if (!connection_checked) {
int server_ready = is_connection_established(server_instance);
int client_ready = is_connection_established(client_instance);
printf("[DEBUG] monitor_and_send: Connection check - server=%d, client=%d\n", server_ready, client_ready);
fflush(stdout);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "monitor_and_send: Connection check - server=%d, client=%d", server_ready, client_ready);
// We only need client link initialized to send packet
// Server will accept packets via its socket
if (client_ready) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "monitor_and_send: Client link initialized, ready to send packet (server=%d, client=%d)", server_ready, client_ready);
connection_checked = 1;
} else {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "monitor_and_send: Waiting for connection... (server=%d, client=%d)", server_ready, client_ready);
}
}
// Send packet once connection is ready
if (connection_checked && !packet_sent_flag) {
send_test_packet();
packet_sent_flag = 1;
}
// Check for received packet
if (packet_sent_flag) {
check_packet_received();
if (packet_received) {
test_completed = 1; // Success
printf("\n=== SUCCESS: Packet transmitted successfully! ===\n");
if (packet_timeout_id) {
uasync_cancel_timeout(server_instance->ua, packet_timeout_id);
packet_timeout_id = NULL;
}
return;
}
}
// Schedule next check
if (!test_completed) {
void* new_timeout = uasync_set_timeout(server_instance->ua, 100, NULL, monitor_and_send, "test_monitor");
if (!new_timeout) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "monitor_and_send: Failed to set timeout - uasync_set_timeout returned NULL");
printf("[ERROR] Failed to set timeout in monitor_and_send\n");
fflush(stdout);
}
packet_timeout_id = new_timeout;
}
}
// Timeout handler
static void test_timeout(void* arg) {
(void)arg;
if (!test_completed) {
printf("\n=== TIMEOUT: Packet not received within %d seconds ===\n", TEST_TIMEOUT_MS/10000);
test_completed = 2; // Timeout/failure
if (packet_timeout_id) {
uasync_cancel_timeout(server_instance->ua, packet_timeout_id);
packet_timeout_id = NULL;
}
}
}
int main() {
// Create temp config files first
if (create_temp_configs() != 0) {
fprintf(stderr, "Failed to create temporary config files\n");
return 1;
}
printf("=== ETCP Simple Traffic Test (Queue-based) ===\n\n");
// Enable debug output - MAXIMUM DEBUGGING
debug_config_init();
debug_set_level(DEBUG_LEVEL_TRACE);
debug_set_categories(DEBUG_CATEGORY_ALL);
debug_enable_function_name(1);
DEBUG_TRACE(DEBUG_CATEGORY_MEMORY, "*************1");
printf("Function names enabled: YES\n");
printf("===========================\n\n");
// Explicitly disable TUN initialization for this test
utun_instance_set_tun_init_enabled(0);
// Create server instance
printf("Creating server instance...\n");
ua = uasync_create();
server_instance = utun_instance_create(ua, server_config_path);
if (!server_instance) {
printf("Failed to create server instance\n");
return 1;
}
// Initialize connections and register sockets regardless of TUN state
if (!server_instance->tun) {
printf("ℹ️ Server TUN disabled - initializing connections only\n");
}
// Initialize ETCP connections (creates sockets and links)
if (init_connections(server_instance) < 0) {
printf("Failed to initialize server connections\n");
utun_instance_destroy(server_instance);
return 1;
} else {
printf("Server connections initialized: sockets=%p, connections=%p\n",
server_instance->etcp_sockets, server_instance->connections);
}
printf("✅ Server instance initialized successfully (node_id=%llx)\n", (unsigned long long)server_instance->node_id);
printf("Server instance ready (node_id=%llx)\n\n", (unsigned long long)server_instance->node_id);
// Create client instance
printf("Creating client instance...\n");
client_instance = utun_instance_create(ua, client_config_path);
if (!client_instance) {
printf("Failed to create client instance\n");
utun_instance_destroy(server_instance);
return 1;
}
// Initialize connections and register sockets regardless of TUN state
if (!client_instance->tun) {
printf("ℹ️ Client TUN disabled - initializing connections only\n");
}
// Initialize ETCP connections (creates sockets and links)
printf("About to call init_connections() for client instance\n");
fflush(stdout);
int conn_result = init_connections(client_instance);
printf("init_connections() returned: %d\n", conn_result);
fflush(stdout);
if (conn_result < 0) {
printf("Failed to initialize client connections (result=%d)\n", conn_result);
printf("But continuing test to analyze the issue...\n");
fflush(stdout);
// Don't return error - continue to analyze
} else {
printf("Client connections initialized: sockets=%p, connections=%p, count=%d\n",
client_instance->etcp_sockets, client_instance->connections,
client_instance ? client_instance->connections_count : -1);
fflush(stdout);
}
printf("✅ Client instance initialized successfully (node_id=%llx)\n", (unsigned long long)client_instance->node_id);
printf("Client instance ready (node_id=%llx)\n\n", (unsigned long long)client_instance->node_id);
// Debug: print connection and link info
printf("\n=== Connection Debug ===\n");
struct ETCP_CONN* conn = client_instance->connections;
while (conn) {
printf("Client connection %p: peer_node_id=%llx\n", conn, (unsigned long long)conn->peer_node_id);
struct ETCP_LINK* link = conn->links;
while (link) {
printf(" Link %p: initialized=%d, is_server=%d, remote_addr family=%d, conn->fd=%d\n",
link, link->initialized, link->is_server, link->remote_addr.ss_family, link->conn ? link->conn->fd : -1);
link = link->next;
}
conn = conn->next;
}
conn = server_instance->connections;
while (conn) {
printf("Server connection %p: peer_node_id=%llx\n", conn, (unsigned long long)conn->peer_node_id);
struct ETCP_LINK* link = conn->links;
while (link) {
printf(" Link %p: initialized=%d, is_server=%d\n", link, link->initialized, link->is_server);
link = link->next;
}
conn = conn->next;
}
printf("=== End Debug ===\n\n");
// Start monitoring and packet transmission
printf("Starting packet transmission test...\n");
packet_timeout_id = uasync_set_timeout(ua, 500, NULL, monitor_and_send, "test_monitor");
void* global_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS, NULL, test_timeout, "test_timeout");
// Main event loop
printf("Running event loop...\n\n");
// Give instances time to initialize
printf("Waiting 0.5 seconds for instances initialization...\n");
for (int i = 0; i < 50; i++) {
if (ua) uasync_poll(ua, 10);
}
printf("Starting connection and packet transmission...\n");
int elapsed = 0;
int poll_interval = 50; // 5ms in uasync timebase (0.1ms units)
int check_counter = 0;
while (!test_completed && elapsed < TEST_TIMEOUT_MS + 1000) {
if (ua) uasync_poll(ua, poll_interval);
elapsed += poll_interval;
check_counter++;
// Check for received packet every 10 iterations (every 50ms)
// This ensures we check even if the timeout callback doesn't fire
if (packet_sent && !packet_received && (check_counter % 10 == 0)) {
check_packet_received();
}
// If packet was received via direct check, mark test as completed
if (packet_received && !test_completed) {
test_completed = 1;
}
// Quick exit if packet received
if (test_completed == 1) {
printf("[TEST] Packet received, exiting early after %d ms\n", elapsed);
break;
}
}
// Cleanup
printf("\nCleaning up...\n");
// Cancel timeouts
if (packet_timeout_id) {
uasync_cancel_timeout(ua, packet_timeout_id);
packet_timeout_id = NULL;
}
if (global_timeout_id) {
uasync_cancel_timeout(ua, global_timeout_id);
}
// Destroy instances
if (server_instance) {
server_instance->running = 0;
utun_instance_destroy(server_instance);
}
if (client_instance) {
client_instance->running = 0;
utun_instance_destroy(client_instance);
}
// Destroy shared uasync instance after both instances are destroyed
if (ua) {
uasync_destroy(ua, 0);
ua = NULL;
}
// Cleanup temp config files
cleanup_temp_configs();
// Evaluate test result
if (test_completed == 1) {
printf("\n=== TEST PASSED ===\n");
printf("✅ Packet successfully transmitted from client input queue to server output queue\n");
printf("✅ ETCP connection and queue mechanisms verified\n");
return 0;
} else if (test_completed == 2) {
printf("\n=== TEST FAILED: Packet not received within timeout ===\n");
printf("❌ Connection may not have been established\n");
printf("❌ Check if UDP sockets were created and bound correctly\n");
return 1;
} else {
printf("\n=== TEST FAILED: Unknown error ===\n");
return 1;
}
}