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
**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
```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 check.bat # Windows, запускает каждый .exe из tests/
@ -89,14 +91,7 @@ powershell check.bat # Windows, запускает каждый .exe
### Run Specific Test
```bash
cd tests/
./test_etcp_crypto
./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

31
src/transport_layer/node_conn_direct.h

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

8
src/transport_layer/secure_channel.c

@ -34,7 +34,7 @@ static void sc_init_random_seed(void)
}
// Конвертация 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;
for (size_t i = 0; i < binary_len; i++) {
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",
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");
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");
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) {
uint8_t peer_public_key[SC_PUBKEY_SIZE];
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");
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);
int sc_hex_to_binary(const char *hex_str, uint8_t *binary, size_t binary_len);
#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);
struct ETCP_SOCKET;
struct TCP_SOCKET;
struct NODE_CONN_DIRECT;
struct tun_if;
struct ETCP_BINDINGS;
struct ETCP_ROUTER_BINDINGS;
@ -64,6 +65,14 @@ struct conn_queue_entry {
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)
struct tcp_conn_entry {
uint64_t node_id; // index key
@ -178,6 +187,13 @@ struct UTUN_INSTANCE {
// NTP time synchronization
struct NTP_TIME ntp;
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

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);
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");
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");
if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; }
@ -125,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, 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" : "?");
if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; }
uasync_call_soon(ua, NULL, t4);
@ -135,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, 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" : "?");
if (r != NCD_REUSED) { fail("expected NCD_REUSED"); return; }
uasync_call_soon(ua, NULL, t5);
@ -158,10 +158,10 @@ static void t5(void* arg) {
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]);
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; }
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; }
fprintf(stderr, " A open(4) → NCD_REUSED\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t6b);
@ -181,7 +181,7 @@ static void t6b(void* arg) {
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]);
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; }
fprintf(stderr, " A re-open(5) → NCD_REUSED\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t6d);
@ -203,7 +203,7 @@ 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]);
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; }
fprintf(stderr, " re-open → NCD_REUSED\n"); fflush(stderr);
uasync_call_soon(ua, NULL, t6f);
@ -231,7 +231,7 @@ 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, 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" : "?");
if (r != NCD_ERR) { fail("expected NCD_ERR"); 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));
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" : "?");
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); }
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);
if (setup() != 0) return 1;
ua = uasync_create();

Loading…
Cancel
Save