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.
 
 
 
 
 
 

277 lines
11 KiB

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../lib/platform_compat.h"
#include "test_utils.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#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 "topo_node.h"
#include "../src/tun_if.h"
#include "secure_channel.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#include "../src/broadcast.h"
#define TEST_TIMEOUT_TB 10000
#define POLL_INTERVAL_MS 1
static struct UTUN_INSTANCE* inst[5];
static struct UASYNC* ua;
static uint64_t nid[5];
static int test_phase = 0;
static void* test_timeout_id = NULL;
static int recv_count[5];
static uint8_t recv_data[5][256];
static uint16_t recv_data_len[5];
static void broadcast_cb(const uint8_t* uuid, const uint8_t* data, uint16_t data_len, void* arg) {
(void)uuid;
int idx = (int)(intptr_t)arg;
recv_count[idx]++;
if (data_len <= 256) { recv_data_len[idx] = data_len; memcpy(recv_data[idx], data, data_len); }
}
static void test_timeout_cb(void* arg) {
(void)arg;
if (test_phase == 0) test_phase = 2;
}
#define FAIL(msg) do { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: %s", msg); test_phase = 2; goto cleanup; } while(0)
#define ASSERT(cond, msg) do { if (!(cond)) { FAIL(msg); } } while(0)
struct ncfg {
uint64_t node_id; const char* tun_ip; const char* priv_hex; const char* pub_hex;
int srv_cnt; struct { const char* name; int port; } srvs[3];
int cli_cnt; struct { const char* name; const char* peer_pubkey_hex; int lnk_cnt; struct { const char* local_srv; int remote_port; } lnks[2]; } clis[2];
};
static void build_cfg(char* buf, size_t size, const struct ncfg* c) {
int off = snprintf(buf, size,
"[global]\n"
"my_private_key=%s\n"
"my_public_key=%s\n"
"tun_ip=%s\n"
"tun_ifname=tun99\n"
"keepalive_timeout=200\n"
"keepalive_interval=20\n"
"keepalive_adaptive=0\n"
"\n",
c->priv_hex, c->pub_hex, c->tun_ip);
for (int i = 0; i < c->srv_cnt; i++)
off += snprintf(buf + off, size - off, "[server: %s]\naddr=127.0.0.1:%d\ntype=public\n\n", c->srvs[i].name, c->srvs[i].port);
for (int i = 0; i < c->cli_cnt; i++) {
off += snprintf(buf + off, size - off, "[client: %s]\nkeepalive=1\npeer_public_key=%s\n", c->clis[i].name, c->clis[i].peer_pubkey_hex);
for (int j = 0; j < c->clis[i].lnk_cnt; j++)
off += snprintf(buf + off, size - off, "link=%s:127.0.0.1:%d\n", c->clis[i].lnks[j].local_srv, c->clis[i].lnks[j].remote_port);
off += snprintf(buf + off, size - off, "\n");
}
off += snprintf(buf + off, size - off, "[allowed_keys]\nallow_all=1\n");
}
static int peer_in_nodes(int inst_idx, uint64_t node_id) {
struct TOPO_GROUP* g = topo_groups_get_default(inst[inst_idx]->topo_groups);
return g && topo_node_find_by_id(g, node_id) != NULL;
}
static struct ETCP_LINK* find_client_link(struct UTUN_INSTANCE* ins, const char* cname) {
if (!ins || !ins->connections) return NULL;
struct ll_entry* e = ins->connections->head;
while (e) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
if (!ce->conn->name || strcmp(ce->conn->name, cname) != 0) { e = e->next; continue; }
struct ETCP_LINK* l = ce->conn->links;
while (l) { if (l->is_server == 0) return l; l = l->next; }
e = e->next;
}
return NULL;
}
static int count_initialized_links(void) {
int n = 0;
for (int i = 0; i < 5; i++) {
if (!inst[i] || !inst[i]->connections) continue;
struct ll_entry* e = inst[i]->connections->head;
while (e) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
struct ETCP_LINK* l = ce->conn->links;
while (l) { if (l->initialized) n++; l = l->next; }
e = e->next;
}
}
return n;
}
static int cond_links_init(void) { return count_initialized_links() >= 5; }
static int cond_e_in_a(void) { return peer_in_nodes(0, nid[4]); }
static int cond_d_in_a(void) { return peer_in_nodes(0, nid[3]); }
static int cond_c_in_a(void) { return peer_in_nodes(0, nid[2]); }
static int cond_all_bgp(void) { return cond_c_in_a() && cond_d_in_a() && cond_e_in_a(); }
static int wait_for(const char* desc, int (*cond)(void), int timeout_tb) {
uint64_t start = get_time_tb();
while (!cond() && (get_time_tb() - start) < (uint64_t)timeout_tb && test_phase == 0)
uasync_poll(ua, POLL_INTERVAL_MS);
if (!cond() && test_phase == 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "wait_for timeout: %s", desc); return 0; }
return test_phase == 0;
}
static int all_recv(int sender_idx, int expect) {
for (int i = 0; i < 5; i++) {
if (i == sender_idx) continue;
if (recv_count[i] != expect) return 0;
}
return 1;
}
static int wait_recv(const char* desc, int sender_idx, int expect, int timeout_tb) {
uint64_t start = get_time_tb();
while (!all_recv(sender_idx, expect) && (get_time_tb() - start) < (uint64_t)timeout_tb && test_phase == 0)
uasync_poll(ua, POLL_INTERVAL_MS);
if (!all_recv(sender_idx, expect) && test_phase == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "wait_recv timeout: %s", desc);
return 0;
}
return test_phase == 0;
}
int main(void) {
debug_config_init();
debug_set_level(DEBUG_LEVEL_ERROR);
debug_set_categories(DEBUG_CATEGORY_BGP);
debug_set_categories(DEBUG_CATEGORY_BGP);
utun_instance_set_tun_init_enabled(0);
struct SC_MYKEYS keys[5];
char pub_hex[5][SC_PUBKEY_SIZE * 2 + 1];
char priv_hex[5][SC_PRIVKEY_SIZE * 2 + 1];
for (int i = 0; i < 5; i++) {
sc_generate_keypair(&keys[i]);
bytes_to_hex(keys[i].public_key, SC_PUBKEY_SIZE, pub_hex[i], sizeof(pub_hex[i]));
bytes_to_hex(keys[i].private_key, SC_PRIVKEY_SIZE, priv_hex[i], sizeof(priv_hex[i]));
}
int base = 43000 + (getpid() % 10000);
int p_ab_a = base++, p_ab_b = base++;
int p_ac_a = base++, p_ac_c = base++;
int p_bc_b = base++, p_bc_c = base++;
int p_cd_c = base++, p_cd_d = base++;
int p_de_d = base++, p_de_e = base++;
/* A: servers for B and C, clients to B and C */
char cfg_a[2048]; build_cfg(cfg_a, sizeof(cfg_a), &(struct ncfg){
.priv_hex = priv_hex[0], .pub_hex = pub_hex[0], .tun_ip = "10.200.0.1/24",
.srv_cnt = 2, .srvs = {{"a_srv_b", p_ab_a}, {"a_srv_c", p_ac_a}},
.cli_cnt = 2, .clis = {
{"to_b", pub_hex[1], 1, {{"a_srv_b", p_ab_b}}},
{"to_c", pub_hex[2], 1, {{"a_srv_c", p_ac_c}}},
}
});
/* B: server for A, server for C, client to C */
char cfg_b[2048]; build_cfg(cfg_b, sizeof(cfg_b), &(struct ncfg){
.priv_hex = priv_hex[1], .pub_hex = pub_hex[1], .tun_ip = "10.200.0.2/24",
.srv_cnt = 2, .srvs = {{"b_srv", p_ab_b}, {"b_srv_c", p_bc_b}},
.cli_cnt = 1, .clis = {{"to_c", pub_hex[2], 1, {{"b_srv_c", p_bc_c}}}}
});
/* C: servers for A, B, D; client to D */
char cfg_c[2048]; build_cfg(cfg_c, sizeof(cfg_c), &(struct ncfg){
.priv_hex = priv_hex[2], .pub_hex = pub_hex[2], .tun_ip = "10.200.0.3/24",
.srv_cnt = 3, .srvs = {{"c_srv_a", p_ac_c}, {"c_srv_b", p_bc_c}, {"c_srv_d", p_cd_c}},
.cli_cnt = 1, .clis = {{"to_d", pub_hex[3], 1, {{"c_srv_d", p_cd_d}}}}
});
/* D: server for C, server for E; client to E */
char cfg_d[2048]; build_cfg(cfg_d, sizeof(cfg_d), &(struct ncfg){
.priv_hex = priv_hex[3], .pub_hex = pub_hex[3], .tun_ip = "10.200.0.4/24",
.srv_cnt = 2, .srvs = {{"d_srv", p_cd_d}, {"d_srv_e", p_de_d}},
.cli_cnt = 1, .clis = {{"to_e", pub_hex[4], 1, {{"d_srv_e", p_de_e}}}}
});
/* E: server for D, no clients */
char cfg_e[2048]; build_cfg(cfg_e, sizeof(cfg_e), &(struct ncfg){
.priv_hex = priv_hex[4], .pub_hex = pub_hex[4], .tun_ip = "10.200.0.5/24",
.srv_cnt = 1, .srvs = {{"e_srv", p_de_e}},
.cli_cnt = 0, .clis = {}
});
ua = uasync_create(); ASSERT(ua, "uasync_create");
inst[0] = utun_instance_create_from_str(ua, cfg_a); ASSERT(inst[0], "inst A");
inst[1] = utun_instance_create_from_str(ua, cfg_b); ASSERT(inst[1], "inst B");
inst[2] = utun_instance_create_from_str(ua, cfg_c); ASSERT(inst[2], "inst C");
inst[3] = utun_instance_create_from_str(ua, cfg_d); ASSERT(inst[3], "inst D");
inst[4] = utun_instance_create_from_str(ua, cfg_e); ASSERT(inst[4], "inst E");
for (int i = 0; i < 5; i++) { ASSERT(utun_instance_init(inst[i]) == 0, "init"); nid[i] = inst[i]->node_id; }
test_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_TB, NULL, test_timeout_cb, "test_timeout");
/* Phase 1: BGP sync */
DEBUG_INFO(DEBUG_CATEGORY_BGP, "=== PHASE 1: links init ===");
wait_for("links init", cond_links_init, 5000);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "=== PHASE 1: BGP sync ===");
wait_for("all nodes visible", cond_all_bgp, 5000);
ASSERT(peer_in_nodes(0, nid[1]), "B missing in A");
ASSERT(peer_in_nodes(0, nid[2]), "C missing in A");
ASSERT(peer_in_nodes(0, nid[3]), "D missing in A");
ASSERT(peer_in_nodes(0, nid[4]), "E missing in A");
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 1 PASSED: all nodes visible");
/* Phase 2: broadcast from A */
DEBUG_INFO(DEBUG_CATEGORY_BGP, "=== PHASE 2: broadcast ===");
for (int i = 0; i < 5; i++) {
struct TOPO_GROUP* g = topo_groups_get_default(inst[i]->topo_groups);
ASSERT(g, "no default group");
broadcast_add_cbk(g, broadcast_cb, (void*)(intptr_t)i);
}
{ struct TOPO_GROUP* g = topo_groups_get_default(inst[0]->topo_groups);
const uint8_t* msg = (const uint8_t*)"hello_broadcast";
ASSERT(broadcast_send(g, msg, 15) == 0, "broadcast_send failed"); }
wait_recv("broadcast received", 0, 1, 5000);
for (int i = 1; i < 5; i++) {
ASSERT(recv_count[i] == 1, "wrong recv count");
ASSERT(recv_data_len[i] == 15, "wrong data len");
ASSERT(memcmp(recv_data[i], "hello_broadcast", 15) == 0, "wrong data");
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 2 PASSED: all 4 nodes received broadcast");
/* Phase 3: second broadcast — dedup check */
DEBUG_INFO(DEBUG_CATEGORY_BGP, "=== PHASE 3: second broadcast ===");
{ struct TOPO_GROUP* g = topo_groups_get_default(inst[0]->topo_groups);
const uint8_t* msg = (const uint8_t*)"second_msg";
ASSERT(broadcast_send(g, msg, 10) == 0, "broadcast_send2 failed"); }
wait_recv("broadcast2 received", 0, 2, 5000);
for (int i = 1; i < 5; i++) ASSERT(recv_count[i] == 2, "wrong recv2 count");
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 3 PASSED: all 4 nodes received second broadcast, no dups");
test_phase = 1;
cleanup:
for (int i = 0; i < 5; i++) {
if (inst[i]) {
struct TOPO_GROUP* g = topo_groups_get_default(inst[i]->topo_groups);
if (g) broadcast_remove_cbk(g, broadcast_cb, (void*)(intptr_t)i);
}
}
if (test_timeout_id) uasync_cancel_timeout(ua, test_timeout_id);
for (int i = 0; i < 5; i++) { if (inst[i]) { inst[i]->running = 0; utun_instance_destroy(inst[i]); inst[i] = NULL; } }
if (ua) { uasync_destroy(ua, 0); ua = NULL; }
printf("=== %s ===\n", test_phase == 1 ? "TEST PASSED" : "TEST FAILED");
return test_phase == 1 ? 0 : 1;
}