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.
340 lines
11 KiB
340 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 "../src/etcp.h" |
|
#include "../src/etcp_connections.h" |
|
#include "../src/config_parser.h" |
|
#include "../src/config_updater.h" |
|
#include "../src/utun_instance.h" |
|
#include "../src/routing.h" |
|
#include "../src/route_bgp.h" |
|
#include "../src/route_ping.h" |
|
#include "../src/tun_if.h" |
|
#include "../src/secure_channel.h" |
|
#include "../lib/u_async.h" |
|
#include "../lib/debug_config.h" |
|
|
|
#define TEST_TIMEOUT_MS 10000 |
|
#define NODE_ID_A 0x1111111111111111ULL |
|
#define NODE_ID_B 0x2222222222222222ULL |
|
#define NODE_ID_C 0x3333333333333333ULL |
|
|
|
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" |
|
"my_node_id=0x3333333333333333\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" |
|
"my_node_id=0x2222222222222222\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" |
|
"my_node_id=0x1111111111111111\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) return 0; |
|
struct ETCP_CONN* conn = inst->connections; |
|
while (conn) { |
|
struct ETCP_LINK* link = conn->links; |
|
while (link) { |
|
if (link->initialized) return 1; |
|
link = link->next; |
|
} |
|
conn = conn->next; |
|
} |
|
return 0; |
|
} |
|
|
|
static struct ETCP_CONN* find_conn_to_peer(struct UTUN_INSTANCE* inst, uint64_t peer_node_id) { |
|
if (!inst) return NULL; |
|
struct ETCP_CONN* conn = inst->connections; |
|
while (conn) { |
|
if (conn->peer_node_id == peer_node_id) return conn; |
|
conn = conn->next; |
|
} |
|
return 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; |
|
} |
|
|
|
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 about C |
|
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) { |
|
if (inst_b->bgp && nodeinfo_find_by_id(inst_b->bgp, NODE_ID_C) != NULL) { |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "B learned about C"); |
|
break; |
|
} |
|
uasync_poll(ua, 10); |
|
bgp_wait_cycles++; |
|
} |
|
if (!inst_b->bgp || nodeinfo_find_by_id(inst_b->bgp, NODE_ID_C) == NULL) { |
|
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, 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 NODEINFO_Q* nq = inst_b->bgp ? nodeinfo_find_by_id(inst_b->bgp, NODE_ID_C) : NULL; |
|
if (nq) { |
|
const struct NODEINFO_IPV4_ADDR* addrs; |
|
int sc = get_node_v4_addrs(nq, &addrs); |
|
if (sc > 0) { |
|
memcpy(&target_ip, addrs[0].addr, 4); /* network byte order */ |
|
target_port = addrs[0].port; /* host byte order */ |
|
} |
|
} |
|
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->bgp, conn_ab, target_ip, target_port, |
|
3, // count |
|
10, // interval_ms |
|
200, // timeout_ms |
|
5000,// wait_timeout_ms |
|
ping_resp_cb, NULL, |
|
nq ? nq->node.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; |
|
}
|
|
|