node_conn_direct: add open_node variant, refactor internals

This commit is contained in:
evgeny committed 2026-08-01 09:45:32 +03:00
1 parent 153483fd96
commit eb2a8f6975
3 files changed
+252 -222

No files matched your search

+112 -23
View File
@@ -7,10 +7,17 @@
* 2 — poll → init_cb → NCD_EVENT_UP на обоих
* 3 — open на ready conn → NCD_REUSED + async cb
* 4 — ещё open → NCD_REUSED
* 5 — close h3,h2 → conn жив
* 6 — close h1,h0 (last) → conn закрыт (fin_wait → CLOSE → DOWN)
* 7 — NCD_ERR unknown node
* 8 — inject unreachable → NCD_NEW → TIMEOUT
* 5 — close h3,h2 → conn жив (h0,h1 остаются)
* 6a — bidirectional: inject A→B, открыть B→A
* 6b — poll: A и B UP. A закрывает новый handle → CLOSE к B
* 6c — B имеет handle → KEEP_ALIVE. A переоткрывает → NCD_REUSED
* 6d — poll: A get_conn OK после KEEP_ALIVE. Cleanup B-side.
* 6e — open во время fin_wait: A закрывает h1, сразу же переоткрывает → NCD_REUSED
* 6f — poll: UP на переоткрытом handle. Cleanup.
* 6g — CLOSE от peer когда нет handle'ов: A закрывает → CLOSE к B, B без handles → conn dead → entry удалён → NCD_NEW при переоткрытии
* 6 — close h1,h0 (last) → conn закрыт (fin_wait → CLOSE → reinit)
* 7 — NCD_ERR unknown node
* 8 — inject unreachable → NCD_NEW → TIMEOUT
*/
#include <stdio.h>
#include <stdlib.h>
@@ -50,11 +57,13 @@ static void *g_ttimer = NULL;
static char tdir[] = "/tmp/utun_ncd_XXXXXX";
static char ca[256], cb[256];
static int pa = 0, pb = 0;
static uint64_t nid_b = 0;
static uint8_t pubkey_b[SC_PUBKEY_SIZE];
static uint64_t nid_a = 0, nid_b = 0;
static uint8_t pubkey_a[SC_PUBKEY_SIZE], pubkey_b[SC_PUBKEY_SIZE];
static struct NODE_CONN_DIRECT *gh[4];
static volatile int g_up[4], g_down[4], g_tout[4];
static struct NODE_CONN_DIRECT *gh[8];
static volatile int g_up[8], g_down[8], g_tout[8];
static struct NODE_CONN_DIRECT *gh_b[2];
static volatile int g_up_b[2], g_down_b[2];
static void ncd_cb(struct NODE_CONN_DIRECT* h, enum ncd_event event, void* arg) {
int idx = (int)(intptr_t)arg;
@@ -64,14 +73,26 @@ static void ncd_cb(struct NODE_CONN_DIRECT* h, enum ncd_event event, void* arg)
fprintf(stderr, " cb[%d]: event=%d (up=%d down=%d tout=%d)\n", idx, (int)event, g_up[idx], g_down[idx], g_tout[idx]); fflush(stderr);
}
static void ncd_cb_b(struct NODE_CONN_DIRECT* h, enum ncd_event event, void* arg) {
int idx = (int)(intptr_t)arg;
if (event == NCD_EVENT_UP) g_up_b[idx]++;
else if (event == NCD_EVENT_DOWN) g_down_b[idx]++;
fprintf(stderr, " cb_b[%d]: event=%d (up=%d down=%d)\n", idx, (int)event, g_up_b[idx], g_down_b[idx]); fflush(stderr);
}
static void to_cb(void* arg) { (void)arg; fprintf(stderr, "GLOBAL TIMEOUT phase=%d\n", g_phase); g_result = 2; }
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 void fail(const char* msg) { fprintf(stderr, "FAIL[%d]: %s\n", g_phase, msg); fflush(stderr); g_result = 2; }
static int inject_node(struct UTUN_INSTANCE* inst, uint64_t nid, const uint8_t pk[SC_PUBKEY_SIZE], uint16_t port);
static void t1(void* arg); static void t2(void* arg); static void t3(void* arg);
static void t4(void* arg); static void t5(void* arg); static void t6(void* arg);
static void t7(void* arg); static void t8(void* arg); static void t9(void* arg);
static void t4(void* arg); static void t5(void* arg);
static void t6a(void* arg); static void t6b(void* arg); static void t6c(void* arg);
static void t6d(void* arg); static void t6e(void* arg); static void t6f(void* arg);
static void t6g(void* arg);
static void t6(void* arg); static void t7(void* arg);
static void t8(void* arg); static void t9(void* arg);
static void t_done(void* arg);
static void t1(void* arg) {
@@ -79,11 +100,11 @@ static void t1(void* arg) {
fprintf(stderr, "\n=== P1: two opens before poll ===\n"); fflush(stderr);
g_a->etcp_connect_timeout_tb = TIMEOUT_TB;
int r = node_conn_direct_open(g_a, nid_b, 0, ncd_cb, (void*)0, &gh[0]);
int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)0, &gh[0]);
fprintf(stderr, " open#0 → %s\n", r == NCD_NEW ? "NCD_NEW" : r == NCD_REUSED ? "NCD_REUSED" : "ERR");
if (r != NCD_NEW) { fail("expected NCD_NEW"); return; }
r = node_conn_direct_open(g_a, nid_b, 0, ncd_cb, (void*)1, &gh[1]);
r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)1, &gh[1]);
fprintf(stderr, " open#1 → %s\n", r == NCD_NEW ? "NCD_NEW" : r == NCD_REUSED ? "NCD_REUSED" : "ERR");
if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; }
@@ -104,7 +125,7 @@ static void t2(void* arg) {
static void t3(void* arg) {
(void)arg; g_phase = 3;
fprintf(stderr, "\n=== P3: open on ready conn ===\n"); fflush(stderr);
int r = node_conn_direct_open(g_a, nid_b, 0, ncd_cb, (void*)2, &gh[2]);
int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)2, &gh[2]);
fprintf(stderr, " open#2 → %s\n", r == NCD_REUSED ? "NCD_REUSED" : "?");
if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; }
uasync_call_soon(ua, NULL, t4);
@@ -114,7 +135,7 @@ static void t4(void* arg) {
(void)arg; g_phase = 4;
if (g_up[2] == 0) { uasync_set_timeout(ua, POLL_MS, NULL, (timeout_callback_t)t4, "t4"); return; }
fprintf(stderr, "\n=== P4: async cb OK, open h3 ===\n"); fflush(stderr);
int r = node_conn_direct_open(g_a, nid_b, 0, ncd_cb, (void*)3, &gh[3]);
int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)3, &gh[3]);
fprintf(stderr, " open#3 → %s\n", r == NCD_REUSED ? "NCD_REUSED" : "?");
if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; }
uasync_call_soon(ua, NULL, t5);
@@ -129,7 +150,71 @@ static void t5(void* arg) {
node_conn_direct_close(gh[2]); gh[2] = NULL;
if (node_conn_direct_get_conn(gh[1]) == NULL) { fail("conn died after h2 close"); return; }
fprintf(stderr, " OK\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t6);
uasync_call_soon(ua, NULL, t6a);
}
/* ═══════════ Scenario 1: KEEP_ALIVE handshake (conn ещё жив после P1-P5) ═══════════ */
static void t6a(void* arg) {
(void)arg; g_phase = 16;
fprintf(stderr, "\n=== P6a: bidirectional — B opens handle to A ===\n"); fflush(stderr);
int r = node_conn_direct_open(g_b, nid_a, ncd_cb_b, (void*)0, &gh_b[0]);
if (r != NCD_REUSED) { fail("P6a: B open expected NCD_REUSED (incoming conn exists)"); return; }
fprintf(stderr, " B open → NCD_REUSED\n");
r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)4, &gh[4]);
if (r != NCD_REUSED) { fail("P6a: A open(4) expected NCD_REUSED (conn alive)"); return; }
fprintf(stderr, " A open(4) → NCD_REUSED\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t6b);
}
static void t6b(void* arg) {
(void)arg; g_phase = 17;
if (g_up[4] > 0 && g_up_b[0] > 0) {
fprintf(stderr, "\n=== P6b: both UP, A closes handle 4 → CLOSE to B ===\n"); fflush(stderr);
node_conn_direct_close(gh[4]); gh[4] = NULL;
uasync_set_timeout(ua, 200, NULL, (timeout_callback_t)t6c, "t6b_d");
return;
}
uasync_set_timeout(ua, POLL_MS, NULL, (timeout_callback_t)t6b, "t6b");
}
static void t6c(void* arg) {
(void)arg; g_phase = 18;
fprintf(stderr, "\n=== P6c: KEEP_ALIVE round-trip, A re-opens ===\n"); fflush(stderr);
int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)5, &gh[5]);
if (r != NCD_REUSED) { fail("P6c: expected NCD_REUSED (KEEP_ALIVE saved conn)"); return; }
fprintf(stderr, " A re-open(5) → NCD_REUSED\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t6d);
}
static void t6d(void* arg) {
(void)arg; g_phase = 19;
if (g_up[5] == 0) { uasync_set_timeout(ua, POLL_MS, NULL, (timeout_callback_t)t6d, "t6d"); return; }
if (node_conn_direct_get_conn(gh[5]) == NULL) { fail("P6d: get_conn NULL after KEEP_ALIVE"); return; }
fprintf(stderr, "\n=== P6d: KEEP_ALIVE works, get_conn OK ===\n"); fflush(stderr);
node_conn_direct_close(gh[5]); gh[5] = NULL;
node_conn_direct_close(gh_b[0]); gh_b[0] = NULL;
uasync_set_timeout(ua, 300, NULL, (timeout_callback_t)t6e, "t6d_d");
}
/* ═══════════ Scenario 2: Open during fin_wait ═══════════ */
static void t6e(void* arg) {
(void)arg; g_phase = 20;
fprintf(stderr, "\n=== P6e: close h1 → fin_wait, immediately re-open ===\n"); fflush(stderr);
node_conn_direct_close(gh[1]); gh[1] = NULL;
int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)4, &gh[4]);
if (r != NCD_REUSED) { fail("P6e: expected NCD_REUSED (entry found, fin_wait cleared)"); return; }
fprintf(stderr, " re-open → NCD_REUSED\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t6f);
}
static void t6f(void* arg) {
(void)arg; g_phase = 21;
if (g_up[4] == 0) { uasync_set_timeout(ua, POLL_MS, NULL, (timeout_callback_t)t6f, "t6f"); return; }
fprintf(stderr, "\n=== P6f: re-opened handle got UP, Scenario 2 OK ===\n"); fflush(stderr);
node_conn_direct_close(gh[4]); gh[4] = NULL;
uasync_set_timeout(ua, 300, NULL, (timeout_callback_t)t6, "t6f_d");
}
static void t6(void* arg) {
@@ -139,14 +224,14 @@ static void t6(void* arg) {
if (node_conn_direct_get_conn(gh[0]) == NULL) { fail("conn died before last handle"); return; }
node_conn_direct_close(gh[0]); gh[0] = NULL;
fprintf(stderr, " OK\n"); fflush(stderr);
uasync_set_timeout(ua, 200, NULL, (timeout_callback_t)t7, "t6d");
uasync_set_timeout(ua, 3000, NULL, (timeout_callback_t)t7, "t6d");
}
static void t7(void* arg) {
(void)arg; g_phase = 7;
fprintf(stderr, "\n=== P7: NCD_ERR unknown node ===\n"); fflush(stderr);
struct NODE_CONN_DIRECT* hx = NULL;
int r = node_conn_direct_open(g_a, 0xDEADBEEF00000001ULL, 0, ncd_cb, (void*)99, &hx);
int r = node_conn_direct_open(g_a, 0xDEADBEEF00000001ULL, ncd_cb, (void*)99, &hx);
fprintf(stderr, " open(unknown) → %s\n", r == NCD_ERR ? "NCD_ERR" : "?");
if (r != NCD_ERR) { fail("expected NCD_ERR"); return; }
if (hx != NULL) { fail("handle not NULL"); return; }
@@ -176,7 +261,7 @@ static void t8(void* arg) {
memset((void*)g_tout, 0, sizeof(g_tout));
g_a->etcp_connect_timeout_tb = SHORT_TO_TB;
int r = node_conn_direct_open(g_a, fake_id, 0, ncd_cb, (void*)0, &gh[0]);
int r = node_conn_direct_open(g_a, fake_id, ncd_cb, (void*)0, &gh[0]);
fprintf(stderr, " open(unreachable) → %s\n", r == NCD_NEW ? "NCD_NEW" : "?");
if (r != NCD_NEW) { fail("expected NCD_NEW"); return; }
@@ -216,8 +301,8 @@ static int setup(void) {
test_mkdtemp(tdir);
int base = 49000 + (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]\ntun_ip=10.97.0.1/24\ntun_ifname=tun95\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", pa);
wf(cb, "[global]\ntun_ip=10.97.0.2/24\ntun_ifname=tun94\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", pb);
wf(ca, "[global]\ntun_ip=10.97.0.1/24\ntun_ifname=tun95\nkeepalive_timeout=100\nkeepalive_interval=10\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", pa);
wf(cb, "[global]\ntun_ip=10.97.0.2/24\ntun_ifname=tun94\nkeepalive_timeout=100\nkeepalive_interval=10\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", pb);
config_ensure_keys_and_node_id(ca); config_ensure_keys_and_node_id(cb);
return 0;
}
@@ -231,17 +316,21 @@ int main(void) {
g_a = utun_instance_create(ua, ca); g_b = utun_instance_create(ua, cb);
if (!g_a || !g_b) { g_result = 2; goto done; }
nid_b = g_b->node_id; memcpy(pubkey_b, g_b->my_keys.public_key, SC_PUBKEY_SIZE);
fprintf(stderr, "B: 0x%016llx port=%d\n", (unsigned long long)nid_b, pb); fflush(stderr);
nid_a = g_a->node_id; memcpy(pubkey_a, g_a->my_keys.public_key, SC_PUBKEY_SIZE);
fprintf(stderr, "A: 0x%016llx port=%d B: 0x%016llx port=%d\n",
(unsigned long long)nid_a, pa, (unsigned long long)nid_b, pb); fflush(stderr);
utun_instance_init(g_a); utun_instance_init(g_b);
if (inject_node(g_a, nid_b, pubkey_b, pb) != 0) { fail("inject_node"); goto done; }
fprintf(stderr, "injected B into A registry\n"); fflush(stderr);
if (inject_node(g_b, nid_a, pubkey_a, pa) != 0) { fail("inject_node A→B"); goto done; }
fprintf(stderr, "injected B→A and A→B registries\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t1);
g_ttimer = uasync_set_timeout(ua, TIMEOUT_TB, NULL, to_cb, "to");
{ uint64_t st = get_time_tb(); while (!g_result && (int)(get_time_tb() - st) < TIMEOUT_TB + 50000) uasync_poll(ua, POLL_MS); }
if (g_result == 0) g_result = 2;
done:
if (g_ttimer && ua) uasync_cancel_timeout(ua, g_ttimer);
for (int i = 0; i < 4; i++) if (gh[i]) node_conn_direct_close(gh[i]);
for (int i = 0; i < 8; i++) if (gh[i]) node_conn_direct_close(gh[i]);
for (int i = 0; i < 2; i++) if (gh_b[i]) node_conn_direct_close(gh_b[i]);
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);