Browse Source

feat: conn_mgr, dummynet block-addr, indirect routing, alien nodes, nodeinfo refactor

feature/x25519-migration
Evgeny 3 months ago
parent
commit
a7b20f268f
  1. 1
      src/Makefile.am
  2. 1000
      src/conn_mgr.c
  3. 190
      src/conn_mgr.h
  4. 35
      src/dummynet.c
  5. 2
      src/dummynet.h
  6. 2
      src/etcp.h
  7. 3
      src/etcp_api.h
  8. 15
      src/etcp_router.c
  9. 2
      src/msg_transport.c
  10. 25
      src/route_bgp.c
  11. 7
      src/route_bgp.h
  12. 9
      src/route_node.c
  13. 13
      src/route_node.h
  14. 7
      src/route_ping.c
  15. 6
      src/utun_instance.c
  16. 2
      src/utun_instance.h
  17. 6
      tests/Makefile.am
  18. 136
      tests/test_conn_mgr.c
  19. 2
      tests/test_ipv6_sockets.c
  20. 16
      tests/test_nat_detection.c
  21. 4
      tests/test_nat_transport.c
  22. 6
      tests/test_route_ping.c

1
src/Makefile.am

@ -13,6 +13,7 @@ utun_CORE_SOURCES = \
route_node.c \
route_node_lmdb.c \
route_connectivity.c \
conn_mgr.c \
routing.c \
tun_if.c \
tun_route.c \

1000
src/conn_mgr.c

File diff suppressed because it is too large Load Diff

190
src/conn_mgr.h

@ -0,0 +1,190 @@
#ifndef CONN_MGR_H
#define CONN_MGR_H
#include <stdint.h>
#include <stddef.h>
#include "../lib/ll_queue.h"
#include "etcp_api.h"
#include "etcp_router.h"
#include "route_node.h"
struct UTUN_INSTANCE;
struct ETCP_CONN;
struct NODEINFO_Q;
struct ROUTE_BGP;
#define CONN_MGR_MAX_CANDIDATES 3
#define CONN_MGR_CANDIDATE_CACHE_MS 20000
#define CONN_MGR_CANDIDATE_STALE_TB 300000
#define CONN_MGR_CANDIDATE_PING_TB 20000
#define CONN_MGR_BG_PING_INTERVAL_TB 1000
#define CONN_MGR_BG_PING_CYCLE_MIN_TB 100000
#define CONN_MGR_IDLE_CHECK_INTERVAL_TB 10000
#define CONN_MGR_LOCAL_SCAN_ATTEMPTS 3
#define CONN_MGR_LOCAL_SCAN_TIMEOUT_MS 100
#define CONN_MGR_CONNECT_DIRECT_TIMEOUT_MS 5000
#define CONN_MGR_CONNECT_REVERSE_TIMEOUT_MS 15000
#define CONN_MGR_INTERM_EXCHANGE_TIMEOUT_MS 15000
#define CONN_MGR_OK 0
#define CONN_MGR_ERR_NOT_FOUND -1
#define CONN_MGR_ERR_NO_ADDRESSES -2
#define CONN_MGR_ERR_TIMEOUT -3
#define CONN_MGR_ERR_UNREACHABLE -4
#define CONN_MGR_ERR_REFUSED -5
#define CONN_MGR_ERR_ALREADY_CONNECTED -6
#define CONN_MGR_ERR_INTERNAL -7
enum CONN_MGR_STATE {
CONN_MGR_STATE_DISCONNECTED = 0,
CONN_MGR_STATE_CONNECTING = 1,
CONN_MGR_STATE_CONNECTED = 2,
};
enum CM_TRY_STATE {
CM_TRY_NONE = 0,
CM_TRY_PENDING = 1,
CM_TRY_OK = 2,
CM_TRY_FAILED = 3,
};
#define CONN_MGR_SUBCMD_DIRECT_REQ 0x01
#define CONN_MGR_SUBCMD_DIRECT_RESP 0x02
#define CONN_MGR_SUBCMD_INTERM_EXCHANGE_REQ 0x03
#define CONN_MGR_SUBCMD_INTERM_EXCHANGE_RESP 0x04
#define CONN_MGR_SUBCMD_INTERM_SELECTED 0x05
#define CONN_MGR_SUBCMD_DISCONNECT 0x06
struct CONN_MGR_CANDIDATE {
uint64_t node_id;
uint16_t rtt;
} __attribute__((packed));
#pragma pack(push, 1)
struct CONN_MGR_DIRECT_REQ {
uint8_t cmd;
uint8_t subcmd;
uint32_t request_id;
uint8_t addr_count;
};
struct CONN_MGR_DIRECT_RESP {
uint8_t cmd;
uint8_t subcmd;
uint32_t request_id;
uint8_t accepted;
};
struct CONN_MGR_INTERM_EXCHANGE_REQ {
uint8_t cmd;
uint8_t subcmd;
uint32_t request_id;
uint8_t candidate_count;
struct CONN_MGR_CANDIDATE candidates[4];
};
struct CONN_MGR_INTERM_EXCHANGE_RESP {
uint8_t cmd;
uint8_t subcmd;
uint32_t request_id;
uint8_t my_count;
uint8_t your_count;
struct CONN_MGR_CANDIDATE my_candidates[4];
struct CONN_MGR_CANDIDATE your_candidates[4];
};
struct CONN_MGR_INTERM_SELECTED {
uint8_t cmd;
uint8_t subcmd;
uint32_t request_id;
uint8_t count;
struct CONN_MGR_CANDIDATE selected[3];
};
struct CONN_MGR_DISCONNECT {
uint8_t cmd;
uint8_t subcmd;
uint64_t node_id;
};
#pragma pack(pop)
#define CONN_MGR_DIRECT_REQ_HDR_SIZE sizeof(struct CONN_MGR_DIRECT_REQ)
#define CONN_MGR_DIRECT_RESP_HDR_SIZE sizeof(struct CONN_MGR_DIRECT_RESP)
#define CONN_MGR_INTERM_EXCHANGE_REQ_SIZE sizeof(struct CONN_MGR_INTERM_EXCHANGE_REQ)
#define CONN_MGR_INTERM_EXCHANGE_RESP_SIZE sizeof(struct CONN_MGR_INTERM_EXCHANGE_RESP)
#define CONN_MGR_INTERM_SELECTED_SIZE sizeof(struct CONN_MGR_INTERM_SELECTED)
#define CONN_MGR_DISCONNECT_SIZE sizeof(struct CONN_MGR_DISCONNECT)
typedef void (*conn_mgr_connect_callback_t)(int result, uint64_t node_id, void* arg);
struct cm_cb_node {
conn_mgr_connect_callback_t cb;
void* arg;
struct cm_cb_node* next;
};
struct CONN_MGR_ENTRY {
uint64_t node_id;
uint8_t state;
uint8_t conn_type;
uint8_t alien;
uint32_t idle_timeout_ms;
uint64_t last_traffic_tb;
void* idle_timer;
struct cm_cb_node* cb_list;
uint64_t intermediaries[CONN_MGR_MAX_INTERMEDIARIES];
uint8_t intermediariy_count;
uint8_t rr_idx;
uint8_t local_scan_state;
uint8_t main_connect_state;
struct {
uint8_t phase;
void* timer;
uint32_t request_id;
void* conn_ctx;
} main;
struct CONN_MGR* mgr;
};
struct CONN_MGR {
struct UTUN_INSTANCE* instance;
struct CONN_MGR_ENTRY* entries;
size_t entry_count;
size_t entry_capacity;
void* bg_ping_timer;
size_t bg_ping_cursor;
uint64_t bg_ping_cycle_start_tb;
void* candidate_ping_timer;
struct CONN_MGR_CANDIDATE best_candidates[CONN_MGR_MAX_CANDIDATES];
uint8_t best_candidate_count;
uint32_t next_request_id;
uint8_t initialized;
struct cm_reverse_pending* reverse_pending;
struct cm_exchange_pending* exchange_pending;
};
struct CONN_MGR* conn_mgr_init(struct UTUN_INSTANCE* instance);
void conn_mgr_destroy(struct CONN_MGR* mgr);
int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id,
uint32_t idle_timeout_ms,
conn_mgr_connect_callback_t cb, void* cb_arg);
int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id);
int conn_mgr_set_idle_timeout(struct CONN_MGR* mgr, uint64_t node_id, uint32_t timeout_ms);
int conn_mgr_get_status(struct CONN_MGR* mgr, uint64_t node_id,
uint8_t* out_state, uint8_t* out_conn_type);
int conn_mgr_add_alien_node(struct CONN_MGR* mgr, const uint8_t* nodeinfo_data, size_t len);
int conn_mgr_send(struct CONN_MGR* mgr, uint64_t node_id, struct ll_entry* entry);
void conn_mgr_update_best_candidates(struct CONN_MGR* mgr, uint64_t node_id, uint16_t rtt);
#endif

35
src/dummynet.c

@ -577,6 +577,11 @@ struct dummynet_filter {
uint32_t jitter_ms;
struct ETCP_LINK* link;
struct dummynet_stats stats;
struct {
uint32_t ip;
uint16_t port;
} block_addrs[4];
uint8_t block_count;
};
static ssize_t dummynet_filter_send_hook(socket_t fd, const void* buf, size_t len,
@ -585,6 +590,16 @@ static ssize_t dummynet_filter_send_hook(socket_t fd, const void* buf, size_t le
struct dummynet_filter* df = (struct dummynet_filter*)ctx;
(void)link;
df->stats.recv++;
if (addr && addr->sa_family == AF_INET && df->block_count > 0) {
const struct sockaddr_in* sin = (const struct sockaddr_in*)addr;
for (uint8_t i = 0; i < df->block_count; i++) {
if (sin->sin_addr.s_addr == df->block_addrs[i].ip &&
sin->sin_port == df->block_addrs[i].port) {
df->stats.lost++;
return (ssize_t)len;
}
}
}
if (df->loss_permille > 0) {
uint32_t roll = (uint32_t)rand() % 1000;
if (roll < df->loss_permille) {
@ -637,6 +652,26 @@ void dummynet_filter_destroy(struct dummynet_filter* df) {
u_free(df);
}
void dummynet_filter_block_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port) {
if (!df || df->block_count >= 4) return;
for (uint8_t i = 0; i < df->block_count; i++)
if (df->block_addrs[i].ip == ip && df->block_addrs[i].port == port) return;
df->block_addrs[df->block_count].ip = ip;
df->block_addrs[df->block_count].port = port;
df->block_count++;
}
void dummynet_filter_unblock_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port) {
if (!df) return;
for (uint8_t i = 0; i < df->block_count; i++) {
if (df->block_addrs[i].ip == ip && df->block_addrs[i].port == port) {
df->block_count--;
if (i < df->block_count) memmove(&df->block_addrs[i], &df->block_addrs[i+1], (df->block_count - i) * sizeof(df->block_addrs[0]));
return;
}
}
}
const struct dummynet_stats* dummynet_filter_get_stats(struct dummynet_filter* df) {
return df ? &df->stats : NULL;
}

2
src/dummynet.h

@ -176,5 +176,7 @@ void dummynet_filter_attach(struct dummynet_filter* df, struct ETCP_LINK* link);
void dummynet_filter_detach(struct dummynet_filter* df);
void dummynet_filter_destroy(struct dummynet_filter* df);
const struct dummynet_stats* dummynet_filter_get_stats(struct dummynet_filter* df);
void dummynet_filter_block_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
void dummynet_filter_unblock_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
#endif // DUMMYNET_H

2
src/etcp.h

@ -133,7 +133,7 @@ struct ETCP_CONN {
// IDs and state
uint32_t next_tx_id; // Next TX ID
uint32_t next_tx_id; // ID для добавления в очередь отправки (с этим id будет добавлен следующий пакет)
uint32_t last_rx_id; // Last received ID
uint32_t last_delivered_id; // Last delivered to output_queue

3
src/etcp_api.h

@ -6,6 +6,8 @@
* - etcp_send() - отправить пакет в очередь normalizer
* - etcp_bind() - подписаться на пакеты с определенным ID
* - etcp_int_recv() - коллбэк для сбора пакетов из всех подключений
* !!! для обычной отправки-приёма между узлами используем более универсальный модушь etcp_router.
* это в первую очередь - апи для использования etcp-router-ом
*
* Формат кодограмм: <cmd 1 byte> <data ... n bytes>
* cmd = 0 - пакет для передачи адресату
@ -30,6 +32,7 @@
#define ETCP_ID_ICMP_PROXY 0x06 // ICMP echo прокси (ping через exit)
#define ETCP_ID_TCP_PROXY_CLIENT 0x07 // TCP proxy client (клиент)
#define ETCP_ID_MSG_TRANSPORT 0x10 // msg_transport — локальный IPC транспорт сообщений
#define ETCP_ID_CONN_MGR 0x11 // Connection Manager — management connections
// Forward declarations
struct ETCP_CONN;

15
src/etcp_router.c

@ -76,7 +76,18 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t*
}
if (pl_len > 0) memcpy(dgram + SVC_ROUTE_HDR_SIZE, payload, pl_len);
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, rconn->remote_node_id);
struct ETCP_CONN* conn = NULL;
struct ROUTE_BGP* bgp = inst->bgp;
if (bgp) {
struct NODEINFO_Q* nq = nodeinfo_find_by_id(bgp, rconn->remote_node_id);
if (nq && nq->conn_mgr_type == CONN_TYPE_INDIRECT && nq->conn_mgr_intermediariy_count > 0) {
for (uint8_t i = 0; i < nq->conn_mgr_intermediariy_count; i++) {
conn = route_bgp_find_conn_for_node(bgp, nq->conn_mgr_intermediaries[i]);
if (conn) break;
}
}
}
if (!conn) conn = route_bgp_find_conn_for_node(bgp, rconn->remote_node_id);
if (!conn) {
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: no route to %016llx svc_id=%u",
(unsigned long long)rconn->remote_node_id, rconn->svc_id);
@ -376,7 +387,7 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry)
actual_payload = pl_len - SC_SIGN_SIZE;
uint8_t* sig = pl + actual_payload;
struct ROUTE_BGP* bgp = inst->bgp;
struct NODEINFO_Q* nq = bgp ? route_bgp_get_node(bgp, hdr->src_node_id) : NULL;
struct NODEINFO_Q* nq = bgp ? nodeinfo_find_by_id(bgp, hdr->src_node_id) : NULL;
if (!nq) {
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: SIGNED packet from unknown node %016llx — dropping", (unsigned long long)hdr->src_node_id);
queue_dgram_free(entry); queue_entry_free(entry);

2
src/msg_transport.c

@ -462,7 +462,7 @@ static void handle_client_data(struct msg_transport* t, struct msg_client* clien
send_error(client, MSG_ERR_PEER_NOT_FOUND, "BGP not initialized");
break;
}
struct NODEINFO_Q* nq = route_bgp_get_node(bgp, peer_id);
struct NODEINFO_Q* nq = nodeinfo_find_by_id(bgp, peer_id);
if (!nq) {
send_error(client, MSG_ERR_PEER_NOT_FOUND, "peer not found");
DEBUG_WARN(DEBUG_CATEGORY_MSGTRANSPORT, "GET_PEER_INFO: peer=%016llx not found",

25
src/route_bgp.c

@ -370,9 +370,16 @@ void route_bgp_new_conn(struct ETCP_CONN* conn) {
}
struct ROUTE_BGP* bgp = conn->instance->bgp;
struct NODEINFO_Q* peer_nq = nodeinfo_find_by_id(bgp, conn->peer_node_id);
route_bgp_add_to_senders(bgp, conn);
if (peer_nq && peer_nq->alien) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: peer 0x%016llx is alien, skipping route exchange",
(unsigned long long)conn->peer_node_id);
return;
}
// Scan ALL connections and ALL links in instance - start NAT check if not started
struct ETCP_CONN* c = conn->instance->connections;
while (c) {
@ -483,19 +490,9 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
* NEW NODEINFO BASED IMPLEMENTATION
* ================================================ */
struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id) {
if (!bgp || !bgp->nodes) {
return NULL;
}
uint64_t key = node_id;
struct ll_entry* e = queue_find_data_by_index(bgp->nodes, &key);
return e ? (struct NODEINFO_Q*)e : NULL;
}
struct ETCP_CONN* route_bgp_find_conn_for_node(struct ROUTE_BGP* bgp, uint64_t node_id) {
if (!bgp) return NULL;
struct NODEINFO_Q* nq = route_bgp_get_node(bgp, node_id);
struct NODEINFO_Q* nq = nodeinfo_find_by_id(bgp, node_id);
if (!nq || !nq->paths || !nq->paths->head) return NULL;
struct ll_entry* e = nq->paths->head;
struct NODEINFO_PATH* best = NULL;
@ -744,7 +741,7 @@ int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, co
return -1;
}
struct NODEINFO_Q* nodeinfo1 = route_bgp_get_node(bgp, node_id);
struct NODEINFO_Q* nodeinfo1 = nodeinfo_find_by_id(bgp, node_id);
uint8_t new_ver = ni->ver;
if (nodeinfo1 && (int8_t)(nodeinfo1->last_ver-new_ver)>=0) {
@ -787,6 +784,8 @@ int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, co
nodeinfo1->connectivity.interface_status = PROBE_RESULT_UNKNOWN;
nodeinfo1->connectivity.nat_status = PROBE_RESULT_UNKNOWN;
nodeinfo1->connectivity.real_status = PROBE_RESULT_UNKNOWN;
nodeinfo1->connectivity.ping_req_time = 0;
nodeinfo1->alien = 0;
queue_data_put_with_index(bgp->nodes, &nodeinfo1->ll);
} else {
socks_changed = (nodeinfo1->node.local_v4_sockets != ni->local_v4_sockets) ||
@ -872,7 +871,7 @@ int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* sender,
uint64_t node_id = wp->node_id;
uint64_t wd_source = wp->wd_source;
struct NODEINFO_Q* nq = route_bgp_get_node(bgp, node_id);
struct NODEINFO_Q* nq = nodeinfo_find_by_id(bgp, node_id);
if (!nq) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found");
return 0;

7
src/route_bgp.h

@ -155,13 +155,6 @@ void route_bgp_send_nodeinfo(struct NODEINFO_Q* node, struct ETCP_CONN* conn);
*/
void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id);
/**
* @brief Поиск NODEINFO_Q по node_id через hash в nodes queue.
*
* @return node или NULL
*/
struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id);
int nodeinfo_dyn_size(struct NODEINFO* node);
/**

9
src/route_node.c

@ -121,6 +121,13 @@ int get_node_v6_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_SUBNE
return (int)info->local_v6_subnets;
}
struct NODEINFO_Q* nodeinfo_find_by_id(struct ROUTE_BGP* bgp, uint64_t node_id) {
if (!bgp || !bgp->nodes) return NULL;
uint64_t key = node_id;
struct ll_entry* e = queue_find_data_by_index(bgp->nodes, &key);
return e ? (struct NODEINFO_Q*)e : NULL;
}
int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp) {
if (!instance || !bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_update_my_nodeinfo: invalid args");
@ -181,7 +188,7 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG
}
}
if (changed) {
if (changed) {
if (bgp->local_node) u_free(bgp->local_node);
bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + dyn);
if (!bgp->local_node) return -1;

13
src/route_node.h

@ -33,6 +33,12 @@ struct UTUN_INSTANCE;
#define CONN_PROBE_COUNT 3
#define CONN_PROBE_TIMEOUT_MS 1000
#define CONN_TYPE_NONE 0
#define CONN_TYPE_DIRECT 1
#define CONN_TYPE_REVERSE 2
#define CONN_TYPE_INDIRECT 3
#define CONN_MGR_MAX_INTERMEDIARIES 3
// ---- состояние связности с удалённым узлом (локальное, не передаётся по BGP) ----
struct NODE_CONNECTIVITY {
@ -49,6 +55,7 @@ struct NODE_CONNECTIVITY {
uint64_t interface_probe_time;
uint64_t nat_probe_time;
uint64_t real_probe_time;
uint64_t ping_req_time; // время последнего полученного ping request от этого узла (0.1ms tb)
};
/**
@ -133,6 +140,10 @@ struct NODEINFO_Q {
struct ll_queue* paths; // сюда помещаем struct NODEINFO_PATH
uint8_t dirty;
uint8_t last_ver;
uint8_t alien; // 1 = чужой узел (не запрашиваем роутинг при connect)
uint8_t conn_mgr_type; // CONN_TYPE_* — тип managed-подключения
uint64_t conn_mgr_intermediaries[CONN_MGR_MAX_INTERMEDIARIES]; // посредники для INDIRECT
uint8_t conn_mgr_intermediariy_count;
struct NODE_CONNECTIVITY connectivity; // состояние связности (локальное)
struct ETCP_SOCKET* best_socket;
struct NODEINFO node; // Всегда в конце структуры - динамически расширяемый блок
@ -174,4 +185,6 @@ int get_node_v6_sockets_meta(struct NODEINFO_Q *node, const struct NODEINFO_IPV6
*/
int get_node_v6_addrs(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_ADDR **out_addrs);
struct NODEINFO_Q* nodeinfo_find_by_id(struct ROUTE_BGP* bgp, uint64_t node_id);
#endif // ROUTE_NODE_H

7
src/route_ping.c

@ -325,6 +325,11 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp,
ctx->request_id = req_pkt->request_id;
ctx->count_total = req_pkt->count;
ctx->timeout_ms = req_pkt->timeout_ms;
{
struct NODEINFO_Q* req_nq = nodeinfo_find_by_id(bgp, from_conn->peer_node_id);
if (req_nq) req_nq->connectivity.ping_req_time = get_time_tb();
}
if (len >= sizeof(struct BGP_PING_REQUEST)) {
memcpy(ctx->pubkey, req_pkt->pubkey, SC_PUBKEY_SIZE);
}
@ -338,7 +343,7 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp,
/* Если target не указан — можно разрешить из nodeinfo */
/*
if (sin->sin_addr.s_addr == 0 && sin->sin_port == 0) {
struct NODEINFO_Q* nq = route_bgp_get_node(bgp, req_pkt->node_id);
struct NODEINFO_Q* nq = nodeinfo_find_by_id(bgp, req_pkt->node_id);
if (nq) {
const struct NODEINFO_IPV4_ADDR* addrs;
int count = get_node_v4_addrs(nq, &addrs);

6
src/utun_instance.c

@ -11,6 +11,7 @@
#include "route_bgp.h"
#include "etcp_connections.h"
#include "etcp.h"
#include "conn_mgr.h"
#include "control_server.h"
#include "msg_transport.h"
#include "../lib/u_async.h"
@ -130,6 +131,9 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
}
}
instance->conn_mgr = conn_mgr_init(instance);
if (!instance->conn_mgr) DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "Failed to initialize Connection Manager (non-fatal)");
// Initialize firewall
fw_init(&instance->fw);
if (fw_load_rules(&instance->fw, &config->global) != 0) {
@ -374,6 +378,8 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
// Cleanup etcp_router
etcp_router_destroy(instance);
if (instance->conn_mgr) { conn_mgr_destroy(instance->conn_mgr); instance->conn_mgr = NULL; }
// Cleanup BGP module
if (instance->bgp) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Destroying BGP module");

2
src/utun_instance.h

@ -33,6 +33,7 @@ struct ROUTE_BGP;
struct control_server;
struct msg_transport;
struct PING_CONTEXT;
struct CONN_MGR;
// uTun instance configuration
struct UTUN_INSTANCE {
@ -112,6 +113,7 @@ struct UTUN_INSTANCE {
// etcp_router bindings и seq-connections (per-instance service routing)
struct ETCP_ROUTER_BINDINGS router_bindings;
struct ll_queue* router_conns;
struct CONN_MGR* conn_mgr; // Connection Manager (может быть NULL)
// TCP proxy server (exit node)
struct tcp_proxy_server tcp_proxy_server;

6
tests/Makefile.am

@ -44,6 +44,7 @@ check_PROGRAMS = \
test_icmp_proxy \
test_tcp_proxy_client \
test_bgp_route_exchange \
test_conn_mgr \
test_bbr_integration \
test_intensive_memory_pool \
test_tcp_io \
@ -94,6 +95,7 @@ ETCP_FULL_OBJS = \
$(top_builddir)/src/utun-route_node.o \
$(top_builddir)/src/utun-route_node_lmdb.o \
$(top_builddir)/src/utun-route_connectivity.o \
$(top_builddir)/src/utun-conn_mgr.o \
$(top_builddir)/src/utun-routing.o \
$(top_builddir)/src/utun-tun_if.o \
$(top_builddir)/src/utun-tun_route.o \
@ -308,6 +310,10 @@ test_bgp_route_exchange_SOURCES = test_bgp_route_exchange.c
test_bgp_route_exchange_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_bgp_route_exchange_LDADD = $(top_builddir)/src/utun-dummynet.o $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS)
test_conn_mgr_SOURCES = test_conn_mgr.c
test_conn_mgr_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_conn_mgr_LDADD = $(top_builddir)/src/utun-dummynet.o $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS)
test_bbr_integration_SOURCES = bbr_integration/test_bbr_integration.c
test_bbr_integration_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_bbr_integration_LDADD = $(top_builddir)/src/utun-dummynet.o $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS)

136
tests/test_conn_mgr.c

@ -0,0 +1,136 @@
/**
* @file test_conn_mgr.c
* @brief Connection Manager — smoke test
*
* Test 1: Direct via existing ETCP conn → CONN_TYPE_DIRECT
* Test 2: Status check → state/type
* Test 3: Alien node → NODEINFO_Q.alien=1
*
* Pending:
* Test 4: New link UP callback (INIT to new peer via conn_mgr)
* Test 5: Indirect through intermediary (needs full Phase 3 exchange)
* Test 6: Idle timeout
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "../lib/platform_compat.h"
#include "test_utils.h"
#ifndef _WIN32
#include <unistd.h>
#endif
#include "../src/etcp.h"
#include "../src/etcp_connections.h"
#include "../src/config_parser.h"
#include "../src/config_updater.h"
#include "../src/utun_instance.h"
#include "../src/route_bgp.h"
#include "../src/conn_mgr.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#define TIMEOUT_TB 300000
#define POLL_MS 5
#define NID_A 0xAAAA000000000001ULL
#define NID_B 0xBBBB000000000002ULL
static struct UTUN_INSTANCE* g_a = NULL, *g_b = NULL;
static struct UASYNC* ua = NULL;
static int result = 0;
static void* ttimer = NULL;
static char tdir[] = "/tmp/utun_cm_XXXXXX";
static char ca[256], cb[256];
static int pa = 0, pb = 0;
static volatile int cdone = 0, cresult = 0;
static int wf(const char* p, const char* f, ...) {
va_list ap; FILE* fp = fopen(p, "w"); if (!fp) return -1;
va_start(ap, f); vfprintf(fp, f, ap); va_end(ap); fclose(fp); return 0;
}
static char* gv(const char* p, const char* k) {
struct utun_config* c = parse_config(p); if (!c) return NULL;
char* r = (strcmp(k, "pub") == 0) ? u_strdup(c->global.my_public_key_hex) : u_strdup(c->global.my_private_key_hex);
free_config(c); return r;
}
static int lks(struct UTUN_INSTANCE* i) {
int n = 0; struct ETCP_CONN* c = i->connections;
while (c) { struct ETCP_LINK* l = c->links; while (l) { if (l->initialized && l->link_status) n++; l = l->next; } c = c->next; }
return n;
}
static void fail(const char* msg) { fprintf(stderr, "FAIL: %s\n", msg); fflush(stderr); result = 2; }
static void test1(void* arg);
static void test2(void* arg);
static void test3(void* arg);
static void to_cb(void* arg) { (void)arg; fprintf(stderr, "TIMEOUT\n"); result = 2; }
static void ccb(int r, uint64_t id, void* arg) {
(void)arg; fprintf(stderr, "connect_cb: result=%d node=0x%llx\n", r, (unsigned long long)id); fflush(stderr);
cdone = 1; cresult = r;
}
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; }
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);
uasync_set_timeout(ua, 100, NULL, (timeout_callback_t)test2, "t2a");
}
static void test2(void* arg) {
(void)arg; if (result) return;
if (!cdone) { uasync_set_timeout(ua, 100, 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);
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; }
test3(NULL);
}
static void test3(void* arg) {
(void)arg;
uint64_t aid = 0xEEEE000000000001ULL;
struct NODEINFO ni; memset(&ni, 0, sizeof(ni)); ni.node_id = aid; ni.ver = 1;
conn_mgr_add_alien_node(g_a->conn_mgr, (uint8_t*)&ni, sizeof(ni));
struct NODEINFO_Q* nq = nodeinfo_find_by_id(g_a->bgp, aid);
fprintf(stderr, "Test 3: alien alien=%d\n", nq ? nq->alien : -1); fflush(stderr);
if (nq && nq->alien == 1) fprintf(stderr, " OK: alien flag set\n");
else { fail("alien flag not set"); return; }
fprintf(stderr, "=== ALL DONE ===\n"); fflush(stderr);
result = (result == 0) ? 1 : 2;
}
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);
config_ensure_keys_and_node_id(ca); config_ensure_keys_and_node_id(cb);
char *p0 = gv(ca,"pub"), *r0 = gv(ca,"priv"), *p1 = gv(cb,"pub"), *r1 = gv(cb,"priv");
wf(ca, "[global]\nmy_node_id=0x%llx\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.99.0.1/24\ntun_ifname=tun99\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[client: to_b]\nkeepalive=1\npeer_public_key=%s\nlink=s1:127.0.0.1:%d\n[allowed_keys]\nallow_all=1\n", NID_A, r0, p0, pa, p1, pb);
wf(cb, "[global]\nmy_node_id=0x%llx\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.99.0.2/24\ntun_ifname=tun98\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", NID_B, r1, p1, pb);
u_free(p0); u_free(r0); u_free(p1); u_free(r1);
}
static void cleanup(void) { test_unlink(ca); test_unlink(cb); test_rmdir(tdir); }
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
utun_instance_set_tun_init_enabled(0); setup();
ua = uasync_create();
g_a = utun_instance_create(ua, ca); g_b = utun_instance_create(ua, cb);
if (!g_a || !g_b) goto done;
utun_instance_init(g_a); utun_instance_init(g_b);
uasync_call_soon(ua, NULL, test1);
ttimer = uasync_set_timeout(ua, TIMEOUT_TB, NULL, to_cb, "to");
{ int el = 0; while (!result && el < TIMEOUT_TB + 5000) { uasync_poll(ua, POLL_MS); el += POLL_MS; } }
fprintf(stderr, "final result=%d\n", result); fflush(stderr);
done:
if (ttimer) uasync_cancel_timeout(ua, ttimer);
if (g_a) { g_a->running = 0; utun_instance_destroy(g_a); }
if (g_b) { g_b->running = 0; utun_instance_destroy(g_b); }
if (ua) { uasync_destroy(ua, 0); ua = NULL; }
cleanup();
return (result == 1) ? 0 : 1;
}

2
tests/test_ipv6_sockets.c

@ -221,7 +221,7 @@ static int verify_remote_v6_nodeinfo(const char* name, struct UTUN_INSTANCE* ins
printf("FAIL [%s]: no bgp\n", name);
return 0;
}
struct NODEINFO_Q* nq = route_bgp_get_node(inst->bgp, peer_node_id);
struct NODEINFO_Q* nq = nodeinfo_find_by_id(inst->bgp, peer_node_id);
if (!nq) {
printf("FAIL [%s]: remote node %016llx not found\n", name, (unsigned long long)peer_node_id);
return 0;

16
tests/test_nat_detection.c

@ -244,16 +244,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 (inst_s->bgp && route_bgp_get_node(inst_s->bgp, NODE_ID_C1) != NULL &&
route_bgp_get_node(inst_s->bgp, NODE_ID_C2) != NULL) {
if (inst_s->bgp && nodeinfo_find_by_id(inst_s->bgp, NODE_ID_C1) != NULL &&
nodeinfo_find_by_id(inst_s->bgp, NODE_ID_C2) != NULL) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "S learned about C1 and C2");
break;
}
uasync_poll(ua, 10);
bgp_wait_cycles++;
}
if (!inst_s->bgp || route_bgp_get_node(inst_s->bgp, NODE_ID_C1) == NULL ||
route_bgp_get_node(inst_s->bgp, NODE_ID_C2) == NULL) {
if (!inst_s->bgp || nodeinfo_find_by_id(inst_s->bgp, NODE_ID_C1) == NULL ||
nodeinfo_find_by_id(inst_s->bgp, NODE_ID_C2) == NULL) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "S did not learn about C1/C2 in time");
goto cleanup;
}
@ -313,7 +313,7 @@ int main(void) {
}
// 5. Verify all NAT fields on server
struct NODEINFO_Q* node_c1 = route_bgp_get_node(inst_s->bgp, NODE_ID_C1);
struct NODEINFO_Q* node_c1 = nodeinfo_find_by_id(inst_s->bgp, NODE_ID_C1);
if (!node_c1) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO_Q for C1 disappeared");
goto cleanup;
@ -408,7 +408,7 @@ int main(void) {
bgp_wait_cycles = 0;
int c2_verified_nat = 0;
while (!test_timed_out && bgp_wait_cycles < 500) {
struct NODEINFO_Q* node_c1_on_c2 = inst_c2->bgp ? route_bgp_get_node(inst_c2->bgp, NODE_ID_C1) : NULL;
struct NODEINFO_Q* node_c1_on_c2 = inst_c2->bgp ? nodeinfo_find_by_id(inst_c2->bgp, NODE_ID_C1) : NULL;
if (node_c1_on_c2) {
const struct NODEINFO_IPV4_SOCKET_META* meta = NULL;
int meta_count = get_node_v4_sockets_meta(node_c1_on_c2, &meta);
@ -477,14 +477,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 (inst_c2->bgp && route_bgp_get_node(inst_c2->bgp, NODE_ID_C1) != NULL) {
if (inst_c2->bgp && nodeinfo_find_by_id(inst_c2->bgp, NODE_ID_C1) != NULL) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "C2 learned C1 nodeinfo");
break;
}
uasync_poll(ua, 10);
bgp_wait_cycles++;
}
if (!inst_c2->bgp || route_bgp_get_node(inst_c2->bgp, NODE_ID_C1) == NULL) {
if (!inst_c2->bgp || nodeinfo_find_by_id(inst_c2->bgp, NODE_ID_C1) == NULL) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: C2 did not learn C1 nodeinfo in time");
goto cleanup;
}

4
tests/test_nat_transport.c

@ -565,8 +565,8 @@ int main(void) {
DEBUG_INFO(DEBUG_CATEGORY_NAT, "Waiting for BGP...");
int bgp_cycles = 0;
while (!test_timed_out && bgp_cycles < 500) {
if (inst_provider->bgp && route_bgp_get_node(inst_provider->bgp, NODE_ID_CLIENT) &&
inst_client->bgp && route_bgp_get_node(inst_client->bgp, NODE_ID_PROVIDER)) {
if (inst_provider->bgp && nodeinfo_find_by_id(inst_provider->bgp, NODE_ID_CLIENT) &&
inst_client->bgp && nodeinfo_find_by_id(inst_client->bgp, NODE_ID_PROVIDER)) {
DEBUG_INFO(DEBUG_CATEGORY_NAT, "BGP exchanged");
break;
}

6
tests/test_route_ping.c

@ -248,14 +248,14 @@ 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 (inst_b->bgp && route_bgp_get_node(inst_b->bgp, NODE_ID_C) != NULL) {
if (inst_b->bgp && nodeinfo_find_by_id(inst_b->bgp, NODE_ID_C) != NULL) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "B learned about C");
break;
}
uasync_poll(ua, 10);
bgp_wait_cycles++;
}
if (!inst_b->bgp || route_bgp_get_node(inst_b->bgp, NODE_ID_C) == NULL) {
if (!inst_b->bgp || nodeinfo_find_by_id(inst_b->bgp, NODE_ID_C) == NULL) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "B did not learn about C in time");
goto cleanup;
}
@ -271,7 +271,7 @@ int main(void) {
/* addr[] в network byte order, port в host byte order */
uint32_t target_ip = 0;
uint16_t target_port = 0;
struct NODEINFO_Q* nq = inst_b->bgp ? route_bgp_get_node(inst_b->bgp, NODE_ID_C) : NULL;
struct NODEINFO_Q* nq = inst_b->bgp ? nodeinfo_find_by_id(inst_b->bgp, NODE_ID_C) : NULL;
if (nq) {
const struct NODEINFO_IPV4_ADDR* addrs;
int sc = get_node_v4_addrs(nq, &addrs);

Loading…
Cancel
Save