Browse Source

node_conn_direct: add specific_sock param, move registry to per-instance; expose sc_hex_to_binary; add CONFIG_CONN_HANDLE

topo_upd
evgeny 2 months ago
parent
commit
625382de34
  1. 11
      AGENTS.md
  2. 31
      src/transport_layer/node_conn_direct.h
  3. 8
      src/transport_layer/secure_channel.c
  4. 2
      src/transport_layer/secure_channel.h
  5. 16
      src/utun_instance.h
  6. 22
      tests/test_node_conn_direct.c

11
AGENTS.md

@ -20,6 +20,8 @@ This file contains essential information for AI coding agents working in the uTu
Новый код обязательно должен иметь логи показывающие его правильное функционирование. Новый код обязательно должен иметь логи показывающие его правильное функционирование.
Все ошибки и нештатное поведение должно логироваться Все ошибки и нештатное поведение должно логироваться
Ты постоянно тупишь на отладке и раздалбываешь код. Чтобы этого не было - лог понятный сделай - выводи всё что нужно с подробностями и регулярно чисти что не нужно особенно из категории DEBUG чтобы не засорять и проще было найти что нужно. И всё бустро найдешь. И нечего гадать когда можно просто посмотреть нормальный, правильно сделанный лог и сразу УВИДЕТЬ проблему не гадать, ошибаться, запарывать код еще сильнее и застревать в куче говна
## Quick Reference ## Quick Reference
**Repository:** uTun - Secure VPN tunnel with ETCP protocol **Repository:** uTun - Secure VPN tunnel with ETCP protocol
@ -80,7 +82,7 @@ ASAN_OPTIONS=detect_leaks=0:halt_on_error=0 ./src/utun -f -p /tmp/utun.pid -l /t
### Run All Tests ### Run All Tests
```bash ```bash
make check # Run all tests via automake, логи в tests/logs/ - запускай после make без grep, вывод короткий make check # Run all tests via automake, логи в tests/logs/ - запускай тесты с ПОЛНЫМ ВЫОДОМ, не урезая его ( grep итд) - иначе будешь 10 раз повторять зпауск что жрёт много времени
``` ```
```powershell ```powershell
powershell check.bat # Windows, запускает каждый .exe из tests/ powershell check.bat # Windows, запускает каждый .exe из tests/
@ -89,14 +91,7 @@ powershell check.bat # Windows, запускает каждый .exe
### Run Specific Test ### Run Specific Test
```bash ```bash
cd tests/ cd tests/
./test_etcp_crypto
./test_etcp_two_instances ./test_etcp_two_instances
./test_etcp_simple_traffic
./test_pkt_normalizer_etcp
./test_etcp_api
./test_ll_queue
./test_nat_detection
./test_bgp_route_exchange
``` ```
### Run Single Test with Debug Info ### Run Single Test with Debug Info

31
src/transport_layer/node_conn_direct.h

@ -6,40 +6,45 @@
struct UTUN_INSTANCE; struct UTUN_INSTANCE;
struct ETCP_CONN; struct ETCP_CONN;
struct TOPO_NODE; struct TOPO_NODE;
struct ETCP_SOCKET;
/* ─── Return codes ─── */ /* ─── Return codes ─── */
#define NCD_NEW 0 #define NCD_NEW 0 /* создано новое соединение */
#define NCD_REUSED 1 #define NCD_REUSED 1 /* переиспользовано существующее */
#define NCD_ERR -1 #define NCD_ERR -1 /* ошибка */
/* ─── Opaque handle ─── */ /* ─── Opaque handle ─── */
struct NODE_CONN_DIRECT; struct NODE_CONN_DIRECT;
/* ─── Callback events (единый callback на handle) ─── */ /* ─── Callback events (единый callback на handle) ─── */
enum ncd_event { enum ncd_event {
NCD_EVENT_UP = 0, NCD_EVENT_UP = 0, /* соединение поднялось (линки работают) */
NCD_EVENT_DOWN = 1, NCD_EVENT_DOWN = 1, /* соединение упало */
NCD_EVENT_TIMEOUT = 2, NCD_EVENT_TIMEOUT = 2, /* таймаут первого подключения */
}; };
typedef void (*ncd_callback)(struct NODE_CONN_DIRECT* h, enum ncd_event event, void* arg); typedef void (*ncd_callback)(struct NODE_CONN_DIRECT* h, enum ncd_event event, void* arg);
/* /*
* Открыть handle к узлу по node_id. * Открыть handle к узлу по node_id.
* Node info — из node_registry, fallback SQLite. * Node info ищется через node_registry, fallback — SQLite.
* cb — вызывается при каждом изменении статуса / таймауте. * cb вызывается при изменении статуса / таймауте.
* Если conn уже готов — cb(h, NCD_EVENT_UP) через uasync_call_soon (не синхронно). * Если conn уже готов — cb(NCD_EVENT_UP) через uasync_call_soon (не синхронно).
*
* Возвращает NCD_NEW / NCD_REUSED / NCD_ERR.
*/ */
int node_conn_direct_open(struct UTUN_INSTANCE* inst, uint64_t node_id, int node_conn_direct_open(struct UTUN_INSTANCE* inst, uint64_t node_id,
ncd_callback cb, void* cb_arg, ncd_callback cb, void* cb_arg,
struct NODE_CONN_DIRECT** out_handle); struct NODE_CONN_DIRECT** out_handle,
struct ETCP_SOCKET* specific_sock); /* NULL — авто-подбор всех совместимых сокетов для линков */
/*
* То же, но с уже загруженным TOPO_NODE (ni не владеем — можно передать временный на стеке).
* ni должен содержать public_key, v4_addrs/v6_addrs для создания линков.
*/
int node_conn_direct_open_node(struct UTUN_INSTANCE* inst, uint64_t node_id, int node_conn_direct_open_node(struct UTUN_INSTANCE* inst, uint64_t node_id,
ncd_callback cb, void* cb_arg, ncd_callback cb, void* cb_arg,
struct NODE_CONN_DIRECT** out_handle, struct NODE_CONN_DIRECT** out_handle,
struct TOPO_NODE* ni); struct TOPO_NODE* ni, /* публичный ключ + адреса пира (не владеем) */
struct ETCP_SOCKET* specific_sock); /* NULL — авто-подбор */
void node_conn_direct_close(struct NODE_CONN_DIRECT* h); void node_conn_direct_close(struct NODE_CONN_DIRECT* h);

8
src/transport_layer/secure_channel.c

@ -34,7 +34,7 @@ static void sc_init_random_seed(void)
} }
// Конвертация hex строки в бинарный формат // Конвертация hex строки в бинарный формат
static int hex_to_binary(const char *hex_str, uint8_t *binary, size_t binary_len) { int sc_hex_to_binary(const char *hex_str, uint8_t *binary, size_t binary_len) {
if (!hex_str || !binary || strlen(hex_str) != binary_len * 2) return -1; if (!hex_str || !binary || strlen(hex_str) != binary_len * 2) return -1;
for (size_t i = 0; i < binary_len; i++) { for (size_t i = 0; i < binary_len; i++) {
unsigned int byte; unsigned int byte;
@ -166,11 +166,11 @@ sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_key,
} }
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "public_key len=%zu, private_key len=%zu", DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "public_key len=%zu, private_key len=%zu",
strlen(public_key), strlen(private_key)); strlen(public_key), strlen(private_key));
if (hex_to_binary(public_key, mykeys->public_key, SC_PUBKEY_SIZE)) { if (sc_hex_to_binary(public_key, mykeys->public_key, SC_PUBKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "failed to convert public key from hex"); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "failed to convert public key from hex");
return SC_ERR_INVALID_ARG; return SC_ERR_INVALID_ARG;
} }
if (hex_to_binary(private_key, mykeys->private_key, SC_PRIVKEY_SIZE)) { if (sc_hex_to_binary(private_key, mykeys->private_key, SC_PRIVKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "failed to convert private key from hex"); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "failed to convert private key from hex");
return SC_ERR_INVALID_ARG; return SC_ERR_INVALID_ARG;
} }
@ -185,7 +185,7 @@ sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_key,
sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const uint8_t *peer_public_key_h, int mode) { sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const uint8_t *peer_public_key_h, int mode) {
uint8_t peer_public_key[SC_PUBKEY_SIZE]; uint8_t peer_public_key[SC_PUBKEY_SIZE];
if (mode) { if (mode) {
if (hex_to_binary((const char*)peer_public_key_h, peer_public_key, SC_PUBKEY_SIZE)) { if (sc_hex_to_binary((const char*)peer_public_key_h, peer_public_key, SC_PUBKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid hex key format"); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid hex key format");
return SC_ERR_INVALID_ARG; return SC_ERR_INVALID_ARG;
} }

2
src/transport_layer/secure_channel.h

@ -116,6 +116,8 @@ sc_status_t sc_derive_ed25519_pubkey(const uint8_t *x25519_privkey, uint8_t *ed2
uint64_t sc_derive_node_id_from_pubkey(const uint8_t *public_key); uint64_t sc_derive_node_id_from_pubkey(const uint8_t *public_key);
int sc_hex_to_binary(const char *hex_str, uint8_t *binary, size_t binary_len);
#ifdef __cplusplus #ifdef __cplusplus
} }

16
src/utun_instance.h

@ -37,6 +37,7 @@ struct ETCP_CONN;
typedef void (*etcp_new_conn_fn)(struct ETCP_CONN* conn, void* arg); typedef void (*etcp_new_conn_fn)(struct ETCP_CONN* conn, void* arg);
struct ETCP_SOCKET; struct ETCP_SOCKET;
struct TCP_SOCKET; struct TCP_SOCKET;
struct NODE_CONN_DIRECT;
struct tun_if; struct tun_if;
struct ETCP_BINDINGS; struct ETCP_BINDINGS;
struct ETCP_ROUTER_BINDINGS; struct ETCP_ROUTER_BINDINGS;
@ -64,6 +65,14 @@ struct conn_queue_entry {
struct ETCP_CONN* conn; struct ETCP_CONN* conn;
}; };
// Handle for config-based connections via node_conn_direct
struct CONFIG_CONN_HANDLE {
uint64_t node_id;
char name[MAX_CONN_NAME_LEN];
struct NODE_CONN_DIRECT* handle;
struct CONFIG_CONN_HANDLE* next;
};
// Queue entry for instance->tcp_connections (indexed by node_id) // Queue entry for instance->tcp_connections (indexed by node_id)
struct tcp_conn_entry { struct tcp_conn_entry {
uint64_t node_id; // index key uint64_t node_id; // index key
@ -178,6 +187,13 @@ struct UTUN_INSTANCE {
// NTP time synchronization // NTP time synchronization
struct NTP_TIME ntp; struct NTP_TIME ntp;
struct NTP_NODE_TIME ntp_node; struct NTP_NODE_TIME ntp_node;
// Config-based connection handles (node_conn_direct)
struct CONFIG_CONN_HANDLE* config_conn_handles;
// Per-instance NCD state (replaces global statics in node_conn_direct.c)
void* ncd_registry; // struct ncd_entry* linked list
uint8_t ncd_control_bound; // 1 = etcp_bind(ETCP_RT_ID_NCD_CONTROL) сделан
}; };
// Functions // Functions

22
tests/test_node_conn_direct.c

@ -100,11 +100,11 @@ static void t1(void* arg) {
fprintf(stderr, "\n=== P1: two opens before poll ===\n"); fflush(stderr); fprintf(stderr, "\n=== P1: two opens before poll ===\n"); fflush(stderr);
g_a->etcp_connect_timeout_tb = TIMEOUT_TB; g_a->etcp_connect_timeout_tb = TIMEOUT_TB;
int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)0, &gh[0]); int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)0, &gh[0], NULL);
fprintf(stderr, " open#0 → %s\n", r == NCD_NEW ? "NCD_NEW" : r == NCD_REUSED ? "NCD_REUSED" : "ERR"); 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; } if (r != NCD_NEW) { fail("expected NCD_NEW"); return; }
r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)1, &gh[1]); r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)1, &gh[1], NULL);
fprintf(stderr, " open#1 → %s\n", r == NCD_NEW ? "NCD_NEW" : r == NCD_REUSED ? "NCD_REUSED" : "ERR"); 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; } if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; }
@ -125,7 +125,7 @@ static void t2(void* arg) {
static void t3(void* arg) { static void t3(void* arg) {
(void)arg; g_phase = 3; (void)arg; g_phase = 3;
fprintf(stderr, "\n=== P3: open on ready conn ===\n"); fflush(stderr); fprintf(stderr, "\n=== P3: open on ready conn ===\n"); fflush(stderr);
int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)2, &gh[2]); int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)2, &gh[2], NULL);
fprintf(stderr, " open#2 → %s\n", r == NCD_REUSED ? "NCD_REUSED" : "?"); fprintf(stderr, " open#2 → %s\n", r == NCD_REUSED ? "NCD_REUSED" : "?");
if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; } if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; }
uasync_call_soon(ua, NULL, t4); uasync_call_soon(ua, NULL, t4);
@ -135,7 +135,7 @@ static void t4(void* arg) {
(void)arg; g_phase = 4; (void)arg; g_phase = 4;
if (g_up[2] == 0) { uasync_set_timeout(ua, POLL_MS, NULL, (timeout_callback_t)t4, "t4"); return; } 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); fprintf(stderr, "\n=== P4: async cb OK, open h3 ===\n"); fflush(stderr);
int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)3, &gh[3]); int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)3, &gh[3], NULL);
fprintf(stderr, " open#3 → %s\n", r == NCD_REUSED ? "NCD_REUSED" : "?"); fprintf(stderr, " open#3 → %s\n", r == NCD_REUSED ? "NCD_REUSED" : "?");
if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; } if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; }
uasync_call_soon(ua, NULL, t5); uasync_call_soon(ua, NULL, t5);
@ -158,10 +158,10 @@ static void t5(void* arg) {
static void t6a(void* arg) { static void t6a(void* arg) {
(void)arg; g_phase = 16; (void)arg; g_phase = 16;
fprintf(stderr, "\n=== P6a: bidirectional — B opens handle to A ===\n"); fflush(stderr); 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]); int r = node_conn_direct_open(g_b, nid_a, ncd_cb_b, (void*)0, &gh_b[0], NULL);
if (r != NCD_REUSED) { fail("P6a: B open expected NCD_REUSED (incoming conn exists)"); return; } if (r != NCD_REUSED) { fail("P6a: B open expected NCD_REUSED (incoming conn exists)"); return; }
fprintf(stderr, " B open → NCD_REUSED\n"); fprintf(stderr, " B open → NCD_REUSED\n");
r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)4, &gh[4]); r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)4, &gh[4], NULL);
if (r != NCD_REUSED) { fail("P6a: A open(4) expected NCD_REUSED (conn alive)"); return; } 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); fprintf(stderr, " A open(4) → NCD_REUSED\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t6b); uasync_call_soon(ua, NULL, t6b);
@ -181,7 +181,7 @@ static void t6b(void* arg) {
static void t6c(void* arg) { static void t6c(void* arg) {
(void)arg; g_phase = 18; (void)arg; g_phase = 18;
fprintf(stderr, "\n=== P6c: KEEP_ALIVE round-trip, A re-opens ===\n"); fflush(stderr); 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]); int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)5, &gh[5], NULL);
if (r != NCD_REUSED) { fail("P6c: expected NCD_REUSED (KEEP_ALIVE saved conn)"); return; } 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); fprintf(stderr, " A re-open(5) → NCD_REUSED\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t6d); uasync_call_soon(ua, NULL, t6d);
@ -203,7 +203,7 @@ static void t6e(void* arg) {
(void)arg; g_phase = 20; (void)arg; g_phase = 20;
fprintf(stderr, "\n=== P6e: close h1 → fin_wait, immediately re-open ===\n"); fflush(stderr); fprintf(stderr, "\n=== P6e: close h1 → fin_wait, immediately re-open ===\n"); fflush(stderr);
node_conn_direct_close(gh[1]); gh[1] = NULL; 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]); int r = node_conn_direct_open(g_a, nid_b, ncd_cb, (void*)4, &gh[4], NULL);
if (r != NCD_REUSED) { fail("P6e: expected NCD_REUSED (entry found, fin_wait cleared)"); return; } if (r != NCD_REUSED) { fail("P6e: expected NCD_REUSED (entry found, fin_wait cleared)"); return; }
fprintf(stderr, " re-open → NCD_REUSED\n"); fflush(stderr); fprintf(stderr, " re-open → NCD_REUSED\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t6f); uasync_call_soon(ua, NULL, t6f);
@ -231,7 +231,7 @@ static void t7(void* arg) {
(void)arg; g_phase = 7; (void)arg; g_phase = 7;
fprintf(stderr, "\n=== P7: NCD_ERR unknown node ===\n"); fflush(stderr); fprintf(stderr, "\n=== P7: NCD_ERR unknown node ===\n"); fflush(stderr);
struct NODE_CONN_DIRECT* hx = NULL; struct NODE_CONN_DIRECT* hx = NULL;
int r = node_conn_direct_open(g_a, 0xDEADBEEF00000001ULL, ncd_cb, (void*)99, &hx); int r = node_conn_direct_open(g_a, 0xDEADBEEF00000001ULL, ncd_cb, (void*)99, &hx, NULL);
fprintf(stderr, " open(unknown) → %s\n", r == NCD_ERR ? "NCD_ERR" : "?"); fprintf(stderr, " open(unknown) → %s\n", r == NCD_ERR ? "NCD_ERR" : "?");
if (r != NCD_ERR) { fail("expected NCD_ERR"); return; } if (r != NCD_ERR) { fail("expected NCD_ERR"); return; }
if (hx != NULL) { fail("handle not NULL"); return; } if (hx != NULL) { fail("handle not NULL"); return; }
@ -261,7 +261,7 @@ static void t8(void* arg) {
memset((void*)g_tout, 0, sizeof(g_tout)); memset((void*)g_tout, 0, sizeof(g_tout));
g_a->etcp_connect_timeout_tb = SHORT_TO_TB; g_a->etcp_connect_timeout_tb = SHORT_TO_TB;
int r = node_conn_direct_open(g_a, fake_id, ncd_cb, (void*)0, &gh[0]); int r = node_conn_direct_open(g_a, fake_id, ncd_cb, (void*)0, &gh[0], NULL);
fprintf(stderr, " open(unreachable) → %s\n", r == NCD_NEW ? "NCD_NEW" : "?"); fprintf(stderr, " open(unreachable) → %s\n", r == NCD_NEW ? "NCD_NEW" : "?");
if (r != NCD_NEW) { fail("expected NCD_NEW"); return; } if (r != NCD_NEW) { fail("expected NCD_NEW"); return; }
@ -309,7 +309,7 @@ static int setup(void) {
static void cleanup(void) { test_unlink(ca); test_unlink(cb); test_rmdir(tdir); } static void cleanup(void) { test_unlink(ca); test_unlink(cb); test_rmdir(tdir); }
int main(void) { int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_config_init(); debug_set_level(DEBUG_LEVEL_TRACE);
utun_instance_set_tun_init_enabled(0); utun_instance_set_tun_init_enabled(0);
if (setup() != 0) return 1; if (setup() != 0) return 1;
ua = uasync_create(); ua = uasync_create();

Loading…
Cancel
Save