From 3d04c37f56eed934255d2198d4e0a6a05f68b6cd Mon Sep 17 00:00:00 2001 From: Evgeny Date: Thu, 16 Jul 2026 19:04:00 +0300 Subject: [PATCH] stcp: add on_close callback to stcp_server_create/stcp_client_connect; member_sync: add join_ts/update_sig/update_ts fields, verify update_sig in apply; topo_node_sqlite: add join_ts/update_sig/update_ts to member_put, add member_get_join, add node_update_verified; chat_sync: include join_ts in join_msg, add update_sig/update_ts to CHANNEL_INFO_RESP; WELCOME verify join_sig; tests: use generated node_ids, reduce max ticks --- src/config_updater.c | 14 +- src/etcp.c | 3 + src/etcp_connect.c | 19 +++ src/etcp_connect.h | 73 ++++++++++ src/etcp_connections.c | 4 +- src/secure_channel.c | 10 ++ src/secure_channel.h | 2 + src/stcp_client.c | 4 +- src/stcp_client.h | 4 +- src/stcp_link.c | 4 +- src/stcp_server.c | 9 +- src/stcp_server.h | 6 +- src/topo_group.c | 2 +- src/topo_node_sqlite.c | 115 ++++++++++++--- src/topo_node_sqlite.h | 18 ++- src/utun_instance.c | 13 +- tests/bbr_integration/test_bbr_integration.c | 14 +- tests/test_bgp_route_exchange.c | 35 +++-- tests/test_bgp_triangle.c | 143 +++++++++--------- tests/test_conn_mgr.c | 21 +-- tests/test_etcp_connect.c | 21 +-- tests/test_etcp_router.c | 10 +- tests/test_etcp_router_reconnect.c | 21 +-- tests/test_icmp_proxy.c | 10 +- tests/test_ipv6_sockets.c | 4 +- tests/test_lwip_tcp.c | 26 +++- tests/test_nat_detection.c | 33 ++--- tests/test_nat_transport.c | 35 +++-- tests/test_route_ping.c | 19 ++- tests/test_socks_http_proxy.c | 4 +- tests/test_stcp.c | 37 +++-- tests/test_uasync_socket_race.c | 2 +- tests/test_udp_proxy.c | 10 +- tools/chatgui/transport/chat_core.c | 12 +- tools/chatgui/transport/chat_sync.c | 134 ++++++++++++----- tools/chatgui/transport/member_sync.c | 145 ++++++++++++------- tools/chatgui/transport/member_sync.h | 21 +-- tools/chatgui/transport/merkle_sync.h | 4 +- 38 files changed, 703 insertions(+), 358 deletions(-) create mode 100644 src/etcp_connect.h diff --git a/src/config_updater.c b/src/config_updater.c index c8da33f4..f6f5bee1 100644 --- a/src/config_updater.c +++ b/src/config_updater.c @@ -5,7 +5,6 @@ #include "secure_channel.h" #include "debug_config.h" #include "../lib/platform_compat.h" -#include "../lib/sha256.h" #include #include #include @@ -111,10 +110,10 @@ static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_cap char *opt_pos = find_option(*buf, *buf_len, option); if (opt_pos) { - // Determine if old line has trailing \n + // Determine end of old line (1 byte after \n or end of buffer) char *nl = strchr(opt_pos, '\n'); int has_trailing_nl = (nl != NULL); - char *line_end = nl ? nl : *buf + *buf_len; + char *line_end = nl ? nl + 1 : *buf + *buf_len; // Prepare new line char new_line[MAX_LINE_LEN]; @@ -127,7 +126,7 @@ static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_cap if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1; // Calculate length difference - size_t old_line_len = has_trailing_nl ? (line_end - opt_pos + 1) : (line_end - opt_pos); + size_t old_line_len = line_end - opt_pos; long len_diff = new_len - old_line_len; // Ensure buffer capacity @@ -143,7 +142,7 @@ static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_cap if (!opt_pos) return -1; nl = strchr(opt_pos, '\n'); has_trailing_nl = (nl != NULL); - line_end = nl ? nl : *buf + *buf_len; + line_end = nl ? nl + 1 : *buf + *buf_len; } // Move content and insert new line @@ -294,9 +293,8 @@ int config_ensure_keys_and_node_id(const char *filename) { global->my_public_key_hex[0] ? global->my_public_key_hex : "NULL", new_pub_key); } - // ── Step 3: derive node_id from privkey via SHA-256 ── - { uint8_t sha_hash[32]; SC_SHA256_CTX ctx; sc_sha256_init(&ctx); sc_sha256_update(&ctx, priv_bin, SC_PRIVKEY_SIZE); sc_sha256_final(&ctx, sha_hash); - memcpy(&new_node_id, sha_hash, 8); new_node_id &= 0x7FFFFFFFFFFFFFFFULL; } + // ── Step 3: derive node_id from privkey ── + new_node_id = sc_derive_node_id(priv_bin); if (!is_valid_node_id(global->my_node_id) || global->my_node_id != new_node_id) { need_node_id = 1; DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Node_id mismatch (or missing), fixing: config=%016llx derived=%016llx file=%s", diff --git a/src/etcp.c b/src/etcp.c index acbaa1c7..0772375e 100644 --- a/src/etcp.c +++ b/src/etcp.c @@ -1,6 +1,7 @@ // etcp.c - ETCP Protocol Implementation (refactored and expanded based on etcp_protocol.txt) #include "etcp.h" +#include "etcp_connect.h" #include "etcp_debug.h" #include "etcp_loadbalancer.h" #include "etcp_router.h" @@ -328,6 +329,8 @@ static void etcp_connection_free_resources(struct ETCP_CONN* etcp) { if (etcp->inflight_pool) { memory_pool_destroy(etcp->inflight_pool); etcp->inflight_pool = NULL; } if (etcp->io_pool) { memory_pool_destroy(etcp->io_pool); etcp->io_pool = NULL; } + etcp_connect_cancel_for_conn(etcp->instance, etcp); + u_free(etcp->name); u_free(etcp); } diff --git a/src/etcp_connect.c b/src/etcp_connect.c index 3f909126..d4068d7a 100644 --- a/src/etcp_connect.c +++ b/src/etcp_connect.c @@ -1,5 +1,6 @@ #include "etcp_api.h" #include "etcp.h" +#include "etcp_connect.h" #include "utun_instance.h" #include "topo_node.h" #include "stcp_link.h" @@ -284,3 +285,21 @@ int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_NODEQ* node, DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] started for node 0x%016llx", (unsigned long long)node_id); return 0; } + +void etcp_connect_cancel_for_conn(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn) { + if (!inst || !conn) return; + struct ETCP_CONNECT** pp = &inst->pending_connects; + while (*pp) { + struct ETCP_CONNECT* ctx = *pp; + if (ctx->conn == conn) { + *pp = ctx->next; + ctx->done = 1; + ctx->conn = NULL; + 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); + } else { + pp = &(*pp)->next; + } + } +} diff --git a/src/etcp_connect.h b/src/etcp_connect.h new file mode 100644 index 00000000..a7e0f9a0 --- /dev/null +++ b/src/etcp_connect.h @@ -0,0 +1,73 @@ +#ifndef ETCP_CONNECT_H +#define ETCP_CONNECT_H + +struct UTUN_INSTANCE; +struct ETCP_CONN; + +/* + * ============================================================================ + * Штатное закрытие исходящих соединений (etcp_connect) + * ============================================================================ + * + * Исходящее соединение создаётся через etcp_connect(): + * etcp_connect(inst, node, cb, arg, flags) + * ├── etcp_connection_create(inst, NULL) → conn (добавляется в inst->connections) + * ├── ctx = u_calloc(...); ctx->conn = conn + * ├── для TCP: ctx->tcp_link = stcp_link_connect(...) + * │ conn->transport_link = ctx->tcp_link + * └── ctx->next = inst->pending_connects; inst->pending_connects = ctx + * + * Пути завершения: + * + * 1. Таймаут установки (connect_initial_timeout_cb): + * stcp_link_close(tcp_link) → etcp_connection_close(conn) → connect_cancel(ctx) + * + * 2. Settle таймаут (connect_settle_timeout_cb): + * stcp_link_close(tcp_link) (если !tcp_ready) → connect_cancel(ctx) + * + * 3. Штатное закрытие через utun_instance_destroy(): + * utun_instance_destroy(inst) + * ├── etcp_connection_close(conn) для всех conn + * │ Фаза 1 (detach): закрыть линки, удалить из inst->connections, state=2 + * │ Фаза 2 (deferred): uasync_call_soon(ua, conn, etcp_connection_free_deferred) + * │ + * ├── while (immediate_queue_head) uasync_poll(ua, 0) // ждём завершения + * │ └── etcp_connection_free_resources(conn) + * │ ├── drain_and_free_queue(*) + * │ ├── etcp_connect_cancel_for_conn(inst, conn) ← эта функция + * │ └── u_free(etcp) + * │ + * └── memory_pool_destroy(pkt/ack/data) // теперь безопасно + * + * Закрытие tcp_link НЕ входит в ответственность cancel-функций. + * tcp_link закрывается ДО вызова connect_cancel / cancel_for_conn + * (в путях 1 и 2 явно; в пути 3 tcp_link закрыт ранее или утекает — pre-existing). + * + * ============================================================================ + */ + + +/* + * ============================================================================ + * etcp_connect_cancel_for_conn() + * ============================================================================ + * + * Аналог приватного connect_cancel(), но ищет ETCP_CONNECT ctx по совпадению + * ctx->conn == conn (а не по указателю на ctx). + * + * Делает: + * 1. Удаляет ctx из inst->pending_connects + * 2. ctx->done = 1; ctx->conn = NULL + * 3. uasync_cancel_timeout(initial_timer / settle_timer) + * 4. u_free(ctx) + * + * НЕ закрывает ctx->tcp_link (чужая ответственность). + * + * Вызывается ТОЛЬКО из etcp_connection_free_resources (фаза 2 deferred cleanup). + * НЕ вызывать из mainloop / uasync_poll callbacks — conn уже может быть в state=2. + * + * ============================================================================ + */ +void etcp_connect_cancel_for_conn(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn); + +#endif diff --git a/src/etcp_connections.c b/src/etcp_connections.c index e67039d2..a0efc124 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -329,7 +329,7 @@ static void keepalive_timer_cb(void* arg) { // Adaptive keepalive period (only if adaptive enabled) if (link->pkt_sent_since_keepalive) - link->ka_period_ms = KA_PERIOD_MIN_MS; + link->ka_period_ms = (uint16_t)link->keepalive_interval; else if (link->etcp->instance && link->etcp->instance->config && link->etcp->instance->config->global.keepalive_adaptive) { uint32_t next = (uint32_t)link->ka_period_ms * 105 / 100 + 1; @@ -791,7 +791,7 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn if (link->keepalive_interval < 10) link->keepalive_interval = 10; link->keepalive_sent_count = 0; link->keepalive_recv_count = 0; - link->ka_period_ms = KA_PERIOD_MIN_MS; + link->ka_period_ms = (uint16_t)link->keepalive_interval; link->inflight_lim_bytes = link->mtu * 4; // BBR init_cwnd (~4 packets) link->bandwidth = 10000; // начальная оценка 10 Mbps для шейпера link->burst_id = 0; diff --git a/src/secure_channel.c b/src/secure_channel.c index 120211f5..f5374b58 100644 --- a/src/secure_channel.c +++ b/src/secure_channel.c @@ -407,6 +407,16 @@ sc_status_t sc_derive_ed25519_pubkey(const uint8_t *x25519_privkey, uint8_t *ed2 return SC_OK; } +uint64_t sc_derive_node_id(const uint8_t *private_key) { + if (!private_key) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_derive_node_id: NULL private_key"); return 0; } + uint8_t sha_hash[SC_HASH_SIZE]; SC_SHA256_CTX ctx; + sc_sha256_init(&ctx); sc_sha256_update(&ctx, private_key, SC_PRIVKEY_SIZE); sc_sha256_final(&ctx, sha_hash); + uint64_t node_id; + memcpy(&node_id, sha_hash, 8); + node_id &= 0x7FFFFFFFFFFFFFFFULL; + return node_id; +} + sc_status_t sc_stream_sign_init(sc_context_t *ctx, struct sc_stream_sign_state *state) { if (!ctx || !state) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; } if (!ctx->initialized || !ctx->pk) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "ctx not initialized"); return SC_ERR_NOT_INITIALIZED; } diff --git a/src/secure_channel.h b/src/secure_channel.h index 342e846e..e94bee0f 100644 --- a/src/secure_channel.h +++ b/src/secure_channel.h @@ -111,6 +111,8 @@ void sc_stream_sign_cleanup(struct sc_stream_sign_state *state); sc_status_t sc_derive_ed25519_pubkey(const uint8_t *x25519_privkey, uint8_t *ed25519_pubkey_out); +uint64_t sc_derive_node_id(const uint8_t *private_key); + #ifdef __cplusplus } diff --git a/src/stcp_client.c b/src/stcp_client.c index 1dab926d..0dee38f6 100644 --- a/src/stcp_client.c +++ b/src/stcp_client.c @@ -245,7 +245,8 @@ static void client_connect_write_cb(socket_t sock, void *arg) { struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uint16_t port, struct SC_MYKEYS *keys, const uint8_t *peer_pubkey, - stcp_ready_cb ready_cb, void *arg) { + stcp_ready_cb ready_cb, void *arg, + stcp_close_cb close_cb, void *close_arg) { if (!ua || !addr || !keys || !peer_pubkey || !ready_cb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "invalid args"); return NULL; } struct stcp_client *cli = u_calloc(1, sizeof(struct stcp_client)); if (!cli) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "calloc failed"); return NULL; } @@ -254,6 +255,7 @@ struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uin struct stcp_conn *c = &cli->conn; c->ua = ua; c->state = STCP_STATE_INIT; c->is_server = 0; c->my_keys = *keys; c->on_ready = ready_cb; c->ready_arg = arg; + c->on_close = close_cb; c->close_arg = close_arg; c->tx_cb = client_tx_queue_cb; c->sock = socket(AF_INET, SOCK_STREAM, 0); diff --git a/src/stcp_client.h b/src/stcp_client.h index deb40878..10c4333e 100644 --- a/src/stcp_client.h +++ b/src/stcp_client.h @@ -10,10 +10,12 @@ extern "C" { #include "stcp.h" typedef void (*stcp_ready_cb)(struct stcp_conn *conn, void *arg); +typedef void (*stcp_close_cb)(struct stcp_conn *conn, int err, void *arg); struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uint16_t port, struct SC_MYKEYS *keys, const uint8_t *peer_pubkey, - stcp_ready_cb ready_cb, void *arg); + stcp_ready_cb ready_cb, void *arg, + stcp_close_cb close_cb, void *close_arg); void stcp_client_destroy(struct stcp_client *cli); struct stcp_conn *stcp_client_get_conn(struct stcp_client *cli); diff --git a/src/stcp_link.c b/src/stcp_link.c index 840f7d46..471ea7d1 100644 --- a/src/stcp_link.c +++ b/src/stcp_link.c @@ -117,7 +117,7 @@ struct stcp_server *stcp_server_listen(struct stcp_link_config *cfg, uint16_t po ss->cfg = *cfg; ss->on_link = on_link; ss->on_link_arg = arg; - ss->srv = stcp_server_create(cfg->ua, port, cfg->my_keys, server_accept_cb, ss); + ss->srv = stcp_server_create(cfg->ua, port, cfg->my_keys, server_accept_cb, ss, NULL, NULL); if (!ss->srv) { u_free(ss); return NULL; } DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "port=%u", port); return ss; @@ -156,7 +156,7 @@ struct stcp_link *stcp_link_connect(struct stcp_link_config *cfg) { } link->cli = stcp_client_connect(cfg->ua, addr_str, port, cfg->my_keys, link->peer_pubkey, - client_ready_cb, link); + client_ready_cb, link, NULL, NULL); if (!link->cli) { u_free(link); return NULL; } link->etcp_conn.instance = cfg->inst; diff --git a/src/stcp_server.c b/src/stcp_server.c index e39c907d..5cdcca45 100644 --- a/src/stcp_server.c +++ b/src/stcp_server.c @@ -25,6 +25,8 @@ struct stcp_server { struct SC_MYKEYS my_keys; stcp_connect_cb connect_cb; void *cb_arg; + stcp_close_cb close_cb; + void *close_arg; }; static void server_accept_cb(socket_t sock, void *arg); @@ -333,13 +335,16 @@ static void server_accept_cb(socket_t listen_sock, void *arg) { c->my_keys = srv->my_keys; c->on_ready = srv->connect_cb; c->ready_arg = srv->cb_arg; + c->on_close = srv->close_cb; + c->close_arg = srv->close_arg; c->socket_id = uasync_add_socket_t(srv->ua, cli_sock, server_conn_read_cb, server_conn_write_cb, NULL, c); DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_server: accepted connection fd=%d", (int)cli_sock); } struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port, struct SC_MYKEYS *keys, - stcp_connect_cb connect_cb, void *arg) { + stcp_connect_cb connect_cb, void *arg, + stcp_close_cb close_cb, void *close_arg) { if (!ua || !keys || !connect_cb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "invalid args"); return NULL; } struct stcp_server *srv = u_calloc(1, sizeof(struct stcp_server)); if (!srv) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "calloc failed"); return NULL; } @@ -347,6 +352,8 @@ struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port, srv->my_keys = *keys; srv->connect_cb = connect_cb; srv->cb_arg = arg; + srv->close_cb = close_cb; + srv->close_arg = close_arg; srv->listen_sock = socket(AF_INET, SOCK_STREAM, 0); if (srv->listen_sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socket failed"); u_free(srv); return NULL; } diff --git a/src/stcp_server.h b/src/stcp_server.h index a8dfba66..8b1d076a 100644 --- a/src/stcp_server.h +++ b/src/stcp_server.h @@ -10,10 +10,12 @@ extern "C" { #include "stcp.h" typedef void (*stcp_connect_cb)(struct stcp_conn *conn, void *arg); +typedef void (*stcp_close_cb)(struct stcp_conn *conn, int err, void *arg); struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port, - struct SC_MYKEYS *keys, - stcp_connect_cb connect_cb, void *arg); + struct SC_MYKEYS *keys, + stcp_connect_cb connect_cb, void *arg, + stcp_close_cb close_cb, void *close_arg); void stcp_server_destroy(struct stcp_server *srv); diff --git a/src/topo_group.c b/src/topo_group.c index 1d8f57e1..45f08a21 100644 --- a/src/topo_group.c +++ b/src/topo_group.c @@ -643,7 +643,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from if (sdb) { topo_node_sqlite_node_put(sdb, nodeinfo1); if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->channel_id[0]) { - topo_node_sqlite_member_put(sdb, group->channel_id, node_id, NULL, NULL); + topo_node_sqlite_member_put(sdb, group->channel_id, node_id, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL); DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "SQLite member_put: ch=%s node=%016llx", group->channel_id, (unsigned long long)node_id); } } diff --git a/src/topo_node_sqlite.c b/src/topo_node_sqlite.c index 5a2f69be..d0b89f79 100644 --- a/src/topo_node_sqlite.c +++ b/src/topo_node_sqlite.c @@ -5,6 +5,7 @@ #include #define PEERS_JOIN_SIG_SIZE 64 +#define PEERS_JOIN_TS_SIZE 8 static void sanitize_ch_id(const char* ch_id, char* out, size_t out_sz) { size_t i = 0; @@ -35,6 +36,7 @@ int topo_node_sqlite_init(sqlite3* db) { " ed25519_pubkey BLOB," " last_seen_at INTEGER," " online INTEGER DEFAULT 0," + " update_ts INTEGER DEFAULT 0," " created_at INTEGER DEFAULT (unixepoch())" ");" @@ -147,9 +149,15 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id, char sql[512]; snprintf(sql, sizeof(sql), "CREATE TABLE IF NOT EXISTS \"%s\" (" - " node_id INTEGER NOT NULL," - " join_sig BLOB NOT NULL," - " creator_sig BLOB," + " node_id INTEGER NOT NULL," + " x25519_pubkey BLOB NOT NULL," + " ed25519_pubkey BLOB NOT NULL," + " join_sig BLOB NOT NULL," + " join_ts INTEGER NOT NULL," + " update_sig BLOB," + " update_ts INTEGER DEFAULT 0," + " name TEXT NOT NULL DEFAULT ''," + " creator_sig BLOB," " comment TEXT," " joined_at INTEGER DEFAULT (unixepoch())," " PRIMARY KEY (node_id))", peers_tbl); @@ -178,23 +186,71 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id, } int topo_node_sqlite_member_put(sqlite3* db, const char* channel_id, uint64_t node_id, - const uint8_t* join_sig, const uint8_t* creator_sig) { - if (!db || !channel_id) return -1; + const uint8_t* join_sig, uint64_t join_ts, + const uint8_t* update_sig, uint64_t update_ts, + const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey, const char* name, + const uint8_t* creator_sig) { + if (!db || !channel_id || !x25519_pubkey || !ed25519_pubkey) return -1; char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl)); char sql[256]; snprintf(sql, sizeof(sql), - "INSERT OR REPLACE INTO \"%s\"(node_id, join_sig, creator_sig) VALUES(?,?,?)", peers_tbl); + "INSERT OR REPLACE INTO \"%s\"(node_id, x25519_pubkey, ed25519_pubkey," + " join_sig, join_ts, update_sig, update_ts, name, creator_sig)" + " VALUES(?,?,?,?,?,?,?,?,?)", peers_tbl); sqlite3_stmt* stmt = NULL; if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1; sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id); - if (join_sig) sqlite3_bind_blob(stmt, 2, join_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); - else { static const unsigned char zsig[PEERS_JOIN_SIG_SIZE] = {0}; sqlite3_bind_blob(stmt, 2, zsig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); } - if (creator_sig) sqlite3_bind_blob(stmt, 3, creator_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); - else sqlite3_bind_null(stmt, 3); + sqlite3_bind_blob(stmt, 2, x25519_pubkey, 32, SQLITE_STATIC); + sqlite3_bind_blob(stmt, 3, ed25519_pubkey, 32, SQLITE_STATIC); + if (join_sig) sqlite3_bind_blob(stmt, 4, join_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); + else { static const unsigned char zsig[PEERS_JOIN_SIG_SIZE] = {0}; sqlite3_bind_blob(stmt, 4, zsig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); } + sqlite3_bind_int64(stmt, 5, (sqlite3_int64)join_ts); + if (update_sig) sqlite3_bind_blob(stmt, 6, update_sig, PEERS_UPDATE_SIG_SIZE, SQLITE_STATIC); + else sqlite3_bind_null(stmt, 6); + sqlite3_bind_int64(stmt, 7, (sqlite3_int64)update_ts); + sqlite3_bind_text(stmt, 8, name ? name : "", -1, SQLITE_STATIC); + if (creator_sig) sqlite3_bind_blob(stmt, 9, creator_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); + else sqlite3_bind_null(stmt, 9); int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE ? 0 : -1; } +int topo_node_sqlite_node_update_verified(sqlite3* db, uint64_t node_id, + const char* name, const uint8_t* x25519, const uint8_t* ed25519, uint64_t update_ts) { + if (!db || !name || !x25519 || !ed25519) return -1; + sqlite3_stmt* stmt = NULL; + if (sqlite3_prepare_v2(db, + "UPDATE nodes SET name=?, x25519_pubkey=?, ed25519_pubkey=?, update_ts=?" + " WHERE node_id=? AND (update_ts IS NULL OR update_ts < ?)", -1, &stmt, NULL) != SQLITE_OK) return -1; + sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC); + sqlite3_bind_blob(stmt, 2, x25519, 32, SQLITE_STATIC); + sqlite3_bind_blob(stmt, 3, ed25519, 32, SQLITE_STATIC); + sqlite3_bind_int64(stmt, 4, (sqlite3_int64)update_ts); + sqlite3_bind_int64(stmt, 5, (sqlite3_int64)node_id); + sqlite3_bind_int64(stmt, 6, (sqlite3_int64)update_ts); + sqlite3_step(stmt); + sqlite3_finalize(stmt); + return 0; +} + +int topo_node_sqlite_member_get_join(sqlite3* db, const char* channel_id, uint64_t node_id, + uint8_t* join_sig_out, uint64_t* join_ts_out) { + if (!db || !channel_id || !join_sig_out || !join_ts_out) return -1; + char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl)); + char sql[256]; + snprintf(sql, sizeof(sql), "SELECT join_sig, join_ts FROM \"%s\" WHERE node_id=?", peers_tbl); + sqlite3_stmt* stmt = NULL; + if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1; + sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id); + int rc = -1; + if (sqlite3_step(stmt) == SQLITE_ROW) { + const uint8_t* sig = (const uint8_t*)sqlite3_column_blob(stmt, 0); + if (sig) { memcpy(join_sig_out, sig, PEERS_JOIN_SIG_SIZE); *join_ts_out = (uint64_t)sqlite3_column_int64(stmt, 1); rc = 0; } + } + sqlite3_finalize(stmt); + return rc; +} + int topo_node_sqlite_member_del(sqlite3* db, const char* channel_id, uint64_t node_id) { if (!db || !channel_id) return -1; char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl)); @@ -236,11 +292,11 @@ int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id, char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl)); sqlite3_stmt* stmt = NULL; - char sql[256]; + char sql[512]; snprintf(sql, sizeof(sql), - "SELECT p.node_id, p.join_sig, p.creator_sig," - " n.x25519_pubkey, n.ed25519_pubkey" - " FROM \"%s\" p LEFT JOIN nodes n ON p.node_id = n.node_id", peers_tbl); + "SELECT node_id, x25519_pubkey, ed25519_pubkey," + " join_sig, join_ts, update_sig, update_ts, name" + " FROM \"%s\" ORDER BY node_id ASC", peers_tbl); if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1; size_t off = 0; @@ -250,10 +306,15 @@ int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id, while (sqlite3_step(stmt) == SQLITE_ROW) { uint64_t node_id = (uint64_t)sqlite3_column_int64(stmt, 0); - const uint8_t* join_sig = (const uint8_t*)sqlite3_column_blob(stmt, 1); - const uint8_t* x25519 = (const uint8_t*)sqlite3_column_blob(stmt, 3); - const uint8_t* ed_pub = (const uint8_t*)sqlite3_column_blob(stmt, 4); - if (!join_sig || !x25519 || !ed_pub) continue; + const uint8_t* x25519 = (const uint8_t*)sqlite3_column_blob(stmt, 1); + const uint8_t* ed_pub = (const uint8_t*)sqlite3_column_blob(stmt, 2); + const uint8_t* join_sig = (const uint8_t*)sqlite3_column_blob(stmt, 3); + uint64_t join_ts = (uint64_t)sqlite3_column_int64(stmt, 4); + const uint8_t* update_sig = (const uint8_t*)sqlite3_column_blob(stmt, 5); + uint64_t update_ts = (uint64_t)sqlite3_column_int64(stmt, 6); + const char* pname = (const char*)sqlite3_column_text(stmt, 7); + if (!x25519 || !ed_pub) continue; + uint8_t nl = pname ? (uint8_t)strnlen(pname, 255) : 0; sqlite3_stmt* ast = NULL; snprintf(sql, sizeof(sql), @@ -280,7 +341,7 @@ int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id, sqlite3_finalize(ast); if (addr_cnt == 0) continue; - size_t peer_needed = 8 + 32 + 32 + 64 + 1; + size_t peer_needed = 8 + 32 + 32 + 1 + 64 + 8 + 64 + 8 + 1 + (size_t)nl + 1; size_t addr_sz = off - addr_start; if (off + peer_needed > buf_sz) { sqlite3_finalize(stmt); return -2; } memmove(buf + addr_start + peer_needed, buf + addr_start, addr_sz); @@ -288,7 +349,21 @@ int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id, memcpy(buf + off, &node_id, 8); off += 8; memcpy(buf + off, x25519, 32); off += 32; memcpy(buf + off, ed_pub, 32); off += 32; - memcpy(buf + off, join_sig, 64); off += 64; + uint8_t flags = (join_sig && join_ts) ? PEERS_FLAG_HAS_JOIN : 0; + buf[off++] = flags; + if (flags & PEERS_FLAG_HAS_JOIN) { + memcpy(buf + off, join_sig, 64); off += 64; + memcpy(buf + off, &join_ts, 8); off += 8; + } + if (update_sig && update_ts) { + memcpy(buf + off, update_sig, 64); off += 64; + memcpy(buf + off, &update_ts, 8); off += 8; + } else { + memset(buf + off, 0, 64); off += 64; + uint64_t z = 0; memcpy(buf + off, &z, 8); off += 8; + } + buf[off++] = nl; + if (nl) { memcpy(buf + off, pname, nl); off += nl; } buf[off++] = addr_cnt; off += addr_sz; (*cnt_ptr)++; diff --git a/src/topo_node_sqlite.h b/src/topo_node_sqlite.h index ac6b886e..13c691e7 100644 --- a/src/topo_node_sqlite.h +++ b/src/topo_node_sqlite.h @@ -7,7 +7,12 @@ struct TOPO_NODEQ; -#define PEERS_JOIN_SIG_SIZE 64 +#define PEERS_JOIN_SIG_SIZE 64 +#define PEERS_JOIN_TS_SIZE 8 +#define PEERS_UPDATE_SIG_SIZE 64 + +/* flags byte in wire format */ +#define PEERS_FLAG_HAS_JOIN 0x01 int topo_node_sqlite_init(sqlite3* db); @@ -20,10 +25,19 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id, const uint8_t* signature); int topo_node_sqlite_member_put(sqlite3* db, const char* channel_id, uint64_t node_id, - const uint8_t* join_sig, const uint8_t* creator_sig); + const uint8_t* join_sig, uint64_t join_ts, + const uint8_t* update_sig, uint64_t update_ts, + const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey, const char* name, + const uint8_t* creator_sig); + +int topo_node_sqlite_member_get_join(sqlite3* db, const char* channel_id, uint64_t node_id, + uint8_t* join_sig_out, uint64_t* join_ts_out); int topo_node_sqlite_member_del(sqlite3* db, const char* channel_id, uint64_t node_id); +int topo_node_sqlite_node_update_verified(sqlite3* db, uint64_t node_id, + const char* name, const uint8_t* x25519, const uint8_t* ed25519, uint64_t update_ts); + int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id, char* name_out, int name_sz, int* is_dm, uint64_t* owner_node_id, uint8_t* x25519_pub, uint8_t* ed25519_pub, uint8_t* signature); diff --git a/src/utun_instance.c b/src/utun_instance.c index a01a93a5..78d93b63 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -92,13 +92,7 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u // Derive node_id from privkey if not set in config if (!instance->node_id) { - uint8_t sha_hash[32]; - SC_SHA256_CTX ctx; - sc_sha256_init(&ctx); - sc_sha256_update(&ctx, instance->my_keys.private_key, SC_PRIVKEY_SIZE); - sc_sha256_final(&ctx, sha_hash); - memcpy(&instance->node_id, sha_hash, 8); - instance->node_id &= 0x7FFFFFFFFFFFFFFFULL; + instance->node_id = sc_derive_node_id(instance->my_keys.private_key); DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "node_id derived from privkey: %016llx", (unsigned long long)instance->node_id); } @@ -428,6 +422,11 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) { } queue_free(instance->connections); instance->connections = NULL; DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP connections cleanup complete"); + + // Wait for all deferred callbacks (etcp_connection_free_deferred) + // to finish before destroying pools + while (instance->ua && instance->ua->immediate_queue_head) + uasync_poll(instance->ua, 0); struct PING_CONTEXT* p = instance->pending_pings; while (p) { diff --git a/tests/bbr_integration/test_bbr_integration.c b/tests/bbr_integration/test_bbr_integration.c index f546b107..f1641f59 100644 --- a/tests/bbr_integration/test_bbr_integration.c +++ b/tests/bbr_integration/test_bbr_integration.c @@ -3,7 +3,7 @@ * @brief BBR integration test — max-speed traffic under emulated network conditions * * Network: 50ms delay, 5% loss, 1 Mbit shaper, ~100KB queue - * Duration: 10 seconds, metrics every 100ms + * Duration: 3.5 seconds, metrics every 100ms * * Architecture (single uasync, single thread): * Sender(client:20000) → Dummynet(:20001) → Receiver(server:20002) @@ -43,7 +43,7 @@ #define CLI_PORT 21000 #define PAYLOAD_SIZE 1200 -#define TEST_DURATION_MS 5000 +#define TEST_DURATION_MS 3500 #define METRICS_TB 1000 /* 100ms in 0.1ms units */ #define SEND_TIMER_TB 1 /* 0.1ms re-schedule */ #define BURST_MAX 64 @@ -117,6 +117,8 @@ static struct UTUN_INSTANCE* create_instance(struct UASYNC* u, uint64_t node_id, inst->data_pool = memory_pool_init(PACKET_DATA_SIZE, "data_pool"); inst->pkt_pool = memory_pool_init(sizeof(struct ETCP_DGRAM) + PACKET_DATA_SIZE, "pkt_pool"); if (!inst->ack_pool || !inst->data_pool || !inst->pkt_pool) { u_free(inst); return NULL; } + inst->connections = queue_new(u, 256, 0, 8, "connections"); + if (!inst->connections) { u_free(inst); return NULL; } struct utun_config* cfg = u_calloc(1, sizeof(*cfg)); if (!cfg) { u_free(inst); return NULL; } strncpy(cfg->global.my_public_key_hex, pub_hex, MAX_KEY_LEN - 1); @@ -382,7 +384,7 @@ int main(void) { printf("Waiting for connection...\n"); struct ETCP_CONN* conn = NULL; uint64_t t0 = now_us(); - while ((now_us() - t0) < 10000000ULL) { + while ((now_us() - t0) < 5000000ULL) { uasync_poll(ctx.ua, 1); if (ctx.sender->connections && ctx.sender->connections->head) conn = ((struct conn_queue_entry*)ctx.sender->connections->head->data)->conn; @@ -393,7 +395,7 @@ int main(void) { } printf("Connection established, stabilizing...\n"); t0 = now_us(); - while ((now_us() - t0) < 500000ULL) uasync_poll(ctx.ua, 1); + while ((now_us() - t0) < 100000ULL) uasync_poll(ctx.ua, 1); if (conn && conn->normalizer && conn->normalizer->input) { queue_set_threshold(conn->normalizer->input, 0, 0); @@ -401,7 +403,7 @@ int main(void) { } /* Start test */ - printf("\n=== Starting traffic (%d seconds) ===\n\n", TEST_DURATION_MS / 1000); + printf("\n=== Starting traffic (%.1f seconds) ===\n\n", TEST_DURATION_MS / 1000.0); ctx.log_file = fopen("test_bbr_integration.log", "w"); if (ctx.log_file) fprintf(ctx.log_file, "# BBR Integration Test — %ums delay %u%% loss %ukbps\n\n", @@ -444,7 +446,7 @@ int main(void) { printf("\nDraining...\n"); ctx.test_done = 1; t0 = now_us(); - while ((now_us() - t0) < 2000000ULL) uasync_poll(ctx.ua, 10); + while ((now_us() - t0) < 500000ULL) uasync_poll(ctx.ua, 10); print_metrics(&ctx); /* Final summary */ diff --git a/tests/test_bgp_route_exchange.c b/tests/test_bgp_route_exchange.c index 0bf4a9db..30859238 100644 --- a/tests/test_bgp_route_exchange.c +++ b/tests/test_bgp_route_exchange.c @@ -51,9 +51,9 @@ #define PHASE_TIMEOUT_TB 100000 // 10s per phase #define POLL_INTERVAL_MS 5 -#define NODE_ID_A 0x1111111111111111ULL -#define NODE_ID_B 0x2222222222222222ULL -#define NODE_ID_C 0x3333333333333333ULL +static uint64_t g_node_id_a = 0; +static uint64_t g_node_id_b = 0; +static uint64_t g_node_id_c = 0; static struct UTUN_INSTANCE* inst_a = NULL; static struct UTUN_INSTANCE* inst_b = NULL; @@ -104,9 +104,9 @@ static int create_temp_configs(void) { // Config C: server only, no keys (config_ensure_keys_and_node_id adds them) if (write_file(config_c, "[global]\n" - "my_node_id=0x3333333333333333\n" "tun_ip=10.100.0.3/24\n" "tun_ifname=tun102\n" + "keepalive_interval=50\n" "keepalive_adaptive=0\n" "\n" "[routing]\n" @@ -126,9 +126,9 @@ static int create_temp_configs(void) { // Config B: server for A + client to C, needs C's pubkey if (write_file(config_b, "[global]\n" - "my_node_id=0x2222222222222222\n" "tun_ip=10.100.0.2/24\n" "tun_ifname=tun101\n" + "keepalive_interval=50\n" "keepalive_adaptive=0\n" "\n" "[routing]\n" @@ -158,9 +158,9 @@ static int create_temp_configs(void) { // Config A: two servers + client to B with 2 links, needs B's pubkey if (write_file(config_a, "[global]\n" - "my_node_id=0x1111111111111111\n" "tun_ip=10.100.0.1/24\n" "tun_ifname=tun100\n" + "keepalive_interval=50\n" "keepalive_adaptive=0\n" "\n" "[routing]\n" @@ -211,11 +211,10 @@ static int count_initialized_links(struct UTUN_INSTANCE* inst) { static int check_learned_route(struct UTUN_INSTANCE* inst, uint32_t network, uint8_t prefix_len, uint64_t node_id) { if (!inst || !inst->rt) return 0; - uint64_t expected_be = htobe64(node_id); for (size_t i = 0; i < inst->rt->count; i++) { struct ROUTE_ENTRY* e = &inst->rt->entries[i]; if (e->network == network && e->prefix_length == prefix_len && - e->v_node_info && e->v_node_info->node->node_id == expected_be) return 1; + e->v_node_info && e->v_node_info->node->node_id == node_id) return 1; } return 0; } @@ -277,16 +276,16 @@ static int cond_all_links_init(void) { } static int cond_bgp_a_to_c(void) { - return check_learned_route(inst_a, ntohl(inet_addr("192.168.30.0")), 24, NODE_ID_C); + return check_learned_route(inst_a, ntohl(inet_addr("192.168.30.0")), 24, g_node_id_c); } static int cond_bgp_c_to_a(void) { - return check_learned_route(inst_c, ntohl(inet_addr("192.168.10.0")), 24, NODE_ID_A); + return check_learned_route(inst_c, ntohl(inet_addr("192.168.10.0")), 24, g_node_id_a); } static int cond_bgp_a_to_c_gone(void) { - return !check_learned_route(inst_a, ntohl(inet_addr("192.168.30.0")), 24, NODE_ID_C); + return !check_learned_route(inst_a, ntohl(inet_addr("192.168.30.0")), 24, g_node_id_c); } static int cond_bgp_c_to_a_gone(void) { - return !check_learned_route(inst_c, ntohl(inet_addr("192.168.10.0")), 24, NODE_ID_A); + return !check_learned_route(inst_c, ntohl(inet_addr("192.168.10.0")), 24, g_node_id_a); } // -------------------- Main -------------------- @@ -311,6 +310,8 @@ int main(void) { ASSERT(utun_instance_init(inst_b) == 0, "inst_b init"); ASSERT(utun_instance_init(inst_c) == 0, "inst_c init"); + g_node_id_a = inst_a->node_id; g_node_id_b = inst_b->node_id; g_node_id_c = inst_c->node_id; + // Find links for dummynet filters (client links: idx 0,1 on A, idx 0 on B) struct ETCP_LINK* ab_link1 = find_client_link(inst_a, 0); struct ETCP_LINK* ab_link2 = find_client_link(inst_a, 1); @@ -335,10 +336,12 @@ int main(void) { PHASE("2: Kill one A↔B link"); dummynet_filter_attach(df_ab1, ab_link1); dummynet_filter_set_loss(df_ab1, 1000); - // Wait for keepalive timeout + margin, then verify routes survive (link2 still up) - for (int i = 0; i < 600 && test_phase == 0; i++) uasync_poll(ua, POLL_INTERVAL_MS); // 3s - ASSERT(check_learned_route(inst_a, ntohl(inet_addr("192.168.30.0")), 24, NODE_ID_C), "routes lost after 1 link down"); - ASSERT(check_learned_route(inst_c, ntohl(inet_addr("192.168.10.0")), 24, NODE_ID_A), "routes lost C side"); + // Wait for link-down detection by keepalive, then verify routes survive (link2 still up) + for (int i = 0; i < 200 && test_phase == 0 && ab_link1->link_status; i++) uasync_poll(ua, POLL_INTERVAL_MS); + // Small margin after link-down before checks + for (int i = 0; i < 40 && test_phase == 0; i++) uasync_poll(ua, POLL_INTERVAL_MS); + ASSERT(check_learned_route(inst_a, ntohl(inet_addr("192.168.30.0")), 24, g_node_id_c), "routes lost after 1 link down"); + ASSERT(check_learned_route(inst_c, ntohl(inet_addr("192.168.10.0")), 24, g_node_id_a), "routes lost C side"); // --- Phase 3: Restore the link --- PHASE("3: Restore one A↔B link"); diff --git a/tests/test_bgp_triangle.c b/tests/test_bgp_triangle.c index 01eceb35..e258d74c 100644 --- a/tests/test_bgp_triangle.c +++ b/tests/test_bgp_triangle.c @@ -56,11 +56,11 @@ #define PHASE_TIMEOUT_TB 100000 #define POLL_INTERVAL_MS 5 -#define NODE_ID_A 0x1111111111111111ULL -#define NODE_ID_B 0x2222222222222222ULL -#define NODE_ID_C 0x3333333333333333ULL -#define NODE_ID_D 0x4444444444444444ULL -#define NODE_ID_E 0x5555555555555555ULL +static uint64_t g_node_id_a = 0; +static uint64_t g_node_id_b = 0; +static uint64_t g_node_id_c = 0; +static uint64_t g_node_id_d = 0; +static uint64_t g_node_id_e = 0; static struct UTUN_INSTANCE* inst_a = NULL; static struct UTUN_INSTANCE* inst_b = NULL; @@ -91,7 +91,6 @@ struct ncfg { static void build_node_config(char* buf, size_t size, const struct ncfg* c) { int off = snprintf(buf, size, "[global]\n" - "my_node_id=0x%llx\n" "my_private_key=%s\n" "my_public_key=%s\n" "tun_ip=%s\n" @@ -101,7 +100,7 @@ static void build_node_config(char* buf, size_t size, const struct ncfg* c) { "[routing]\n" "my_subnet=%s\n" "\n", - (unsigned long long)c->node_id, c->priv_hex, c->pub_hex, c->tun_ip, c->my_subnet); + c->priv_hex, c->pub_hex, c->tun_ip, c->my_subnet); 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); @@ -152,11 +151,10 @@ static int node_version(struct UTUN_INSTANCE* inst, uint64_t node_id) { static int route_count_for_node(struct UTUN_INSTANCE* inst, uint64_t node_id) { if (!inst || !inst->rt) return 0; - uint64_t expected_be = htobe64(node_id); int cnt = 0; for (size_t i = 0; i < inst->rt->count; i++) if (inst->rt->entries[i].v_node_info && - inst->rt->entries[i].v_node_info->node->node_id == expected_be) cnt++; + inst->rt->entries[i].v_node_info->node->node_id == node_id) cnt++; return cnt; } @@ -173,16 +171,14 @@ static int count_initialized_links(struct UTUN_INSTANCE* inst) { return n; } -static struct ETCP_LINK* find_client_link(struct UTUN_INSTANCE* inst, int idx) { +static struct ETCP_LINK* find_client_link(struct UTUN_INSTANCE* inst, const char* conn_name) { if (!inst || !inst->connections) return NULL; struct ll_entry* entry = inst->connections->head; while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data; + if (!ce->conn->name || strcmp(ce->conn->name, conn_name) != 0) { entry = entry->next; continue; } struct ETCP_LINK* l = ce->conn->links; - while (l) { - if (l->is_server == 0) { if (idx == 0) return l; idx--; } - l = l->next; - } + while (l) { if (l->is_server == 0) return l; l = l->next; } entry = entry->next; } return NULL; @@ -229,26 +225,26 @@ static int cond_links_init(void) { count_initialized_links(inst_e) >= 1; } -static int cond_d_in_a(void) { return peer_in_nodes(inst_a, NODE_ID_D); } -static int cond_d_gone_a(void) { return !peer_in_nodes(inst_a, NODE_ID_D); } -static int cond_e_in_a(void) { return peer_in_nodes(inst_a, NODE_ID_E); } -static int cond_e_gone_a(void) { return !peer_in_nodes(inst_a, NODE_ID_E); } -static int cond_c_in_a(void) { return peer_in_nodes(inst_a, NODE_ID_C); } -static int cond_c_gone_a(void) { return !peer_in_nodes(inst_a, NODE_ID_C); } +static int cond_d_in_a(void) { return peer_in_nodes(inst_a, g_node_id_d); } +static int cond_d_gone_a(void) { return !peer_in_nodes(inst_a, g_node_id_d); } +static int cond_e_in_a(void) { return peer_in_nodes(inst_a, g_node_id_e); } +static int cond_e_gone_a(void) { return !peer_in_nodes(inst_a, g_node_id_e); } +static int cond_c_in_a(void) { return peer_in_nodes(inst_a, g_node_id_c); } +static int cond_c_gone_a(void) { return !peer_in_nodes(inst_a, g_node_id_c); } static int cond_c_two_paths(void) { - return node_path_count(inst_a, NODE_ID_C) >= 2 && - node_path_has_peer(inst_a, NODE_ID_C, NODE_ID_C) && - node_path_has_peer(inst_a, NODE_ID_C, NODE_ID_B); + return node_path_count(inst_a, g_node_id_c) >= 2 && + node_path_has_peer(inst_a, g_node_id_c, g_node_id_c) && + node_path_has_peer(inst_a, g_node_id_c, g_node_id_b); } static int cond_c_one_path_B(void) { - return node_path_count(inst_a, NODE_ID_C) == 1 && - node_path_has_peer(inst_a, NODE_ID_C, NODE_ID_B); + return node_path_count(inst_a, g_node_id_c) == 1 && + node_path_has_peer(inst_a, g_node_id_c, g_node_id_b); } -static int cond_b_gone_a(void) { return !peer_in_nodes(inst_a, NODE_ID_B); } +static int cond_b_gone_a(void) { return !peer_in_nodes(inst_a, g_node_id_b); } -static int cond_c_one_path(void) { return node_path_count(inst_a, NODE_ID_C) == 1; } +static int cond_c_one_path(void) { return node_path_count(inst_a, g_node_id_c) == 1; } /* ================================================================ * Main @@ -282,7 +278,7 @@ int main(void) { char cfg_a[2048], cfg_b[2048], cfg_c[2048], cfg_d[2048], cfg_e[2048]; build_node_config(cfg_a, sizeof(cfg_a), &(struct ncfg){ - .node_id = NODE_ID_A, .tun_ip = "10.100.0.1/24", .my_subnet = "192.168.10.0/24", + .node_id = g_node_id_a, .tun_ip = "10.100.0.1/24", .my_subnet = "192.168.10.0/24", .priv_hex = priv_hex[0], .pub_hex = pub_hex[0], .srv_cnt = 2, .srvs = {{"a_srv_b", p_ab_a}, {"a_srv_c", p_ac_a}}, .cli_cnt = 2, .clis = { @@ -292,7 +288,7 @@ int main(void) { }); build_node_config(cfg_b, sizeof(cfg_b), &(struct ncfg){ - .node_id = NODE_ID_B, .tun_ip = "10.100.0.2/24", .my_subnet = "192.168.20.0/24", + .node_id = g_node_id_b, .tun_ip = "10.100.0.2/24", .my_subnet = "192.168.20.0/24", .priv_hex = priv_hex[1], .pub_hex = pub_hex[1], .srv_cnt = 3, .srvs = {{"b_srv", p_ab_b}, {"b_cli_c", p_bc_b}, {"b_cli_e", p_be_b}}, .cli_cnt = 2, .clis = { @@ -302,7 +298,7 @@ int main(void) { }); build_node_config(cfg_c, sizeof(cfg_c), &(struct ncfg){ - .node_id = NODE_ID_C, .tun_ip = "10.100.0.3/24", .my_subnet = "192.168.30.0/24", + .node_id = g_node_id_c, .tun_ip = "10.100.0.3/24", .my_subnet = "192.168.30.0/24", .priv_hex = priv_hex[2], .pub_hex = pub_hex[2], .srv_cnt = 3, .srvs = {{"c_srv_a", p_ac_c}, {"c_srv_b", p_bc_c}, {"c_cli_d", p_cd_c}}, .cli_cnt = 1, .clis = { @@ -311,14 +307,14 @@ int main(void) { }); build_node_config(cfg_d, sizeof(cfg_d), &(struct ncfg){ - .node_id = NODE_ID_D, .tun_ip = "10.100.0.4/24", .my_subnet = "192.168.40.0/24", + .node_id = g_node_id_d, .tun_ip = "10.100.0.4/24", .my_subnet = "192.168.40.0/24", .priv_hex = priv_hex[3], .pub_hex = pub_hex[3], .srv_cnt = 1, .srvs = {{"d_srv", p_cd_d}}, .cli_cnt = 0, .clis = {}, }); build_node_config(cfg_e, sizeof(cfg_e), &(struct ncfg){ - .node_id = NODE_ID_E, .tun_ip = "10.100.0.5/24", .my_subnet = "192.168.50.0/24", + .node_id = g_node_id_e, .tun_ip = "10.100.0.5/24", .my_subnet = "192.168.50.0/24", .priv_hex = priv_hex[4], .pub_hex = pub_hex[4], .srv_cnt = 1, .srvs = {{"e_srv", p_be_e}}, .cli_cnt = 0, .clis = {}, @@ -340,9 +336,12 @@ int main(void) { ASSERT(utun_instance_init(inst_d) == 0, "init d"); ASSERT(utun_instance_init(inst_e) == 0, "init e"); + g_node_id_a = inst_a->node_id; g_node_id_b = inst_b->node_id; g_node_id_c = inst_c->node_id; + g_node_id_d = inst_d->node_id; g_node_id_e = inst_e->node_id; + /* ---------- Dummynet filters (2 managed links) ---------- */ - struct ETCP_LINK* ab_link = find_client_link(inst_a, 0); - struct ETCP_LINK* ac_link = find_client_link(inst_a, 1); + struct ETCP_LINK* ab_link = find_client_link(inst_a, "to_b"); + struct ETCP_LINK* ac_link = find_client_link(inst_a, "to_c"); ASSERT(ab_link && ac_link, "A links not found"); df_ab = dummynet_filter_create(ua); ASSERT(df_ab, "df_ab"); @@ -358,25 +357,25 @@ int main(void) { ASSERT(wait_for("D in A nodes", cond_d_in_a, PHASE_TIMEOUT_TB), "D not in A"); ASSERT(wait_for("E in A nodes", cond_e_in_a, PHASE_TIMEOUT_TB), "E not in A"); - ASSERT(peer_in_nodes(inst_a, NODE_ID_B), "B missing"); - ASSERT(peer_in_nodes(inst_a, NODE_ID_C), "C missing"); - ASSERT(peer_in_nodes(inst_a, NODE_ID_D), "D missing"); - ASSERT(peer_in_nodes(inst_a, NODE_ID_E), "E missing"); + ASSERT(peer_in_nodes(inst_a, g_node_id_b), "B missing"); + ASSERT(peer_in_nodes(inst_a, g_node_id_c), "C missing"); + ASSERT(peer_in_nodes(inst_a, g_node_id_d), "D missing"); + ASSERT(peer_in_nodes(inst_a, g_node_id_e), "E missing"); { - int pb = node_path_count(inst_a, NODE_ID_B); - int pc = node_path_count(inst_a, NODE_ID_C); - int pd = node_path_count(inst_a, NODE_ID_D); - int pe = node_path_count(inst_a, NODE_ID_E); + int pb = node_path_count(inst_a, g_node_id_b); + int pc = node_path_count(inst_a, g_node_id_c); + int pd = node_path_count(inst_a, g_node_id_d); + int pe = node_path_count(inst_a, g_node_id_e); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 1: B_paths=%d C_paths=%d D_paths=%d E_paths=%d", pb, pc, pd, pe); ASSERT(pb >= 1, "B: expected >=1"); ASSERT(pc >= 1, "C: expected >=1"); ASSERT(pd >= 1, "D: expected >=1"); ASSERT(pe >= 1, "E: expected >=1"); } - ASSERT(route_count_for_node(inst_a, NODE_ID_B) > 0, "B routes missing"); - ASSERT(route_count_for_node(inst_a, NODE_ID_C) > 0, "C routes missing"); - ASSERT(route_count_for_node(inst_a, NODE_ID_D) > 0, "D routes missing"); - ASSERT(route_count_for_node(inst_a, NODE_ID_E) > 0, "E routes missing"); + ASSERT(route_count_for_node(inst_a, g_node_id_b) > 0, "B routes missing"); + ASSERT(route_count_for_node(inst_a, g_node_id_c) > 0, "C routes missing"); + ASSERT(route_count_for_node(inst_a, g_node_id_d) > 0, "D routes missing"); + ASSERT(route_count_for_node(inst_a, g_node_id_e) > 0, "E routes missing"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 1 PASSED"); /* ================================================================ @@ -387,9 +386,9 @@ int main(void) { dummynet_filter_set_loss(df_ac, 1000); ASSERT(wait_for("C: 1 path via B after A-C kill", cond_c_one_path_B, PHASE_TIMEOUT_TB), "C paths not 1"); { - int pc = node_path_count(inst_a, NODE_ID_C); - int pd = node_path_count(inst_a, NODE_ID_D); - int pe = node_path_count(inst_a, NODE_ID_E); + int pc = node_path_count(inst_a, g_node_id_c); + int pd = node_path_count(inst_a, g_node_id_d); + int pe = node_path_count(inst_a, g_node_id_e); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 2: C_paths=%d D_paths=%d E_paths=%d", pc, pd, pe); ASSERT(cond_c_in_a(), "C missing after A-C kill"); ASSERT(cond_d_in_a(), "D missing after A-C kill"); @@ -407,7 +406,7 @@ int main(void) { dummynet_filter_set_loss(df_ac, 0); ASSERT(wait_for("C: >=2 paths after A-C restore", cond_c_two_paths, PHASE_TIMEOUT_TB), "C paths not >=2"); { - int cp = node_path_count(inst_a, NODE_ID_C); + int cp = node_path_count(inst_a, g_node_id_c); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 3 PASSED (C paths=%d)", cp); } @@ -420,15 +419,15 @@ int main(void) { /* After A-B kill: direct paths removed, B/C/E survive via C relay */ ASSERT(wait_for("A-B paths removed", cond_c_one_path, PHASE_TIMEOUT_TB), "A-B paths not removed"); { - int pb = node_path_count(inst_a, NODE_ID_B); - int pe = node_path_count(inst_a, NODE_ID_E); + int pb = node_path_count(inst_a, g_node_id_b); + int pe = node_path_count(inst_a, g_node_id_e); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 4: B_paths=%d E_paths=%d", pb, pe); - ASSERT(peer_in_nodes(inst_a, NODE_ID_B), "B missing — should survive via C"); - ASSERT(peer_in_nodes(inst_a, NODE_ID_E), "E missing — should survive via C"); + ASSERT(peer_in_nodes(inst_a, g_node_id_b), "B missing — should survive via C"); + ASSERT(peer_in_nodes(inst_a, g_node_id_e), "E missing — should survive via C"); ASSERT(cond_c_in_a(), "C missing"); ASSERT(cond_d_in_a(), "D missing"); - ASSERT(node_path_count(inst_a, NODE_ID_C) == 1, "C: expected 1 path via A-C"); - ASSERT(node_path_has_peer(inst_a, NODE_ID_C, NODE_ID_C), "C: missing direct path"); + ASSERT(node_path_count(inst_a, g_node_id_c) == 1, "C: expected 1 path via A-C"); + ASSERT(node_path_has_peer(inst_a, g_node_id_c, g_node_id_c), "C: missing direct path"); } DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 4 PASSED"); @@ -439,10 +438,10 @@ int main(void) { dummynet_filter_set_loss(df_ac, 1000); ASSERT(wait_for("C gone from A", cond_c_gone_a, PHASE_TIMEOUT_TB), "C still in A"); ASSERT(wait_for("D gone from A", cond_d_gone_a, PHASE_TIMEOUT_TB), "D still in A"); - ASSERT(!peer_in_nodes(inst_a, NODE_ID_B), "B should be gone"); - ASSERT(!peer_in_nodes(inst_a, NODE_ID_C), "C should be gone"); - ASSERT(!peer_in_nodes(inst_a, NODE_ID_D), "D should be gone"); - ASSERT(!peer_in_nodes(inst_a, NODE_ID_E), "E should be gone"); + ASSERT(!peer_in_nodes(inst_a, g_node_id_b), "B should be gone"); + ASSERT(!peer_in_nodes(inst_a, g_node_id_c), "C should be gone"); + ASSERT(!peer_in_nodes(inst_a, g_node_id_d), "D should be gone"); + ASSERT(!peer_in_nodes(inst_a, g_node_id_e), "E should be gone"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 5 PASSED"); /* ================================================================ @@ -454,18 +453,18 @@ int main(void) { ASSERT(wait_for("D in A after restore", cond_d_in_a, 2 * PHASE_TIMEOUT_TB), "D not back"); ASSERT(wait_for("E in A after restore", cond_e_in_a, PHASE_TIMEOUT_TB), "E not back"); ASSERT(wait_for("C: >=2 paths after restore", cond_c_two_paths, PHASE_TIMEOUT_TB), "C paths not >=2"); - ASSERT(peer_in_nodes(inst_a, NODE_ID_B), "B missing after restore"); - ASSERT(peer_in_nodes(inst_a, NODE_ID_C), "C missing after restore"); - ASSERT(peer_in_nodes(inst_a, NODE_ID_D), "D missing after restore"); - ASSERT(peer_in_nodes(inst_a, NODE_ID_E), "E missing after restore"); - ASSERT(node_path_count(inst_a, NODE_ID_C) >= 2, "C: expected >=2 paths"); - ASSERT(node_path_count(inst_a, NODE_ID_D) >= 1, "D: expected >=1"); - ASSERT(node_path_count(inst_a, NODE_ID_B) >= 1, "B: expected >=1"); - ASSERT(node_path_count(inst_a, NODE_ID_E) >= 1, "E: expected >=1"); - ASSERT(route_count_for_node(inst_a, NODE_ID_B) > 0, "B routes"); - ASSERT(route_count_for_node(inst_a, NODE_ID_C) > 0, "C routes"); - ASSERT(route_count_for_node(inst_a, NODE_ID_D) > 0, "D routes"); - ASSERT(route_count_for_node(inst_a, NODE_ID_E) > 0, "E routes"); + ASSERT(peer_in_nodes(inst_a, g_node_id_b), "B missing after restore"); + ASSERT(peer_in_nodes(inst_a, g_node_id_c), "C missing after restore"); + ASSERT(peer_in_nodes(inst_a, g_node_id_d), "D missing after restore"); + ASSERT(peer_in_nodes(inst_a, g_node_id_e), "E missing after restore"); + ASSERT(node_path_count(inst_a, g_node_id_c) >= 2, "C: expected >=2 paths"); + ASSERT(node_path_count(inst_a, g_node_id_d) >= 1, "D: expected >=1"); + ASSERT(node_path_count(inst_a, g_node_id_b) >= 1, "B: expected >=1"); + ASSERT(node_path_count(inst_a, g_node_id_e) >= 1, "E: expected >=1"); + ASSERT(route_count_for_node(inst_a, g_node_id_b) > 0, "B routes"); + ASSERT(route_count_for_node(inst_a, g_node_id_c) > 0, "C routes"); + ASSERT(route_count_for_node(inst_a, g_node_id_d) > 0, "D routes"); + ASSERT(route_count_for_node(inst_a, g_node_id_e) > 0, "E routes"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "Phase 6 PASSED"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "=== ALL PHASES PASSED ==="); diff --git a/tests/test_conn_mgr.c b/tests/test_conn_mgr.c index 070d87d0..c8e77fcd 100644 --- a/tests/test_conn_mgr.c +++ b/tests/test_conn_mgr.c @@ -34,8 +34,8 @@ #define TIMEOUT_TB 300000 #define POLL_MS 5 -#define NID_A 0xAAAA000000000001ULL -#define NID_B 0xBBBB000000000002ULL +static uint64_t g_nid_a = 0; +static uint64_t g_nid_b = 0; static struct UTUN_INSTANCE* g_a = NULL, *g_b = NULL; static struct UASYNC* ua = NULL; @@ -76,16 +76,16 @@ static void ccb(int r, uint64_t id, void* arg) { static void test1(void* arg) { (void)arg; if (result) return; - if (!topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), NID_B)) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test1, "t1"); return; } + if (!topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), g_nid_b)) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test1, "t1"); return; } fprintf(stderr, "Test 1: direct — conn_mgr_connect_node(B)\n"); fflush(stderr); - conn_mgr_connect_node(g_a->conn_mgr, NID_B, 0, ccb, NULL); + conn_mgr_connect_node(g_a->conn_mgr, g_nid_b, 0, ccb, NULL); uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test2, "t2a"); } static void test2(void* arg) { (void)arg; if (result) return; if (!cdone) { uasync_set_timeout(ua, 50, NULL, (timeout_callback_t)test2, "t2b"); return; } if (cresult != CONN_MGR_OK) { fail("connect failed"); return; } - uint8_t st, ty; conn_mgr_get_status(g_a->conn_mgr, NID_B, &st, &ty); + uint8_t st, ty; conn_mgr_get_status(g_a->conn_mgr, g_nid_b, &st, &ty); fprintf(stderr, "Test 2: status state=%d type=%d\n", st, ty); fflush(stderr); if (st == CONN_MGR_STATE_CONNECTED && ty == CONN_TYPE_DIRECT) fprintf(stderr, " OK: DIRECT\n"); else { fail("status mismatch"); return; } @@ -108,12 +108,15 @@ static void setup(void) { test_mkdtemp(tdir); int base = 47000 + (getpid() % 15000); 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); + wf(ca, "[global]\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", pa); + wf(cb, "[global]\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", pb); config_ensure_keys_and_node_id(ca); config_ensure_keys_and_node_id(cb); + { struct utun_config* cfa = parse_config(ca); struct utun_config* cfb = parse_config(cb); + g_nid_a = cfa->global.my_node_id; g_nid_b = cfb->global.my_node_id; + free_config(cfa); free_config(cfb); } 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); + wf(ca, "[global]\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", r0, p0, pa, p1, pb); + wf(cb, "[global]\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", 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); } diff --git a/tests/test_etcp_connect.c b/tests/test_etcp_connect.c index 4b99549d..b884294e 100644 --- a/tests/test_etcp_connect.c +++ b/tests/test_etcp_connect.c @@ -24,8 +24,8 @@ #define TIMEOUT_TB 300000 #define POLL_MS 5 -#define NID_A 0xAAAA000000000001ULL -#define NID_B 0xBBBB000000000002ULL +static uint64_t nid_a = 0; +static uint64_t nid_b = 0; static struct UTUN_INSTANCE* g_a = NULL, *g_b = NULL; static struct UASYNC* ua = NULL; @@ -87,7 +87,7 @@ static struct TOPO_NODEQ* mknode(uint64_t nid, const uint8_t pubkey[32], 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 TOPO_NODEQ* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), NID_B); + struct TOPO_NODEQ* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), 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; @@ -162,7 +162,7 @@ static void test6(void* arg) { /* ======================== Test 7: EARLY-only flag ======================== */ static void test7(void* arg) { (void)arg; if (result) return; - struct TOPO_NODEQ* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), NID_B); + struct TOPO_NODEQ* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), 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; @@ -182,7 +182,7 @@ static void test8(void* arg) { /* ======================== Test 9: LATE-only flag ======================== */ static void test9(void* arg) { (void)arg; if (result) return; - struct TOPO_NODEQ* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), NID_B); + struct TOPO_NODEQ* nb = topo_node_find_by_id(topo_groups_get_default(g_a->topo_groups), 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; @@ -206,12 +206,15 @@ 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); + wf(ca, "[global]\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", pa); + wf(cb, "[global]\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", pb); config_ensure_keys_and_node_id(ca); config_ensure_keys_and_node_id(cb); + { struct utun_config* cfa = parse_config(ca); struct utun_config* cfb = parse_config(cb); + nid_a = cfa->global.my_node_id; nid_b = cfb->global.my_node_id; + free_config(cfa); free_config(cfb); } 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); + wf(ca, "[global]\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", r0, p0, pa, p1, pb); + wf(cb, "[global]\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", 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); } diff --git a/tests/test_etcp_router.c b/tests/test_etcp_router.c index 3cc78608..296c6937 100644 --- a/tests/test_etcp_router.c +++ b/tests/test_etcp_router.c @@ -58,8 +58,8 @@ static struct UASYNC* ua = NULL; static struct dummynet* dn = NULL; static int g_dn_port = 0, g_srv_port = 0, g_cli_port = 0; -static const uint64_t server_node_id = 0x1111111111111111ULL; -static const uint64_t client_node_id = 0x2222222222222222ULL; +static uint64_t server_node_id = 0; +static uint64_t client_node_id = 0; // Bitmap: 0=not received, 1=received static uint8_t rcvd_bitmap[BITMAP_SIZE]; @@ -164,7 +164,6 @@ static void write_configs(void) { if (!f) { fprintf(stderr, "fopen server fail\n"); exit(1); } fprintf(f, "[global]\n" - "my_node_id=0x1111111111111111\n" "my_private_key=%s\n" "my_public_key=%s\n" "tun_ip=10.99.0.1/24\n" @@ -185,7 +184,6 @@ static void write_configs(void) { if (!f) { fprintf(stderr, "fopen client fail\n"); exit(1); } fprintf(f, "[global]\n" - "my_node_id=0x2222222222222222\n" "my_private_key=%s\n" "my_public_key=%s\n" "tun_ip=10.99.0.2/24\n" @@ -496,7 +494,7 @@ static void monitor(void* arg) { g_total_drop++; g_cycle_drop_count[g_cycle]++; } - g_mon_id = uasync_set_timeout(ua, 500, NULL, monitor, "mon"); // 50ms = ROUTER_SEND_RESUME_TB + g_mon_id = uasync_set_timeout(ua, 10, NULL, monitor, "mon"); return; } @@ -628,6 +626,8 @@ int main(void) { cli = utun_instance_create(ua, client_conf); if (!cli || utun_instance_init(cli) < 0) { printf("[FAIL] client create\n"); goto done; } + server_node_id = srv->node_id; client_node_id = cli->node_id; + // Create dummynet between client and server if (setup_dummynet() != 0) { printf("[FAIL] dummynet setup\n"); goto done; } diff --git a/tests/test_etcp_router_reconnect.c b/tests/test_etcp_router_reconnect.c index e130029d..2cb15a8b 100644 --- a/tests/test_etcp_router_reconnect.c +++ b/tests/test_etcp_router_reconnect.c @@ -50,9 +50,9 @@ 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 uint64_t node_a = 0; +static uint64_t node_b = 0; +static uint64_t node_c = 0; 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]; @@ -104,21 +104,21 @@ static int create_temp_configs(void) { 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); + fprintf(f,"[global]\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",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" + fprintf(f,"[global]\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); + 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); + fprintf(f,"[global]\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",privhex_c,pubhex_c,c_port); fclose(f); return 0; } @@ -243,6 +243,9 @@ int main(void) { 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;} + + node_a=g_a->node_id; node_b=g_b->node_id; node_c=g_c->node_id; + etcp_router_bind(g_c, TEST_SVC_ID, recv_handler); struct TOPO_NODEQ* nq_a = mknode(node_a, keys_a.public_key, 127,0,0,1, (uint16_t)a_port); diff --git a/tests/test_icmp_proxy.c b/tests/test_icmp_proxy.c index 20334ee0..692a7dfa 100644 --- a/tests/test_icmp_proxy.c +++ b/tests/test_icmp_proxy.c @@ -40,7 +40,6 @@ static const char* cfg_node_client(void) { static char buf[1024]; snprintf(buf, sizeof(buf), "[global]\n" - "my_node_id=0xEEEE000000000001\n" "my_private_key=38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68\n" "my_public_key=ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a\n" "tun_ip=10.99.0.1/24\n" @@ -52,7 +51,7 @@ static const char* cfg_node_client(void) { "enabled=yes\n" "tun_name=tun_tcp\n" "tun_ip=10.99.0.1\n" - "via_node=0xEEEE000000000002\n"); + "via_node=0x2b2d74c71e38c7fe\n"); return buf; } @@ -60,7 +59,6 @@ static const char* cfg_node_exit(void) { static char buf[1024]; snprintf(buf, sizeof(buf), "[global]\n" - "my_node_id=0xEEEE000000000002\n" "my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n" "my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" "tun_ip=10.99.0.2/24\n" @@ -75,8 +73,8 @@ static struct UTUN_INSTANCE* cli = NULL; static struct UTUN_INSTANCE* exit_node = NULL; static struct UASYNC* ua = NULL; static int g_ok = 0, g_done = 0, g_test_phase = 0; -static uint64_t exit_node_id = 0xEEEE000000000002ULL; -static uint64_t client_node_id = 0xEEEE000000000001ULL; +static uint64_t exit_node_id = 0; +static uint64_t client_node_id = 0; static uint16_t test_echo_id = 0x1234, test_echo_seq = 0x0001; static uint8_t send_buf[ICMP_PAYLOAD_SIZE]; static int reply_rcvd = 0; @@ -160,6 +158,8 @@ int main(void) { if (utun_instance_init(cli) < 0) { printf("[FAIL] client init\n"); goto done; } if (utun_instance_init(exit_node) < 0) { printf("[FAIL] exit init\n"); goto done; } + exit_node_id = exit_node->node_id; client_node_id = cli->node_id; + icmp_proxy_set_test_loopback(exit_node, 1); g_to_id = uasync_set_timeout(ua, 100, NULL, monitor, "mon"); diff --git a/tests/test_ipv6_sockets.c b/tests/test_ipv6_sockets.c index ded353a7..4e1101c4 100644 --- a/tests/test_ipv6_sockets.c +++ b/tests/test_ipv6_sockets.c @@ -57,7 +57,6 @@ static char client_config_path[256]; static const char* server_config = "[global]\n" "my_node_name=server_v6\n" - "my_node_id=0xAAAA000000000001\n" "my_private_key=38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68\n" "my_public_key=ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a\n" "tun_ip=10.99.0.1/24\n" @@ -77,7 +76,6 @@ static const char* server_config = static const char* client_config = "[global]\n" "my_node_name=client_v6\n" - "my_node_id=0xBBBB000000000002\n" "my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n" "my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" "tun_ip=10.99.0.2/24\n" @@ -246,7 +244,7 @@ static void monitor(void* arg) { return; } case 2: { - int ok = verify_remote_v6_nodeinfo("client_remote", client_instance, 0xAAAA000000000001ULL); + int ok = verify_remote_v6_nodeinfo("client_remote", client_instance, server_instance->node_id); if (!ok) { test_phase = 4; return; } printf("=== ALL CHECKS PASSED ===\n"); diff --git a/tests/test_lwip_tcp.c b/tests/test_lwip_tcp.c index 77c78964..7e4d36c2 100644 --- a/tests/test_lwip_tcp.c +++ b/tests/test_lwip_tcp.c @@ -195,6 +195,20 @@ static char stc(struct conn_state *s) } } +static int check_all_done(struct tcp_bridge *br) +{ + int done = 0; + for (int i = 0; i < br->sc; i++) { + struct conn_state *c = br->st[i]; + if (!c) { done++; continue; } + if (c->err) { done++; } + else if (!c->pcb) { done++; } + else if (c->pcb->state >= FIN_WAIT_1) { done++; } + else if (c->sdone && c->rdone) { done++; } + } + return done >= br->sc; +} + static void progress_timer(void *arg) { struct tcp_bridge *br = arg; @@ -393,9 +407,10 @@ static int run_test1(struct tcp_bridge *br) tcp_connect(cl->pcb, IP_A, port, (tcp_connected_fn)t1_connected); // poll until done - while (!br->ad && !sv->err && !cl->err) { + while (!br->ad) { uasync_poll(br->ua, POLL_TB); bridge_deliver(br); + if (check_all_done(br)) br->ad = 1; } uasync_poll(br->ua, POLL_TB); bridge_deliver(br); @@ -573,6 +588,7 @@ static int run_test2(struct tcp_bridge *br) while (!br->ad) { uasync_poll(br->ua, POLL_TB); bridge_deliver(br); + if (check_all_done(br)) br->ad = 1; } uasync_poll(br->ua, POLL_TB); bridge_deliver(br); @@ -646,8 +662,8 @@ int main(int argc, char *argv[]) br->b = lwip_tcp_init(ua, bo_b, br); if (!br->a || !br->b) { fprintf(stderr, "[FAIL] init\n"); return 1; } br->a->trace_id = 'A'; br->b->trace_id = 'B'; - lwip_tcp_set_timer(br->a, 1, 10, 40); - lwip_tcp_set_timer(br->b, 1, 10, 40); + lwip_tcp_set_timer(br->a, 10, 50, 200); + lwip_tcp_set_timer(br->b, 10, 50, 200); int r1 = run_test1(br); @@ -657,8 +673,8 @@ int main(int argc, char *argv[]) br->b = lwip_tcp_init(ua, bo_b, br); if (!br->a || !br->b) { fprintf(stderr, "[FAIL] reinit\n"); return 1; } br->a->trace_id = 'A'; br->b->trace_id = 'B'; - lwip_tcp_set_timer(br->a, 1, 10, 40); - lwip_tcp_set_timer(br->b, 1, 10, 40); + lwip_tcp_set_timer(br->a, 10, 50, 200); + lwip_tcp_set_timer(br->b, 10, 50, 200); int r2 = run_test2(br); diff --git a/tests/test_nat_detection.c b/tests/test_nat_detection.c index 3d7986e7..623a3561 100644 --- a/tests/test_nat_detection.c +++ b/tests/test_nat_detection.c @@ -30,9 +30,9 @@ #include "../lib/debug_config.h" #define TEST_TIMEOUT_MS 5000 -#define NODE_ID_S 0x1111111111111111ULL -#define NODE_ID_C1 0x2222222222222222ULL -#define NODE_ID_C2 0x3333333333333333ULL +static uint64_t g_node_id_s = 0; +static uint64_t g_node_id_c1 = 0; +static uint64_t g_node_id_c2 = 0; static struct UTUN_INSTANCE* inst_s = NULL; static struct UTUN_INSTANCE* inst_c1 = NULL; @@ -81,7 +81,6 @@ static int create_temp_configs(void) { const char* tpl_s = "[global]\n" - "my_node_id=0x1111111111111111\n" "tun_ip=10.100.0.1/24\n" "tun_ifname=tun100\n" "\n" @@ -98,7 +97,6 @@ static int create_temp_configs(void) { const char* tpl_c2 = "[global]\n" - "my_node_id=0x3333333333333333\n" "tun_ip=10.100.0.3/24\n" "tun_ifname=tun103\n" "\n" @@ -119,7 +117,6 @@ static int create_temp_configs(void) { const char* tpl_c1 = "[global]\n" - "my_node_id=0x2222222222222222\n" "tun_ip=10.100.0.2/24\n" "tun_ifname=tun102\n" "\n" @@ -221,6 +218,8 @@ int main(void) { goto cleanup; } + g_node_id_s = inst_s->node_id; g_node_id_c1 = inst_c1->node_id; g_node_id_c2 = inst_c2->node_id; + test_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS * 10, NULL, test_timeout_cb, "test_nat"); // 1. Wait for links C1->S and C2->S to initialize @@ -242,16 +241,16 @@ int main(void) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for BGP exchange (S learns C1 and C2)..."); int bgp_wait_cycles = 0; while (!test_timed_out && bgp_wait_cycles < 500) { - if (topo_groups_get_default(inst_s->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), NODE_ID_C1) != NULL && - topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), NODE_ID_C2) != NULL) { + if (topo_groups_get_default(inst_s->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), g_node_id_c1) != NULL && + topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), g_node_id_c2) != NULL) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "S learned about C1 and C2"); break; } uasync_poll(ua, 10); bgp_wait_cycles++; } - if (!topo_groups_get_default(inst_s->topo_groups) || topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), NODE_ID_C1) == NULL || - topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), NODE_ID_C2) == NULL) { + if (!topo_groups_get_default(inst_s->topo_groups) || topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), g_node_id_c1) == NULL || + topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), g_node_id_c2) == NULL) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "S did not learn about C1/C2 in time"); goto cleanup; } @@ -268,7 +267,7 @@ int main(void) { } // 3. Wait for NAT detection to complete for C1 - check link on server - struct ETCP_LINK* link_sc1 = find_link_to_node(inst_s, NODE_ID_C1); + struct ETCP_LINK* link_sc1 = find_link_to_node(inst_s, g_node_id_c1); if (!link_sc1) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "No link to C1 on server"); goto cleanup; @@ -311,7 +310,7 @@ int main(void) { } // 5. Verify all NAT fields on server - struct TOPO_NODEQ* node_c1 = topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), NODE_ID_C1); + struct TOPO_NODEQ* node_c1 = topo_node_find_by_id(topo_groups_get_default(inst_s->topo_groups), g_node_id_c1); if (!node_c1) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "TOPO_NODEQ for C1 disappeared"); goto cleanup; @@ -337,7 +336,7 @@ int main(void) { { struct ll_entry* e = inst_s->connections->head; while (e) { struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data; - if (ce->conn->peer_node_id == NODE_ID_C1) { conn_sc1 = ce->conn; break; } + if (ce->conn->peer_node_id == g_node_id_c1) { conn_sc1 = ce->conn; break; } e = e->next; } } if (!link_sc1 || link_sc1->nat_type != NAT_TYPE_EIM) { @@ -395,7 +394,7 @@ int main(void) { bgp_wait_cycles = 0; int c2_verified_nat = 0; while (!test_timed_out && bgp_wait_cycles < 500) { - struct TOPO_NODEQ* node_c1_on_c2 = topo_groups_get_default(inst_c2->topo_groups) ? topo_node_find_by_id(topo_groups_get_default(inst_c2->topo_groups), NODE_ID_C1) : NULL; + struct TOPO_NODEQ* node_c1_on_c2 = topo_groups_get_default(inst_c2->topo_groups) ? topo_node_find_by_id(topo_groups_get_default(inst_c2->topo_groups), g_node_id_c1) : NULL; if (node_c1_on_c2 && node_c1_on_c2->node) { for (const struct TOPO_SOCKMETA4* m = node_c1_on_c2->node->v4_sock_meta; m; m = m->next) { if (m->nat_type == NAT_VERIFIED_EIM) { c2_verified_nat = 1; break; } @@ -417,7 +416,7 @@ int main(void) { struct ll_entry* entry = inst_s->connections->head; while (entry) { struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data; - if (ce->conn->peer_node_id == NODE_ID_C2) { conn_sc2 = ce->conn; break; } + if (ce->conn->peer_node_id == g_node_id_c2) { conn_sc2 = ce->conn; break; } entry = entry->next; } if (!conn_sc2) { @@ -460,14 +459,14 @@ int main(void) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for C2 to learn C1 nodeinfo..."); bgp_wait_cycles = 0; while (!test_timed_out && bgp_wait_cycles < 500) { - if (topo_groups_get_default(inst_c2->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_c2->topo_groups), NODE_ID_C1) != NULL) { + if (topo_groups_get_default(inst_c2->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_c2->topo_groups), g_node_id_c1) != NULL) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "C2 learned C1 nodeinfo"); break; } uasync_poll(ua, 10); bgp_wait_cycles++; } - if (!topo_groups_get_default(inst_c2->topo_groups) || topo_node_find_by_id(topo_groups_get_default(inst_c2->topo_groups), NODE_ID_C1) == NULL) { + if (!topo_groups_get_default(inst_c2->topo_groups) || topo_node_find_by_id(topo_groups_get_default(inst_c2->topo_groups), g_node_id_c1) == NULL) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: C2 did not learn C1 nodeinfo in time"); goto cleanup; } diff --git a/tests/test_nat_transport.c b/tests/test_nat_transport.c index 3f9e7ffe..245c82ca 100644 --- a/tests/test_nat_transport.c +++ b/tests/test_nat_transport.c @@ -33,8 +33,8 @@ #include "../lib/ll_queue.h" #define TEST_TIMEOUT_MS 15000 -#define NODE_ID_PROVIDER 0xAAAA000000000001ULL -#define NODE_ID_CLIENT 0xBBBB000000000001ULL +static uint64_t g_provider_node_id = 0; +static uint64_t g_client_node_id = 0; #define NAT_SVC_HDR_SIZE 9 // svc_id(1) + src_node_id(8) static struct UTUN_INSTANCE* inst_provider = NULL; @@ -72,6 +72,14 @@ static int write_config(const char* path, const char* content) { return 0; } +static uint64_t get_node_id_from_config(const char* path) { + struct utun_config* cfg = parse_config(path); + if (!cfg) return 0; + uint64_t nid = cfg->global.my_node_id; + free_config(cfg); + return nid; +} + static char* get_pubkey_from_config(const char* path) { struct utun_config* cfg = parse_config(path); if (!cfg) return NULL; @@ -90,7 +98,6 @@ static int create_temp_configs(void) { const char* tpl_provider = "[global]\n" - "my_node_id=0xAAAA000000000001\n" "tun_ip=10.100.0.1/24\n" "tun_ifname=tun_provider\n" "tun_test_mode=1\n" @@ -110,13 +117,13 @@ static int create_temp_configs(void) { "port_end=20099\n"; if (write_config(config_provider, tpl_provider) != 0) return -1; if (config_ensure_keys_and_node_id(config_provider) != 0) return -1; + uint64_t prov_nid = get_node_id_from_config(config_provider); char* pub_prov = get_pubkey_from_config(config_provider); if (!pub_prov) return -1; char tpl_client_full[4096]; snprintf(tpl_client_full, sizeof(tpl_client_full), "[global]\n" - "my_node_id=0xBBBB000000000001\n" "tun_ip=10.200.0.1/24\n" "tun_ifname=tun_client\n" "tun_test_mode=1\n" @@ -134,8 +141,8 @@ static int create_temp_configs(void) { "enabled=1\n" "tun_ifname=tun_nat_client\n" "tun_ip=100.64.1.1/24\n" - "nat_via=0xAAAA000000000001\n", - pub_prov); + "nat_via=0x%016llx\n", + pub_prov, (unsigned long long)prov_nid); free(pub_prov); if (write_config(config_client, tpl_client_full) != 0) return -1; if (config_ensure_keys_and_node_id(config_client) != 0) return -1; @@ -265,7 +272,7 @@ static int test_init_destroy(void) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Client NAT transport not initialized"); ok = 0; } - if (inst_client->nat_tr.nat_via_node_id != NODE_ID_PROVIDER) { + if (inst_client->nat_tr.nat_via_node_id != g_provider_node_id) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Client nat_via_node_id mismatch: 0x%016llx", (unsigned long long)inst_client->nat_tr.nat_via_node_id); ok = 0; @@ -300,7 +307,7 @@ static int test_provider_egress(void) { entry->dgram = dgram; entry->len = total; - int ret = etcp_route_send(inst_client, NODE_ID_PROVIDER, entry, 0); + int ret = etcp_route_send(inst_client, g_provider_node_id, entry, 0); if (ret != 0) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "etcp_route_send failed"); queue_dgram_free(entry); @@ -337,9 +344,9 @@ static int test_provider_egress(void) { e->internal_ip, be16toh(e->internal_port), e->proto, inst_provider->nat.port_start, (unsigned long long)e->src_node_id); - if (e->src_node_id != NODE_ID_CLIENT) { + if (e->src_node_id != g_client_node_id) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "src_node_id mismatch: %016llx vs %016llx", - (unsigned long long)e->src_node_id, NODE_ID_CLIENT); + (unsigned long long)e->src_node_id, g_client_node_id); return 0; } if (e->internal_ip != 0x0A0000FE) { @@ -469,7 +476,7 @@ static int test_full_roundtrip(void) { struct ll_entry* entry = queue_entry_new(0); entry->dgram = dgram; entry->len = total; - int ret = etcp_route_send(inst_client, NODE_ID_PROVIDER, entry, 0); + int ret = etcp_route_send(inst_client, g_provider_node_id, entry, 0); if (ret != 0) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "etcp_route_send failed"); queue_dgram_free(entry); queue_entry_free(entry); return 0; } // Poll for provider to process egress @@ -549,6 +556,8 @@ int main(void) { goto cleanup; } + g_provider_node_id = inst_provider->node_id; g_client_node_id = inst_client->node_id; + test_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS, NULL, test_timeout_cb, "test_nat_transport"); // Wait for ETCP link between client and provider @@ -566,8 +575,8 @@ int main(void) { DEBUG_INFO(DEBUG_CATEGORY_NAT, "Waiting for BGP..."); int bgp_cycles = 0; while (!test_timed_out && bgp_cycles < 500) { - if (topo_groups_get_default(inst_provider->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_provider->topo_groups), NODE_ID_CLIENT) && - topo_groups_get_default(inst_client->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_client->topo_groups), NODE_ID_PROVIDER)) { + if (topo_groups_get_default(inst_provider->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_provider->topo_groups), g_client_node_id) && + topo_groups_get_default(inst_client->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_client->topo_groups), g_provider_node_id)) { DEBUG_INFO(DEBUG_CATEGORY_NAT, "BGP exchanged"); break; } diff --git a/tests/test_route_ping.c b/tests/test_route_ping.c index c3ce344c..f843711b 100644 --- a/tests/test_route_ping.c +++ b/tests/test_route_ping.c @@ -29,9 +29,9 @@ #include "../lib/debug_config.h" #define TEST_TIMEOUT_MS 10000 -#define NODE_ID_A 0x1111111111111111ULL -#define NODE_ID_B 0x2222222222222222ULL -#define NODE_ID_C 0x3333333333333333ULL +static uint64_t g_node_id_a = 0; +static uint64_t g_node_id_b = 0; +static uint64_t g_node_id_c = 0; static struct UTUN_INSTANCE* inst_a = NULL; static struct UTUN_INSTANCE* inst_b = NULL; @@ -81,7 +81,6 @@ static int create_temp_configs(void) { // 1. Config C (no keys) const char* tpl_c = "[global]\n" - "my_node_id=0x3333333333333333\n" "tun_ip=10.100.0.3/24\n" "tun_ifname=tun102\n" "\n" @@ -100,7 +99,6 @@ static int create_temp_configs(void) { char tpl_b[4096]; snprintf(tpl_b, sizeof(tpl_b), "[global]\n" - "my_node_id=0x2222222222222222\n" "tun_ip=10.100.0.2/24\n" "tun_ifname=tun101\n" "\n" @@ -126,7 +124,6 @@ static int create_temp_configs(void) { char tpl_a[4096]; snprintf(tpl_a, sizeof(tpl_a), "[global]\n" - "my_node_id=0x1111111111111111\n" "tun_ip=10.100.0.1/24\n" "tun_ifname=tun100\n" "\n" @@ -226,6 +223,8 @@ int main(void) { goto cleanup; } + g_node_id_a = inst_a->node_id; g_node_id_b = inst_b->node_id; g_node_id_c = inst_c->node_id; + test_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS * 10, NULL, test_timeout_cb, "test_route_ping"); // 1. Wait for links A->B and B->C to initialize @@ -246,20 +245,20 @@ int main(void) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for BGP exchange (B learns C)..."); int bgp_wait_cycles = 0; while (!test_timed_out && bgp_wait_cycles < 500) { - if (topo_groups_get_default(inst_b->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_b->topo_groups), NODE_ID_C) != NULL) { + if (topo_groups_get_default(inst_b->topo_groups) && topo_node_find_by_id(topo_groups_get_default(inst_b->topo_groups), g_node_id_c) != NULL) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "B learned about C"); break; } uasync_poll(ua, 10); bgp_wait_cycles++; } - if (!topo_groups_get_default(inst_b->topo_groups) || topo_node_find_by_id(topo_groups_get_default(inst_b->topo_groups), NODE_ID_C) == NULL) { + if (!topo_groups_get_default(inst_b->topo_groups) || topo_node_find_by_id(topo_groups_get_default(inst_b->topo_groups), g_node_id_c) == NULL) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "B did not learn about C in time"); goto cleanup; } // 3. Get target info for C and send route ping request from A to B - struct ETCP_CONN* conn_ab = find_conn_to_peer(inst_a, NODE_ID_B); + struct ETCP_CONN* conn_ab = find_conn_to_peer(inst_a, g_node_id_b); if (!conn_ab) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "A has no connection to B"); goto cleanup; @@ -269,7 +268,7 @@ int main(void) { /* addr[] в network byte order, port в host byte order */ uint32_t target_ip = 0; uint16_t target_port = 0; - struct TOPO_NODEQ* nq = topo_groups_get_default(inst_b->topo_groups) ? topo_node_find_by_id(topo_groups_get_default(inst_b->topo_groups), NODE_ID_C) : NULL; + struct TOPO_NODEQ* nq = topo_groups_get_default(inst_b->topo_groups) ? topo_node_find_by_id(topo_groups_get_default(inst_b->topo_groups), g_node_id_c) : NULL; if (nq && nq->node) { const struct TOPO_ADDR4* a = nq->node->v4_addrs; if (a) { diff --git a/tests/test_socks_http_proxy.c b/tests/test_socks_http_proxy.c index eb89db00..bbfa0d6d 100644 --- a/tests/test_socks_http_proxy.c +++ b/tests/test_socks_http_proxy.c @@ -184,7 +184,6 @@ static char* make_cfg_server(void) { static char buf[1024]; snprintf(buf, sizeof(buf), "[global]\ntun_enabled=no\n" - "my_node_id=0x2222000000000001\n" "my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n" "my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" "[server: s1]\naddr=127.0.0.1:%d\ntype=public\n" @@ -197,7 +196,6 @@ static char* make_cfg_client(void) { static char buf[1024]; snprintf(buf, sizeof(buf), "[global]\ntun_enabled=no\n" - "my_node_id=0x1111000000000001\n" "my_private_key=38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68\n" "my_public_key=ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a\n" "[server: s1]\naddr=127.0.0.1:%d\ntype=public\n" @@ -209,7 +207,7 @@ static char* make_cfg_client(void) { "socks_addr=127.0.0.1:%d\n" "http_proxy_enabled=yes\n" "http_proxy_addr=127.0.0.1:%d\n" - "via_node=0x2222000000000001\n", + "via_node=0x2b2d74c71e38c7fe\n", g_cli_port, g_srv_port, g_socks_port, g_http_proxy_port); return buf; } diff --git a/tests/test_stcp.c b/tests/test_stcp.c index cd41a84a..f469c2a8 100644 --- a/tests/test_stcp.c +++ b/tests/test_stcp.c @@ -63,7 +63,6 @@ static void setup_peer(struct test_peer *p, struct stcp_conn *conn) { queue_set_callback(p->rx, peer_rx_cb, p); stcp_conn_set_rx_queue(conn, p->rx); stcp_conn_set_tx_queue(conn, p->tx); - stcp_conn_set_on_close(conn, peer_close_cb, p); p->ready = 1; } @@ -101,8 +100,8 @@ static int test1_sizes(void) { struct test_peer srv = {0}, cli = {0}; uint16_t port = BASE_PORT + 1; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv); TASSERT(ss); - struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli); TASSERT(sc); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv); TASSERT(ss); + struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc); size_t sizes[] = {0, 1, 16, 17, 255, 256, 1000, 65535}; int n_sizes = 8; @@ -111,7 +110,7 @@ static int test1_sizes(void) { for (int i = 0; i < 65536; i++) payload[i] = (uint8_t)(i * 7 + 13); int sent = 0, ticks = 0; - while (srv.msg_count < n_sizes && ticks < 5000) { + while (srv.msg_count < n_sizes && ticks < 200) { uasync_poll(ua, 10); if (srv.ready && cli.ready && !sent) { for (int i = 0; i < n_sizes; i++) TASSERT(peer_send(&cli, payload, sizes[i]) == 0); @@ -141,11 +140,11 @@ static int test2_many(void) { struct test_peer srv = {0}, cli = {0}; uint16_t port = BASE_PORT + 2; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv); TASSERT(ss); - struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli); TASSERT(sc); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv); TASSERT(ss); + struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc); int sent = 0, ticks = 0; - while (srv.msg_count < 200 && ticks < 5000) { + while (srv.msg_count < 200 && ticks < 200) { uasync_poll(ua, 10); if (srv.ready && cli.ready && !sent) { for (int i = 0; i < 200; i++) { @@ -179,14 +178,14 @@ static int test3_wrong_key(void) { struct test_peer srv = {0}, cli = {0}; uint16_t port = BASE_PORT + 3; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv); TASSERT(ss); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv); TASSERT(ss); struct SC_MYKEYS rogue; TASSERT(sc_generate_keypair(&rogue) == SC_OK); - struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, rogue.public_key, client_ready_cb, &cli); TASSERT(sc); + struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, rogue.public_key, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc); int ticks = 0; - while (ticks < 3000) { + while (ticks < 200) { uasync_poll(ua, 10); if (cli.closed || srv.closed) break; ticks++; @@ -207,11 +206,11 @@ static int test4_close(void) { struct test_peer srv = {0}, cli = {0}; uint16_t port = BASE_PORT + 4; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv); TASSERT(ss); - struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli); TASSERT(sc); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv); TASSERT(ss); + struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc); int closed = 0, ticks = 0; - while (!srv.closed && ticks < 3000) { + while (!srv.closed && ticks < 200) { uasync_poll(ua, 10); if (srv.ready && cli.ready && !closed) { uint8_t m = 0xAB; peer_send(&cli, &m, 1); @@ -254,16 +253,16 @@ static int test5_multi(void) { memset(srvp, 0, sizeof(srvp)); memset(clip, 0, sizeof(clip)); g_multi_peers = srvp; g_multi_idx = 0; g_multi_max = NCLI; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, multi_connect_cb, NULL); TASSERT(ss); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, multi_connect_cb, NULL, NULL, NULL); TASSERT(ss); struct stcp_client *clients[NCLI] = {0}; for (int i = 0; i < NCLI; i++) { - clients[i] = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &clip[i]); + clients[i] = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &clip[i], peer_close_cb, &clip[i]); TASSERT(clients[i]); } int sent = 0, ticks = 0; - while (ticks < 5000) { + while (ticks < 200) { uasync_poll(ua, 10); if (!sent) { int all_ready = 1; @@ -306,8 +305,8 @@ static int test6_interleaved(void) { struct test_peer srv = {0}, cli = {0}; uint16_t port = BASE_PORT + 6; - struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv); TASSERT(ss); - struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli); TASSERT(sc); + struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv, peer_close_cb, &srv); TASSERT(ss); + struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc); int round = 0, ticks = 0; while (srv.msg_count < 50 || cli.msg_count < 50) { @@ -317,7 +316,7 @@ static int test6_interleaved(void) { uint8_t sb = (uint8_t)(round + 200); if (peer_send(&cli, &cb, 1) == 0 && peer_send(&srv, &sb, 1) == 0) round++; } - if (++ticks > 5000) break; + if (++ticks > 200) break; } TASSERT(srv.msg_count >= 50); TASSERT(cli.msg_count >= 50); diff --git a/tests/test_uasync_socket_race.c b/tests/test_uasync_socket_race.c index 4f0b420c..568d2c8c 100644 --- a/tests/test_uasync_socket_race.c +++ b/tests/test_uasync_socket_race.c @@ -150,7 +150,7 @@ int main(void) { g_mon_id = uasync_set_timeout(g_ua, 100, NULL, monitor, "mon"); void* to_id = uasync_set_timeout(g_ua, TIMEOUT_MS * 10, NULL, test_timeout, "to"); - while (!g_done) uasync_poll(g_ua, 50); + while (!g_done) uasync_poll(g_ua, 0); if (to_id) uasync_cancel_timeout(g_ua, to_id); diff --git a/tests/test_udp_proxy.c b/tests/test_udp_proxy.c index 3dc96613..7ade1bf0 100644 --- a/tests/test_udp_proxy.c +++ b/tests/test_udp_proxy.c @@ -39,7 +39,6 @@ static const char* cfg_node_client(void) { static char buf[1024]; snprintf(buf, sizeof(buf), "[global]\n" - "my_node_id=0xDDDD000000000001\n" "my_private_key=38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68\n" "my_public_key=ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a\n" "tun_ip=10.99.0.1/24\n" @@ -51,7 +50,7 @@ static const char* cfg_node_client(void) { "enabled=yes\n" "tun_name=tun_tcp\n" "tun_ip=10.99.0.1\n" - "via_node=0xDDDD000000000002\n"); + "via_node=0x2b2d74c71e38c7fe\n"); return buf; } @@ -59,7 +58,6 @@ static const char* cfg_node_exit(void) { static char buf[1024]; snprintf(buf, sizeof(buf), "[global]\n" - "my_node_id=0xDDDD000000000002\n" "my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n" "my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" "tun_ip=10.99.0.2/24\n" @@ -79,8 +77,8 @@ static int g_echo_count = 0; static uint8_t g_echo_buf[8192]; static ssize_t g_echo_len = 0; static int g_ok = 0, g_done = 0, g_test_phase = 0; -static uint64_t exit_node_id = 0xDDDD000000000002ULL; -static uint64_t client_node_id = 0xDDDD000000000001ULL; +static uint64_t exit_node_id = 0; +static uint64_t client_node_id = 0; static uint8_t send_buf[PAYLOAD_SIZE], recv_buf[PAYLOAD_SIZE]; static int reply_rcvd = 0; static void* g_to_id = NULL; @@ -167,6 +165,8 @@ int main(void) { if (utun_instance_init(cli) < 0) { printf("[FAIL] client init\n"); goto done; } if (utun_instance_init(exit_node) < 0) { printf("[FAIL] exit init\n"); goto done; } + exit_node_id = exit_node->node_id; client_node_id = cli->node_id; + // UDP echo server (same uasync, no fork) g_echo_sock = socket(AF_INET, SOCK_DGRAM, 0); if (g_echo_sock == SOCKET_INVALID) { printf("[FAIL] echo socket\n"); goto done; } diff --git a/tools/chatgui/transport/chat_core.c b/tools/chatgui/transport/chat_core.c index 7f1d004a..4655a2e4 100644 --- a/tools/chatgui/transport/chat_core.c +++ b/tools/chatgui/transport/chat_core.c @@ -19,6 +19,7 @@ #include "../../../src/etcp.h" #include "../../../src/etcp_connections.h" #include "../../../src/secure_channel.h" +#include "../../../src/ntp_time.h" #include "../../../lib/u_async.h" #include "../../../lib/ll_queue.h" #include "../../../lib/mem.h" @@ -302,6 +303,8 @@ void chat_core_update_my_name(const char* name) { memcpy(join_msg + mlen, g_cc.inst->my_keys.public_key, 32); mlen += 32; { const char* nm = name[0] ? name : ""; size_t nl = strlen(nm); memcpy(join_msg + mlen, nm, nl); mlen += nl; join_msg[mlen++] = '\0'; } + uint64_t join_ts = (uint64_t)ntp_time_get_seconds(g_cc.inst); + memcpy(join_msg + mlen, &join_ts, 8); mlen += 8; uint8_t new_sig[64]; memset(new_sig, 0, 64); EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, g_cc.inst->my_ed25519_privkey, 32); @@ -314,9 +317,10 @@ void chat_core_update_my_name(const char* name) { } EVP_PKEY_free(pkey); } - topo_node_sqlite_member_put(g_cc.db, ch, myid, new_sig, NULL); + topo_node_sqlite_member_put(g_cc.db, ch, myid, new_sig, join_ts, NULL, 0, + g_cc.inst->my_keys.public_key, g_cc.inst->my_ed25519_pubkey, name, NULL); member_sync_put(g_cc.inst, ch, myid, g_cc.inst->my_keys.public_key, - g_cc.inst->my_ed25519_pubkey, new_sig, NULL, 0); + g_cc.inst->my_ed25519_pubkey, new_sig, join_ts, NULL, 0, name, NULL, 0); uint8_t evt[65]; uint8_t cl = (uint8_t)strlen(ch); evt[0] = cl; memcpy(evt + 1, ch, cl); gui_bridge_post(GUI_EVT_MEMBERS_CHANGED, evt, 1 + cl); @@ -1071,6 +1075,8 @@ void chat_core_create_channel(struct chat_channel_create* req) { memcpy(join_msg + mlen, g_cc.inst->my_keys.public_key, 32); mlen += 32; { const char* nm = g_cc.inst->name[0] ? g_cc.inst->name : ""; size_t nl = strlen(nm); memcpy(join_msg + mlen, nm, nl); mlen += nl; join_msg[mlen++] = '\0'; } + uint64_t join_ts = (uint64_t)ntp_time_get_seconds(g_cc.inst); + memcpy(join_msg + mlen, &join_ts, 8); mlen += 8; uint8_t join_sig[64]; EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, g_cc.inst->my_ed25519_privkey, 32); @@ -1089,7 +1095,7 @@ void chat_core_create_channel(struct chat_channel_create* req) { } member_sync_put(g_cc.inst, req->channel_id, myid, g_cc.inst->my_keys.public_key, g_cc.inst->my_ed25519_pubkey, - join_sig, NULL, 0); + join_sig, join_ts, NULL, 0, g_cc.inst->name, NULL, 0); } /* notify GUI — members already in DB, channel will show with self as participant */ diff --git a/tools/chatgui/transport/chat_sync.c b/tools/chatgui/transport/chat_sync.c index 268775c6..b165a3b3 100644 --- a/tools/chatgui/transport/chat_sync.c +++ b/tools/chatgui/transport/chat_sync.c @@ -10,6 +10,7 @@ #include "../../../src/conn_mgr.h" #include "../../../src/topo_group.h" #include "../../../src/secure_channel.h" +#include "../../../src/ntp_time.h" #include "../../../lib/u_async.h" #include "../../../lib/ll_queue.h" #include "../../../lib/debug_config.h" @@ -791,17 +792,34 @@ static void cs_handle_channel_info_req(struct chat_sync* cs, uint64_t peer, if (memcmp(cs->inst->my_keys.public_key, x25519, 32) != 0) DEBUG_WARN(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_INFO_RESP pubkey MISMATCH: my=%016llx ch=%016llx — channel was created with DIFFERENT keys!", CS_ID, *(const uint64_t*)cs->inst->my_keys.public_key, *(const uint64_t*)x25519); - uint8_t my_join_sig[64] = {0}; - uint8_t join_msg[256]; size_t mlen = 0; - mlen += snprintf((char*)join_msg + mlen, sizeof(join_msg) - mlen, "%s", ch_id) + 1; - memcpy(join_msg + mlen, &myid, 8); mlen += 8; - memcpy(join_msg + mlen, cs->inst->my_keys.public_key, 32); mlen += 32; - { const char* nm = cs->inst->name[0] ? cs->inst->name : ""; - size_t nl = strlen(nm); memcpy(join_msg + mlen, nm, nl); mlen += nl; join_msg[mlen++] = '\0'; } - cs_ed25519_sign(cs->inst->my_ed25519_privkey, join_msg, mlen, my_join_sig); - DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "%s: CHANNEL_INFO_RESP signed my_join_sig: myid=%016llx my_pub=%016llx ch_pub=%016llx name=%s", - CS_ID, (unsigned long long)myid, *(const uint64_t*)cs->inst->my_keys.public_key, - *(const uint64_t*)x25519, cs->inst->name); + /* load or create join_sig */ + uint8_t my_join_sig[64] = {0}; uint64_t my_join_ts = 0; + { + sqlite3* vdb = cs->inst->topo_groups->topo_sqlite_db; + if (topo_node_sqlite_member_get_join(vdb, ch_id, myid, my_join_sig, &my_join_ts) != 0) { + uint8_t join_msg[256]; size_t mlen = 0; + mlen += snprintf((char*)join_msg + mlen, sizeof(join_msg) - mlen, "%s", ch_id) + 1; + memcpy(join_msg + mlen, &myid, 8); mlen += 8; + memcpy(join_msg + mlen, cs->inst->my_keys.public_key, 32); mlen += 32; + my_join_ts = (uint64_t)ntp_time_get_seconds(cs->inst); + memcpy(join_msg + mlen, &my_join_ts, 8); mlen += 8; + cs_ed25519_sign(cs->inst->my_ed25519_privkey, join_msg, mlen, my_join_sig); + DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "%s: CHANNEL_INFO_RESP created NEW join_sig: myid=%016llx ts=%llu", + CS_ID, (unsigned long long)myid, (unsigned long long)my_join_ts); + } + } + /* generate update_sig */ + uint8_t my_update_sig[64] = {0}; uint64_t my_update_ts; + { + uint8_t umsg[256]; size_t ulen = 0; + ulen += snprintf((char*)umsg + ulen, sizeof(umsg) - ulen, "%s", ch_id) + 1; + memcpy(umsg + ulen, &myid, 8); ulen += 8; + memcpy(umsg + ulen, cs->inst->my_keys.public_key, 32); ulen += 32; + my_update_ts = (uint64_t)ntp_time_get_seconds(cs->inst); + memcpy(umsg + ulen, &my_update_ts, 8); ulen += 8; + memcpy(umsg + ulen, my_join_sig, 64); ulen += 64; + cs_ed25519_sign(cs->inst->my_ed25519_privkey, umsg, ulen, my_update_sig); + } uint8_t buf[1024]; size_t boff = 0; buf[boff++] = CS_MSG_CHANNEL_INFO_RESP; @@ -812,7 +830,14 @@ static void cs_handle_channel_info_req(struct chat_sync* cs, uint64_t peer, memcpy(buf + boff, x25519, 32); boff += 32; memcpy(buf + boff, ed_pub, 32); boff += 32; memcpy(buf + boff, ch_sig, 64); boff += 64; - memcpy(buf + boff, my_join_sig, 64); boff += 64; + uint8_t inv_flags = (my_join_sig[0] || my_join_ts) ? PEERS_FLAG_HAS_JOIN : 0; + buf[boff++] = inv_flags; + if (inv_flags & PEERS_FLAG_HAS_JOIN) { + memcpy(buf + boff, my_join_sig, 64); boff += 64; + memcpy(buf + boff, &my_join_ts, 8); boff += 8; + } + memcpy(buf + boff, my_update_sig, 64); boff += 64; + memcpy(buf + boff, &my_update_ts, 8); boff += 8; { const char* iname = cs->inst->name[0] ? cs->inst->name : ""; uint8_t il = (uint8_t)strlen(iname); buf[boff++] = il; memcpy(buf + boff, iname, il); boff += il; } @@ -826,7 +851,7 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer, if (cs->info_req_timer) { uasync_cancel_timeout(cs->inst->ua, cs->info_req_timer); cs->info_req_timer = NULL; } if (len < 1) { DEBUG_WARN(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_INFO_RESP too short len=%zu peer=%016llx", CS_ID, len, (unsigned long long)peer); return; } uint8_t nl = pl[0]; - if (1 + nl + 8 + 1 + 32 + 32 + 64 + 64 + 1 > len) { DEBUG_WARN(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_INFO_RESP truncated len=%zu min=%d", CS_ID, len, 1 + nl + 8 + 1 + 32 + 32 + 64 + 64 + 1); return; } + if (1 + nl + 8 + 1 + 32 + 32 + 64 + 1 + 64 + 8 + 1 > len) { DEBUG_WARN(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_INFO_RESP truncated len=%zu", CS_ID, len); return; } const uint8_t* p = pl + 1; char name[128]; memcpy(name, p, nl); name[nl] = '\0'; p += nl; uint64_t owner; memcpy(&owner, p, 8); p += 8; @@ -834,7 +859,12 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer, const uint8_t* x25519 = p; p += 32; const uint8_t* ed_pub = p; p += 32; const uint8_t* ch_sig = p; p += 64; - const uint8_t* inviter_join_sig = p; p += 64; + uint8_t inv_flags = *p++; + const uint8_t* inviter_join_sig = NULL; uint64_t inviter_join_ts = 0; + if (inv_flags & PEERS_FLAG_HAS_JOIN) { + if (p + 72 > pl + len) return; inviter_join_sig = p; p += 64; memcpy(&inviter_join_ts, p, 8); p += 8; } + const uint8_t* inviter_update_sig = p; p += 64; uint64_t inviter_update_ts; + memcpy(&inviter_update_ts, p, 8); p += 8; uint8_t inv_name_len = *p++; char inv_name[128] = ""; if (p + inv_name_len <= pl + len) { memcpy(inv_name, p, inv_name_len); inv_name[inv_name_len] = '\0'; } @@ -872,12 +902,14 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer, ilen += snprintf((char*)ivmsg + ilen, sizeof(ivmsg) - ilen, "%s", ch_id) + 1; memcpy(ivmsg + ilen, &peer, 8); ilen += 8; memcpy(ivmsg + ilen, inv_x25519, 32); ilen += 32; + memcpy(ivmsg + ilen, &inviter_update_ts, 8); ilen += 8; { const char* nm = inv_name[0] ? inv_name : ""; - size_t nl2 = strlen(nm); memcpy(ivmsg + ilen, nm, nl2); ilen += nl2; ivmsg[ilen++] = '\0'; - if (cs_ed25519_verify(inv_ed, ivmsg, ilen, inviter_join_sig) != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_INFO_RESP invalid inviter_join_sig peer=%016llx x25519=%016llx inv_ed=%016llx inv_name=%s", - CS_ID, (unsigned long long)peer, *(const uint64_t*)inv_x25519, *(const uint64_t*)inv_ed, nm); - } + size_t nl2 = strlen(nm); (void)nl2; } + if (inviter_join_sig) memcpy(ivmsg + ilen, inviter_join_sig, 64); else memset(ivmsg + ilen, 0, 64); + ilen += 64; + if (cs_ed25519_verify(inv_ed, ivmsg, ilen, inviter_update_sig) != 0) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_INFO_RESP invalid inviter_update_sig peer=%016llx ts=%llu", + CS_ID, (unsigned long long)peer, (unsigned long long)inviter_update_ts); } /* save inviter node_info to local DB */ sqlite3* vdb = cs->inst->topo_groups->topo_sqlite_db; @@ -911,17 +943,15 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer, lk = lk->next; } } + topo_node_sqlite_member_put(vdb, ch_id, peer, inviter_join_sig, inviter_join_ts, inviter_update_sig, inviter_update_ts, inv_x25519, inv_ed, inv_name, inviter_join_sig); + topo_node_sqlite_node_update_verified(vdb, peer, inv_name, inv_x25519, inv_ed, inviter_join_ts); } - /* save inviter as node and member */ - topo_node_sqlite_member_put(cs->inst->topo_groups->topo_sqlite_db, ch_id, peer, - inviter_join_sig, inviter_join_sig); - /* generate our own join_sig */ uint64_t myid = cs->inst->node_id; uint8_t my_x25519[32]; memcpy(my_x25519, cs->inst->my_keys.public_key, 32); - uint8_t join_sig[64]; + uint8_t join_sig[64]; uint64_t join_ts; { uint8_t msg[256]; size_t mlen = 0; mlen += snprintf((char*)msg + mlen, sizeof(msg) - mlen, "%s", ch_id) + 1; @@ -929,6 +959,8 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer, memcpy(msg + mlen, my_x25519, 32); mlen += 32; { const char* nm = cs->inst->name[0] ? cs->inst->name : ""; size_t nl = strlen(nm); memcpy(msg + mlen, nm, nl); mlen += nl; msg[mlen++] = '\0'; } + join_ts = (uint64_t)ntp_time_get_seconds(cs->inst); + memcpy(msg + mlen, &join_ts, 8); mlen += 8; cs_ed25519_sign(cs->inst->my_ed25519_privkey, msg, mlen, join_sig); } @@ -939,6 +971,7 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer, memcpy(jbuf + joff, my_x25519, 32); joff += 32; memcpy(jbuf + joff, cs->inst->my_ed25519_pubkey, 32); joff += 32; memcpy(jbuf + joff, join_sig, 64); joff += 64; + memcpy(jbuf + joff, &join_ts, 8); joff += 8; { const char* nm = cs->inst->name[0] ? cs->inst->name : ""; uint8_t nml = (uint8_t)strlen(nm); jbuf[joff++] = nml; memcpy(jbuf + joff, nm, nml); joff += nml; } @@ -984,12 +1017,13 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer, static void cs_handle_channel_join(struct chat_sync* cs, uint64_t peer, const char* ch_id, const uint8_t* pl, size_t len) { - if (len < 8 + 32 + 32 + 64 + 1 + 1) { DEBUG_WARN(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_JOIN too short len=%zu peer=%016llx", CS_ID, len, (unsigned long long)peer); return; } + if (len < 8 + 32 + 32 + 64 + 8 + 1 + 1) { DEBUG_WARN(DEBUG_CATEGORY_CONNECTIVITY, "%s: CHANNEL_JOIN too short len=%zu peer=%016llx", CS_ID, len, (unsigned long long)peer); return; } const uint8_t* p = pl; uint64_t node_id; memcpy(&node_id, p, 8); p += 8; const uint8_t* x25519 = p; p += 32; const uint8_t* ed_pub = p; p += 32; const uint8_t* join_sig = p; p += 64; + uint64_t join_ts; memcpy(&join_ts, p, 8); p += 8; uint8_t name_len = *p++; char joiner_name[128] = ""; if (p + name_len <= pl + len) { memcpy(joiner_name, p, name_len); joiner_name[name_len] = '\0'; p += name_len; } @@ -1002,15 +1036,17 @@ static void cs_handle_channel_join(struct chat_sync* cs, uint64_t peer, memcpy(vmsg + vlen, x25519, 32); vlen += 32; { const char* nm = joiner_name[0] ? joiner_name : ""; size_t nl = strlen(nm); memcpy(vmsg + vlen, nm, nl); vlen += nl; vmsg[vlen++] = '\0'; } + memcpy(vmsg + vlen, &join_ts, 8); vlen += 8; if (cs_ed25519_verify(ed_pub, vmsg, vlen, join_sig) != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: JOIN invalid sig node=0x%016llx ch=%s — signed(node=%016llx x25519=%016llx ed=%016llx name=%s)", CS_ID, + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: JOIN invalid sig node=0x%016llx ch=%s — signed(node=%016llx x25519=%016llx ed=%016llx name=%s ts=%llu)", CS_ID, (unsigned long long)node_id, ch_id, - (unsigned long long)node_id, *(const uint64_t*)x25519, *(const uint64_t*)ed_pub, joiner_name); + (unsigned long long)node_id, *(const uint64_t*)x25519, *(const uint64_t*)ed_pub, joiner_name, (unsigned long long)join_ts); return; } sqlite3* db = cs->inst->topo_groups->topo_sqlite_db; - topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, NULL); + topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, join_ts, NULL, 0, x25519, ed_pub, joiner_name, NULL); + topo_node_sqlite_node_update_verified(db, node_id, joiner_name, x25519, ed_pub, join_ts); /* save joiner node_info to local DB */ if (db && joiner_name[0]) { sqlite3_stmt* ns = NULL; @@ -1065,12 +1101,13 @@ static void cs_handle_channel_join(struct chat_sync* cs, uint64_t peer, memcpy(ubuf + uoff, x25519, 32); uoff += 32; memcpy(ubuf + uoff, ed_pub, 32); uoff += 32; memcpy(ubuf + uoff, join_sig, 64); uoff += 64; + memcpy(ubuf + uoff, &join_ts, 8); uoff += 8; ubuf[uoff++] = name_len; memcpy(ubuf + uoff, joiner_name, name_len); uoff += name_len; ubuf[uoff++] = addr_cnt; - size_t addr_data_sz = (size_t)(p - (pl + 8 + 32 + 32 + 64 + 1 + name_len + 1)); + size_t addr_data_sz = (size_t)(p - (pl + 8 + 32 + 32 + 64 + 8 + 1 + name_len + 1)); if (uoff + addr_data_sz <= sizeof(ubuf)) { - memcpy(ubuf + uoff, pl + 8 + 32 + 32 + 64 + 1 + name_len + 1, addr_data_sz); + memcpy(ubuf + uoff, pl + 8 + 32 + 32 + 64 + 8 + 1 + name_len + 1, addr_data_sz); uoff += addr_data_sz; } cs_propagate(cs, ch_id, peer, ubuf, uoff); @@ -1107,14 +1144,40 @@ static void cs_handle_welcome(struct chat_sync* cs, uint64_t peer, const uint8_t* p = pl; uint16_t pc; memcpy(&pc, p, 2); p += 2; for (uint16_t i = 0; i < pc; i++) { - if ((size_t)(p - pl) + 8 + 32 + 32 + 64 + 1 > len) break; + if ((size_t)(p - pl) + 8 + 32 + 32 + 64 + 8 + 1 + 1 > len) break; uint64_t node_id; memcpy(&node_id, p, 8); p += 8; const uint8_t* x25519 = p; p += 32; const uint8_t* ed_pub = p; p += 32; const uint8_t* join_sig = p; p += 64; + uint64_t join_ts; memcpy(&join_ts, p, 8); p += 8; + uint8_t nl = *p++; + char peer_name[256] = ""; + if (nl && p + nl <= pl + len) { memcpy(peer_name, p, nl); peer_name[nl] = '\0'; p += nl; } uint8_t ac = *p++; - topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, NULL); + /* verify join_sig — all fields present in WELCOME wire */ + uint8_t vmsg[256]; size_t vlen = 0; + vlen += snprintf((char*)vmsg + vlen, sizeof(vmsg) - vlen, "%s", ch_id) + 1; + memcpy(vmsg + vlen, &node_id, 8); vlen += 8; + memcpy(vmsg + vlen, x25519, 32); vlen += 32; + { const char* nm = peer_name[0] ? peer_name : ""; + size_t nls = strlen(nm); memcpy(vmsg + vlen, nm, nls); vlen += nls; vmsg[vlen++] = '\0'; } + memcpy(vmsg + vlen, &join_ts, 8); vlen += 8; + { EVP_PKEY* pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, ed_pub, 32); + if (pkey) { + EVP_MD_CTX* ver = EVP_MD_CTX_new(); + if (ver) { + if (EVP_DigestVerifyInit(ver, NULL, NULL, NULL, pkey) != 1 + || EVP_DigestVerify(ver, join_sig, 64, vmsg, vlen) != 1) + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: WELCOME invalid join_sig node=0x%016llx", CS_ID, (unsigned long long)node_id); + EVP_MD_CTX_free(ver); + } + EVP_PKEY_free(pkey); + } + } + + topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, join_ts, NULL, 0, x25519, ed_pub, peer_name, NULL); + topo_node_sqlite_node_update_verified(db, node_id, peer_name, x25519, ed_pub, join_ts); for (uint8_t j = 0; j < ac; j++) { if (p + 1 > pl + len) break; @@ -1157,12 +1220,13 @@ static void cs_handle_welcome(struct chat_sync* cs, uint64_t peer, static void cs_handle_peer_upsert(struct chat_sync* cs, uint64_t peer, const char* ch_id, const uint8_t* pl, size_t len) { - if (len < 8 + 32 + 32 + 64 + 1 + 1 + 1) { DEBUG_WARN(DEBUG_CATEGORY_CONNECTIVITY, "%s: PEER_UPSERT too short len=%zu", CS_ID, len); return; } + if (len < 8 + 32 + 32 + 64 + 8 + 1 + 1 + 1) { DEBUG_WARN(DEBUG_CATEGORY_CONNECTIVITY, "%s: PEER_UPSERT too short len=%zu", CS_ID, len); return; } const uint8_t* p = pl; uint64_t node_id; memcpy(&node_id, p, 8); p += 8; const uint8_t* x25519 = p; p += 32; const uint8_t* ed_pub = p; p += 32; const uint8_t* join_sig = p; p += 64; + uint64_t join_ts; memcpy(&join_ts, p, 8); p += 8; uint8_t name_len = *p++; char peer_name[128] = ""; if (p + name_len <= pl + len) { memcpy(peer_name, p, name_len); peer_name[name_len] = '\0'; p += name_len; } @@ -1175,6 +1239,7 @@ static void cs_handle_peer_upsert(struct chat_sync* cs, uint64_t peer, memcpy(vmsg + vlen, x25519, 32); vlen += 32; { const char* nm = peer_name[0] ? peer_name : ""; size_t nl = strlen(nm); memcpy(vmsg + vlen, nm, nl); vlen += nl; vmsg[vlen++] = '\0'; } + memcpy(vmsg + vlen, &join_ts, 8); vlen += 8; if (cs_ed25519_verify(ed_pub, vmsg, vlen, join_sig) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: PEER_UPSERT invalid sig node=0x%016llx", CS_ID, (unsigned long long)node_id); @@ -1183,7 +1248,8 @@ static void cs_handle_peer_upsert(struct chat_sync* cs, uint64_t peer, sqlite3* db = cs->inst->topo_groups->topo_sqlite_db; - topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, NULL); + topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, join_ts, NULL, 0, x25519, ed_pub, peer_name, NULL); + topo_node_sqlite_node_update_verified(db, node_id, peer_name, x25519, ed_pub, join_ts); if (db && peer_name[0]) { sqlite3_stmt* ns = NULL; sqlite3_prepare_v2(db, "INSERT OR REPLACE INTO nodes(node_id,name,x25519_pubkey,ed25519_pubkey) VALUES(?,?,?,?)", -1, &ns, NULL); diff --git a/tools/chatgui/transport/member_sync.c b/tools/chatgui/transport/member_sync.c index 8075ed61..a91539f3 100644 --- a/tools/chatgui/transport/member_sync.c +++ b/tools/chatgui/transport/member_sync.c @@ -9,6 +9,7 @@ #include #include #include +#include #define MS_ID "member_sync" @@ -44,8 +45,10 @@ static int _addr_cmp(const void* a, const void* b) { } static void _compute_member_hash(uint64_t node_id, const uint8_t* x25519, - const uint8_t* ed25519, const uint8_t* join_sig, - const uint8_t* addrs_data, int addr_count, int online, + const uint8_t* ed25519, + const uint8_t* join_sig, uint64_t join_ts, + const uint8_t* update_sig, uint64_t update_ts, + const uint8_t* addrs_data, int addr_count, uint8_t hash_out[MT_HASH_SIZE]) { EVP_MD_CTX* ctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); @@ -53,6 +56,9 @@ static void _compute_member_hash(uint64_t node_id, const uint8_t* x25519, EVP_DigestUpdate(ctx, x25519, 32); EVP_DigestUpdate(ctx, ed25519, 32); EVP_DigestUpdate(ctx, join_sig, 64); + EVP_DigestUpdate(ctx, &join_ts, 8); + if (update_sig) EVP_DigestUpdate(ctx, update_sig, 64); else { static const uint8_t z[64]; EVP_DigestUpdate(ctx, z, 64); } + EVP_DigestUpdate(ctx, &update_ts, 8); uint8_t ac = (uint8_t)addr_count; EVP_DigestUpdate(ctx, &ac, 1); @@ -75,8 +81,6 @@ static void _compute_member_hash(uint64_t node_id, const uint8_t* x25519, } } - uint8_t ol = (uint8_t)(online ? 1 : 0); - EVP_DigestUpdate(ctx, &ol, 1); EVP_DigestFinal_ex(ctx, hash_out, NULL); EVP_MD_CTX_free(ctx); } @@ -93,9 +97,10 @@ static int _member_update_bucket_hash(void* ctx, const char* ns, uint8_t level, uint64_t mask = (mask_shift >= 0) ? (~0ULL << mask_shift) : UINT64_MAX; char sql[512]; snprintf(sql, sizeof(sql), - "SELECT p.node_id, n.x25519_pubkey, n.ed25519_pubkey, p.join_sig, n.online" - " FROM \"%s\" p JOIN nodes n ON p.node_id=n.node_id" - " WHERE (p.node_id & %lld) == %lld ORDER BY p.node_id ASC", + "SELECT node_id, x25519_pubkey, ed25519_pubkey," + " join_sig, join_ts, update_sig, update_ts" + " FROM \"%s\"" + " WHERE (node_id & %lld) == %lld ORDER BY node_id ASC", peers_tbl, (long long)mask, (long long)prefix64); sqlite3_stmt* stmt = NULL; @@ -107,10 +112,12 @@ static int _member_update_bucket_hash(void* ctx, const char* ns, uint8_t level, const uint8_t* x25 = (const uint8_t*)sqlite3_column_blob(stmt, 1); const uint8_t* ed = (const uint8_t*)sqlite3_column_blob(stmt, 2); const uint8_t* sig = (const uint8_t*)sqlite3_column_blob(stmt, 3); - int online = sqlite3_column_int(stmt, 4); - if (!x25 || !ed || !sig) continue; + uint64_t jts = (uint64_t)sqlite3_column_int64(stmt, 4); + const uint8_t* usig = (const uint8_t*)sqlite3_column_blob(stmt, 5); + uint64_t uts = (uint64_t)sqlite3_column_int64(stmt, 6); + if (!x25 || !ed) continue; uint8_t mh[MT_HASH_SIZE]; - _compute_member_hash(nid, x25, ed, sig, NULL, 0, online, mh); + _compute_member_hash(nid, x25, ed, sig, jts, usig, uts, NULL, 0, mh); EVP_DigestUpdate(sha_ctx, mh, MT_HASH_SIZE); count++; } @@ -129,9 +136,10 @@ static int _member_get_items(void* ctx, const char* ns, uint8_t level, uint64_t mask = (mask_shift >= 0) ? (~0ULL << mask_shift) : UINT64_MAX; char sql[512]; snprintf(sql, sizeof(sql), - "SELECT p.node_id, n.x25519_pubkey, n.ed25519_pubkey, p.join_sig, n.online" - " FROM \"%s\" p JOIN nodes n ON p.node_id=n.node_id" - " WHERE (p.node_id & %lld) == %lld ORDER BY p.node_id ASC", + "SELECT node_id, x25519_pubkey, ed25519_pubkey," + " join_sig, join_ts, update_sig, update_ts, name" + " FROM \"%s\"" + " WHERE (node_id & %lld) == %lld ORDER BY node_id ASC", peers_tbl, (long long)mask, (long long)prefix); sqlite3_stmt* stmt = NULL; @@ -146,8 +154,12 @@ static int _member_get_items(void* ctx, const char* ns, uint8_t level, const uint8_t* x25 = (const uint8_t*)sqlite3_column_blob(stmt, 1); const uint8_t* ed = (const uint8_t*)sqlite3_column_blob(stmt, 2); const uint8_t* sig = (const uint8_t*)sqlite3_column_blob(stmt, 3); - int online = sqlite3_column_int(stmt, 4); - if (!x25 || !ed || !sig) continue; + uint64_t jts = (uint64_t)sqlite3_column_int64(stmt, 4); + const uint8_t* usig = (const uint8_t*)sqlite3_column_blob(stmt, 5); + uint64_t uts = (uint64_t)sqlite3_column_int64(stmt, 6); + const char* nm = (const char*)sqlite3_column_text(stmt, 7); + if (!x25 || !ed) continue; + uint8_t nl = nm ? (uint8_t)strnlen(nm, 255) : 0; sqlite3_stmt* as = NULL; sqlite3_prepare_v2(db, @@ -170,13 +182,26 @@ static int _member_get_items(void* ctx, const char* ns, uint8_t level, sqlite3_finalize(as); } - size_t need = 8 + 32 + 32 + 64 + 1 + 1 + (size_t)addr_off; + uint8_t flags = (sig && jts) ? PEERS_FLAG_HAS_JOIN : 0; + size_t need = 8 + 32 + 32 + 1 + (flags ? 72ULL : 0ULL) + 64 + 8 + 1 + (size_t)nl + 1 + (size_t)addr_off; if (off + need > *len) { sqlite3_finalize(stmt); return -2; } memcpy(buf + off, &nid, 8); off += 8; memcpy(buf + off, x25, 32); off += 32; memcpy(buf + off, ed, 32); off += 32; - memcpy(buf + off, sig, 64); off += 64; - buf[off++] = (uint8_t)(online ? 1 : 0); + buf[off++] = flags; + if (flags & PEERS_FLAG_HAS_JOIN) { + memcpy(buf + off, sig, 64); off += 64; + memcpy(buf + off, &jts, 8); off += 8; + } + if (usig && uts) { + memcpy(buf + off, usig, 64); off += 64; + memcpy(buf + off, &uts, 8); off += 8; + } else { + memset(buf + off, 0, 64); off += 64; + uint64_t z = 0; memcpy(buf + off, &z, 8); off += 8; + } + buf[off++] = nl; + if (nl) { memcpy(buf + off, nm, nl); off += nl; } buf[off++] = (uint8_t)addr_count; memcpy(buf + off, addrs, (size_t)addr_off); off += (size_t)addr_off; (*cnt)++; @@ -194,12 +219,24 @@ static int _member_apply_items(void* ctx, const char* ns, uint16_t count; memcpy(&count, data, 2); const uint8_t* mp = data + 2; size_t mrem = len - 2; - for (uint16_t i = 0; i < count && mrem >= 137; i++) { + for (uint16_t i = 0; i < count && mrem >= 148; i++) { uint64_t nid; memcpy(&nid, mp, 8); mp += 8; mrem -= 8; const uint8_t* x25 = mp; mp += 32; mrem -= 32; const uint8_t* ed = mp; mp += 32; mrem -= 32; - const uint8_t* sig = mp; mp += 64; mrem -= 64; - uint8_t online = *mp++; mrem--; + uint8_t flags = *mp++; mrem--; + const uint8_t* jsig = NULL; uint64_t jts = 0; + if (flags & PEERS_FLAG_HAS_JOIN) { + if (mrem < 72) break; jsig = mp; mp += 64; mrem -= 64; memcpy(&jts, mp, 8); mp += 8; mrem -= 8; + } else { + /* lookup join from local DB */ + uint8_t buf_jsig[64]; uint64_t buf_jts; + if (topo_node_sqlite_member_get_join(_db(inst), ns, nid, buf_jsig, &buf_jts) == 0) { jsig = buf_jsig; jts = buf_jts; } + } + if (mrem < 72) break; + const uint8_t* usig = mp; mp += 64; mrem -= 64; uint64_t uts; memcpy(&uts, mp, 8); mp += 8; mrem -= 8; + uint8_t nl = *mp++; mrem--; + char nm[256] = ""; + if (nl && mrem >= nl) { memcpy(nm, mp, nl); nm[nl] = '\0'; mp += nl; mrem -= nl; } uint8_t ac = *mp++; mrem--; const uint8_t* addrs = mp; int consumed = 0; @@ -208,8 +245,30 @@ static int _member_apply_items(void* ctx, const char* ns, int sz = fam == 4 ? 4 : 16; consumed += sz + 2; } - member_sync_put(inst, ns, nid, x25, ed, sig, addrs, (int)ac); - if (online) member_sync_set_online(inst, nid, 1); + /* verify update_sig if available */ + if (usig && uts && jsig && ed) { + uint8_t vmsg[256]; size_t vlen = 0; + vlen += snprintf((char*)vmsg + vlen, sizeof(vmsg) - vlen, "%s", ns) + 1; + memcpy(vmsg + vlen, &nid, 8); vlen += 8; + memcpy(vmsg + vlen, x25, 32); vlen += 32; + memcpy(vmsg + vlen, &uts, 8); vlen += 8; + memcpy(vmsg + vlen, jsig, 64); vlen += 64; + { EVP_PKEY* pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, ed, 32); + if (pkey) { + EVP_MD_CTX* ver = EVP_MD_CTX_new(); + if (ver) { + int ok = (EVP_DigestVerifyInit(ver, NULL, NULL, NULL, pkey) == 1) + && (EVP_DigestVerify(ver, usig, 64, vmsg, vlen) == 1); + if (!ok) + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: member_sync invalid update_sig node=0x%016llx ns=%s", MS_ID, + (unsigned long long)nid, ns); + EVP_MD_CTX_free(ver); + } + EVP_PKEY_free(pkey); + } + } + } + member_sync_put(inst, ns, nid, x25, ed, jsig, jts, usig, uts, nm, addrs, (int)ac); mp += consumed; mrem -= (size_t)consumed; } return 0; @@ -278,27 +337,17 @@ void member_sync_cancel(struct UTUN_INSTANCE* inst, uint64_t peer, const char* c int member_sync_put(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t member_id, const uint8_t* x25519, - const uint8_t* ed25519, const uint8_t* join_sig, + const uint8_t* ed25519, + const uint8_t* join_sig, uint64_t join_ts, + const uint8_t* update_sig, uint64_t update_ts, + const char* name, const uint8_t* addrs_data, int addr_count) { if (!inst || !ch_id) return -1; sqlite3* db = _db(inst); if (!db) return -1; - sqlite3_stmt* ns = NULL; - sqlite3_prepare_v2(db, - "INSERT OR REPLACE INTO nodes(node_id, name, x25519_pubkey, ed25519_pubkey, online)" - " VALUES(?,COALESCE((SELECT name FROM nodes WHERE node_id=?),''),?,?," - " COALESCE((SELECT online FROM nodes WHERE node_id=?),0))", - -1, &ns, NULL); - if (ns) { - sqlite3_bind_int64(ns, 1, (sqlite3_int64)member_id); - sqlite3_bind_int64(ns, 2, (sqlite3_int64)member_id); - sqlite3_bind_blob(ns, 3, x25519, 32, SQLITE_STATIC); - sqlite3_bind_blob(ns, 4, ed25519, 32, SQLITE_STATIC); - sqlite3_bind_int64(ns, 5, (sqlite3_int64)member_id); - sqlite3_step(ns); sqlite3_finalize(ns); - } - - topo_node_sqlite_member_put(db, ch_id, member_id, join_sig, NULL); + topo_node_sqlite_member_put(db, ch_id, member_id, + join_sig, join_ts, update_sig, update_ts, + x25519, ed25519, name, NULL); if (addrs_data && addr_count > 0) { sqlite3_exec(db, "BEGIN", NULL, NULL, NULL); @@ -356,22 +405,6 @@ void member_sync_set_online(struct UTUN_INSTANCE* inst, uint64_t node_id, int on if (!inst) return; sqlite3* db = _db(inst); if (!db) return; topo_node_sqlite_node_set_online(db, node_id, online); - sqlite3_stmt* stmt = NULL; - if (sqlite3_prepare_v2(db, "SELECT channel_id FROM channels", -1, &stmt, NULL) != SQLITE_OK) return; - while (sqlite3_step(stmt) == SQLITE_ROW) { - const char* ch_id = (const char*)sqlite3_column_text(stmt, 0); - if (!ch_id) continue; - char peers_tbl[128]; _peers_table(ch_id, peers_tbl, sizeof(peers_tbl)); - char buf[256]; snprintf(buf, sizeof(buf), "SELECT 1 FROM \"%s\" WHERE node_id=?", peers_tbl); - sqlite3_stmt* cs = NULL; - if (sqlite3_prepare_v2(db, buf, -1, &cs, NULL) == SQLITE_OK) { - sqlite3_bind_int64(cs, 1, (sqlite3_int64)node_id); - if (sqlite3_step(cs) == SQLITE_ROW) - merkle_sync_recompute_path(inst, ch_id, node_id); - sqlite3_finalize(cs); - } - } - sqlite3_finalize(stmt); } const uint8_t* member_sync_get_hash(struct UTUN_INSTANCE* inst, const char* ch_id, diff --git a/tools/chatgui/transport/member_sync.h b/tools/chatgui/transport/member_sync.h index e9671125..06fee3f6 100644 --- a/tools/chatgui/transport/member_sync.h +++ b/tools/chatgui/transport/member_sync.h @@ -3,7 +3,7 @@ * * Тонкая прослойка над merkle_sync, адаптированная под мемберов каналов. * Данные хранятся в таблицах: nodes, node_addresses, peers_. - * Хеш мембера = SHA256(node_id || x25519 || ed25519 || join_sig || addrs || online). + * Хеш мембера = SHA256(node_id || x25519 || ed25519 || join_sig || join_ts || addrs). * * ── Использование ── * @@ -20,13 +20,12 @@ * } * * // добавление/обновление мембера (из chat_core_create_channel, cs_handle_welcome) - * member_sync_put(inst, ch_id, node_id, x25519, ed25519, join_sig, addrs, ac); + * member_sync_put(inst, ch_id, node_id, x25519, ed25519, join_sig, join_ts, addrs, ac); * // дерево автоматически пересчитано * * // онлайн-статус (из cs_on_conn_up / cs_on_conn_down) * member_sync_set_online(inst, node_id, 1); // online * member_sync_set_online(inst, node_id, 0); // offline - * // пересчитывает дерево для всех каналов, где состоит node_id * * // удаление мембера * member_sync_del(inst, ch_id, node_id); @@ -85,19 +84,24 @@ void member_sync_cancel(struct UTUN_INSTANCE* inst, uint64_t peer, const char* c * x25519 — X25519 публичный ключ (32 байта) * ed25519 — Ed25519 публичный ключ (32 байта) * join_sig — Ed25519 подпись join-сообщения (64 байта) - * сообщение: ch_id || '\0' || node_id(8LE) || x25519(32) || name || '\0' + * сообщение: ch_id || '\0' || node_id(8LE) || x25519(32) || name || '\0' || join_ts(8LE) + * join_ts — NTP-синхронизированное время подписи (unix epoch seconds) + * name — имя узла (из подписанного сообщения) * addrs_data — адреса в wire-формате: [family:1][ip:4|16][port:2]... * addr_count — количество адресов (0 если нет) * - * Пишет в таблицы: nodes (INSERT OR REPLACE), node_addresses, - * peers_ (INSERT OR REPLACE). - * Затем вызывает merkle_sync_recompute_path() для пересчёта дерева. + * Пишет только в таблицу peers_ (INSERT OR REPLACE). + * nodes обновляется отдельно через topo_node_sqlite_node_update_verified(). + * Вызывает merkle_sync_recompute_path() для пересчёта дерева. * * Возвращает 0 при успехе, -1 при ошибке. */ int member_sync_put(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t member_id, const uint8_t* x25519, - const uint8_t* ed25519, const uint8_t* join_sig, + const uint8_t* ed25519, + const uint8_t* join_sig, uint64_t join_ts, + const uint8_t* update_sig, uint64_t update_ts, + const char* name, const uint8_t* addrs_data, int addr_count); /* Удалить мембера из канала и пересчитать дерево. */ @@ -108,7 +112,6 @@ int member_sync_count(struct UTUN_INSTANCE* inst, const char* ch_id); /* * Установить онлайн-статус узла (nodes.online = 0/1). - * Пересчитывает дерево для всех каналов, где состоит node_id. */ void member_sync_set_online(struct UTUN_INSTANCE* inst, uint64_t node_id, int online); diff --git a/tools/chatgui/transport/merkle_sync.h b/tools/chatgui/transport/merkle_sync.h index 1407459e..fa71ce4d 100644 --- a/tools/chatgui/transport/merkle_sync.h +++ b/tools/chatgui/transport/merkle_sync.h @@ -120,7 +120,7 @@ struct merkle_sync_data_ops { * *len — [in] размер буфера, [out] записанный размер * * Wire-формат для мемберов: - * [count:2][node_id:8][x25519:32][ed25519:32][join_sig:64][online:1][addr_cnt:1][addrs:var]... + * [count:2][node_id:8][x25519:32][ed25519:32][flags:1]([join_sig:64][join_ts:8])[update_sig:64][update_ts:8][name_len:1][name:var][addr_cnt:1][addrs:var]... * * Возвращает 0 при успехе, <0 при ошибке, -2 если буфер мал. */ @@ -207,7 +207,7 @@ void merkle_sync_cancel(struct UTUN_INSTANCE* inst, uint64_t peer, const char* n * Пересчитать Merkle-дерево для ключа key — все уровни от 1 до 5. * Вызывается после изменения данных: merkle_sync сам не знает, * когда данные изменились — потребитель должен вызвать явно. - * member_sync делает это внутри put/del/set_online. + * member_sync делает это внутри put/del. */ void merkle_sync_recompute_path(struct UTUN_INSTANCE* inst, const char* ns, uint64_t key);