Remove obsolete parallel ETCP connection installer
This commit is contained in:
1 parent
8169322d56
commit
008f499367
3 files changed
-523
No files matched your search
@@ -1,419 +0,0 @@
|
||||
#include "etcp_api.h"
|
||||
#include "etcp.h"
|
||||
#include "etcp_connect.h"
|
||||
#include "utun_instance.h"
|
||||
#include "topo_node.h"
|
||||
#include "stcp_link.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include "../lib/socket_compat.h"
|
||||
#include <string.h>
|
||||
|
||||
#define DEBUG_CATEGORY_ETCP_CONNECT DEBUG_CATEGORY_ETCP
|
||||
|
||||
struct etcp_connect_cb_node {
|
||||
etcp_connect_callback_t cb;
|
||||
void* arg;
|
||||
uint8_t flags;
|
||||
struct etcp_connect_cb_node* next;
|
||||
};
|
||||
|
||||
struct ETCP_CONNECT {
|
||||
struct ETCP_CONNECT* next;
|
||||
struct UTUN_INSTANCE* instance;
|
||||
uint64_t node_id;
|
||||
struct ETCP_CONN* conn;
|
||||
struct etcp_connect_cb_node* cb_list;
|
||||
void* initial_timer;
|
||||
void* settle_timer;
|
||||
uint16_t min_rtt;
|
||||
uint8_t early_delivered : 1;
|
||||
uint8_t done : 1;
|
||||
};
|
||||
static void connect_init_cb(struct ETCP_CONN* conn, int event, void* arg);
|
||||
|
||||
static struct ETCP_CONNECT* connect_find(struct UTUN_INSTANCE* inst, uint64_t node_id) {
|
||||
struct ETCP_CONNECT* ctx = inst->pending_connects;
|
||||
while (ctx) { if (ctx->node_id == node_id) return ctx; ctx = ctx->next; }
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void connect_deliver(struct ETCP_CONNECT* ctx, int type, int free_nodes) {
|
||||
struct etcp_connect_cb_node** pp = &ctx->cb_list;
|
||||
while (*pp) {
|
||||
struct etcp_connect_cb_node* node = *pp;
|
||||
if (type == 0 || (node->flags & (uint8_t)type)) {
|
||||
node->cb(node->arg, (type == 0) ? NULL : ctx->conn, type);
|
||||
if (free_nodes) { *pp = node->next; u_free(node); } else { pp = &node->next; }
|
||||
} else {
|
||||
pp = &node->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void connect_cancel(struct ETCP_CONNECT* ctx) {
|
||||
if (!ctx) return;
|
||||
struct UTUN_INSTANCE* inst = ctx->instance;
|
||||
if (inst) {
|
||||
struct ETCP_CONNECT** pp = &inst->pending_connects;
|
||||
while (*pp && *pp != ctx) pp = &(*pp)->next;
|
||||
if (*pp) *pp = ctx->next;
|
||||
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);
|
||||
}
|
||||
|
||||
static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct TOPO_GROUP_NODE* node) {
|
||||
if (!node) return;
|
||||
struct TOPO_NODE* ni = topo_node_registry_find(ctx->instance->topo_groups, node->node_id);
|
||||
if (!ni) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[etcp_connect] links_v4: ni NOT in registry for node=%016llx", (unsigned long long)node->node_id); return; }
|
||||
int addr_count = 0, sock_count = 0, link_count = 0;
|
||||
{ struct ETCP_SOCKET* s = ctx->instance->etcp_sockets; while (s) { sock_count++; s = s->next; } }
|
||||
for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) {
|
||||
addr_count++;
|
||||
if (a->port == 0) continue;
|
||||
{ int zero = 1; for (int j = 0; j < 4; j++) if (a->addr[j] != 0) { zero = 0; break; } if (zero) continue; }
|
||||
struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET;
|
||||
memcpy(&sin.sin_addr.s_addr, a->addr, 4); sin.sin_port = htons(a->port);
|
||||
struct sockaddr_storage sa; memcpy(&sa, &sin, sizeof(sin));
|
||||
int addr_links = 0;
|
||||
if (a->protocol & TOPO_PROTO_UDP) {
|
||||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
|
||||
while (s) { if (s->local_addr.ss_family == AF_INET && etcp_link_new(ctx->conn, s, &sa, 0)) { link_count++; addr_links++; } s = s->next; }
|
||||
}
|
||||
if (a->protocol & TOPO_PROTO_TCP) {
|
||||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
|
||||
while (s) { if (s->is_tcp && s->local_addr.ss_family == AF_INET) {
|
||||
struct ETCP_LINK *tlink = etcp_link_new(ctx->conn, s, &sa, 0);
|
||||
if (tlink) { tlink->is_tcp = 1; topo_node_apply_reality(tlink, topo_node_find_reality_sock(ni, a->socket_id)); etcp_tcp_link_start_connect(tlink, &sa, a->port); link_count++; addr_links++; }
|
||||
} s = s->next; }
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] link_v4: %d.%d.%d.%d:%d proto=%d links=%d conn=[%s]",
|
||||
a->addr[0], a->addr[1], a->addr[2], a->addr[3], a->port, a->protocol, addr_links, ctx->conn->log_name);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] links_v4: TOTAL addrs=%d socks=%d links=%d%s for node=%016llx",
|
||||
addr_count, sock_count, link_count, addr_count == 0 ? " (v4_addrs EMPTY)" : "", (unsigned long long)node->node_id);
|
||||
}
|
||||
|
||||
static void connect_create_links_v6(struct ETCP_CONNECT* ctx, struct TOPO_GROUP_NODE* node) {
|
||||
if (!node) return;
|
||||
struct TOPO_NODE* ni = topo_node_registry_find(ctx->instance->topo_groups, node->node_id);
|
||||
if (!ni) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[etcp_connect] links_v6: ni NOT in registry for node=%016llx", (unsigned long long)node->node_id); return; }
|
||||
int addr_count = 0, sock_count = 0, link_count = 0;
|
||||
{ struct ETCP_SOCKET* s = ctx->instance->etcp_sockets; while (s) { sock_count++; s = s->next; } }
|
||||
for (const struct TOPO_ADDR6* a = ni->v6_addrs; a; a = a->next) {
|
||||
addr_count++;
|
||||
if (a->port == 0) { DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] link_v6: skip port==0 node=%016llx", (unsigned long long)node->node_id); continue; }
|
||||
int zero = 1; for (int j = 0; j < 16; j++) if (a->addr[j] != 0) { zero = 0; break; } if (zero) { DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] link_v6: skip zero addr node=%016llx", (unsigned long long)node->node_id); continue; }
|
||||
struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6;
|
||||
memcpy(&sin6.sin6_addr, a->addr, 16); sin6.sin6_port = htons(a->port);
|
||||
int is_ll = (a->addr[0] == 0xfe && (a->addr[1] & 0xc0) == 0x80);
|
||||
int addr_links = 0;
|
||||
if (a->protocol & TOPO_PROTO_UDP) {
|
||||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
|
||||
while (s) { if (s->local_addr.ss_family == AF_INET6) {
|
||||
if (is_ll) sin6.sin6_scope_id = s->netif_index;
|
||||
struct sockaddr_storage sa; memcpy(&sa, &sin6, sizeof(sin6));
|
||||
if (etcp_link_new(ctx->conn, s, &sa, 0)) { link_count++; addr_links++; }
|
||||
} s = s->next; }
|
||||
}
|
||||
if (a->protocol & TOPO_PROTO_TCP) {
|
||||
if (is_ll) { struct ETCP_SOCKET* sv = ctx->instance->etcp_sockets;
|
||||
while (sv) { if (sv->local_addr.ss_family == AF_INET6 && sv->netif_index)
|
||||
{ sin6.sin6_scope_id = sv->netif_index; break; } sv = sv->next; }
|
||||
}
|
||||
struct sockaddr_storage sa; memcpy(&sa, &sin6, sizeof(sin6));
|
||||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
|
||||
while (s) { if (s->is_tcp && s->local_addr.ss_family == AF_INET6) {
|
||||
struct ETCP_LINK *tlink = etcp_link_new(ctx->conn, s, &sa, 0);
|
||||
if (tlink) { tlink->is_tcp = 1; topo_node_apply_reality(tlink, topo_node_find_reality_sock(ni, a->socket_id)); etcp_tcp_link_start_connect(tlink, &sa, a->port); link_count++; addr_links++; }
|
||||
} s = s->next; }
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] link_v6: %s:%d proto=%d links=%d conn=[%s]",
|
||||
ip_to_str(a->addr, AF_INET6).str, a->port, a->protocol, addr_links, ctx->conn->log_name);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] links_v6: TOTAL addrs=%d socks=%d links=%d%s for node=%016llx",
|
||||
addr_count, sock_count, link_count, addr_count == 0 ? " (v6_addrs EMPTY)" : "", (unsigned long long)node->node_id);
|
||||
}
|
||||
|
||||
|
||||
static void connect_initial_timeout_cb(void* arg) {
|
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||||
if (!ctx || ctx->done) return;
|
||||
ctx->done = 1;
|
||||
ctx->initial_timer = NULL;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] timeout for node 0x%016llx", (unsigned long long)ctx->node_id);
|
||||
struct ETCP_CONN* conn = ctx->conn; ctx->conn = NULL;
|
||||
connect_deliver(ctx, 0, 1);
|
||||
if (conn) etcp_connection_close(conn);
|
||||
connect_cancel(ctx);
|
||||
}
|
||||
|
||||
static void connect_settle_timeout_cb(void* arg) {
|
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||||
if (!ctx || ctx->done) return;
|
||||
ctx->done = 1;
|
||||
ctx->settle_timer = NULL;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] settle for node 0x%016llx, min_rtt=%u",
|
||||
(unsigned long long)ctx->node_id, ctx->min_rtt);
|
||||
struct ETCP_LINK* link = ctx->conn->links;
|
||||
while (link) {
|
||||
struct ETCP_LINK* next = link->next;
|
||||
if (!link->initialized) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] settle: removing link=%d is_tcp=%d state=%d addr=%s for node 0x%016llx",
|
||||
link->local_link_id, link->is_tcp, link->link_state,
|
||||
sockaddr_storage_to_str(&link->remote_addr).str, (unsigned long long)ctx->node_id);
|
||||
etcp_link_close(link);
|
||||
}
|
||||
link = next;
|
||||
}
|
||||
etcp_conn_remove_cbk(ctx->conn, connect_init_cb, ctx);
|
||||
connect_deliver(ctx, ETCP_CONNECT_LATE, 1);
|
||||
connect_cancel(ctx);
|
||||
}
|
||||
|
||||
static void connect_init_cb(struct ETCP_CONN* conn, int event, void* arg) { (void)event;
|
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||||
if (!ctx || ctx->done) return;
|
||||
|
||||
int total_links = 0, up_links = 0;
|
||||
struct ETCP_LINK* link = conn->links;
|
||||
while (link) {
|
||||
total_links++;
|
||||
if (link->initialized && link->rtt_last && link->rtt_last < ctx->min_rtt)
|
||||
ctx->min_rtt = link->rtt_last;
|
||||
if (link->link_state == LINK_STATE_CONNECTED) up_links++;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] init_cb: link=%d state=%d init=%d is_tcp=%d rtt=%u addr=%s",
|
||||
link->local_link_id, link->link_state, link->initialized, link->is_tcp, link->rtt_last,
|
||||
sockaddr_storage_to_str(&link->remote_addr).str);
|
||||
link = link->next;
|
||||
}
|
||||
|
||||
if (total_links == 1 || up_links == total_links || (total_links == 0 && conn->tcp_link_count > 0)) {
|
||||
if (!ctx->early_delivered) {
|
||||
ctx->early_delivered = 1;
|
||||
if (ctx->initial_timer) { uasync_cancel_timeout(ctx->instance->ua, ctx->initial_timer); ctx->initial_timer = NULL; }
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] init_cb: node 0x%016llx links=%d up=%d tcp=%d → immediate LATE",
|
||||
(unsigned long long)ctx->node_id, total_links, up_links, conn->tcp_link_count);
|
||||
{ struct etcp_connect_cb_node* n = ctx->cb_list; while (n) { struct etcp_connect_cb_node* next = n->next; if (n->flags & ETCP_CONNECT_EARLY) n->cb(n->arg, ctx->conn, ETCP_CONNECT_EARLY); if (n->flags & ETCP_CONNECT_LATE) n->cb(n->arg, ctx->conn, ETCP_CONNECT_LATE); u_free(n); n = next; } ctx->cb_list = NULL; }
|
||||
connect_cancel(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->early_delivered) return;
|
||||
ctx->early_delivered = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] init_cb: node 0x%016llx links=%d up=%d tcp=%d → EARLY, settle",
|
||||
(unsigned long long)ctx->node_id, total_links, up_links, conn->tcp_link_count);
|
||||
connect_deliver(ctx, ETCP_CONNECT_EARLY, 0);
|
||||
if (ctx->initial_timer) { uasync_cancel_timeout(ctx->instance->ua, ctx->initial_timer); ctx->initial_timer = NULL; }
|
||||
uint32_t settle_tb;
|
||||
if (ctx->min_rtt == 0xFFFF) settle_tb = 20000;
|
||||
else {
|
||||
settle_tb = (uint32_t)ctx->min_rtt * 8;
|
||||
if (settle_tb < 5000) settle_tb = 5000;
|
||||
if (settle_tb > 20000) settle_tb = 20000;
|
||||
}
|
||||
ctx->settle_timer = uasync_set_timeout(ctx->instance->ua, (int)settle_tb, ctx,
|
||||
connect_settle_timeout_cb, "etcp_connect_settle");
|
||||
}
|
||||
|
||||
int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_GROUP_NODE* node,
|
||||
etcp_connect_callback_t cb, void* arg, uint8_t flags) {
|
||||
if (!inst || !node || !cb || !flags || (flags & ~(ETCP_CONNECT_EARLY | ETCP_CONNECT_LATE))) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "invalid connect arguments/flags=%u", flags); return -1;
|
||||
}
|
||||
|
||||
struct TOPO_NODE* ni = topo_node_registry_find(inst->topo_groups, node->node_id);
|
||||
if (!ni) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node not in registry"); return -1; }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] registry OK node=%016llx pub=%016llx v4addrs=%d v6addrs=%d",
|
||||
(unsigned long long)node->node_id, *(uint64_t*)ni->public_key,
|
||||
topo_list_count((struct _topo_head*)ni->v4_addrs),
|
||||
topo_list_count((struct _topo_head*)ni->v6_addrs));
|
||||
|
||||
uint64_t node_id = ni->node_id;
|
||||
if (node_id == 0) {
|
||||
if (!ni->public_key) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node_id=0 and public_key is NULL");
|
||||
return -1;
|
||||
}
|
||||
int zero = 1;
|
||||
for (int i = 0; i < SC_PUBKEY_SIZE; i++)
|
||||
if (ni->public_key[i]) { zero = 0; break; }
|
||||
if (zero) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node_id=0 and public_key is all zeros");
|
||||
return -1;
|
||||
}
|
||||
node_id = sc_derive_node_id_from_pubkey(ni->public_key);
|
||||
ni->node_id = node_id;
|
||||
}
|
||||
|
||||
struct ETCP_CONN* conn = instance_find_conn(inst, node_id);
|
||||
struct ETCP_CONNECT* ctx = connect_find(inst, node_id);
|
||||
|
||||
// Case 1: conn && ctx — already connecting
|
||||
if (conn && ctx) {
|
||||
if (ctx->done) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx: existing ctx timed out, cb(NULL,0)",
|
||||
(unsigned long long)node_id);
|
||||
cb(arg, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||||
if (!cn) return -1;
|
||||
cn->cb = cb; cn->arg = arg; cn->flags = flags; cn->next = ctx->cb_list; ctx->cb_list = cn;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx already connecting, added cb",
|
||||
(unsigned long long)node_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Case 2: !conn && ctx — context exists but conn was removed (rare)
|
||||
if (!conn && ctx) {
|
||||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||||
if (!cn) return -1;
|
||||
cn->cb = cb; cn->arg = arg; cn->flags = flags; cn->next = ctx->cb_list; ctx->cb_list = cn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Case 3: conn && !ctx — existing connection, no context
|
||||
if (conn && !ctx) {
|
||||
if (conn->state == 0) {
|
||||
// Pending conn from another path (e.g. incoming INIT before etcp_connect)
|
||||
ctx = u_calloc(1, sizeof(struct ETCP_CONNECT));
|
||||
if (!ctx) return -1;
|
||||
ctx->instance = inst; ctx->node_id = node_id; ctx->conn = conn;
|
||||
ctx->min_rtt = 0xFFFF;
|
||||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||||
if (!cn) { u_free(ctx); return -1; }
|
||||
cn->cb = cb; cn->arg = arg; cn->flags = flags; ctx->cb_list = cn;
|
||||
etcp_conn_add_cbk(conn, connect_init_cb, ctx, ETCP_CBK_EVENT_INIT);
|
||||
ctx->initial_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, ctx,
|
||||
connect_initial_timeout_cb, "etcp_connect_init");
|
||||
ctx->next = inst->pending_connects; inst->pending_connects = ctx;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx: existing pending conn, waiting for init",
|
||||
(unsigned long long)node_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// conn->state == 1: already ready — deliver EARLY+LATE immediately (via direct loop to deliver both phases)
|
||||
ctx = u_calloc(1, sizeof(struct ETCP_CONNECT));
|
||||
if (!ctx) return -1;
|
||||
ctx->instance = inst; ctx->node_id = node_id; ctx->conn = conn;
|
||||
ctx->early_delivered = 1;
|
||||
{
|
||||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||||
if (!cn) { u_free(ctx); return -1; }
|
||||
cn->cb = cb; cn->arg = arg; cn->flags = flags;
|
||||
ctx->cb_list = cn;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx: existing ready conn, delivering EARLY+LATE",
|
||||
(unsigned long long)node_id);
|
||||
struct etcp_connect_cb_node* n = ctx->cb_list;
|
||||
while (n) {
|
||||
struct etcp_connect_cb_node* next = n->next;
|
||||
if (n->flags & ETCP_CONNECT_EARLY) n->cb(n->arg, ctx->conn, ETCP_CONNECT_EARLY);
|
||||
if (n->flags & ETCP_CONNECT_LATE) n->cb(n->arg, ctx->conn, ETCP_CONNECT_LATE);
|
||||
u_free(n); n = next;
|
||||
}
|
||||
u_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Case 4: !conn && !ctx — new connection
|
||||
conn = etcp_connection_create(inst, NULL);
|
||||
if (!conn) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] failed to create ETCP_CONN for node 0x%016llx",
|
||||
(unsigned long long)node_id);
|
||||
cb(arg, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
conn->peer_node_id = node_id;
|
||||
etcp_update_log_name(conn);
|
||||
if (conn->conn_queue && conn->conn_queue_entry) {
|
||||
queue_remove_data(conn->conn_queue, conn->conn_queue_entry);
|
||||
queue_entry_free(conn->conn_queue_entry);
|
||||
}
|
||||
{ struct ll_entry* qe = queue_entry_new(sizeof(struct conn_queue_entry)); if (qe) { struct conn_queue_entry* ce = (struct conn_queue_entry*)qe->data; ce->peer_node_id = node_id; ce->conn = conn; conn->conn_queue_entry = qe; conn->conn_queue = inst->connections; queue_data_put_with_index(inst->connections, qe); } }
|
||||
|
||||
if (sc_init_ctx(&conn->crypto_ctx, &inst->my_keys) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_init_ctx failed for node 0x%016llx", (unsigned long long)node_id);
|
||||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1;
|
||||
}
|
||||
if (sc_set_peer_public_key(&conn->crypto_ctx, ni->public_key, 0) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_set_peer_public_key failed for node 0x%016llx", (unsigned long long)node_id);
|
||||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1;
|
||||
}
|
||||
|
||||
ctx = u_calloc(1, sizeof(struct ETCP_CONNECT));
|
||||
if (!ctx) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] alloc failed");
|
||||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1;
|
||||
}
|
||||
ctx->instance = inst; ctx->node_id = node_id; ctx->conn = conn;
|
||||
ctx->min_rtt = 0xFFFF;
|
||||
{
|
||||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||||
if (!cn) { u_free(ctx); etcp_connection_close(conn); cb(arg, NULL, 0); return -1; }
|
||||
cn->cb = cb; cn->arg = arg; cn->flags = flags; ctx->cb_list = cn;
|
||||
}
|
||||
|
||||
etcp_conn_add_cbk(conn, connect_init_cb, ctx, ETCP_CBK_EVENT_INIT);
|
||||
|
||||
connect_create_links_v4(ctx, node);
|
||||
connect_create_links_v6(ctx, node);
|
||||
|
||||
if (!conn->links && conn->tcp_link_count == 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] no links created for node 0x%016llx (links=%p tcp_links=%d)",
|
||||
(unsigned long long)node_id, (void*)conn->links, conn->tcp_link_count);
|
||||
connect_deliver(ctx, 0, 1);
|
||||
etcp_connection_close(conn); u_free(ctx); return -1;
|
||||
}
|
||||
|
||||
ctx->initial_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, ctx, connect_initial_timeout_cb, "etcp_connect_init");
|
||||
ctx->next = inst->pending_connects; inst->pending_connects = ctx;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Полная отмена pending-коннекта по node_id: снимает connect_init_cb
|
||||
* с conn (conn остаётся жив, например, переиспользуется ncd), отменяет таймеры,
|
||||
* вынимает ctx из pending_connects и освобождает. Безопасно вызывать, если ctx нет. */
|
||||
void etcp_connect_cancel_by_node(struct UTUN_INSTANCE* inst, uint64_t node_id) {
|
||||
if (!inst) return;
|
||||
struct ETCP_CONNECT** pp = &inst->pending_connects;
|
||||
while (*pp) {
|
||||
struct ETCP_CONNECT* ctx = *pp;
|
||||
if (ctx->node_id == node_id) {
|
||||
*pp = ctx->next;
|
||||
if (ctx->conn) {
|
||||
etcp_conn_remove_cbk(ctx->conn, connect_init_cb, ctx);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#ifndef ETCP_CONNECT_H
|
||||
#define ETCP_CONNECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
struct ETCP_CONN;
|
||||
struct TOPO_GROUP_NODE;
|
||||
typedef void (*etcp_connect_callback_t)(void* arg, struct ETCP_CONN* conn, int type);
|
||||
|
||||
/* Внутренний установщик прямого ETCP-транспорта; владельцы используют node_conn_direct.h.
|
||||
* node->node_id должен находиться в node_registry: оттуда берутся ключ и IPv4/IPv6 адреса.
|
||||
* Подключение создаёт UDP/STCP-линки либо подписывает callback на текущую попытку к тому же узлу.
|
||||
* Все вызовы/callbacks — в uasync. flags — непустая маска ETCP_CONNECT_EARLY/LATE (etcp_api.h).
|
||||
* EARLY означает появление транспорта; LATE — завершение выбора линков. Они не означают READY группы.
|
||||
* При одном/полностью готовом наборе линков обе запрошенные фазы выдаются подряд;
|
||||
* иначе после EARLY ждём settle 0.5..2с и удаляем неинициализированные линки.
|
||||
* Для уже готового соединения callback вызывается внутри etcp_connect, до возврата.
|
||||
* conn в callback заимствован. type=0/conn=NULL — отказ; initial timeout закрывает соединение.
|
||||
* Возврат 0 означает принятую попытку/выданный результат; -1 — ошибка, причём некоторые
|
||||
* ошибки создания также вызывают cb(NULL,0). Обработчик должен учитывать оба способа отказа. */
|
||||
int etcp_connect(struct UTUN_INSTANCE* instance, struct TOPO_GROUP_NODE* node,
|
||||
etcp_connect_callback_t cb, void* arg, uint8_t flags);
|
||||
|
||||
/* Снять таймеры/контекст по conn без уведомления callbacks. Только teardown соединения,
|
||||
* когда connect_init_cb больше не может быть вызван; транспорт освобождает вызывающий слой. */
|
||||
void etcp_connect_cancel_for_conn(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn);
|
||||
|
||||
/* Полная отмена pending-коннекта по node_id: снимает коллбэки с conn (conn остаётся
|
||||
* жив — переиспользуется, напр. ncd), отменяет таймеры, освобождает ctx. */
|
||||
void etcp_connect_cancel_by_node(struct UTUN_INSTANCE* inst, uint64_t node_id);
|
||||
|
||||
#endif
|
||||
@@ -1,71 +0,0 @@
|
||||
# ETCP Connect (etcp_connect)
|
||||
|
||||
## 1. Назначение
|
||||
Управление исходящими ETCP-соединениями. Предоставляет асинхронный API `etcp_connect()` для установки соединения с удалённым узлом: создаёт ETCP_CONN, настраивает криптографию, поднимает UDP-линки и опционально TCP/STCP-транспорт, отслеживает прогресс установки с таймерами. Доставляет коллбэки о фазах готовности (EARLY, LATE).
|
||||
|
||||
## 2. Как пользоваться
|
||||
|
||||
```c
|
||||
// Запуск подключения к узлу
|
||||
etcp_connect(inst, node, my_connect_cb, my_arg, ETCP_CONNECT_EARLY | ETCP_CONNECT_LATE);
|
||||
```
|
||||
|
||||
Коллбэк вызывается на разных фазах:
|
||||
```c
|
||||
void my_connect_cb(void* arg, struct ETCP_CONN* conn, int type) {
|
||||
if (!conn) {
|
||||
// type == 0 — таймаут/ошибка подключения
|
||||
return;
|
||||
}
|
||||
if (type & ETCP_CONNECT_EARLY)
|
||||
// первый линк готов (можно начинать обмен)
|
||||
if (type & ETCP_CONNECT_LATE)
|
||||
// settle-фаза завершена (все линки проверены, неудачные удалены)
|
||||
}
|
||||
```
|
||||
|
||||
**Жизненный цикл:**
|
||||
1. `etcp_connect()` — создаёт ETCP_CONN, crypto_ctx, UDP-линки и TCP/STCP
|
||||
2. `connect_ready_cb()` — первый линк стал ready → EARLY-коллбэк → запуск settle-таймера
|
||||
3. `connect_settle_timeout_cb()` — settle (min_rtt × 8, clamp 500..2000ms) → удаление неудачных линков → LATE-коллбэк
|
||||
4. При таймауте (`connect_initial_timeout_cb`) — type=0 (conn=NULL), все ресурсы освобождаются
|
||||
|
||||
**Нюансы:**
|
||||
- Если соединение уже установлено — коллбэки вызываются немедленно
|
||||
- Если соединение в процессе — коллбэк добавляется в список ожидающих (поддержка нескольких подписчиков)
|
||||
- `etcp_connect_cancel_for_conn()` — только для deferred cleanup (фаза 2 закрытия), НЕ из mainloop
|
||||
- При таймауте `etcp_connect()` может доставить type=0 с conn=NULL — нужно обрабатывать
|
||||
|
||||
## 3. API
|
||||
|
||||
### Структуры
|
||||
**ETCP_CONNECT** — контекст одного исходящего соединения:
|
||||
- `conn` — целевой ETCP_CONN
|
||||
- `tcp_link` — STCP-транспорт (опционально)
|
||||
- `cb_list` — цепочка коллбэков (несколько подписчиков)
|
||||
- `initial_timer`, `settle_timer` — таймеры контроля прогресса
|
||||
- `min_rtt` — минимальный RTT среди готовых линков (для расчёта settle-времени)
|
||||
- `early_delivered` — флаг однократного вызова EARLY
|
||||
- `tcp_ready` — TCP-линк поднят
|
||||
- `done` — соединение завершено (ошибка/успех), дальнейшая обработка игнорируется
|
||||
|
||||
### Константы коллбэков (etcp_api.h)
|
||||
| Константа | Описание |
|
||||
|---|---|
|
||||
| `ETCP_CONNECT_EARLY` (1) | Первый линк готов |
|
||||
| `ETCP_CONNECT_LATE` (2) | Settle-фаза завершена |
|
||||
|
||||
### Функции
|
||||
| Функция | Описание |
|
||||
|---|---|
|
||||
| `etcp_connect(inst, node, cb, arg, flags)` | Запустить асинхронное подключение к узлу. Возвращает 0 при успехе |
|
||||
| `etcp_connect_cancel_for_conn(inst, conn)` | Отменить ожидание для conn. Только из etcp_connection_free_resources (фаза 2) |
|
||||
|
||||
### Пути завершения
|
||||
1. **Таймаут установки** (`connect_initial_timeout_cb`): stcp_link_close → etcp_connection_close → connect_cancel → type=0
|
||||
2. **Settle-таймаут** (`connect_settle_timeout_cb`): удаление неудачных линков/stcp → LATE → connect_cancel
|
||||
3. **Штатное закрытие** (`utun_instance_destroy`): фаза 1 (detach) → фаза 2 (deferred: `etcp_connect_cancel_for_conn`)
|
||||
|
||||
Готовность BGP проверяется для конкретной группы через `topo_group_peer_ready(group, peer_id)`.
|
||||
Общий транспорт не имеет состояния завершения группового обмена. Для владения
|
||||
соединениями используйте NCD; создание транспорта само по себе не вступает в группу.
|
||||
Reference in new issue
Block a user