diff --git a/src/routing_layer/route_connectivity.c b/src/routing_layer/route_connectivity.c index 1f7a14a1..1f82f062 100644 --- a/src/routing_layer/route_connectivity.c +++ b/src/routing_layer/route_connectivity.c @@ -19,6 +19,7 @@ #include "route_connectivity.h" #include "topo_node_sqlite.h" #include "sock_match.h" +#include "nat_detection.h" #ifdef UTUN_HAVE_STANDBY #include "standby.h" #endif @@ -253,6 +254,24 @@ static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok) { // ---- public API ---- +struct route_nat_defer { + struct UTUN_INSTANCE *instance; + struct TOPO_GROUP *group; + struct TOPO_GROUP_NODE *node; + void *timer; +}; + +/* После NAT-окна запускает обычную проверку; удаление узла отменяет таймер и освобождает контекст. */ +static void route_nat_deferred_cb(void *arg) { + struct route_nat_defer *deferred = arg; + struct UTUN_INSTANCE *instance = deferred->instance; + struct TOPO_GROUP *group = deferred->group; + struct TOPO_GROUP_NODE *node = deferred->node; + node->connectivity.nat_probe_wait = NULL; node->connectivity.probe_status = PROBE_STATUS_NONE; + u_free(deferred); + route_connectivity_probe_node(instance, group, node); +} + #ifdef UTUN_HAVE_STANDBY /* Контекст пробы, отложенной до следующего активного интервала (standby_wait). */ struct route_probe_defer { @@ -277,6 +296,7 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_G struct TOPO_NODE* ni = topo_node_registry_find(instance->topo_groups, nq->node_id); if (!ni) return; if (nq->node_id == instance->node_id) return; // не пингуем себя + if (nq->connectivity.nat_probe_wait) return; /* Пир в SLEEP-фазе: прямое соединение живо, probe будит пира — пропускаем. */ { struct ETCP_CONN* c = instance_find_conn(instance, nq->node_id); @@ -303,6 +323,19 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_G return; } + uint64_t nat_delay = nat_detection_probe_delay(instance->nat_det); + if (nat_delay) { + struct route_nat_defer *deferred = u_calloc(1, sizeof(*deferred)); + if (!deferred) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NAT probe defer allocation failed"); return; } + deferred->instance = instance; deferred->group = group; deferred->node = nq; + deferred->timer = uasync_set_timeout(instance->ua, nat_delay, deferred, route_nat_deferred_cb, "route_nat_defer"); + if (!deferred->timer) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NAT probe defer scheduling failed"); u_free(deferred); return; } + nq->connectivity.nat_probe_wait = deferred; nq->connectivity.probe_status = PROBE_STATUS_DEFERRED; + DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "connectivity probe waits for initial NAT measurement node=%016llx delay_tb=%llu", + (unsigned long long)nq->node_id, (unsigned long long)nat_delay); + return; + } + nq->connectivity.probe_status = PROBE_STATUS_IN_PROGRESS; nq->connectivity.probe_start_time = get_time_tb(); nq->connectivity.interface_status = PROBE_RESULT_UNKNOWN; @@ -422,6 +455,11 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_G * хендла — они отсоединяются и освобождаются своим штатным ping_timeout/PONG. */ static void conn_probe_cancel_nq(struct TOPO_GROUP_NODE* nq) { if (!nq) return; + struct route_nat_defer *deferred = nq->connectivity.nat_probe_wait; + if (deferred) { + uasync_cancel_timeout(deferred->instance->ua, deferred->timer); + nq->connectivity.nat_probe_wait = NULL; u_free(deferred); + } #ifdef UTUN_HAVE_STANDBY { struct route_probe_defer* d = (struct route_probe_defer*)nq->connectivity.probe_deferred_wait; if (d) { diff --git a/src/routing_layer/topo_node.h b/src/routing_layer/topo_node.h index 8a8588dd..4d2d6c38 100644 --- a/src/routing_layer/topo_node.h +++ b/src/routing_layer/topo_node.h @@ -100,6 +100,7 @@ struct TOPO_CONNECTIVITY { uint64_t last_ping_time; void* probe_list; // принадлежащие узлу контексты текущих проб void* probe_deferred_wait; /* контекст отложенной пробы (standby_wait, Android) */ + void* nat_probe_wait; /* контекст таймера: NAT сохраняет холодный ingress перед фоновыми ping */ }; #define PING_TTL_TB (86400ULL * 10000ULL)