Browse Source
- Добавлена опция tun_test_mode в конфиг для работы без реального TUN устройства - Добавлены функции для работы с очередями TUN в тестовом режиме - Добавлен API для программного создания instance из структуры конфига - Создан тест test_routing_mesh.c с 3-instance mesh топологией - Ключи генерируются автоматически и распределяются между instancenodeinfo-routing-update
8 changed files with 1176 additions and 33 deletions
@ -0,0 +1,801 @@
|
||||
// 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
|
||||
|
||||
#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/secure_channel.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 |
||||
|
||||
// Test packet structure with IP header + payload
|
||||
struct test_packet { |
||||
uint8_t ip_header[20]; // Minimal IP header
|
||||
uint8_t payload[80]; // Test payload
|
||||
}; |
||||
|
||||
// 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; |
||||
} |
||||
|
||||
return cfg; |
||||
} |
||||
|
||||
// Free config memory
|
||||
static void free_instance_config(struct utun_config* cfg) { |
||||
if (!cfg) return; |
||||
|
||||
// Free servers
|
||||
struct CFG_SERVER* srv = cfg->servers; |
||||
while (srv) { |
||||
struct CFG_SERVER* next = srv->next; |
||||
free(srv); |
||||
srv = next; |
||||
} |
||||
|
||||
// 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; |
||||
} |
||||
|
||||
// Free routes
|
||||
struct CFG_ROUTE_ENTRY* route = cfg->my_subnets; |
||||
while (route) { |
||||
struct CFG_ROUTE_ENTRY* next = route->next; |
||||
free(route); |
||||
route = next; |
||||
} |
||||
|
||||
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"; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
} |
||||
|
||||
printf("[SETUP] Peer keys configured.\n"); |
||||
} |
||||
|
||||
// ============================================================================
|
||||
// Helper Functions
|
||||
// ============================================================================
|
||||
|
||||
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; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
|
||||
// ============================================================================
|
||||
// Connection Monitoring
|
||||
// ============================================================================
|
||||
|
||||
static int count_initialized_links(struct UTUN_INSTANCE* inst) { |
||||
int count = 0; |
||||
struct ETCP_CONN* conn = inst->connections; |
||||
while (conn) { |
||||
struct ETCP_LINK* link = conn->links; |
||||
while (link) { |
||||
if (link->initialized) count++; |
||||
link = link->next; |
||||
} |
||||
conn = conn->next; |
||||
} |
||||
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) { |
||||
(void)arg; |
||||
|
||||
if (test_phase != 0) return; |
||||
|
||||
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; |
||||
|
||||
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; |
||||
return; |
||||
} |
||||
|
||||
delivery_timeout_id = uasync_set_timeout(ua, 50, NULL, delivery_check_callback); |
||||
} |
||||
|
||||
static void connect_timeout_handler(void* arg) { |
||||
(void)arg; |
||||
if (test_phase == 0) { |
||||
printf("\n[TIMEOUT] Connection establishment failed\n"); |
||||
test_phase = -1; |
||||
} |
||||
} |
||||
|
||||
// ============================================================================
|
||||
// Main Test
|
||||
// ============================================================================
|
||||
|
||||
int main(void) { |
||||
printf("========================================\n"); |
||||
printf(" Routing Mesh Test (3 instances)\n"); |
||||
printf("========================================\n\n"); |
||||
|
||||
// Initialize debug
|
||||
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; |
||||
} |
||||
|
||||
// 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); |
||||
|
||||
if (!inst_a || !inst_b || !inst_c) { |
||||
fprintf(stderr, "Failed to create instances\n"); |
||||
return 1; |
||||
} |
||||
|
||||
printf(" Instance A: node_id=%016llX\n", (unsigned long long)inst_a->node_id); |
||||
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"); |
||||
return 1; |
||||
} |
||||
|
||||
printf(" A connections: OK\n"); |
||||
printf(" B connections: OK\n"); |
||||
printf(" C connections: OK\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"); |
||||
|
||||
int elapsed = 0; |
||||
while (test_phase >= 0 && test_phase < 3 && elapsed < (TEST_TIMEOUT_CONNECT_MS + TEST_TIMEOUT_DELIVERY_MS + 2000)) { |
||||
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); |
||||
|
||||
// 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) { |
||||
printf("\n✓ TEST PASSED\n"); |
||||
return 0; |
||||
} else { |
||||
printf("\n✗ TEST FAILED (phase=%d)\n", test_phase); |
||||
return 1; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue