You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

338 lines
11 KiB

/**
* @file test_route_ping.c
* @brief Интеграционный тест route_ping через три инстанса
*
* Топология: A -> B -> C
* - A отправляет запрос route_ping на B для узла C
* - B пингует C через ETCP ping
* - C отвечает PONG
* - B отправляет BGP_PING_RESPONSE на A
* - A получает ответ через route_ping_send_req callback
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "test_utils.h"
#include "etcp.h"
#include "etcp_connections.h"
#include "../src/config_parser.h"
#include "../src/config_updater.h"
#include "../src/utun_instance.h"
#include "routing.h"
#include "topo_group.h"
#include "route_ping.h"
#include "../src/tun_if.h"
#include "secure_channel.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#define TEST_TIMEOUT_MS 10000
static uint64_t g_node_id_a = 0;
static uint64_t g_node_id_b = 0;
static uint64_t g_node_id_c = 0;
static struct UTUN_INSTANCE* inst_a = NULL;
static struct UTUN_INSTANCE* inst_b = NULL;
static struct UTUN_INSTANCE* inst_c = NULL;
static struct UASYNC* ua = NULL;
static int test_timed_out = 0;
static void* test_timeout_id = NULL;
static char temp_dir[] = "/tmp/utun_route_ping_test_XXXXXX";
static char config_a[256];
static char config_b[256];
static char config_c[256];
static struct {
int done;
int success;
uint16_t avg_rtt;
uint8_t count_sent;
uint8_t count_ok;
} ping_result;
static int write_config(const char* path, const char* content) {
FILE* f = fopen(path, "w");
if (!f) return -1;
fprintf(f, "%s", content);
fclose(f);
return 0;
}
static char* get_pubkey_from_config(const char* path) {
struct utun_config* cfg = parse_config(path);
if (!cfg) return NULL;
char* pub = strdup(cfg->global.my_public_key_hex);
free_config(cfg);
return pub;
}
static int create_temp_configs(void) {
if (test_mkdtemp(temp_dir) != 0) {
fprintf(stderr, "Failed to create temp directory\n");
return -1;
}
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);
// 1. Config C (no keys)
const char* tpl_c =
"[global]\n"
"tun_ip=10.100.0.3/24\n"
"tun_ifname=tun102\n"
"\n"
"[server:test_c]\n"
"addr=127.0.0.1:39013\n"
"type=public\n"
"\n"
"[allowed_keys]\n"
"allow_all=1\n";
if (write_config(config_c, tpl_c) != 0) return -1;
if (config_ensure_keys_and_node_id(config_c) != 0) return -1;
char* pub_c = get_pubkey_from_config(config_c);
if (!pub_c) return -1;
// 2. Config B (no own keys, but needs peer_public_key of C)
char tpl_b[4096];
snprintf(tpl_b, sizeof(tpl_b),
"[global]\n"
"tun_ip=10.100.0.2/24\n"
"tun_ifname=tun101\n"
"\n"
"[server:test_b]\n"
"addr=127.0.0.1:39012\n"
"type=public\n"
"\n"
"[allowed_keys]\n"
"allow_all=1\n"
"\n"
"[client:to_c]\n"
"keepalive=1\n"
"peer_public_key=%s\n"
"link=test_b:127.0.0.1:39013\n",
pub_c);
free(pub_c);
if (write_config(config_b, tpl_b) != 0) return -1;
if (config_ensure_keys_and_node_id(config_b) != 0) return -1;
char* pub_b = get_pubkey_from_config(config_b);
if (!pub_b) return -1;
// 3. Config A (no own keys, but needs peer_public_key of B)
char tpl_a[4096];
snprintf(tpl_a, sizeof(tpl_a),
"[global]\n"
"tun_ip=10.100.0.1/24\n"
"tun_ifname=tun100\n"
"\n"
"[server:test_a]\n"
"addr=127.0.0.1:39011\n"
"type=public\n"
"\n"
"[allowed_keys]\n"
"allow_all=1\n"
"\n"
"[client:to_b]\n"
"keepalive=1\n"
"peer_public_key=%s\n"
"link=test_a:127.0.0.1:39012\n",
pub_b);
free(pub_b);
if (write_config(config_a, tpl_a) != 0) return -1;
if (config_ensure_keys_and_node_id(config_a) != 0) return -1;
return 0;
}
static void cleanup_temp_configs(void) {
test_unlink(config_a);
test_unlink(config_b);
test_unlink(config_c);
test_rmdir(temp_dir);
}
static int has_initialized_link(struct UTUN_INSTANCE* inst) {
if (!inst || !inst->connections) return 0;
struct ll_entry* entry = inst->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
struct ETCP_CONN* conn = ce->conn;
struct ETCP_LINK* link = conn->links;
while (link) {
if (link->initialized) return 1;
link = link->next;
}
entry = entry->next;
}
return 0;
}
static struct ETCP_CONN* find_conn_to_peer(struct UTUN_INSTANCE* inst, uint64_t peer_node_id) {
if (!inst || !inst->connections) return NULL;
struct ll_entry* entry = queue_find_data_by_index(inst->connections, (const uint8_t*)&peer_node_id);
return entry ? ((struct conn_queue_entry*)entry->data)->conn : NULL;
}
static void test_timeout_cb(void* arg) {
(void)arg;
test_timed_out = 1;
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "test_route_ping: overall test timeout");
}
static void ping_resp_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok, void* arg) {
(void)arg;
ping_result.done = 1;
ping_result.success = success;
ping_result.avg_rtt = avg_rtt;
ping_result.count_sent = count_sent;
ping_result.count_ok = count_ok;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "ping_resp_cb: success=%d avg_rtt=%u sent=%u ok=%u",
success, (unsigned)avg_rtt, (unsigned)count_sent, (unsigned)count_ok);
}
int main(void) {
int test_result = 1;
debug_config_init();
debug_set_level(DEBUG_LEVEL_INFO);
debug_set_categories(DEBUG_CATEGORY_ETCP | DEBUG_CATEGORY_BGP | DEBUG_CATEGORY_ROUTING);
utun_instance_set_tun_init_enabled(0);
if (create_temp_configs() != 0) {
fprintf(stderr, "Failed to create temp configs\n");
return 1;
}
ua = uasync_create();
if (!ua) {
cleanup_temp_configs();
return 1;
}
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) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create instances");
goto cleanup;
}
if (init_connections(inst_a) != 0 || init_connections(inst_b) != 0 || init_connections(inst_c) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to init connections");
goto cleanup;
}
g_node_id_a = inst_a->node_id; g_node_id_b = inst_b->node_id; g_node_id_c = inst_c->node_id;
test_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS * 10, NULL, test_timeout_cb, "test_route_ping");
// 1. Wait for links A->B and B->C to initialize
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Waiting for ETCP links to initialize...");
while (!test_timed_out) {
if (has_initialized_link(inst_a) && has_initialized_link(inst_b)) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Links initialized");
break;
}
uasync_poll(ua, 10);
}
if (test_timed_out) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Timeout waiting for links");
goto cleanup;
}
// 2. Wait for BGP exchange so B learns C (registry has C's nodeinfo + addresses)
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for BGP exchange (B learns C)...");
int bgp_wait_cycles = 0;
while (!test_timed_out && bgp_wait_cycles < 500) {
struct TOPO_NODE* ni_c = topo_node_registry_find(inst_b->topo_groups, g_node_id_c);
if (ni_c && ni_c->v4_addrs) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "B learned about C");
break;
}
uasync_poll(ua, 10);
bgp_wait_cycles++;
}
if (!topo_node_registry_find(inst_b->topo_groups, g_node_id_c)) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "B did not learn about C in time");
goto cleanup;
}
// 3. Get target info for C and send route ping request from A to B
struct ETCP_CONN* conn_ab = find_conn_to_peer(inst_a, g_node_id_b);
if (!conn_ab) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "A has no connection to B");
goto cleanup;
}
/* Получаем target IP:port из nodeinfo узла C */
/* addr[] в network byte order, port в host byte order */
uint32_t target_ip = 0;
uint16_t target_port = 0;
struct TOPO_GROUP_NODE* nq = topo_groups_get_default(inst_b->topo_groups) ? topo_node_find_by_id(topo_groups_get_default(inst_b->topo_groups), g_node_id_c) : NULL;
struct TOPO_NODE* ni = nq ? topo_node_registry_find(inst_b->topo_groups, nq->node_id) : NULL;
if (nq && ni) {
const struct TOPO_ADDR4* a = ni->v4_addrs;
if (a) {
memcpy(&target_ip, a->addr, 4);
target_port = a->port;
}
}
if (target_ip == 0 || target_port == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Cannot resolve target for node C");
goto cleanup;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending route ping request A->B for C (%s:%u)",
ip_to_str(&target_ip, AF_INET).str, (unsigned)target_port);
memset(&ping_result, 0, sizeof(ping_result));
int ret = route_ping_send_req_addr(inst_a->nat_det, topo_groups_get_default(inst_a->topo_groups), conn_ab, target_ip, target_port,
3, // count
10, // interval_ms
200, // timeout_ms
5000,// wait_timeout_ms
ping_resp_cb, NULL,
ni ? ni->public_key : NULL);
if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr failed: %d", ret);
goto cleanup;
}
// 4. Wait for response
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for route ping response...");
while (!test_timed_out && !ping_result.done) {
uasync_poll(ua, 10);
}
if (test_timed_out) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Timeout waiting for ping response");
goto cleanup;
}
// 5. Verify results
if (!ping_result.success) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Ping reported failure (success=0)");
goto cleanup;
}
if (ping_result.count_ok == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Ping count_ok is zero");
goto cleanup;
}
if (ping_result.avg_rtt == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Ping avg_rtt is zero");
goto cleanup;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Route ping test PASSED: sent=%u ok=%u avg_rtt=%u",
(unsigned)ping_result.count_sent, (unsigned)ping_result.count_ok, (unsigned)ping_result.avg_rtt);
test_result = 0;
cleanup:
if (test_timeout_id) uasync_cancel_timeout(ua, test_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();
return test_result;
}