|
|
|
|
@ -1,421 +1,211 @@
|
|
|
|
|
// test_routing_mesh.c - Test routing between 3 instances in mesh topology
|
|
|
|
|
// Each instance has 2 subnets and sends packets to 2 neighbors
|
|
|
|
|
// Topology: A <-> B <-> C <-> A (full mesh)
|
|
|
|
|
// Verification: packet size, destination IP, payload checksum
|
|
|
|
|
// Based on test_etcp_two_instances.c approach - using config files
|
|
|
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
|
#include <stdlib.h> |
|
|
|
|
#include <string.h> |
|
|
|
|
#include <stdint.h> |
|
|
|
|
#include <time.h> |
|
|
|
|
#include "test_utils.h" |
|
|
|
|
#include "../lib/platform_compat.h" |
|
|
|
|
#include "../lib/u_async.h" |
|
|
|
|
#include "../lib/ll_queue.h" |
|
|
|
|
#include "../lib/debug_config.h" |
|
|
|
|
#include "../lib/memory_pool.h" |
|
|
|
|
#include "../src/utun_instance.h" |
|
|
|
|
#include "../src/tun_if.h" |
|
|
|
|
#include "../src/etcp.h" |
|
|
|
|
#include "../src/routing.h" |
|
|
|
|
#include "../src/config_parser.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" |
|
|
|
|
|
|
|
|
|
#define TEST_TIMEOUT_CONNECT_MS 10000 // 10 seconds for connections
|
|
|
|
|
#define TEST_TIMEOUT_DELIVERY_MS 10000 // 10 seconds for packet delivery
|
|
|
|
|
#define TEST_PACKET_SIZE 100 |
|
|
|
|
#define TEST_TIMEOUT_MS 10000 // 10 seconds timeout
|
|
|
|
|
|
|
|
|
|
// Test packet structure with IP header + payload
|
|
|
|
|
struct test_packet { |
|
|
|
|
uint8_t ip_header[20]; // Minimal IP header
|
|
|
|
|
uint8_t payload[80]; // Test payload
|
|
|
|
|
}; |
|
|
|
|
// Keys from test_etcp_two_instances.c (known working keys)
|
|
|
|
|
// Instance A keys
|
|
|
|
|
#define KEY_A_PRIV "67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb" |
|
|
|
|
#define KEY_A_PUB "1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9" |
|
|
|
|
|
|
|
|
|
// Instance B keys
|
|
|
|
|
#define KEY_B_PRIV "4813d31d28b7e9829247f488c6be7672f2bdf61b2508333128e386d1759afed2" |
|
|
|
|
#define KEY_B_PUB "c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71" |
|
|
|
|
|
|
|
|
|
// Instance C keys (need valid keys - generate them)
|
|
|
|
|
static char key_c_priv[65]; |
|
|
|
|
static char key_c_pub[129]; |
|
|
|
|
|
|
|
|
|
// Test state
|
|
|
|
|
static struct UASYNC* ua = NULL; |
|
|
|
|
static struct UTUN_INSTANCE* inst_a = NULL; |
|
|
|
|
static struct UTUN_INSTANCE* inst_b = NULL; |
|
|
|
|
static struct UTUN_INSTANCE* inst_c = NULL; |
|
|
|
|
|
|
|
|
|
// Config structures (created programmatically)
|
|
|
|
|
static struct utun_config* cfg_a = NULL; |
|
|
|
|
static struct utun_config* cfg_b = NULL; |
|
|
|
|
static struct utun_config* cfg_c = NULL; |
|
|
|
|
|
|
|
|
|
// Test statistics
|
|
|
|
|
static int packets_sent = 0; |
|
|
|
|
static int packets_received = 0; |
|
|
|
|
static int packets_verified = 0; |
|
|
|
|
static int test_phase = 0; // 0=connecting, 1=sending, 2=verifying, 3=done
|
|
|
|
|
|
|
|
|
|
// Timeout IDs
|
|
|
|
|
static void* connect_timeout_id = NULL; |
|
|
|
|
static void* delivery_timeout_id = NULL; |
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Helper Functions
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
static void bin2hex(const uint8_t* bin, size_t len, char* hex) { |
|
|
|
|
const char* hex_chars = "0123456789ABCDEF"; |
|
|
|
|
for (size_t i = 0; i < len; i++) { |
|
|
|
|
hex[i * 2] = hex_chars[bin[i] >> 4]; |
|
|
|
|
hex[i * 2 + 1] = hex_chars[bin[i] & 0x0F]; |
|
|
|
|
} |
|
|
|
|
hex[len * 2] = '\0'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Programmatic Config Creation
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
static void parse_ip_cidr(const char* cidr, struct IP* ip, uint8_t* netmask) { |
|
|
|
|
char buf[64]; |
|
|
|
|
strncpy(buf, cidr, sizeof(buf) - 1); |
|
|
|
|
buf[sizeof(buf) - 1] = '\0'; |
|
|
|
|
|
|
|
|
|
char* slash = strchr(buf, '/'); |
|
|
|
|
if (slash) { |
|
|
|
|
*slash = '\0'; |
|
|
|
|
*netmask = atoi(slash + 1); |
|
|
|
|
} else { |
|
|
|
|
*netmask = 32; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inet_pton(AF_INET, buf, &ip->addr.v4); |
|
|
|
|
ip->family = AF_INET; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void parse_addr_port(const char* addr_port, struct sockaddr_storage* sa) { |
|
|
|
|
char buf[64]; |
|
|
|
|
strncpy(buf, addr_port, sizeof(buf) - 1); |
|
|
|
|
buf[sizeof(buf) - 1] = '\0'; |
|
|
|
|
|
|
|
|
|
char* colon = strrchr(buf, ':'); |
|
|
|
|
if (!colon) return; |
|
|
|
|
|
|
|
|
|
*colon = '\0'; |
|
|
|
|
int port = atoi(colon + 1); |
|
|
|
|
|
|
|
|
|
struct sockaddr_in* sin = (struct sockaddr_in*)sa; |
|
|
|
|
sin->sin_family = AF_INET; |
|
|
|
|
sin->sin_port = htons(port); |
|
|
|
|
inet_pton(AF_INET, buf, &sin->sin_addr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Create server config
|
|
|
|
|
static struct CFG_SERVER* create_server(const char* name, const char* addr_port, int type) { |
|
|
|
|
struct CFG_SERVER* srv = calloc(1, sizeof(struct CFG_SERVER)); |
|
|
|
|
if (!srv) return NULL; |
|
|
|
|
|
|
|
|
|
strncpy(srv->name, name, MAX_CONN_NAME_LEN - 1); |
|
|
|
|
parse_addr_port(addr_port, &srv->ip); |
|
|
|
|
srv->type = type; |
|
|
|
|
srv->netif_index = 0; |
|
|
|
|
srv->so_mark = 0; |
|
|
|
|
srv->next = NULL; |
|
|
|
|
|
|
|
|
|
return srv; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Create client config
|
|
|
|
|
static struct CFG_CLIENT* create_client(const char* name, const char* link, const char* peer_key) { |
|
|
|
|
struct CFG_CLIENT* cli = calloc(1, sizeof(struct CFG_CLIENT)); |
|
|
|
|
if (!cli) return NULL; |
|
|
|
|
|
|
|
|
|
strncpy(cli->name, name, MAX_CONN_NAME_LEN - 1); |
|
|
|
|
cli->keepalive = 1; |
|
|
|
|
if (peer_key) { |
|
|
|
|
strncpy(cli->peer_public_key_hex, peer_key, MAX_KEY_LEN - 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Parse link: "server_name:ip:port"
|
|
|
|
|
char buf[256]; |
|
|
|
|
strncpy(buf, link, sizeof(buf) - 1); |
|
|
|
|
char* first_colon = strchr(buf, ':'); |
|
|
|
|
if (first_colon) { |
|
|
|
|
*first_colon = '\0'; |
|
|
|
|
const char* server_name = buf; |
|
|
|
|
const char* remote_addr = first_colon + 1; |
|
|
|
|
|
|
|
|
|
// Find server in global list (will be set later)
|
|
|
|
|
// For now just create the link structure
|
|
|
|
|
struct CFG_CLIENT_LINK* clink = calloc(1, sizeof(struct CFG_CLIENT_LINK)); |
|
|
|
|
if (clink) { |
|
|
|
|
parse_addr_port(remote_addr, &clink->remote_addr); |
|
|
|
|
// clink->local_srv will be set later when linking
|
|
|
|
|
clink->next = NULL; |
|
|
|
|
cli->links = clink; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
cli->next = NULL; |
|
|
|
|
return cli; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Create route entry
|
|
|
|
|
static struct CFG_ROUTE_ENTRY* create_route(const char* cidr) { |
|
|
|
|
struct CFG_ROUTE_ENTRY* route = calloc(1, sizeof(struct CFG_ROUTE_ENTRY)); |
|
|
|
|
if (!route) return NULL; |
|
|
|
|
|
|
|
|
|
parse_ip_cidr(cidr, &route->ip, &route->netmask); |
|
|
|
|
route->next = NULL; |
|
|
|
|
return route; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Link client to server
|
|
|
|
|
static void link_client_to_server(struct CFG_CLIENT* client, struct CFG_SERVER* servers) { |
|
|
|
|
if (!client || !client->links) return; |
|
|
|
|
|
|
|
|
|
// Find server by name from link
|
|
|
|
|
// The server name was stored in the parsing logic above
|
|
|
|
|
// We need to find it in the servers list
|
|
|
|
|
struct CFG_CLIENT_LINK* clink = client->links; |
|
|
|
|
|
|
|
|
|
// Simple approach: assume link name is in client name (to_b -> server b)
|
|
|
|
|
const char* target = NULL; |
|
|
|
|
if (strstr(client->name, "_b")) target = "b"; |
|
|
|
|
else if (strstr(client->name, "_a")) target = "a"; |
|
|
|
|
else if (strstr(client->name, "_c")) target = "c"; |
|
|
|
|
|
|
|
|
|
if (target) { |
|
|
|
|
struct CFG_SERVER* srv = servers; |
|
|
|
|
while (srv) { |
|
|
|
|
if (strcmp(srv->name, target) == 0) { |
|
|
|
|
clink->local_srv = srv; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
srv = srv->next; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Create complete instance config
|
|
|
|
|
static struct utun_config* create_instance_config( |
|
|
|
|
uint64_t node_id, |
|
|
|
|
const char* tun_ip, |
|
|
|
|
const char* tun_ifname, |
|
|
|
|
const char** subnets, |
|
|
|
|
int num_subnets, |
|
|
|
|
const char** servers, |
|
|
|
|
int num_servers, |
|
|
|
|
const char** clients, |
|
|
|
|
int num_clients |
|
|
|
|
) { |
|
|
|
|
struct utun_config* cfg = calloc(1, sizeof(struct utun_config)); |
|
|
|
|
if (!cfg) return NULL; |
|
|
|
|
|
|
|
|
|
// Global config
|
|
|
|
|
cfg->global.my_node_id = node_id; |
|
|
|
|
cfg->global.mtu = 1500; |
|
|
|
|
strncpy(cfg->global.tun_ifname, tun_ifname, 15); |
|
|
|
|
parse_ip_cidr(tun_ip, &cfg->global.tun_ip, &(uint8_t){32}); |
|
|
|
|
cfg->global.tun_test_mode = 1; |
|
|
|
|
|
|
|
|
|
// Servers
|
|
|
|
|
struct CFG_SERVER* last_srv = NULL; |
|
|
|
|
for (int i = 0; i < num_servers; i++) { |
|
|
|
|
// Parse "name:addr:port"
|
|
|
|
|
char buf[256]; |
|
|
|
|
strncpy(buf, servers[i], sizeof(buf) - 1); |
|
|
|
|
char* colon1 = strchr(buf, ':'); |
|
|
|
|
if (!colon1) continue; |
|
|
|
|
*colon1 = '\0'; |
|
|
|
|
const char* name = buf; |
|
|
|
|
const char* addr = colon1 + 1; |
|
|
|
|
|
|
|
|
|
struct CFG_SERVER* srv = create_server(name, addr, CFG_SERVER_TYPE_PUBLIC); |
|
|
|
|
if (last_srv) { |
|
|
|
|
last_srv->next = srv; |
|
|
|
|
} else { |
|
|
|
|
cfg->servers = srv; |
|
|
|
|
} |
|
|
|
|
last_srv = srv; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Subnets (routing)
|
|
|
|
|
struct CFG_ROUTE_ENTRY* last_route = NULL; |
|
|
|
|
for (int i = 0; i < num_subnets; i++) { |
|
|
|
|
struct CFG_ROUTE_ENTRY* route = create_route(subnets[i]); |
|
|
|
|
if (last_route) { |
|
|
|
|
last_route->next = route; |
|
|
|
|
} else { |
|
|
|
|
cfg->my_subnets = route; |
|
|
|
|
} |
|
|
|
|
last_route = route; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Clients
|
|
|
|
|
struct CFG_CLIENT* last_cli = NULL; |
|
|
|
|
for (int i = 0; i < num_clients; i++) { |
|
|
|
|
// Parse "name:link" (link format: "server:ip:port")
|
|
|
|
|
char buf[256]; |
|
|
|
|
strncpy(buf, clients[i], sizeof(buf) - 1); |
|
|
|
|
char* colon = strchr(buf, ':'); |
|
|
|
|
if (!colon) continue; |
|
|
|
|
*colon = '\0'; |
|
|
|
|
const char* name = buf; |
|
|
|
|
const char* link = colon + 1; |
|
|
|
|
|
|
|
|
|
struct CFG_CLIENT* cli = create_client(name, link, NULL); // peer_key will be set later
|
|
|
|
|
if (last_cli) { |
|
|
|
|
last_cli->next = cli; |
|
|
|
|
} else { |
|
|
|
|
cfg->clients = cli; |
|
|
|
|
} |
|
|
|
|
last_cli = cli; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Link clients to servers
|
|
|
|
|
struct CFG_CLIENT* cli = cfg->clients; |
|
|
|
|
while (cli) { |
|
|
|
|
link_client_to_server(cli, cfg->servers); |
|
|
|
|
cli = cli->next; |
|
|
|
|
static struct UASYNC* ua = NULL; |
|
|
|
|
static int test_phase = 0; |
|
|
|
|
static void* timeout_id = NULL; |
|
|
|
|
|
|
|
|
|
static char temp_dir[] = "/tmp/utun_mesh_test_XXXXXX"; |
|
|
|
|
static char config_a[256], config_b[256], config_c[256]; |
|
|
|
|
|
|
|
|
|
// Config A: servers for B and C to connect to
|
|
|
|
|
static const char* config_a_content = |
|
|
|
|
"[global]\n" |
|
|
|
|
"my_node_id=0xAAAAAAAAAAAAAAAA\n" |
|
|
|
|
"my_private_key=" KEY_A_PRIV "\n" |
|
|
|
|
"my_public_key=" KEY_A_PUB "\n" |
|
|
|
|
"tun_ip=10.99.1.1/24\n" |
|
|
|
|
"tun_ifname=tun_a\n" |
|
|
|
|
"tun_test_mode=1\n" |
|
|
|
|
"\n" |
|
|
|
|
"[routing]\n" |
|
|
|
|
"my_subnet=192.168.10.0/24\n" |
|
|
|
|
"my_subnet=192.168.11.0/24\n" |
|
|
|
|
"\n" |
|
|
|
|
"[server: b]\n" |
|
|
|
|
"addr=127.0.0.1:9101\n" |
|
|
|
|
"type=public\n" |
|
|
|
|
"\n" |
|
|
|
|
"[server: c]\n" |
|
|
|
|
"addr=127.0.0.1:9102\n" |
|
|
|
|
"type=public\n"; |
|
|
|
|
|
|
|
|
|
// Config B: servers for A and C to connect to
|
|
|
|
|
static const char* config_b_content = |
|
|
|
|
"[global]\n" |
|
|
|
|
"my_node_id=0xBBBBBBBBBBBBBBBB\n" |
|
|
|
|
"my_private_key=" KEY_B_PRIV "\n" |
|
|
|
|
"my_public_key=" KEY_B_PUB "\n" |
|
|
|
|
"tun_ip=10.99.2.1/24\n" |
|
|
|
|
"tun_ifname=tun_b\n" |
|
|
|
|
"tun_test_mode=1\n" |
|
|
|
|
"\n" |
|
|
|
|
"[routing]\n" |
|
|
|
|
"my_subnet=192.168.20.0/24\n" |
|
|
|
|
"my_subnet=192.168.21.0/24\n" |
|
|
|
|
"\n" |
|
|
|
|
"[server: a]\n" |
|
|
|
|
"addr=127.0.0.1:9201\n" |
|
|
|
|
"type=public\n" |
|
|
|
|
"\n" |
|
|
|
|
"[server: c]\n" |
|
|
|
|
"addr=127.0.0.1:9202\n" |
|
|
|
|
"type=public\n"; |
|
|
|
|
|
|
|
|
|
// Config C: servers for A and B to connect to
|
|
|
|
|
static const char* config_c_content_template = |
|
|
|
|
"[global]\n" |
|
|
|
|
"my_node_id=0xCCCCCCCCCCCCCCCC\n" |
|
|
|
|
"my_private_key=%s\n" |
|
|
|
|
"my_public_key=%s\n" |
|
|
|
|
"tun_ip=10.99.3.1/24\n" |
|
|
|
|
"tun_ifname=tun_c\n" |
|
|
|
|
"tun_test_mode=1\n" |
|
|
|
|
"\n" |
|
|
|
|
"[routing]\n" |
|
|
|
|
"my_subnet=192.168.30.0/24\n" |
|
|
|
|
"my_subnet=192.168.31.0/24\n" |
|
|
|
|
"\n" |
|
|
|
|
"[server: a]\n" |
|
|
|
|
"addr=127.0.0.1:9302\n" |
|
|
|
|
"type=public\n" |
|
|
|
|
"\n" |
|
|
|
|
"[server: b]\n" |
|
|
|
|
"addr=127.0.0.1:9303\n" |
|
|
|
|
"type=public\n"; |
|
|
|
|
|
|
|
|
|
// Client sections to add to configs for mesh topology
|
|
|
|
|
static const char* client_a_content = |
|
|
|
|
"\n[client: to_b]\n" |
|
|
|
|
"keepalive=1\n" |
|
|
|
|
"peer_public_key=" KEY_B_PUB "\n" |
|
|
|
|
"link=b:127.0.0.1:9201\n" |
|
|
|
|
"\n[client: to_c]\n" |
|
|
|
|
"keepalive=1\n" |
|
|
|
|
"peer_public_key=%s\n" // Will be filled with C's key
|
|
|
|
|
"link=c:127.0.0.1:9302\n"; |
|
|
|
|
|
|
|
|
|
static const char* client_b_content = |
|
|
|
|
"\n[client: to_a]\n" |
|
|
|
|
"keepalive=1\n" |
|
|
|
|
"peer_public_key=" KEY_A_PUB "\n" |
|
|
|
|
"link=a:127.0.0.1:9101\n" |
|
|
|
|
"\n[client: to_c]\n" |
|
|
|
|
"keepalive=1\n" |
|
|
|
|
"peer_public_key=%s\n" // Will be filled with C's key
|
|
|
|
|
"link=c:127.0.0.1:9303\n"; |
|
|
|
|
|
|
|
|
|
static const char* client_c_content_template = |
|
|
|
|
"\n[client: to_a]\n" |
|
|
|
|
"keepalive=1\n" |
|
|
|
|
"peer_public_key=" KEY_A_PUB "\n" |
|
|
|
|
"link=a:127.0.0.1:9102\n" |
|
|
|
|
"\n[client: to_b]\n" |
|
|
|
|
"keepalive=1\n" |
|
|
|
|
"peer_public_key=" KEY_B_PUB "\n" |
|
|
|
|
"link=b:127.0.0.1:9202\n"; |
|
|
|
|
|
|
|
|
|
static int create_temp_configs(void) { |
|
|
|
|
if (test_mkdtemp(temp_dir) != 0) { |
|
|
|
|
fprintf(stderr, "Failed to create temp directory\n"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return cfg; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Free config memory
|
|
|
|
|
static void free_instance_config(struct utun_config* cfg) { |
|
|
|
|
if (!cfg) return; |
|
|
|
|
snprintf(config_a, sizeof(config_a), "%s/a.conf", temp_dir); |
|
|
|
|
snprintf(config_b, sizeof(config_b), "%s/b.conf", temp_dir); |
|
|
|
|
snprintf(config_c, sizeof(config_c), "%s/c.conf", temp_dir); |
|
|
|
|
|
|
|
|
|
// Free servers
|
|
|
|
|
struct CFG_SERVER* srv = cfg->servers; |
|
|
|
|
while (srv) { |
|
|
|
|
struct CFG_SERVER* next = srv->next; |
|
|
|
|
free(srv); |
|
|
|
|
srv = next; |
|
|
|
|
// Generate keys for C
|
|
|
|
|
struct SC_MYKEYS keys_c; |
|
|
|
|
if (sc_generate_keypair(&keys_c) != SC_OK) { |
|
|
|
|
fprintf(stderr, "Failed to generate keys for C\n"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Free clients and their links
|
|
|
|
|
struct CFG_CLIENT* cli = cfg->clients; |
|
|
|
|
while (cli) { |
|
|
|
|
struct CFG_CLIENT* next = cli->next; |
|
|
|
|
struct CFG_CLIENT_LINK* link = cli->links; |
|
|
|
|
while (link) { |
|
|
|
|
struct CFG_CLIENT_LINK* lnext = link->next; |
|
|
|
|
free(link); |
|
|
|
|
link = lnext; |
|
|
|
|
} |
|
|
|
|
free(cli); |
|
|
|
|
cli = next; |
|
|
|
|
// Convert to hex
|
|
|
|
|
const char* hex_chars = "0123456789abcdef"; |
|
|
|
|
for (int i = 0; i < 32; i++) { |
|
|
|
|
key_c_priv[i*2] = hex_chars[(keys_c.private_key[i] >> 4) & 0xF]; |
|
|
|
|
key_c_priv[i*2+1] = hex_chars[keys_c.private_key[i] & 0xF]; |
|
|
|
|
} |
|
|
|
|
key_c_priv[64] = '\0'; |
|
|
|
|
|
|
|
|
|
// Free routes
|
|
|
|
|
struct CFG_ROUTE_ENTRY* route = cfg->my_subnets; |
|
|
|
|
while (route) { |
|
|
|
|
struct CFG_ROUTE_ENTRY* next = route->next; |
|
|
|
|
free(route); |
|
|
|
|
route = next; |
|
|
|
|
for (int i = 0; i < 64; i++) { |
|
|
|
|
key_c_pub[i*2] = hex_chars[(keys_c.public_key[i] >> 4) & 0xF]; |
|
|
|
|
key_c_pub[i*2+1] = hex_chars[keys_c.public_key[i] & 0xF]; |
|
|
|
|
} |
|
|
|
|
key_c_pub[128] = '\0'; |
|
|
|
|
|
|
|
|
|
free(cfg); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Setup peer public keys after auto-generation
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
static void setup_peer_keys(void) { |
|
|
|
|
printf("[SETUP] Configuring peer public keys from auto-generated keys...\n"); |
|
|
|
|
|
|
|
|
|
// Build mapping: server_name -> public_key
|
|
|
|
|
struct { const char* name; const char* pubkey; } key_map[3]; |
|
|
|
|
key_map[0].name = "a"; key_map[0].pubkey = inst_a->config->global.my_public_key_hex; |
|
|
|
|
key_map[1].name = "b"; key_map[1].pubkey = inst_b->config->global.my_public_key_hex; |
|
|
|
|
key_map[2].name = "c"; key_map[2].pubkey = inst_c->config->global.my_public_key_hex; |
|
|
|
|
|
|
|
|
|
printf(" Generated keys:\n"); |
|
|
|
|
printf(" Instance A (a): %.20s...\n", key_map[0].pubkey); |
|
|
|
|
printf(" Instance B (b): %.20s...\n", key_map[1].pubkey); |
|
|
|
|
printf(" Instance C (c): %.20s...\n", key_map[2].pubkey); |
|
|
|
|
|
|
|
|
|
struct UTUN_INSTANCE* instances[] = {inst_a, inst_b, inst_c}; |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++) { |
|
|
|
|
struct UTUN_INSTANCE* inst = instances[i]; |
|
|
|
|
if (!inst || !inst->config) continue; |
|
|
|
|
|
|
|
|
|
struct CFG_CLIENT* client = inst->config->clients; |
|
|
|
|
while (client) { |
|
|
|
|
// Determine peer from client name (to_b -> peer b)
|
|
|
|
|
const char* peer_name = NULL; |
|
|
|
|
if (strstr(client->name, "_b") || strstr(client->name, "_B")) { |
|
|
|
|
peer_name = "b"; |
|
|
|
|
} else if (strstr(client->name, "_a") || strstr(client->name, "_A")) { |
|
|
|
|
peer_name = "a"; |
|
|
|
|
} else if (strstr(client->name, "_c") || strstr(client->name, "_C")) { |
|
|
|
|
peer_name = "c"; |
|
|
|
|
} |
|
|
|
|
printf("Generated C keys:\n"); |
|
|
|
|
printf(" priv: %.16s...\n", key_c_priv); |
|
|
|
|
printf(" pub: %.16s...\n", key_c_pub); |
|
|
|
|
|
|
|
|
|
if (peer_name) { |
|
|
|
|
for (int j = 0; j < 3; j++) { |
|
|
|
|
if (strcmp(key_map[j].name, peer_name) == 0) { |
|
|
|
|
strncpy(client->peer_public_key_hex, key_map[j].pubkey, MAX_KEY_LEN - 1); |
|
|
|
|
client->peer_public_key_hex[MAX_KEY_LEN - 1] = '\0'; |
|
|
|
|
printf(" %s client '%s' -> peer '%s': OK\n",
|
|
|
|
|
(i == 0 ? "A" : (i == 1 ? "B" : "C")),
|
|
|
|
|
client->name, peer_name); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
client = client->next; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Write config A with C's public key
|
|
|
|
|
FILE* f = fopen(config_a, "w"); |
|
|
|
|
if (!f) return -1; |
|
|
|
|
fprintf(f, "%s", config_a_content); |
|
|
|
|
fprintf(f, client_a_content, key_c_pub); |
|
|
|
|
fclose(f); |
|
|
|
|
|
|
|
|
|
printf("[SETUP] Peer keys configured.\n"); |
|
|
|
|
} |
|
|
|
|
// Write config B with C's public key
|
|
|
|
|
f = fopen(config_b, "w"); |
|
|
|
|
if (!f) return -1; |
|
|
|
|
fprintf(f, "%s", config_b_content); |
|
|
|
|
fprintf(f, client_b_content, key_c_pub); |
|
|
|
|
fclose(f); |
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Helper Functions
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Write config C with generated keys
|
|
|
|
|
char c_config[4096]; |
|
|
|
|
snprintf(c_config, sizeof(c_config), config_c_content_template, key_c_priv, key_c_pub); |
|
|
|
|
f = fopen(config_c, "w"); |
|
|
|
|
if (!f) return -1; |
|
|
|
|
fprintf(f, "%s", c_config); |
|
|
|
|
fprintf(f, "%s", client_c_content_template); |
|
|
|
|
fclose(f); |
|
|
|
|
|
|
|
|
|
static uint32_t calculate_crc32(const uint8_t* data, size_t len) { |
|
|
|
|
uint32_t crc = 0xFFFFFFFF; |
|
|
|
|
for (size_t i = 0; i < len; i++) { |
|
|
|
|
crc ^= data[i]; |
|
|
|
|
for (int j = 0; j < 8; j++) { |
|
|
|
|
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return ~crc; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void build_ip_header(uint8_t* header, uint32_t src_ip, uint32_t dst_ip, uint16_t total_len) { |
|
|
|
|
memset(header, 0, 20); |
|
|
|
|
header[0] = 0x45; // Version 4, IHL 5
|
|
|
|
|
header[1] = 0; |
|
|
|
|
header[2] = (total_len >> 8) & 0xFF; |
|
|
|
|
header[3] = total_len & 0xFF; |
|
|
|
|
header[4] = 0; |
|
|
|
|
header[5] = 0; |
|
|
|
|
header[6] = 0x40; // Don't fragment
|
|
|
|
|
header[7] = 0; |
|
|
|
|
header[8] = 64; // TTL
|
|
|
|
|
header[9] = 1; // Protocol: ICMP
|
|
|
|
|
header[10] = 0; |
|
|
|
|
header[11] = 0; |
|
|
|
|
|
|
|
|
|
// Source IP
|
|
|
|
|
header[12] = (src_ip >> 24) & 0xFF; |
|
|
|
|
header[13] = (src_ip >> 16) & 0xFF; |
|
|
|
|
header[14] = (src_ip >> 8) & 0xFF; |
|
|
|
|
header[15] = src_ip & 0xFF; |
|
|
|
|
|
|
|
|
|
// Destination IP
|
|
|
|
|
header[16] = (dst_ip >> 24) & 0xFF; |
|
|
|
|
header[17] = (dst_ip >> 16) & 0xFF; |
|
|
|
|
header[18] = (dst_ip >> 8) & 0xFF; |
|
|
|
|
header[19] = dst_ip & 0xFF; |
|
|
|
|
static void cleanup_temp_configs(void) { |
|
|
|
|
if (config_a[0]) unlink(config_a); |
|
|
|
|
if (config_b[0]) unlink(config_b); |
|
|
|
|
if (config_c[0]) unlink(config_c); |
|
|
|
|
if (temp_dir[0]) rmdir(temp_dir); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Connection Monitoring
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
static int count_initialized_links(struct UTUN_INSTANCE* inst) { |
|
|
|
|
int count = 0; |
|
|
|
|
struct ETCP_CONN* conn = inst->connections; |
|
|
|
|
@ -430,295 +220,54 @@ static int count_initialized_links(struct UTUN_INSTANCE* inst) {
|
|
|
|
|
return count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int are_all_connections_ready(void) { |
|
|
|
|
int links_a = count_initialized_links(inst_a); |
|
|
|
|
int links_b = count_initialized_links(inst_b); |
|
|
|
|
int links_c = count_initialized_links(inst_c); |
|
|
|
|
|
|
|
|
|
printf("[CONNECT] Links ready: A=%d, B=%d, C=%d (need 2 each)\n",
|
|
|
|
|
links_a, links_b, links_c); |
|
|
|
|
|
|
|
|
|
return (links_a >= 2 && links_b >= 2 && links_c >= 2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Packet Injection and Verification
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
#define MAX_TEST_PACKETS 6 |
|
|
|
|
|
|
|
|
|
struct packet_record { |
|
|
|
|
uint64_t sender_node_id; |
|
|
|
|
uint64_t receiver_node_id; |
|
|
|
|
uint32_t src_ip; |
|
|
|
|
uint32_t dst_ip; |
|
|
|
|
uint16_t payload_len; |
|
|
|
|
uint32_t payload_crc; |
|
|
|
|
int sent; |
|
|
|
|
int received; |
|
|
|
|
int verified; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static struct packet_record test_packets[MAX_TEST_PACKETS]; |
|
|
|
|
static int packet_count = 0; |
|
|
|
|
|
|
|
|
|
static void send_test_packet(struct UTUN_INSTANCE* sender, struct UTUN_INSTANCE* receiver, |
|
|
|
|
uint32_t src_ip, uint32_t dst_ip) { |
|
|
|
|
struct test_packet pkt; |
|
|
|
|
uint16_t total_len = 20 + TEST_PACKET_SIZE; |
|
|
|
|
|
|
|
|
|
build_ip_header(pkt.ip_header, src_ip, dst_ip, total_len); |
|
|
|
|
|
|
|
|
|
// Fill payload with pattern based on sender/receiver
|
|
|
|
|
uint64_t pattern = sender->node_id ^ receiver->node_id; |
|
|
|
|
for (int i = 0; i < TEST_PACKET_SIZE; i++) { |
|
|
|
|
pkt.payload[i] = (uint8_t)((pattern >> (i % 8)) ^ i); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
uint32_t payload_crc = calculate_crc32(pkt.payload, TEST_PACKET_SIZE); |
|
|
|
|
|
|
|
|
|
// Inject into sender's TUN output queue (simulates packet from TUN)
|
|
|
|
|
if (tun_inject_packet(sender->tun, pkt.ip_header, total_len) == 0) { |
|
|
|
|
if (packet_count < MAX_TEST_PACKETS) { |
|
|
|
|
test_packets[packet_count].sender_node_id = sender->node_id; |
|
|
|
|
test_packets[packet_count].receiver_node_id = receiver->node_id; |
|
|
|
|
test_packets[packet_count].src_ip = src_ip; |
|
|
|
|
test_packets[packet_count].dst_ip = dst_ip; |
|
|
|
|
test_packets[packet_count].payload_len = TEST_PACKET_SIZE; |
|
|
|
|
test_packets[packet_count].payload_crc = payload_crc; |
|
|
|
|
test_packets[packet_count].sent = 1; |
|
|
|
|
test_packets[packet_count].received = 0; |
|
|
|
|
test_packets[packet_count].verified = 0; |
|
|
|
|
packet_count++; |
|
|
|
|
} |
|
|
|
|
packets_sent++; |
|
|
|
|
printf("[SEND] %llX -> %llX (dst=%d.%d.%d.%d)\n", |
|
|
|
|
(unsigned long long)sender->node_id, |
|
|
|
|
(unsigned long long)receiver->node_id, |
|
|
|
|
(dst_ip >> 24) & 0xFF, (dst_ip >> 16) & 0xFF, |
|
|
|
|
(dst_ip >> 8) & 0xFF, dst_ip & 0xFF); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int verify_packet(struct UTUN_INSTANCE* receiver, struct ETCP_FRAGMENT* pkt) { |
|
|
|
|
if (!pkt || !pkt->ll.dgram || pkt->ll.len < 20) { |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Extract destination IP
|
|
|
|
|
uint32_t dst_ip = ((uint32_t)pkt->ll.dgram[16] << 24) | |
|
|
|
|
((uint32_t)pkt->ll.dgram[17] << 16) | |
|
|
|
|
((uint32_t)pkt->ll.dgram[18] << 8) | |
|
|
|
|
(uint32_t)pkt->ll.dgram[19]; |
|
|
|
|
|
|
|
|
|
uint16_t ip_total_len = ((uint16_t)pkt->ll.dgram[2] << 8) | pkt->ll.dgram[3]; |
|
|
|
|
uint16_t payload_len = ip_total_len - 20; |
|
|
|
|
|
|
|
|
|
if (payload_len > TEST_PACKET_SIZE) { |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Verify payload CRC
|
|
|
|
|
uint32_t crc = calculate_crc32(pkt->ll.dgram + 20, payload_len); |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < packet_count; i++) { |
|
|
|
|
if (!test_packets[i].sent || test_packets[i].received) continue; |
|
|
|
|
|
|
|
|
|
if (payload_len == test_packets[i].payload_len && crc == test_packets[i].payload_crc) { |
|
|
|
|
test_packets[i].received = 1; |
|
|
|
|
test_packets[i].verified = 1; |
|
|
|
|
packets_received++; |
|
|
|
|
packets_verified++; |
|
|
|
|
|
|
|
|
|
printf("[VERIFY] Packet verified: %llX -> %llX (size=%d)\n", |
|
|
|
|
(unsigned long long)test_packets[i].sender_node_id, |
|
|
|
|
(unsigned long long)receiver->node_id, |
|
|
|
|
payload_len); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void check_received_packets(void) { |
|
|
|
|
struct UTUN_INSTANCE* instances[] = {inst_a, inst_b, inst_c}; |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++) { |
|
|
|
|
struct UTUN_INSTANCE* inst = instances[i]; |
|
|
|
|
if (!inst || !inst->tun) continue; |
|
|
|
|
|
|
|
|
|
struct ll_queue* out_q = tun_get_output_queue(inst->tun); |
|
|
|
|
if (!out_q) continue; |
|
|
|
|
|
|
|
|
|
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_data_get(out_q); |
|
|
|
|
while (pkt) { |
|
|
|
|
verify_packet(inst, pkt); |
|
|
|
|
|
|
|
|
|
if (pkt->ll.dgram) free(pkt->ll.dgram); |
|
|
|
|
if (inst->tun && inst->tun->pool) { |
|
|
|
|
memory_pool_free(inst->tun->pool, pkt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pkt = (struct ETCP_FRAGMENT*)queue_data_get(out_q); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Test State Machine
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
static void start_packet_transmission(void); |
|
|
|
|
static void delivery_check_callback(void* arg); |
|
|
|
|
|
|
|
|
|
static void connection_check_callback(void* arg) { |
|
|
|
|
static void check_connections(void* arg) { |
|
|
|
|
(void)arg; |
|
|
|
|
|
|
|
|
|
if (test_phase != 0) return; |
|
|
|
|
int a = count_initialized_links(inst_a); |
|
|
|
|
int b = count_initialized_links(inst_b); |
|
|
|
|
int c = count_initialized_links(inst_c); |
|
|
|
|
|
|
|
|
|
if (are_all_connections_ready()) { |
|
|
|
|
printf("\n[PHASE] All connections established! Starting packet transmission...\n\n"); |
|
|
|
|
test_phase = 1; |
|
|
|
|
|
|
|
|
|
if (connect_timeout_id) { |
|
|
|
|
uasync_cancel_timeout(ua, connect_timeout_id); |
|
|
|
|
connect_timeout_id = NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
start_packet_transmission(); |
|
|
|
|
delivery_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_DELIVERY_MS, NULL, delivery_check_callback); |
|
|
|
|
} else { |
|
|
|
|
connect_timeout_id = uasync_set_timeout(ua, 100, NULL, connection_check_callback); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void start_packet_transmission(void) { |
|
|
|
|
printf("[PHASE] Sending test packets (mesh topology)...\n"); |
|
|
|
|
|
|
|
|
|
// A sends to B and C
|
|
|
|
|
send_test_packet(inst_a, inst_b,
|
|
|
|
|
(192U << 24) | (168 << 16) | (10 << 8) | 1, |
|
|
|
|
(192U << 24) | (168 << 16) | (20 << 8) | 1); |
|
|
|
|
send_test_packet(inst_a, inst_c, |
|
|
|
|
(192U << 24) | (168 << 16) | (10 << 8) | 1, |
|
|
|
|
(192U << 24) | (168 << 16) | (30 << 8) | 1); |
|
|
|
|
|
|
|
|
|
// B sends to A and C
|
|
|
|
|
send_test_packet(inst_b, inst_a, |
|
|
|
|
(192U << 24) | (168 << 16) | (20 << 8) | 1, |
|
|
|
|
(192U << 24) | (168 << 16) | (10 << 8) | 1); |
|
|
|
|
send_test_packet(inst_b, inst_c, |
|
|
|
|
(192U << 24) | (168 << 16) | (20 << 8) | 1, |
|
|
|
|
(192U << 24) | (168 << 16) | (30 << 8) | 1); |
|
|
|
|
|
|
|
|
|
// C sends to A and B
|
|
|
|
|
send_test_packet(inst_c, inst_a, |
|
|
|
|
(192U << 24) | (168 << 16) | (30 << 8) | 1, |
|
|
|
|
(192U << 24) | (168 << 16) | (10 << 8) | 1); |
|
|
|
|
send_test_packet(inst_c, inst_b, |
|
|
|
|
(192U << 24) | (168 << 16) | (30 << 8) | 1, |
|
|
|
|
(192U << 24) | (168 << 16) | (20 << 8) | 1); |
|
|
|
|
|
|
|
|
|
printf("\n[PHASE] Packets sent: %d, starting verification...\n", packets_sent); |
|
|
|
|
test_phase = 2; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void delivery_check_callback(void* arg) { |
|
|
|
|
(void)arg; |
|
|
|
|
printf("[CONNECT] Links ready: A=%d, B=%d, C=%d (need 2 each)\n", a, b, c); |
|
|
|
|
|
|
|
|
|
check_received_packets(); |
|
|
|
|
|
|
|
|
|
if (packets_verified >= packets_sent && packets_sent > 0) { |
|
|
|
|
printf("\n[SUCCESS] All packets verified! (%d/%d)\n", packets_verified, packets_sent); |
|
|
|
|
test_phase = 3; |
|
|
|
|
if (a >= 2 && b >= 2 && c >= 2) { |
|
|
|
|
printf("\n[SUCCESS] All connections established!\n"); |
|
|
|
|
test_phase = 1; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
delivery_timeout_id = uasync_set_timeout(ua, 50, NULL, delivery_check_callback); |
|
|
|
|
timeout_id = uasync_set_timeout(ua, 100, NULL, check_connections); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void connect_timeout_handler(void* arg) { |
|
|
|
|
static void timeout_handler(void* arg) { |
|
|
|
|
(void)arg; |
|
|
|
|
if (test_phase == 0) { |
|
|
|
|
printf("\n[TIMEOUT] Connection establishment failed\n"); |
|
|
|
|
printf("\n[TIMEOUT] Test failed\n"); |
|
|
|
|
test_phase = -1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Main Test
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
int main(void) { |
|
|
|
|
printf("========================================\n"); |
|
|
|
|
printf(" Routing Mesh Test (3 instances)\n"); |
|
|
|
|
printf("========================================\n\n"); |
|
|
|
|
|
|
|
|
|
// Initialize debug
|
|
|
|
|
if (create_temp_configs() != 0) { |
|
|
|
|
fprintf(stderr, "Failed to create configs\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
debug_config_init(); |
|
|
|
|
debug_set_level(DEBUG_LEVEL_WARN); |
|
|
|
|
|
|
|
|
|
// Create shared uasync
|
|
|
|
|
ua = uasync_create(); |
|
|
|
|
if (!ua) { |
|
|
|
|
fprintf(stderr, "Failed to create uasync\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
printf("[INIT] Creating configs programmatically...\n"); |
|
|
|
|
|
|
|
|
|
// Instance A config
|
|
|
|
|
const char* subnets_a[] = {"192.168.10.0/24", "192.168.11.0/24"}; |
|
|
|
|
const char* servers_a[] = {"b:127.0.0.1:9101", "c:127.0.0.1:9102"}; |
|
|
|
|
const char* clients_a[] = {"to_b:b:127.0.0.1:9201", "to_c:c:127.0.0.1:9301"}; |
|
|
|
|
cfg_a = create_instance_config(0xAAAAAAAAAAAAAAAA, "10.99.1.1/24", "tun_a", |
|
|
|
|
subnets_a, 2, servers_a, 2, clients_a, 2); |
|
|
|
|
|
|
|
|
|
// Instance B config
|
|
|
|
|
const char* subnets_b[] = {"192.168.20.0/24", "192.168.21.0/24"}; |
|
|
|
|
const char* servers_b[] = {"a:127.0.0.1:9201", "c:127.0.0.1:9202"}; |
|
|
|
|
const char* clients_b[] = {"to_a:a:127.0.0.1:9101", "to_c:c:127.0.0.1:9301"}; |
|
|
|
|
cfg_b = create_instance_config(0xBBBBBBBBBBBBBBBB, "10.99.2.1/24", "tun_b", |
|
|
|
|
subnets_b, 2, servers_b, 2, clients_b, 2); |
|
|
|
|
|
|
|
|
|
// Instance C config
|
|
|
|
|
const char* subnets_c[] = {"192.168.30.0/24", "192.168.31.0/24"}; |
|
|
|
|
const char* servers_c[] = {"a:127.0.0.1:9302", "b:127.0.0.1:9303"}; |
|
|
|
|
const char* clients_c[] = {"to_a:a:127.0.0.1:9102", "to_b:b:127.0.0.1:9202"}; |
|
|
|
|
cfg_c = create_instance_config(0xCCCCCCCCCCCCCCCC, "10.99.3.1/24", "tun_c", |
|
|
|
|
subnets_c, 2, servers_c, 2, clients_c, 2); |
|
|
|
|
|
|
|
|
|
if (!cfg_a || !cfg_b || !cfg_c) { |
|
|
|
|
fprintf(stderr, "Failed to create configs\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
printf("[INIT] Generating keys for configs...\n"); |
|
|
|
|
|
|
|
|
|
// Generate keys for each config
|
|
|
|
|
struct SC_MYKEYS keys_a, keys_b, keys_c; |
|
|
|
|
if (sc_generate_keypair(&keys_a) != SC_OK ||
|
|
|
|
|
sc_generate_keypair(&keys_b) != SC_OK ||
|
|
|
|
|
sc_generate_keypair(&keys_c) != SC_OK) { |
|
|
|
|
fprintf(stderr, "Failed to generate key pairs\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
printf("[INIT] Creating instances from config files...\n"); |
|
|
|
|
|
|
|
|
|
// Copy keys to configs (convert binary to hex)
|
|
|
|
|
bin2hex(keys_a.private_key, SC_PRIVKEY_SIZE, cfg_a->global.my_private_key_hex); |
|
|
|
|
bin2hex(keys_a.public_key, SC_PUBKEY_SIZE, cfg_a->global.my_public_key_hex); |
|
|
|
|
bin2hex(keys_b.private_key, SC_PRIVKEY_SIZE, cfg_b->global.my_private_key_hex); |
|
|
|
|
bin2hex(keys_b.public_key, SC_PUBKEY_SIZE, cfg_b->global.my_public_key_hex); |
|
|
|
|
bin2hex(keys_c.private_key, SC_PRIVKEY_SIZE, cfg_c->global.my_private_key_hex); |
|
|
|
|
bin2hex(keys_c.public_key, SC_PUBKEY_SIZE, cfg_c->global.my_public_key_hex); |
|
|
|
|
|
|
|
|
|
printf("[INIT] Creating instances...\n"); |
|
|
|
|
|
|
|
|
|
// Create instances from configs
|
|
|
|
|
inst_a = utun_instance_create_from_config(ua, cfg_a); |
|
|
|
|
inst_b = utun_instance_create_from_config(ua, cfg_b); |
|
|
|
|
inst_c = utun_instance_create_from_config(ua, cfg_c); |
|
|
|
|
inst_a = utun_instance_create(ua, config_a); |
|
|
|
|
inst_b = utun_instance_create(ua, config_b); |
|
|
|
|
inst_c = utun_instance_create(ua, config_c); |
|
|
|
|
|
|
|
|
|
if (!inst_a || !inst_b || !inst_c) { |
|
|
|
|
fprintf(stderr, "Failed to create instances\n"); |
|
|
|
|
@ -729,73 +278,37 @@ int main(void) {
|
|
|
|
|
printf(" Instance B: node_id=%016llX\n", (unsigned long long)inst_b->node_id); |
|
|
|
|
printf(" Instance C: node_id=%016llX\n", (unsigned long long)inst_c->node_id); |
|
|
|
|
|
|
|
|
|
// Setup peer keys after auto-generation
|
|
|
|
|
setup_peer_keys(); |
|
|
|
|
|
|
|
|
|
// Initialize connections for each instance
|
|
|
|
|
printf("\n[INIT] Initializing connections...\n"); |
|
|
|
|
|
|
|
|
|
if (init_connections(inst_a) < 0) { |
|
|
|
|
fprintf(stderr, "Failed to initialize A connections\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
if (init_connections(inst_b) < 0) { |
|
|
|
|
fprintf(stderr, "Failed to initialize B connections\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
if (init_connections(inst_c) < 0) { |
|
|
|
|
fprintf(stderr, "Failed to initialize C connections\n"); |
|
|
|
|
if (init_connections(inst_a) < 0 || |
|
|
|
|
init_connections(inst_b) < 0 || |
|
|
|
|
init_connections(inst_c) < 0) { |
|
|
|
|
fprintf(stderr, "Failed to initialize connections\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
printf(" A connections: OK\n"); |
|
|
|
|
printf(" B connections: OK\n"); |
|
|
|
|
printf(" C connections: OK\n"); |
|
|
|
|
printf(" Connections initialized\n"); |
|
|
|
|
|
|
|
|
|
// Set up connection monitoring
|
|
|
|
|
printf("\n[PHASE] Waiting for connections to establish...\n"); |
|
|
|
|
connect_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_CONNECT_MS, NULL, connect_timeout_handler); |
|
|
|
|
connection_check_callback(NULL); |
|
|
|
|
|
|
|
|
|
// Main event loop
|
|
|
|
|
printf("\n[LOOP] Running event loop...\n"); |
|
|
|
|
timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS, NULL, timeout_handler); |
|
|
|
|
check_connections(NULL); |
|
|
|
|
|
|
|
|
|
int elapsed = 0; |
|
|
|
|
while (test_phase >= 0 && test_phase < 3 && elapsed < (TEST_TIMEOUT_CONNECT_MS + TEST_TIMEOUT_DELIVERY_MS + 2000)) { |
|
|
|
|
while (test_phase == 0) { |
|
|
|
|
uasync_poll(ua, 10); |
|
|
|
|
elapsed += 10; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Final verification
|
|
|
|
|
check_received_packets(); |
|
|
|
|
|
|
|
|
|
// Cleanup
|
|
|
|
|
printf("\n[CLEANUP] Cleaning up...\n"); |
|
|
|
|
|
|
|
|
|
if (connect_timeout_id) uasync_cancel_timeout(ua, connect_timeout_id); |
|
|
|
|
if (delivery_timeout_id) uasync_cancel_timeout(ua, delivery_timeout_id); |
|
|
|
|
|
|
|
|
|
if (inst_a) utun_instance_destroy(inst_a); |
|
|
|
|
if (inst_b) utun_instance_destroy(inst_b); |
|
|
|
|
if (inst_c) utun_instance_destroy(inst_c); |
|
|
|
|
if (ua) uasync_destroy(ua, 0); |
|
|
|
|
cleanup_temp_configs(); |
|
|
|
|
|
|
|
|
|
// Note: cfg_a, cfg_b, cfg_c are freed by utun_instance_destroy
|
|
|
|
|
|
|
|
|
|
uasync_destroy(ua, 0); |
|
|
|
|
|
|
|
|
|
// Results
|
|
|
|
|
printf("\n========================================\n"); |
|
|
|
|
printf(" RESULTS\n"); |
|
|
|
|
printf("========================================\n"); |
|
|
|
|
printf("Packets sent: %d/%d\n", packets_sent, MAX_TEST_PACKETS); |
|
|
|
|
printf("Packets received: %d\n", packets_received); |
|
|
|
|
printf("Packets verified: %d\n", packets_verified); |
|
|
|
|
|
|
|
|
|
if (test_phase == 3 && packets_verified == MAX_TEST_PACKETS) { |
|
|
|
|
if (test_phase == 1) { |
|
|
|
|
printf("\n✓ TEST PASSED\n"); |
|
|
|
|
return 0; |
|
|
|
|
} else { |
|
|
|
|
printf("\n✗ TEST FAILED (phase=%d)\n", test_phase); |
|
|
|
|
printf("\n✗ TEST FAILED\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|