|
|
|
|
@ -26,6 +26,7 @@
|
|
|
|
|
#include "../lib/socket_compat.h" |
|
|
|
|
#include "../lib/platform_compat.h" |
|
|
|
|
#include <string.h> |
|
|
|
|
#include <limits.h> |
|
|
|
|
|
|
|
|
|
#define DEBUG_CATEGORY_NCD DEBUG_CATEGORY_ETCP |
|
|
|
|
|
|
|
|
|
@ -649,11 +650,12 @@ static void ncd_fin_wait_timeout_cb(void* arg) {
|
|
|
|
|
* |
|
|
|
|
* KEEP_ALIVE (пир просит не закрывать conn — у него ещё есть handle'ы): |
|
|
|
|
* - Если мы в fin_wait — отменяем его, conn продолжает работать. |
|
|
|
|
* - Если у нас handle'ов нет — перезапускаем fin_wait (ждём ещё 5 сек). |
|
|
|
|
* - Отсутствие наших handle'ов не отменяет потребность пира: FIN_WAIT снимается полностью. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static void ncd_recv_control_handler(struct ETCP_CONN* conn, struct ll_entry* e) { |
|
|
|
|
if (!conn || !e || e->len < NCD_CONTROL_MSG_SIZE) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] malformed control conn=%p size=%u", (void*)conn, e ? e->len : 0); |
|
|
|
|
if (e) { queue_dgram_free(e); queue_entry_free(e); } |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
@ -676,13 +678,16 @@ static void ncd_recv_control_handler(struct ETCP_CONN* conn, struct ll_entry* e)
|
|
|
|
|
if (entry->handle_count > 0) { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] refusing CLOSE, still have %d handles for 0x%016llx", |
|
|
|
|
entry->handle_count, (unsigned long long)sender_id); |
|
|
|
|
{ struct ncd_control_msg resp; resp.cmd = ETCP_RT_ID_NCD_CONTROL; resp.subcmd = NCD_SUBCMD_KEEP_ALIVE; |
|
|
|
|
resp.node_id = conn->instance->node_id; |
|
|
|
|
struct ll_entry* qe = queue_entry_new(0); |
|
|
|
|
if (qe) { qe->dgram = u_malloc(sizeof(resp)); |
|
|
|
|
if (qe->dgram) { memcpy(qe->dgram, &resp, sizeof(resp)); qe->len = sizeof(resp); |
|
|
|
|
if (etcp_send(conn, qe) != 0) { queue_dgram_free(qe); queue_entry_free(qe); } } |
|
|
|
|
else queue_entry_free(qe); } |
|
|
|
|
{ struct ncd_control_msg resp = { ETCP_RT_ID_NCD_CONTROL, NCD_SUBCMD_KEEP_ALIVE, conn->instance->node_id }; |
|
|
|
|
struct ll_entry* qe = ll_alloc_lldgram(sizeof(resp)); |
|
|
|
|
if (!qe) DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] KEEP_ALIVE allocation failed"); |
|
|
|
|
else { |
|
|
|
|
memcpy(qe->dgram, &resp, sizeof(resp)); qe->len = sizeof(resp); |
|
|
|
|
if (etcp_send(conn, qe) != 0) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] KEEP_ALIVE send failed node=%016llx", (unsigned long long)sender_id); |
|
|
|
|
queue_dgram_free(qe); queue_entry_free(qe); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] deferred close entry for CLOSE from 0x%016llx", (unsigned long long)sender_id); |
|
|
|
|
@ -694,15 +699,12 @@ static void ncd_recv_control_handler(struct ETCP_CONN* conn, struct ll_entry* e)
|
|
|
|
|
if (entry && entry->conn->fin_wait) { |
|
|
|
|
entry->conn->fin_wait = 0; entry->conn->fin_wait_clear_cb = NULL; entry->conn->fin_wait_clear_arg = NULL; |
|
|
|
|
if (entry->fin_wait_timer) { uasync_cancel_timeout(entry->ua, entry->fin_wait_timer); entry->fin_wait_timer = NULL; } |
|
|
|
|
if (entry->handle_count <= 0) { |
|
|
|
|
entry->conn->fin_wait = 1; |
|
|
|
|
entry->fin_wait_timer = uasync_set_timeout(entry->ua, NCD_FIN_WAIT_TIMEOUT_TB, entry, ncd_fin_wait_timeout_cb, "ncd_fin_wait"); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] KEEP_ALIVE but no handles, restart fin_wait"); |
|
|
|
|
} else { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] KEEP_ALIVE: fin_wait cleared, conn stays alive"); |
|
|
|
|
} |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] KEEP_ALIVE: fin_wait cleared, conn stays alive handles=%d", entry->handle_count); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] unknown control subcmd=%u node=%016llx", msg->subcmd, (unsigned long long)sender_id); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
queue_dgram_free(e); queue_entry_free(e); |
|
|
|
|
} |
|
|
|
|
@ -710,15 +712,19 @@ static void ncd_recv_control_handler(struct ETCP_CONN* conn, struct ll_entry* e)
|
|
|
|
|
/* ═══════════ инициализация глобального обработчика ═══════════ */ |
|
|
|
|
/*
|
|
|
|
|
* Однократная привязка обработчика NCD control-сообщений к ETCP (etcp_bind). |
|
|
|
|
* Вызывается при первом node_conn_direct_open, повторные вызовы безвредны |
|
|
|
|
* Вызывается при запуске сети и node_conn_direct_open, повторные вызовы безвредны |
|
|
|
|
* (защита через inst->ncd_control_bound). |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static void ncd_init_control_binding(struct UTUN_INSTANCE* inst) { |
|
|
|
|
if (inst->ncd_control_bound) return; |
|
|
|
|
etcp_bind(inst, ETCP_RT_ID_NCD_CONTROL, ncd_recv_control_handler); |
|
|
|
|
int node_conn_direct_init(struct UTUN_INSTANCE* inst) { |
|
|
|
|
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] init without instance"); return -1; } |
|
|
|
|
if (inst->ncd_control_bound) return 0; |
|
|
|
|
if (etcp_bind(inst, ETCP_RT_ID_NCD_CONTROL, ncd_recv_control_handler) < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] cannot bind CLOSE/KEEP_ALIVE handler"); return -1; |
|
|
|
|
} |
|
|
|
|
inst->ncd_control_bound = 1; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] bound ETCP_RT_ID_NCD_CONTROL handler"); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ═══════════ оживление «зависшего» (timed_out) entry ═══════════ */ |
|
|
|
|
@ -776,7 +782,7 @@ int node_conn_direct_open(struct UTUN_INSTANCE* inst, uint64_t node_id,
|
|
|
|
|
struct ETCP_SOCKET* specific_sock) { |
|
|
|
|
if (!inst || !out_handle) return NCD_ERR; |
|
|
|
|
*out_handle = NULL; |
|
|
|
|
ncd_init_control_binding(inst); |
|
|
|
|
if (node_conn_direct_init(inst) < 0) return NCD_ERR; |
|
|
|
|
|
|
|
|
|
/* 1. Ищем в реестре — conn уже есть */ |
|
|
|
|
struct ncd_entry* entry = ncd_registry_find(inst, node_id); |
|
|
|
|
@ -938,7 +944,7 @@ static int ncd_open_node(struct UTUN_INSTANCE* inst, uint64_t node_id,
|
|
|
|
|
struct ETCP_SOCKET* specific_sock) { |
|
|
|
|
if (!inst || !out_handle || !ni) return NCD_ERR; |
|
|
|
|
*out_handle = NULL; |
|
|
|
|
ncd_init_control_binding(inst); |
|
|
|
|
if (node_conn_direct_init(inst) < 0) return NCD_ERR; |
|
|
|
|
|
|
|
|
|
/* 1. Ищем в реестре — conn уже есть */ |
|
|
|
|
{ struct ncd_entry* entry = ncd_registry_find(inst, node_id); |
|
|
|
|
@ -1134,9 +1140,14 @@ static void ncd_handle_close(struct NODE_CONN_DIRECT* h, int force) {
|
|
|
|
|
(unsigned long long)entry->node_id, entry->handle_count, force); |
|
|
|
|
if (entry->handle_count || entry->closed) { ncd_collect(entry); return; } |
|
|
|
|
struct ETCP_CONN* conn = entry->conn; |
|
|
|
|
if (force || !conn || conn->close_requested || conn->state == 2 || entry->timed_out || !conn->links_up) { |
|
|
|
|
if (force || !conn || conn->close_requested || conn->state == 2 || entry->timed_out) { |
|
|
|
|
ncd_finish(entry, 1); return; |
|
|
|
|
} |
|
|
|
|
if (!conn->links_up) { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] last handle released while DOWN, retaining until ETCP timeout node=%016llx", |
|
|
|
|
(unsigned long long)entry->node_id); |
|
|
|
|
ncd_finish(entry, 0); return; |
|
|
|
|
} |
|
|
|
|
if (entry->connect_timer) { uasync_cancel_timeout(entry->ua, entry->connect_timer); entry->connect_timer = NULL; } |
|
|
|
|
conn->fin_wait = 1; conn->fin_wait_clear_cb = ncd_fin_wait_cancelled; conn->fin_wait_clear_arg = entry; |
|
|
|
|
struct ncd_control_msg msg = { ETCP_RT_ID_NCD_CONTROL, NCD_SUBCMD_CLOSE, entry->inst->node_id }; |
|
|
|
|
@ -1148,8 +1159,18 @@ static void ncd_handle_close(struct NODE_CONN_DIRECT* h, int force) {
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] CLOSE send failed"); queue_dgram_free(e); queue_entry_free(e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
entry->fin_wait_timer = uasync_set_timeout(entry->ua, NCD_FIN_WAIT_TIMEOUT_TB, entry, ncd_fin_wait_timeout_cb, "ncd_fin_wait"); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] fin_wait node=%016llx", (unsigned long long)entry->node_id); |
|
|
|
|
uint64_t now = get_time_tb(), wait_tb = NCD_FIN_WAIT_TIMEOUT_TB; |
|
|
|
|
for (struct ETCP_LINK* link = conn->links; link; link = link->next) { |
|
|
|
|
if (!link->link_status || (!link->ka_my_sleeping && !link->ka_peer_sleeping)) continue; |
|
|
|
|
if (link->rx_deadline_tb > now && link->rx_deadline_tb - now > wait_tb) wait_tb = link->rx_deadline_tb - now; |
|
|
|
|
} |
|
|
|
|
int delay = wait_tb > INT_MAX ? INT_MAX : (int)wait_tb; |
|
|
|
|
entry->fin_wait_timer = uasync_set_timeout(entry->ua, delay, entry, ncd_fin_wait_timeout_cb, "ncd_fin_wait"); |
|
|
|
|
if (!entry->fin_wait_timer) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] cannot schedule FIN_WAIT node=%016llx", (unsigned long long)entry->node_id); |
|
|
|
|
ncd_finish(entry, 1); return; |
|
|
|
|
} |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] fin_wait node=%016llx timeout_ms=%d", (unsigned long long)entry->node_id, delay / 10); |
|
|
|
|
ncd_collect(entry); |
|
|
|
|
} |
|
|
|
|
void node_conn_direct_close(struct NODE_CONN_DIRECT* h) { ncd_handle_close(h, 0); } |
|
|
|
|
|