Browse Source
- etcp_connect.c: async connect API with EARLY/LATE/BGP_READY callbacks - etcp_connect.h: declaration (via etcp_api.h) - connect_deliver: fix to preserve unfired callback nodes - etcp_set_routing_exchange_state: fires bgp_ready_cbk on TABLE_COMPLETE - route_bgp: TABLE_COMPLETE handler + route_bgp_send_table_complete - etcp.c: connections_count tracking, cleanup on close - test_etcp_router_reconnect: a-b-c topology with b restart (etcp_connect) - test_etcp_connect: unit tests for async connect API - etcp_router: inflight/retrans/close improvements - Various proxy/NAT/router cleanupschatgui
36 changed files with 1350 additions and 148 deletions
@ -0,0 +1,248 @@
|
||||
#include "etcp_api.h" |
||||
#include "etcp.h" |
||||
#include "utun_instance.h" |
||||
#include "route_node.h" |
||||
#include "../lib/debug_config.h" |
||||
#include "../lib/mem.h" |
||||
#include "../lib/u_async.h" |
||||
#include "../lib/socket_compat.h" |
||||
#include <string.h> |
||||
|
||||
#define DEBUG_CATEGORY_ETCP_CONNECT DEBUG_CATEGORY_ETCP |
||||
|
||||
struct etcp_connect_cb_node { |
||||
etcp_connect_callback_t cb; |
||||
void* arg; |
||||
uint8_t flags; |
||||
struct etcp_connect_cb_node* next; |
||||
}; |
||||
|
||||
struct ETCP_CONNECT { |
||||
struct ETCP_CONNECT* next; |
||||
struct UTUN_INSTANCE* instance; |
||||
uint64_t node_id; |
||||
struct ETCP_CONN* conn; |
||||
struct etcp_connect_cb_node* cb_list; |
||||
void* initial_timer; |
||||
void* settle_timer; |
||||
uint16_t min_rtt; |
||||
uint8_t early_delivered : 1; |
||||
uint8_t done : 1; |
||||
}; |
||||
|
||||
static struct ETCP_CONNECT* connect_find(struct UTUN_INSTANCE* inst, uint64_t node_id) { |
||||
struct ETCP_CONNECT* ctx = inst->pending_connects; |
||||
while (ctx) { if (ctx->node_id == node_id) return ctx; ctx = ctx->next; } |
||||
return NULL; |
||||
} |
||||
|
||||
static void connect_deliver(struct ETCP_CONNECT* ctx, int type) { |
||||
struct etcp_connect_cb_node** pp = &ctx->cb_list; |
||||
while (*pp) { |
||||
struct etcp_connect_cb_node* node = *pp; |
||||
if (type == 0 || (node->flags & (uint8_t)type)) { |
||||
*pp = node->next; |
||||
node->cb(node->arg, (type == 0) ? NULL : ctx->conn, type); |
||||
u_free(node); |
||||
} else { |
||||
pp = &node->next; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void connect_cancel(struct ETCP_CONNECT* ctx) { |
||||
if (!ctx) return; |
||||
struct UTUN_INSTANCE* inst = ctx->instance; |
||||
if (inst) { |
||||
struct ETCP_CONNECT** pp = &inst->pending_connects; |
||||
while (*pp && *pp != ctx) pp = &(*pp)->next; |
||||
if (*pp) *pp = ctx->next; |
||||
if (ctx->initial_timer) { uasync_cancel_timeout(inst->ua, ctx->initial_timer); ctx->initial_timer = NULL; } |
||||
if (ctx->settle_timer) { uasync_cancel_timeout(inst->ua, ctx->settle_timer); ctx->settle_timer = NULL; } |
||||
} |
||||
u_free(ctx); |
||||
} |
||||
|
||||
static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct NODEINFO_Q* node) { |
||||
const struct NODEINFO_IPV4_ADDR* addrs; int ac = get_node_v4_addrs(node, &addrs); |
||||
for (int i = 0; i < ac; i++) { |
||||
if (addrs[i].port == 0) continue; |
||||
{ int zero = 1; for (int j = 0; j < 4; j++) if (addrs[i].addr[j] != 0) { zero = 0; break; } if (zero) continue; } |
||||
struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); |
||||
sin.sin_family = AF_INET; |
||||
memcpy(&sin.sin_addr.s_addr, addrs[i].addr, 4); |
||||
sin.sin_port = htons(addrs[i].port); |
||||
struct sockaddr_storage sa; memcpy(&sa, &sin, sizeof(sin)); |
||||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets; |
||||
while (s) { |
||||
if (s->local_addr.ss_family == AF_INET) |
||||
etcp_link_new(ctx->conn, s, &sa, 0); |
||||
s = s->next; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void connect_create_links_v6(struct ETCP_CONNECT* ctx, struct NODEINFO_Q* node) { |
||||
const struct NODEINFO_IPV6_ADDR* addrs; int ac = get_node_v6_addrs(node, &addrs); |
||||
for (int i = 0; i < ac; i++) { |
||||
if (addrs[i].port == 0) continue; |
||||
int zero = 1; |
||||
for (int j = 0; j < 16; j++) if (addrs[i].addr[j] != 0) { zero = 0; break; } |
||||
if (zero) continue; |
||||
struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); |
||||
sin6.sin6_family = AF_INET6; |
||||
memcpy(&sin6.sin6_addr, addrs[i].addr, 16); |
||||
sin6.sin6_port = htons(addrs[i].port); |
||||
struct sockaddr_storage sa; memcpy(&sa, &sin6, sizeof(sin6)); |
||||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets; |
||||
while (s) { |
||||
if (s->local_addr.ss_family == AF_INET6) |
||||
etcp_link_new(ctx->conn, s, &sa, 0); |
||||
s = s->next; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void connect_bgp_ready_cb(struct ETCP_CONN* conn) { |
||||
struct ETCP_CONNECT* ctx = connect_find(conn->instance, conn->peer_node_id); |
||||
if (!ctx || ctx->done) return; |
||||
connect_deliver(ctx, ETCP_CONNECT_BGP_READY); |
||||
} |
||||
|
||||
static void connect_initial_timeout_cb(void* arg) { |
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg; |
||||
if (!ctx || ctx->done) return; |
||||
ctx->done = 1; |
||||
ctx->initial_timer = NULL; |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] timeout for node 0x%016llx", (unsigned long long)ctx->node_id); |
||||
struct ETCP_CONN* conn = ctx->conn; ctx->conn = NULL; |
||||
connect_deliver(ctx, 0); |
||||
if (conn) etcp_connection_close(conn); |
||||
connect_cancel(ctx); |
||||
} |
||||
|
||||
static void connect_settle_timeout_cb(void* arg) { |
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg; |
||||
if (!ctx || ctx->done) return; |
||||
ctx->done = 1; |
||||
ctx->settle_timer = NULL; |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] settle for node 0x%016llx, min_rtt=%u", |
||||
(unsigned long long)ctx->node_id, ctx->min_rtt); |
||||
struct ETCP_LINK* link = ctx->conn->links; |
||||
while (link) { |
||||
struct ETCP_LINK* next = link->next; |
||||
if (!link->initialized) { |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] removing failed link=%p for node 0x%016llx", |
||||
(void*)link, (unsigned long long)ctx->node_id); |
||||
etcp_link_close(link); |
||||
} |
||||
link = next; |
||||
} |
||||
ctx->conn->ready_cbk = NULL; |
||||
ctx->conn->ready_arg = NULL; |
||||
connect_deliver(ctx, ETCP_CONNECT_LATE); |
||||
connect_cancel(ctx); |
||||
} |
||||
|
||||
static void connect_ready_cb(struct ETCP_CONN* conn, void* arg) { |
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg; |
||||
if (!ctx || ctx->done) return; |
||||
struct ETCP_LINK* link = conn->links; |
||||
while (link) { |
||||
if (link->initialized && link->rtt_last && link->rtt_last < ctx->min_rtt) |
||||
ctx->min_rtt = link->rtt_last; |
||||
link = link->next; |
||||
} |
||||
if (ctx->early_delivered) return; |
||||
ctx->early_delivered = 1; |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] early ready for node 0x%016llx, min_rtt=%u", |
||||
(unsigned long long)ctx->node_id, ctx->min_rtt); |
||||
connect_deliver(ctx, ETCP_CONNECT_EARLY); |
||||
if (ctx->initial_timer) { uasync_cancel_timeout(ctx->instance->ua, ctx->initial_timer); ctx->initial_timer = NULL; } |
||||
uint32_t settle_tb; |
||||
if (ctx->min_rtt == 0xFFFF) settle_tb = 20000; |
||||
else { |
||||
settle_tb = (uint32_t)ctx->min_rtt * 8; |
||||
if (settle_tb < 5000) settle_tb = 5000; |
||||
if (settle_tb > 20000) settle_tb = 20000; |
||||
} |
||||
ctx->settle_timer = uasync_set_timeout(ctx->instance->ua, (int)settle_tb, ctx, |
||||
connect_settle_timeout_cb, "etcp_connect_settle"); |
||||
} |
||||
|
||||
int etcp_connect(struct UTUN_INSTANCE* inst, struct NODEINFO_Q* node, |
||||
etcp_connect_callback_t cb, void* arg, uint8_t flags) { |
||||
if (!inst || !node || !cb) return -1; |
||||
uint64_t node_id = node->node.node_id; |
||||
|
||||
struct ETCP_CONN* existing = inst->connections; |
||||
while (existing) { |
||||
if (existing->peer_node_id == node_id) { |
||||
struct ETCP_LINK* l = existing->links; |
||||
while (l) { if (l->link_state == 3) break; l = l->next; } |
||||
if (l) { |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx already connected, delivering immediately", |
||||
(unsigned long long)node_id); |
||||
if (flags & ETCP_CONNECT_EARLY) cb(arg, existing, ETCP_CONNECT_EARLY); |
||||
if (flags & ETCP_CONNECT_LATE) cb(arg, existing, ETCP_CONNECT_LATE); |
||||
if ((flags & ETCP_CONNECT_BGP_READY) && existing->routing_exchange_active >= 3) |
||||
cb(arg, existing, ETCP_CONNECT_BGP_READY); |
||||
return 0; |
||||
} |
||||
} |
||||
existing = existing->next; |
||||
} |
||||
|
||||
struct ETCP_CONNECT* ctx = connect_find(inst, node_id); |
||||
if (ctx) { |
||||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node)); |
||||
if (!cn) return -1; |
||||
cn->cb = cb; cn->arg = arg; cn->flags = flags; cn->next = ctx->cb_list; ctx->cb_list = cn; |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx already connecting, added cb", |
||||
(unsigned long long)node_id); |
||||
return 0; |
||||
} |
||||
|
||||
struct ETCP_CONN* conn = etcp_connection_create(inst, NULL); |
||||
if (!conn) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] failed to create ETCP_CONN for node 0x%016llx", |
||||
(unsigned long long)node_id); cb(arg, NULL, 0); return -1; } |
||||
conn->next = inst->connections; inst->connections = conn; inst->connections_count++; |
||||
if (sc_init_ctx(&conn->crypto_ctx, &inst->my_keys) != SC_OK) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_init_ctx failed for node 0x%016llx", (unsigned long long)node_id); |
||||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1; |
||||
} |
||||
if (sc_set_peer_public_key(&conn->crypto_ctx, node->node.public_key, 0) != SC_OK) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_set_peer_public_key failed for node 0x%016llx", (unsigned long long)node_id); |
||||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1; |
||||
} |
||||
|
||||
ctx = u_calloc(1, sizeof(struct ETCP_CONNECT)); |
||||
if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] alloc failed"); |
||||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1; } |
||||
ctx->instance = inst; ctx->node_id = node_id; ctx->conn = conn; |
||||
ctx->min_rtt = 0xFFFF; |
||||
{ |
||||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node)); |
||||
if (!cn) { u_free(ctx); etcp_connection_close(conn); cb(arg, NULL, 0); return -1; } |
||||
cn->cb = cb; cn->arg = arg; cn->flags = flags; ctx->cb_list = cn; |
||||
} |
||||
|
||||
conn->ready_cbk = connect_ready_cb; |
||||
conn->ready_arg = ctx; |
||||
conn->bgp_ready_cbk = connect_bgp_ready_cb; |
||||
|
||||
connect_create_links_v4(ctx, node); |
||||
connect_create_links_v6(ctx, node); |
||||
|
||||
if (!conn->links) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] no links created for node 0x%016llx", |
||||
(unsigned long long)node_id); |
||||
connect_deliver(ctx, 0); |
||||
etcp_connection_close(conn); u_free(ctx); return -1; |
||||
} |
||||
|
||||
ctx->initial_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, ctx, connect_initial_timeout_cb, "etcp_connect_init"); |
||||
ctx->next = inst->pending_connects; inst->pending_connects = ctx; |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] started for node 0x%016llx", (unsigned long long)node_id); |
||||
return 0; |
||||
} |
||||
@ -0,0 +1,242 @@
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdarg.h> |
||||
#include "../lib/platform_compat.h" |
||||
#include "test_utils.h" |
||||
#ifndef _WIN32 |
||||
#include <unistd.h> |
||||
#endif |
||||
|
||||
#include "../src/etcp.h" |
||||
#include "../src/etcp_connections.h" |
||||
#include "../src/etcp_api.h" |
||||
#include "../src/config_parser.h" |
||||
#include "../src/config_updater.h" |
||||
#include "../src/utun_instance.h" |
||||
#include "../src/route_bgp.h" |
||||
#include "../src/route_node.h" |
||||
#include "../src/secure_channel.h" |
||||
#include "../lib/u_async.h" |
||||
#include "../lib/ll_queue.h" |
||||
#include "../lib/debug_config.h" |
||||
#include "../lib/mem.h" |
||||
|
||||
#define TIMEOUT_TB 300000 |
||||
#define POLL_MS 5 |
||||
#define NID_A 0xAAAA000000000001ULL |
||||
#define NID_B 0xBBBB000000000002ULL |
||||
|
||||
static struct UTUN_INSTANCE* g_a = NULL, *g_b = NULL; |
||||
static struct UASYNC* ua = NULL; |
||||
static volatile int result = 0; |
||||
static void* ttimer = NULL; |
||||
static char tdir[] = "/tmp/utun_ec_XXXXXX"; |
||||
static char ca[256], cb[256]; |
||||
static int pa = 0, pb = 0; |
||||
|
||||
static int wf(const char* p, const char* f, ...) { |
||||
va_list ap; FILE* fp = fopen(p, "w"); if (!fp) return -1; |
||||
va_start(ap, f); vfprintf(fp, f, ap); va_end(ap); fclose(fp); return 0; |
||||
} |
||||
static char* gv(const char* p, const char* k) { |
||||
struct utun_config* c = parse_config(p); if (!c) return NULL; |
||||
char* r = (strcmp(k, "pub") == 0) ? u_strdup(c->global.my_public_key_hex) : u_strdup(c->global.my_private_key_hex); |
||||
free_config(c); return r; |
||||
} |
||||
static int lks(struct UTUN_INSTANCE* i) { |
||||
int n = 0; struct ETCP_CONN* c = i->connections; |
||||
while (c) { struct ETCP_LINK* l = c->links; while (l) { if (l->initialized && l->link_status) n++; l = l->next; } c = c->next; } |
||||
return n; |
||||
} |
||||
static void fail(const char* msg) { fprintf(stderr, "FAIL: %s\n", msg); fflush(stderr); result = 1; } |
||||
static void to_cb(void* arg) { (void)arg; fprintf(stderr, "TIMEOUT\n"); result = 1; } |
||||
|
||||
static void test1(void* arg); static void test2(void* arg); |
||||
static void test3(void* arg); static void test4(void* arg); |
||||
static void test5(void* arg); static void test6(void* arg); |
||||
static void test7(void* arg); static void test8(void* arg); |
||||
static void test9(void* arg); static void test10(void* arg); |
||||
|
||||
/* ----- callback state per test ----- */ |
||||
static volatile int cb_type = 0, cb_conn_ok = 0, cb_count = 0; |
||||
|
||||
static void connect_ccb(void* a, struct ETCP_CONN* conn, int type) { cb_type = type; cb_conn_ok = (conn != NULL); cb_count++; } |
||||
|
||||
/* ----- helper: create minimal NODEINFO_Q with 1 v4 addr ----- */ |
||||
static struct NODEINFO_Q* mknode(uint64_t nid, const uint8_t pubkey[32], |
||||
uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t port) { |
||||
size_t extra = sizeof(struct NODEINFO_IPV4_SOCKET_META) + sizeof(struct NODEINFO_IPV4_ADDR); |
||||
size_t data_sz = sizeof(struct NODEINFO_Q) - sizeof(struct ll_entry) + extra; |
||||
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)queue_entry_new(data_sz); |
||||
if (!nq) return NULL; |
||||
|
||||
struct NODEINFO* ni = &nq->node; |
||||
ni->node_id = nid; ni->ver = 0; |
||||
memcpy(ni->public_key, pubkey, SC_PUBKEY_SIZE); |
||||
ni->local_v4_sockets = 1; |
||||
ni->local_v4_addrs = 1; |
||||
|
||||
uint8_t* dyn = (uint8_t*)(ni + 1); |
||||
struct NODEINFO_IPV4_SOCKET_META* meta = (struct NODEINFO_IPV4_SOCKET_META*)dyn; |
||||
meta->id = 0; meta->config_type = CFG_SERVER_TYPE_PUBLIC; meta->nat_type = NAT_TYPE_UNKNOWN; |
||||
dyn += sizeof(struct NODEINFO_IPV4_SOCKET_META); |
||||
struct NODEINFO_IPV4_ADDR* addr = (struct NODEINFO_IPV4_ADDR*)dyn; |
||||
addr->addr[0] = a; addr->addr[1] = b; addr->addr[2] = c; addr->addr[3] = d; |
||||
addr->port = port; addr->type = ADDR_TYPE_INTERFACE; addr->socket_id = 0; |
||||
return nq; |
||||
} |
||||
|
||||
/* ======================== Test 1: already connected → immediate EARLY+LATE ======================== */ |
||||
static void test1(void* arg) { |
||||
(void)arg; if (result) return; |
||||
if (lks(g_a) < 1 || lks(g_b) < 1) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test1, "t1"); return; } |
||||
struct NODEINFO_Q* nb = nodeinfo_find_by_id(g_a->bgp, NID_B); |
||||
if (!nb) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test1, "t1b"); return; } |
||||
fprintf(stderr, "Test 1: already connected → EARLY|LATE\n"); fflush(stderr); |
||||
cb_type = 0; cb_conn_ok = 0; cb_count = 0; |
||||
int r = etcp_connect(g_a, nb, connect_ccb, NULL, ETCP_CONNECT_EARLY | ETCP_CONNECT_LATE); |
||||
if (r != 0) { fail("etcp_connect returned error"); return; } |
||||
uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test2, "t2"); |
||||
} |
||||
static void test2(void* arg) { |
||||
(void)arg; if (result) return; |
||||
if (!cb_count) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test2, "t2"); return; } |
||||
if (cb_count != 2) { fail("test1: expected 2 callbacks"); return; } |
||||
if (!cb_conn_ok) { fail("test1: conn=NULL in callback"); return; } |
||||
fprintf(stderr, " OK: %d callbacks, conn=OK\n", cb_count); fflush(stderr); |
||||
/* advance to test3 */ |
||||
uasync_call_soon(ua, NULL, test3); |
||||
} |
||||
|
||||
/* ======================== Test 3: unreachable node → timeout error ======================== */ |
||||
static void test3(void* arg) { |
||||
(void)arg; if (result) return; |
||||
fprintf(stderr, "Test 3: unreachable node → error\n"); fflush(stderr); |
||||
struct SC_MYKEYS fk; sc_generate_keypair(&fk); |
||||
uint64_t fnid = 0xF000000000000001ULL; |
||||
struct NODEINFO_Q* fn = mknode(fnid, fk.public_key, 127,0,0,1, 49999); |
||||
if (!fn) { fail("mknode failed"); return; } |
||||
queue_data_put_with_index(g_a->bgp->nodes, &fn->ll); |
||||
g_a->etcp_connect_timeout_tb = 2000; |
||||
cb_type = -1; cb_conn_ok = -1; cb_count = 0; |
||||
int r = etcp_connect(g_a, fn, connect_ccb, NULL, ETCP_CONNECT_EARLY | ETCP_CONNECT_LATE); |
||||
if (r != 0) { fail("etcp_connect(unreachable) returned error"); return; } |
||||
uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test4, "t4"); |
||||
} |
||||
static void test4(void* arg) { |
||||
(void)arg; if (result) return; |
||||
if (!cb_count) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test4, "t4"); return; } |
||||
if (cb_count != 1) { fail("test3: expected 1 callback"); return; } |
||||
if (cb_conn_ok != 0) { fail("test3: expected conn=NULL"); return; } |
||||
if (cb_type != 0) { fail("test3: expected type=0 (error)"); return; } |
||||
fprintf(stderr, " OK: error callback delivered (NULL, type=0)\n"); fflush(stderr); |
||||
uasync_call_soon(ua, NULL, test5); |
||||
} |
||||
|
||||
/* ======================== Test 5: double connect to unreachable → both callbacks ======================== */ |
||||
static int t5_cb_count = 0; |
||||
static void t5_ccb(void* a, struct ETCP_CONN* conn, int type) { |
||||
(void)a; (void)conn; if (type == 0) t5_cb_count++; |
||||
} |
||||
|
||||
static void test5(void* arg) { |
||||
(void)arg; if (result) return; |
||||
fprintf(stderr, "Test 5: double connect to unreachable → both callbacks\n"); fflush(stderr); |
||||
struct SC_MYKEYS fk; sc_generate_keypair(&fk); |
||||
uint64_t fnid = 0xF000000000000002ULL; |
||||
struct NODEINFO_Q* fn = mknode(fnid, fk.public_key, 127,0,0,1, 49998); |
||||
if (!fn) { fail("mknode failed"); return; } |
||||
queue_data_put_with_index(g_a->bgp->nodes, &fn->ll); |
||||
g_a->etcp_connect_timeout_tb = 2000; |
||||
t5_cb_count = 0; |
||||
int r1 = etcp_connect(g_a, fn, t5_ccb, NULL, ETCP_CONNECT_EARLY | ETCP_CONNECT_LATE); |
||||
int r2 = etcp_connect(g_a, fn, t5_ccb, NULL, ETCP_CONNECT_EARLY); |
||||
if (r1 != 0 || r2 != 0) { fail("etcp_connect double call returned error"); return; } |
||||
uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test6, "t6"); |
||||
} |
||||
static void test6(void* arg) { |
||||
(void)arg; if (result) return; |
||||
if (t5_cb_count < 2) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test6, "t6"); return; } |
||||
if (t5_cb_count != 2) { fail("test5: expected 2 error callbacks"); return; } |
||||
fprintf(stderr, " OK: both callbacks fired\n"); fflush(stderr); |
||||
uasync_call_soon(ua, NULL, test7); |
||||
} |
||||
|
||||
/* ======================== Test 7: EARLY-only flag ======================== */ |
||||
static void test7(void* arg) { |
||||
(void)arg; if (result) return; |
||||
struct NODEINFO_Q* nb = nodeinfo_find_by_id(g_a->bgp, NID_B); |
||||
if (!nb || lks(g_a) < 1) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test7, "t7"); return; } |
||||
fprintf(stderr, "Test 7: EARLY-only flag\n"); fflush(stderr); |
||||
cb_type = 0; cb_conn_ok = 0; cb_count = 0; |
||||
int r = etcp_connect(g_a, nb, connect_ccb, NULL, ETCP_CONNECT_EARLY); |
||||
if (r != 0) { fail("etcp_connect(EARLY) returned error"); return; } |
||||
uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test8, "t8"); |
||||
} |
||||
static void test8(void* arg) { |
||||
(void)arg; if (result) return; |
||||
if (!cb_count) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test8, "t8"); return; } |
||||
if (cb_count != 1) { fail("test7: expected exactly 1 callback"); return; } |
||||
if (cb_type != ETCP_CONNECT_EARLY) { fail("test7: expected EARLY type"); return; } |
||||
fprintf(stderr, " OK: only EARLY fired\n"); fflush(stderr); |
||||
uasync_call_soon(ua, NULL, test9); |
||||
} |
||||
|
||||
/* ======================== Test 9: LATE-only flag ======================== */ |
||||
static void test9(void* arg) { |
||||
(void)arg; if (result) return; |
||||
struct NODEINFO_Q* nb = nodeinfo_find_by_id(g_a->bgp, NID_B); |
||||
if (!nb || lks(g_a) < 1) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test9, "t9"); return; } |
||||
fprintf(stderr, "Test 9: LATE-only flag\n"); fflush(stderr); |
||||
cb_type = 0; cb_conn_ok = 0; cb_count = 0; |
||||
int r = etcp_connect(g_a, nb, connect_ccb, NULL, ETCP_CONNECT_LATE); |
||||
if (r != 0) { fail("etcp_connect(LATE) returned error"); return; } |
||||
uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test10, "t10"); |
||||
} |
||||
static void test10(void* arg) { |
||||
(void)arg; if (result) return; |
||||
if (!cb_count) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test10, "t10"); return; } |
||||
if (cb_count != 1) { fail("test9: expected exactly 1 callback"); return; } |
||||
if (cb_type != ETCP_CONNECT_LATE) { fail("test9: expected LATE type"); return; } |
||||
fprintf(stderr, " OK: only LATE fired\n"); fflush(stderr); |
||||
fprintf(stderr, "=== ALL PASSED ===\n"); fflush(stderr); |
||||
result = 2; |
||||
} |
||||
|
||||
/* ======================== setup / cleanup / main ======================== */ |
||||
|
||||
static void setup(void) { |
||||
test_mkdtemp(tdir); |
||||
int base = 48000 + (getpid() % 10000); pa = base; pb = base + 1; |
||||
snprintf(ca, sizeof(ca), "%s/a.conf", tdir); snprintf(cb, sizeof(cb), "%s/b.conf", tdir); |
||||
wf(ca, "[global]\nmy_node_id=0x%llx\ntun_ip=10.99.0.1/24\ntun_ifname=tun99\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", NID_A, pa); |
||||
wf(cb, "[global]\nmy_node_id=0x%llx\ntun_ip=10.99.0.2/24\ntun_ifname=tun98\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", NID_B, pb); |
||||
config_ensure_keys_and_node_id(ca); config_ensure_keys_and_node_id(cb); |
||||
char *p0 = gv(ca,"pub"), *r0 = gv(ca,"priv"), *p1 = gv(cb,"pub"), *r1 = gv(cb,"priv"); |
||||
wf(ca, "[global]\nmy_node_id=0x%llx\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.99.0.1/24\ntun_ifname=tun99\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[client: to_b]\nkeepalive=1\npeer_public_key=%s\nlink=s1:127.0.0.1:%d\n[allowed_keys]\nallow_all=1\n", NID_A, r0, p0, pa, p1, pb); |
||||
wf(cb, "[global]\nmy_node_id=0x%llx\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.99.0.2/24\ntun_ifname=tun98\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", NID_B, r1, p1, pb); |
||||
u_free(p0); u_free(r0); u_free(p1); u_free(r1); |
||||
} |
||||
static void cleanup(void) { test_unlink(ca); test_unlink(cb); test_rmdir(tdir); } |
||||
|
||||
int main(void) { |
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); |
||||
utun_instance_set_tun_init_enabled(0); setup(); |
||||
ua = uasync_create(); |
||||
g_a = utun_instance_create(ua, ca); g_b = utun_instance_create(ua, cb); |
||||
if (!g_a || !g_b) goto done; |
||||
utun_instance_init(g_a); utun_instance_init(g_b); |
||||
uasync_call_soon(ua, NULL, test1); |
||||
ttimer = uasync_set_timeout(ua, TIMEOUT_TB, NULL, to_cb, "to"); |
||||
{ uint64_t start = get_time_tb(); |
||||
while (!result && (int)(get_time_tb() - start) < TIMEOUT_TB + 50000) { uasync_poll(ua, POLL_MS); } } |
||||
fprintf(stderr, "final result=%d\n", result); fflush(stderr); |
||||
done: |
||||
if (ttimer) uasync_cancel_timeout(ua, ttimer); |
||||
if (g_a) { g_a->running = 0; utun_instance_destroy(g_a); } |
||||
if (g_b) { g_b->running = 0; utun_instance_destroy(g_b); } |
||||
if (ua) { uasync_destroy(ua, 0); ua = NULL; } |
||||
cleanup(); |
||||
return (result == 2) ? 0 : 1; |
||||
} |
||||
@ -0,0 +1,287 @@
|
||||
// test_etcp_router_reconnect.c — ETCP router test: a-b-c, b restarts
|
||||
// Топология: a [server] ←—[клиент b]—→ c [server], один UASYNC
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <time.h> |
||||
|
||||
#include "../lib/platform_compat.h" |
||||
#include "test_utils.h" |
||||
#ifdef _WIN32 |
||||
#include <windows.h> |
||||
#include <direct.h> |
||||
#include <process.h> |
||||
#define getpid _getpid |
||||
#else |
||||
#include <unistd.h> |
||||
#endif |
||||
|
||||
#include "../src/etcp.h" |
||||
#include "../src/etcp_connections.h" |
||||
#include "../src/etcp_router.h" |
||||
#include "../src/etcp_api.h" |
||||
#include "../src/route_bgp.h" |
||||
#include "../src/route_node.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/ll_queue.h" |
||||
#include "../lib/debug_config.h" |
||||
#include "../lib/mem.h" |
||||
|
||||
#define TEST_SVC_ID 0x10 |
||||
#define TEST_TIMEOUT_MS 60000 |
||||
#define PHASE_PACKETS 500 |
||||
#define MAX_PAYLOAD 1500 |
||||
#define MIN_PAYLOAD 20 |
||||
#define MAX_BURST 20 |
||||
#define MIN_BURST 2 |
||||
#define POLL_TB 50 |
||||
|
||||
static struct UTUN_INSTANCE* g_a = NULL; |
||||
static struct UTUN_INSTANCE* g_b = NULL; |
||||
static struct UTUN_INSTANCE* g_c = NULL; |
||||
static struct UASYNC* ua = NULL; |
||||
|
||||
static char temp_dir[] = "/tmp/utun_test_XXXXXX"; |
||||
static char a_conf[512], b_conf[512], c_conf[512]; |
||||
static int a_port, b_port_a, b_port_c, c_port; |
||||
|
||||
static const uint64_t node_a = 0xAAAAAAAA00000001ULL; |
||||
static const uint64_t node_b = 0xBBBBBBBB00000001ULL; |
||||
static const uint64_t node_c = 0xCCCCCCCC00000001ULL; |
||||
|
||||
static struct SC_MYKEYS keys_a, keys_b, keys_c; |
||||
static char privhex_a[65], privhex_b[65], privhex_c[65], pubhex_a[65], pubhex_b[65], pubhex_c[65]; |
||||
|
||||
enum { ST_INIT, ST_PHASE1_SEND, ST_PHASE1_WAIT, ST_B_KILL, ST_B_RESTART, ST_PHASE3_SEND, ST_PHASE3_WAIT, ST_DONE }; |
||||
static int g_state = ST_INIT; |
||||
static int g_test_ok = 0; |
||||
static int g_fail_code = 0; |
||||
|
||||
static uint32_t g_total_sent_phase1 = 0, g_total_sent_phase3 = 0; |
||||
static uint32_t g_phase_sent = 0, g_seq = 0; |
||||
static int g_tick_counter = 0; |
||||
static uint32_t g_rcvd_total = 0, g_expected_seq = 0; |
||||
static int g_bgp_ready_mask = 0; |
||||
static int g_loop_cnt = 0; |
||||
|
||||
static void hex_encode(const uint8_t* bin, int len, char* out) { |
||||
static const char hex[] = "0123456789abcdef"; |
||||
for (int i = 0; i < len; i++) { out[i*2]=hex[bin[i]>>4]; out[i*2+1]=hex[bin[i]&0xF]; } |
||||
out[len*2]=0; |
||||
} |
||||
static void gen_payload(uint32_t seq, uint8_t* buf, int len) { |
||||
for (int i=0;i<len;i++) buf[i]=(uint8_t)((i^seq^0xA5)&0xFF); |
||||
} |
||||
|
||||
static struct NODEINFO_Q* mknode(uint64_t nid, const uint8_t pubkey[32], |
||||
uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t port) { |
||||
size_t extra = sizeof(struct NODEINFO_IPV4_SOCKET_META) + sizeof(struct NODEINFO_IPV4_ADDR); |
||||
size_t data_sz = sizeof(struct NODEINFO_Q) - sizeof(struct ll_entry) + extra; |
||||
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)queue_entry_new(data_sz); |
||||
if (!nq) return NULL; |
||||
struct NODEINFO* ni = &nq->node; |
||||
ni->node_id = nid; ni->ver = 0; |
||||
memcpy(ni->public_key, pubkey, SC_PUBKEY_SIZE); |
||||
ni->local_v4_sockets = 1; |
||||
ni->local_v4_addrs = 1; |
||||
uint8_t* dyn = (uint8_t*)(ni + 1); |
||||
struct NODEINFO_IPV4_SOCKET_META* meta = (struct NODEINFO_IPV4_SOCKET_META*)dyn; |
||||
meta->id = 0; meta->config_type = CFG_SERVER_TYPE_PUBLIC; meta->nat_type = NAT_TYPE_UNKNOWN; |
||||
dyn += sizeof(struct NODEINFO_IPV4_SOCKET_META); |
||||
struct NODEINFO_IPV4_ADDR* addr = (struct NODEINFO_IPV4_ADDR*)dyn; |
||||
addr->addr[0] = a; addr->addr[1] = b; addr->addr[2] = c; addr->addr[3] = d; |
||||
addr->port = port; addr->type = ADDR_TYPE_INTERFACE; addr->socket_id = 0; |
||||
return nq; |
||||
} |
||||
|
||||
static int create_temp_configs(void) { |
||||
if (test_mkdtemp(temp_dir) != 0) { fprintf(stderr, "mkdtemp fail\n"); return -1; } |
||||
int base = 42000+(getpid()%15000); |
||||
a_port=base; b_port_a=base+1; b_port_c=base+2; c_port=base+3; |
||||
if (sc_generate_keypair(&keys_a)!=SC_OK||sc_generate_keypair(&keys_b)!=SC_OK||sc_generate_keypair(&keys_c)!=SC_OK) { fprintf(stderr,"keygen fail\n"); return -1; } |
||||
hex_encode(keys_a.private_key,32,privhex_a); hex_encode(keys_a.public_key,32,pubhex_a); |
||||
hex_encode(keys_b.private_key,32,privhex_b); hex_encode(keys_b.public_key,32,pubhex_b); |
||||
hex_encode(keys_c.private_key,32,privhex_c); hex_encode(keys_c.public_key,32,pubhex_c); |
||||
|
||||
snprintf(a_conf,sizeof(a_conf),"%s/a.conf",temp_dir); |
||||
FILE* f=fopen(a_conf,"w"); if(!f){perror("a_conf");return -1;} |
||||
fprintf(f,"[global]\nmy_node_id=0x%016llX\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.99.0.1/24\ntun_ifname=tun99\n\n" |
||||
"[server:hub_a]\naddr=127.0.0.1:%d\ntype=public\n\n[allowed_keys]\nallow_all=1\n",(unsigned long long)node_a,privhex_a,pubhex_a,a_port); |
||||
fclose(f); |
||||
|
||||
snprintf(b_conf,sizeof(b_conf),"%s/b.conf",temp_dir); |
||||
f=fopen(b_conf,"w"); if(!f){perror("b_conf");return -1;} |
||||
fprintf(f,"[global]\nmy_node_id=0x%016llX\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.99.0.2/24\ntun_ifname=tun98\n\n" |
||||
"[server:s_a]\naddr=127.0.0.1:%d\ntype=public\n\n[server:s_c]\naddr=127.0.0.1:%d\ntype=public\n", |
||||
(unsigned long long)node_b,privhex_b,pubhex_b,b_port_a,b_port_c); |
||||
fclose(f); |
||||
|
||||
snprintf(c_conf,sizeof(c_conf),"%s/c.conf",temp_dir); |
||||
f=fopen(c_conf,"w"); if(!f){perror("c_conf");return -1;} |
||||
fprintf(f,"[global]\nmy_node_id=0x%016llX\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.99.0.3/24\ntun_ifname=tun97\n\n" |
||||
"[server:hub_c]\naddr=127.0.0.1:%d\ntype=public\n\n[allowed_keys]\nallow_all=1\n",(unsigned long long)node_c,privhex_c,pubhex_c,c_port); |
||||
fclose(f); |
||||
return 0; |
||||
} |
||||
static void cleanup(void) { test_unlink(a_conf); test_unlink(b_conf); test_unlink(c_conf); test_rmdir(temp_dir); } |
||||
|
||||
static void recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry) { |
||||
if (!entry || !entry->dgram) { fprintf(stderr, "RECV: null entry\n"); g_test_ok=-1; g_fail_code=10; return; } |
||||
if (!conn && entry->len == 9) { queue_dgram_free(entry); queue_entry_free(entry); return; } |
||||
if (entry->len < 7) { fprintf(stderr, "RECV: short entry len=%u\n", (unsigned)entry->len); g_test_ok=-1; g_fail_code=10; queue_dgram_free(entry); queue_entry_free(entry); return; } |
||||
uint32_t seq=0; memcpy(&seq,entry->dgram+1,4); |
||||
uint16_t sz=0; memcpy(&sz, entry->dgram+5,2); |
||||
if (entry->len != (size_t)(7+sz)) { fprintf(stderr, "FAIL: size mismatch seq=%u\n", seq); g_test_ok=-1; g_fail_code=11; queue_dgram_free(entry); queue_entry_free(entry); return; } |
||||
if (seq != g_expected_seq) { fprintf(stderr, "FAIL: seq broken seq=%u expected=%u\n", seq, g_expected_seq); g_test_ok=-1; g_fail_code=12; queue_dgram_free(entry); queue_entry_free(entry); return; } |
||||
uint8_t exp[MAX_PAYLOAD]; gen_payload(seq, exp, sz); |
||||
if (memcmp(entry->dgram+7, exp, sz) != 0) { fprintf(stderr, "FAIL: corrupt seq=%u\n", seq); g_test_ok=-1; g_fail_code=13; queue_dgram_free(entry); queue_entry_free(entry); return; } |
||||
g_expected_seq++; g_rcvd_total++; |
||||
queue_dgram_free(entry); queue_entry_free(entry); |
||||
} |
||||
|
||||
static int send_one_pkt(void) { |
||||
int len = MIN_PAYLOAD + (rand()%(MAX_PAYLOAD-MIN_PAYLOAD+1)); |
||||
uint8_t* buf = u_malloc(7+len); if(!buf) return -1; |
||||
buf[0]=TEST_SVC_ID; memcpy(buf+1,&g_seq,4); |
||||
uint16_t s16=(uint16_t)len; memcpy(buf+5,&s16,2); |
||||
gen_payload(g_seq, buf+7, len); |
||||
struct ll_entry* e=queue_entry_new(0); if(!e){u_free(buf);return -1;} |
||||
e->dgram=buf; e->len=7+len; |
||||
int ret = etcp_route_send(g_a, node_c, e, 0); |
||||
if (ret == 0) g_seq++; |
||||
return ret; |
||||
} |
||||
|
||||
static void connect_bgp_ready_cb(void* arg, struct ETCP_CONN* conn, int type) { |
||||
(void)conn; |
||||
if (type == ETCP_CONNECT_BGP_READY) { |
||||
int id = *(int*)arg; g_bgp_ready_mask |= (1 << id); |
||||
} |
||||
} |
||||
|
||||
static const char* state_name(int s) { |
||||
static const char* n[]={"INIT","P1SEND","P1WAIT","B_KILL","B_RESTART","P3SEND","P3WAIT","DONE"}; return s<8?n[s]:"?"; |
||||
} |
||||
|
||||
static void send_burst(void) { |
||||
int b = MIN_BURST+(rand()%(MAX_BURST-MIN_BURST+1)); |
||||
if (b>(int)(PHASE_PACKETS-g_phase_sent)) b=PHASE_PACKETS-g_phase_sent; |
||||
for (int i=0;i<b;i++) { if (send_one_pkt()==0) g_phase_sent++; else break; } |
||||
} |
||||
|
||||
static void state_step(void) { |
||||
switch (g_state) { |
||||
case ST_INIT: |
||||
if (g_bgp_ready_mask == 3) { |
||||
struct ETCP_CONN* rc = route_bgp_find_conn_for_node(g_a->bgp, node_c); |
||||
if (rc) g_state=ST_PHASE1_SEND; |
||||
} |
||||
break; |
||||
case ST_PHASE1_SEND: |
||||
if (g_phase_sent < PHASE_PACKETS && g_tick_counter <= 0) { |
||||
send_burst(); g_tick_counter = 1; g_total_sent_phase1 = g_phase_sent; |
||||
} |
||||
break; |
||||
case ST_PHASE1_WAIT: |
||||
if (g_rcvd_total >= g_total_sent_phase1) g_state=ST_B_KILL; |
||||
break; |
||||
case ST_B_KILL: |
||||
g_b->running=0; utun_instance_destroy(g_b); g_b=NULL; |
||||
{ struct ETCP_CONN *c,*n; |
||||
for(c=g_a->connections;c;c=n){n=c->next;if(c->peer_node_id==node_b)etcp_connection_close(c);} |
||||
for(c=g_c->connections;c;c=n){n=c->next;if(c->peer_node_id==node_b)etcp_connection_close(c);} } |
||||
g_phase_sent=0; g_bgp_ready_mask=0; g_state=ST_B_RESTART; |
||||
break; |
||||
case ST_B_RESTART: |
||||
if (!g_b) { |
||||
g_b=utun_instance_create(ua,b_conf); |
||||
if (!g_b||utun_instance_init(g_b)<0) { fprintf(stderr,"FAIL: b restart\n"); g_test_ok=-1; g_fail_code=20; return; } |
||||
g_b->etcp_connect_timeout_tb = 300000; |
||||
struct NODEINFO_Q* nq_a = mknode(node_a, keys_a.public_key, 127,0,0,1, (uint16_t)a_port); |
||||
struct NODEINFO_Q* nq_c = mknode(node_c, keys_c.public_key, 127,0,0,1, (uint16_t)c_port); |
||||
if (!nq_a || !nq_c) { fprintf(stderr,"FAIL: mknode\n"); g_test_ok=-1; return; } |
||||
static int cc_a=0,cc_c=1; |
||||
etcp_connect(g_b,nq_a,connect_bgp_ready_cb,&cc_a,ETCP_CONNECT_BGP_READY); |
||||
etcp_connect(g_b,nq_c,connect_bgp_ready_cb,&cc_c,ETCP_CONNECT_BGP_READY); |
||||
} |
||||
if (g_bgp_ready_mask == 3) { |
||||
struct ETCP_CONN* rc = route_bgp_find_conn_for_node(g_a->bgp, node_c); |
||||
if (rc) { g_phase_sent=0; g_state=ST_PHASE3_SEND; } |
||||
} |
||||
break; |
||||
case ST_PHASE3_SEND: |
||||
if (g_phase_sent < PHASE_PACKETS && g_tick_counter <= 0) { |
||||
send_burst(); g_tick_counter = 1; g_total_sent_phase3 = g_phase_sent; |
||||
} |
||||
break; |
||||
case ST_PHASE3_WAIT: |
||||
if (g_rcvd_total >= g_total_sent_phase1 + g_total_sent_phase3) g_state=ST_DONE; |
||||
break; |
||||
case ST_DONE: |
||||
if (g_rcvd_total == g_total_sent_phase1+g_total_sent_phase3 && g_rcvd_total>0) g_test_ok=1; |
||||
else { fprintf(stderr, "FAIL: sent=%u rcvd=%u\n", g_total_sent_phase1+g_total_sent_phase3, g_rcvd_total); g_test_ok=-1; g_fail_code=30; } |
||||
return; |
||||
} |
||||
if (g_tick_counter>0) g_tick_counter--; |
||||
} |
||||
|
||||
static void timeout_cb(void* arg) { |
||||
(void)arg; |
||||
fprintf(stderr, "TIMEOUT: state=%s sent_p1=%u sent_p3=%u rcvd=%u\n", state_name(g_state), g_total_sent_phase1, g_total_sent_phase3, g_rcvd_total); |
||||
g_test_ok=-1; g_fail_code=99; |
||||
} |
||||
|
||||
int main(void) { |
||||
srand((unsigned)time(NULL)); |
||||
if (create_temp_configs()!=0) return 1; |
||||
printf("=== ETCP Router Reconnect Test ===\nPorts: a=%d b_a=%d b_c=%d c=%d\n", a_port, b_port_a, b_port_c, c_port); |
||||
debug_config_init(); |
||||
debug_set_level(DEBUG_LEVEL_ERROR); |
||||
utun_instance_set_tun_init_enabled(0); |
||||
|
||||
ua=uasync_create(); if(!ua){cleanup();return 1;} |
||||
g_a=utun_instance_create(ua,a_conf); if(!g_a||utun_instance_init(g_a)<0){fprintf(stderr,"FAIL: a\n");goto fail;} |
||||
g_c=utun_instance_create(ua,c_conf); if(!g_c||utun_instance_init(g_c)<0){fprintf(stderr,"FAIL: c\n");goto fail;} |
||||
g_b=utun_instance_create(ua,b_conf); if(!g_b||utun_instance_init(g_b)<0){fprintf(stderr,"FAIL: b\n");goto fail;} |
||||
etcp_router_bind(g_c, TEST_SVC_ID, recv_handler); |
||||
|
||||
struct NODEINFO_Q* nq_a = mknode(node_a, keys_a.public_key, 127,0,0,1, (uint16_t)a_port); |
||||
struct NODEINFO_Q* nq_c = mknode(node_c, keys_c.public_key, 127,0,0,1, (uint16_t)c_port); |
||||
if (!nq_a || !nq_c) { fprintf(stderr,"FAIL: mknode\n"); goto fail; } |
||||
g_b->etcp_connect_timeout_tb = 300000; |
||||
{ static int cc_a=0,cc_c=1; |
||||
etcp_connect(g_b,nq_a,connect_bgp_ready_cb,&cc_a,ETCP_CONNECT_BGP_READY); |
||||
etcp_connect(g_b,nq_c,connect_bgp_ready_cb,&cc_c,ETCP_CONNECT_BGP_READY); } |
||||
|
||||
void* to_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS*10, NULL, timeout_cb, "to"); |
||||
int max_loops = TEST_TIMEOUT_MS * 10 / POLL_TB; |
||||
while (!g_test_ok && g_loop_cnt < max_loops) { |
||||
uasync_poll(ua, POLL_TB); |
||||
state_step(); |
||||
if (g_state==ST_PHASE1_SEND && g_phase_sent>=PHASE_PACKETS) g_state=ST_PHASE1_WAIT; |
||||
else if (g_state==ST_PHASE3_SEND && g_phase_sent>=PHASE_PACKETS) g_state=ST_PHASE3_WAIT; |
||||
g_loop_cnt++; |
||||
} |
||||
|
||||
uasync_cancel_timeout(ua,to_id); |
||||
if(g_a){g_a->running=0;utun_instance_destroy(g_a);} |
||||
if(g_b){g_b->running=0;utun_instance_destroy(g_b);} |
||||
if(g_c){g_c->running=0;utun_instance_destroy(g_c);} |
||||
if(ua) uasync_destroy(ua,0); |
||||
cleanup(); |
||||
if(g_test_ok==1){printf("=== TEST PASSED ===\n");return 0;} |
||||
printf("=== TEST FAILED: code=%d ===\n",g_fail_code); |
||||
return 1; |
||||
fail: |
||||
if(g_a){g_a->running=0;utun_instance_destroy(g_a);} |
||||
if(g_b){g_b->running=0;utun_instance_destroy(g_b);} |
||||
if(g_c){g_c->running=0;utun_instance_destroy(g_c);} |
||||
if(ua) uasync_destroy(ua,0); |
||||
cleanup(); |
||||
return 1; |
||||
} |
||||
Loading…
Reference in new issue