Files
utun2/src/routing_layer/nat_detection.c
T

1766 lines
116 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Владелец измеряет рабочий UDP endpoint по разным IP. Наблюдения возвращаются
* через router, независимо от прямого UDP-ответа. Входящие пробы не получают PONG;
* незнакомость фактического source IP проверяет только получатель. */
#include "nat_detection.h"
#include "chat/chat_event.h"
#include <stdio.h>
#include <string.h>
#include "utun_instance.h"
#include "config_parser.h"
#include "etcp_api.h"
#include "etcp.h"
#include "etcp_connections.h"
#include "route_ping.h"
#include "topo_node.h"
#include "topo_group.h"
#include "etcp_router.h"
#include "route_crypto.h"
#include "stun_nat.h"
#include "sock_match.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include "../lib/u_async.h"
#define NAT_WIRE_VERSION 4
#define NAT_MAX_BINDINGS 128
#define NAT_MAX_PROBES 64
#define NAT_MAX_CONTACTS 256
#define NAT_TICK_TB 1000
#define NAT_ROUND_TB 300000
#define NAT_REFRESH_TB 18000000
#define NAT_RETRY_TB 300000
#define NAT_FAILURE_RECHECK_TB 3000000
#define NAT_PROBE_COUNT 3
#define NAT_QUERY_TAG_SIZE (ROUTE_OBSERVE_TAG_SIZE + 9)
enum nat_command { NAT_RUN = 1, NAT_RESULT, NAT_RECHECK, NAT_OBSERVATION };
enum nat_phase { NAT_IDLE, NAT_OBSERVE_B, NAT_PREPARE_C, NAT_PRE, NAT_OBSERVE_C,
NAT_POST, NAT_REPEAT_B, NAT_REPEAT_C, NAT_PRE_WAIT, NAT_POST_WAIT };
enum nat_step { NAT_STEP_PREPARE = 1, NAT_STEP_PRE, NAT_STEP_POST, NAT_STEP_HELPER_OBSERVE = 128 };
/* Только байты: формат независим от alignment/endian платформы. */
struct nat_wire {
uint8_t cmd, command, version, owner_socket, helper_socket, step, reason, sent, received, errors;
uint8_t owner[8], generation[8], round[8], token[8], observer[8], helper[8], timeout_ms[2];
uint8_t target[ROUTE_ADDR_WIRE_SIZE], primary[ROUTE_ADDR_WIRE_SIZE];
uint8_t observer_address[ROUTE_ADDR_WIRE_SIZE];
};
struct nat_pair {
uint64_t group, observer, helper;
uint8_t helper_priority; /* Публичные IP: свежий/знакомый; затем приватные: свежий/знакомый; затем один B. */
uint8_t observer_endpoint[ROUTE_ADDR_WIRE_SIZE], helper_endpoint[ROUTE_ADDR_WIRE_SIZE];
};
struct nat_recheck_wire { uint8_t cmd, command, version, socket_id, address[ROUTE_ADDR_WIRE_SIZE]; };
struct nat_socket {
struct nat_socket *next;
struct NAT_DETECTION *detector;
struct ETCP_SOCKET *socket, *helper_primary;
struct sockaddr_storage local, observer_address, helper_address;
struct sockaddr_storage baseline, helper_seen;
struct sockaddr_storage measured_address, observed_address;
struct address_profile measured_profile;
uint64_t select_due, available_fingerprint, reservation_fingerprint, cold_until, failure_recheck_tb;
uint8_t observer_key[32], helper_key[32];
uint64_t group_id, round, observer, helper, token, observation_token, deadline, due, completed_tb, preferred_observer;
uint64_t cycle_deadline, heartbeat_due, heartbeat_received, pre_started;
struct nat_pair cursor, current_pair; /* Курсор продвигается только после завершённой попытки. */
uint8_t contacts[NAT_MAX_CONTACTS][17];
unsigned contact_count, contact_cutoff, cycle_contact_cutoff, phase, reason, retries;
uint8_t history_loaded, history_complete, history_incomplete, filter_clean, pre_received, post_received, pre_healthy;
uint8_t cycle_history_complete, cycle_mapping_seen;
uint8_t helper_socket, active_ping, heartbeat_active, forced, cycling;
uint32_t rounds, helper_reselections;
};
struct nat_binding {
struct nat_binding *next;
uint64_t group_id, owner, generation, round, expires, helper, token;
uint8_t owner_socket, owner_key[32];
struct sockaddr_storage target;
};
struct nat_probe {
struct nat_probe *next;
struct NAT_DETECTION *detector;
struct nat_wire message;
uint64_t group_id, reply, source_generation, io_generation, due, expires;
uint8_t owner_key[32], observer_key[32];
uint8_t source_socket, io_socket, active, cancelled, finding_primary;
};
struct NAT_DETECTION {
struct UTUN_INSTANCE *instance;
struct nat_socket *sockets;
struct nat_binding *bindings;
struct nat_probe *probes;
void *timer;
void *topology_wake;
uint64_t publish_after;
uint8_t allow_local, stopping, history_table_ready, automatic, publish_pending, reservation_dirty;
};
static void owner_ping_cb(int, uint16_t, void *, uint64_t, const uint8_t *, size_t);
static void owner_heartbeat_cb(int, uint16_t, void *, uint64_t, const uint8_t *, size_t);
static void helper_ping_cb(int, uint16_t, void *, uint64_t, const uint8_t *, size_t);
static void detector_tick(void *);
static int owner_observe(struct nat_socket *);
static int owner_run(struct nat_socket *, uint8_t);
static void owner_finish(struct nat_socket *, uint8_t);
static void helper_remove(struct nat_probe *);
static int helper_send(struct nat_probe *);
static void owner_start(struct nat_socket *);
static int contacted(struct nat_socket *, const struct sockaddr_storage *);
/* Не сбрасывает последний успех; повторные ошибки одного endpoint объединяются и ограничиваются по частоте. */
static int schedule_failure_recheck(struct nat_socket *state, uint64_t peer, int remote) {
uint64_t now = get_time_tb();
if (!state || !state->detector->automatic || state->phase || state->cycling || state->forced ||
(state->failure_recheck_tb && now - state->failure_recheck_tb < NAT_FAILURE_RECHECK_TB)) return 0;
state->failure_recheck_tb = now; state->due = now; state->forced = 1; state->preferred_observer = 0;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT failure recheck scheduled socket=%s generation=%016llx peer=%016llx remote_request=%d",
state->socket->name, (unsigned long long)state->socket->generation, (unsigned long long)peer, remote);
return 1;
}
static uint64_t read64(const uint8_t *data) {
uint64_t value = 0;
for (unsigned i = 0; i < 8; i++) value = (value << 8) | data[i];
return value;
}
static void write64(uint8_t *data, uint64_t value) {
for (unsigned i = 0; i < 8; i++) data[i] = (uint8_t)(value >> (56 - i * 8));
}
static int random_id(uint64_t *value) {
do {
if (random_bytes((uint8_t *)value, sizeof(*value))) {
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT round/token RNG failed"); return -1;
}
} while (!*value);
return 0;
}
const char *nat_check_reason_name(uint8_t reason) {
static const char *const names[] = {"none", "no_observer", "no_helper", "control_lost", "send_error", "timeout",
"address_changed", "contact_history", "proxy", "no_listener", "cancelled", "invalid_observation", "dependent_mapping",
"cycle_budget"};
return reason < sizeof(names) / sizeof(names[0]) ? names[reason] : "invalid_reason";
}
static struct ETCP_SOCKET *find_socket(struct NAT_DETECTION *detector, uint8_t id, uint64_t generation) {
for (struct ETCP_SOCKET *socket = detector->instance->etcp_sockets; socket; socket = socket->next)
if (socket->sock_id == id && (!generation || socket->generation == generation)) return socket;
return NULL;
}
static int public_address(struct NAT_DETECTION *detector, const struct sockaddr_storage *address) {
if (address->ss_family == AF_INET) {
uint32_t ip = ((const struct sockaddr_in *)address)->sin_addr.s_addr;
return ip && (ip_is_public(ip) || detector->allow_local);
}
if (address->ss_family == AF_INET6) {
const uint8_t *ip = ((const struct sockaddr_in6 *)address)->sin6_addr.s6_addr;
return (ip[0] & 0xe0) == 0x20 || (detector->allow_local && !memcmp(ip, &in6addr_loopback, 16));
}
return 0;
}
/* Подписанная идентичность участника выбранной группы; сохранённый bootstrap не разрешает управление. */
static const struct TOPO_NODE *group_node(struct NAT_DETECTION *detector, uint64_t group_id, uint64_t node_id) {
struct TOPO_GROUP *group = topo_groups_find(detector->instance->topo_groups, group_id);
if (!group || group->stopping || (node_id != detector->instance->node_id && !topo_node_find_by_id(group, node_id))) return NULL;
const struct TOPO_NODE *node = topo_node_registry_find(detector->instance->topo_groups, node_id);
return node && node->timestamp ? node : NULL;
}
/* Управление допускает транзит и TCP, но следующий хоп должен завершить согласование этой группы. */
static int control_route(struct NAT_DETECTION *detector, uint64_t group_id, uint64_t node_id) {
if (!group_node(detector, group_id, node_id)) return 0;
struct ETCP_ROUTER_PATH path = etcp_router_get_path(detector->instance, group_id, node_id, ETCP_RT_ID_NAT_DETECTION);
struct TOPO_GROUP *group = topo_groups_find(detector->instance->topo_groups, group_id);
return path.type != CONN_TYPE_NONE && topo_group_peer_ready(group, path.next_hop_node_id);
}
/* Выбирает сокет по NODEINFO. Помощнику разрешён приватный endpoint: его mapping измеряется перед ingress. */
static int node_udp_endpoint(struct NAT_DETECTION *detector, const struct TOPO_NODE *node, int family, int allow_private,
const uint8_t after[ROUTE_ADDR_WIRE_SIZE], struct sockaddr_storage *address, uint8_t *socket_id) {
uint8_t best[ROUTE_ADDR_WIRE_SIZE] = {0};
if (!node) return 0;
for (const struct TOPO_ADDR4 *a = family == AF_INET ? node->v4_addrs : NULL; a; a = a->next) {
if (!(a->protocol & TOPO_PROTO_UDP) || !a->port) continue;
const struct TOPO_SOCKMETA4 *meta = node->v4_sock_meta;
while (meta && meta->id != a->socket_id) meta = meta->next;
if (meta && (meta->config_type == CFG_SERVER_TYPE_PRIVATE || meta->config_type == CFG_SERVER_TYPE_LOCAL)) continue;
struct sockaddr_storage candidate = {0}; struct sockaddr_in *v4 = (struct sockaddr_in *)&candidate;
v4->sin_family = AF_INET; memcpy(&v4->sin_addr, a->addr, 4); v4->sin_port = htons(a->port);
uint8_t wire[ROUTE_ADDR_WIRE_SIZE];
if ((!allow_private && !public_address(detector, &candidate)) || route_ping_addr_encode(wire, &candidate)) continue;
if ((after && memcmp(wire, after, sizeof(wire)) <= 0) || (best[0] && memcmp(wire, best, sizeof(wire)) >= 0)) continue;
memcpy(best, wire, sizeof(best)); *address = candidate; *socket_id = a->socket_id;
}
for (const struct TOPO_ADDR6 *a = family == AF_INET6 ? node->v6_addrs : NULL; a; a = a->next) {
if (!(a->protocol & TOPO_PROTO_UDP) || !a->port) continue;
const struct TOPO_SOCKMETA6 *meta = node->v6_sock_meta;
while (meta && meta->id != a->socket_id) meta = meta->next;
if (meta && (meta->config_type == CFG_SERVER_TYPE_PRIVATE || meta->config_type == CFG_SERVER_TYPE_LOCAL)) continue;
struct sockaddr_storage candidate = {0}; struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)&candidate;
v6->sin6_family = AF_INET6; memcpy(&v6->sin6_addr, a->addr, 16); v6->sin6_port = htons(a->port);
uint8_t wire[ROUTE_ADDR_WIRE_SIZE];
if ((!allow_private && !public_address(detector, &candidate)) || route_ping_addr_encode(wire, &candidate)) continue;
if ((after && memcmp(wire, after, sizeof(wire)) <= 0) || (best[0] && memcmp(wire, best, sizeof(wire)) >= 0)) continue;
memcpy(best, wire, sizeof(best)); *address = candidate; *socket_id = a->socket_id;
}
/* Наблюдаемый адрес уже работающего UDP помогает bootstrap за NAT; прямой линк не является условием отбора. */
struct ETCP_CONN *connection = instance_find_conn(detector->instance, node->node_id);
for (struct ETCP_LINK *link = connection ? connection->links : NULL; link; link = link->next) {
if (link->is_tcp || !link->initialized || !link->link_status || link->remote_only_local ||
link->remote_addr.ss_family != family || !public_address(detector, &link->remote_addr)) continue;
uint8_t wire[ROUTE_ADDR_WIRE_SIZE];
if (route_ping_addr_encode(wire, &link->remote_addr)) continue;
if ((after && memcmp(wire, after, sizeof(wire)) <= 0) || (best[0] && memcmp(wire, best, sizeof(wire)) >= 0)) continue;
memcpy(best, wire, sizeof(best)); *address = link->remote_addr; *socket_id = link->remote_socket_id;
}
return best[0] != 0;
}
static int pair_after(struct nat_pair a, struct nat_pair b) {
if (!a.group || !b.group) return a.group != 0;
if (a.helper_priority != b.helper_priority) return a.helper_priority > b.helper_priority;
if (a.group != b.group) return a.group > b.group;
if (a.observer != b.observer) return a.observer > b.observer;
if (a.helper != b.helper) return a.helper > b.helper;
int order = memcmp(a.observer_endpoint, b.observer_endpoint, sizeof(a.observer_endpoint));
return order ? order > 0 : memcmp(a.helper_endpoint, b.helper_endpoint, sizeof(a.helper_endpoint)) > 0;
}
/* Обходит пары всех групп без ограничения числа узлов и без хранения указателей на топологию. */
static int select_pair(struct nat_socket *state) {
struct NAT_DETECTION *detector = state->detector;
struct nat_pair best = {0};
struct nat_pair single = {0};
struct sockaddr_storage single_address = {0}; uint8_t single_key[32] = {0};
struct sockaddr_storage observer_address = {0}, helper_address = {0};
uint8_t observer_key[32] = {0}, helper_key[32] = {0}, helper_socket = 0;
unsigned observers = 0, groups = 0;
for (struct ll_entry *e = detector->instance->topo_groups->group_list->head; e; e = e->next) {
struct TOPO_GROUP *group = (struct TOPO_GROUP *)e;
if (group->stopping || !group->local_node) continue;
groups++;
for (struct ll_entry *b = group->nodes->head; b; b = b->next) {
uint64_t observer = ((struct TOPO_GROUP_NODE *)b)->node_id;
if (observer == detector->instance->node_id || (state->preferred_observer && observer != state->preferred_observer) ||
!control_route(detector, group->group_id, observer)) continue;
const struct TOPO_NODE *bn = group_node(detector, group->group_id, observer);
uint8_t previous_b[ROUTE_ADDR_WIRE_SIZE] = {0};
struct sockaddr_storage ba = {0}; uint8_t bs = 0;
int more_b = node_udp_endpoint(detector, bn, state->local.ss_family, 0, previous_b, &ba, &bs);
for (; more_b; more_b =
node_udp_endpoint(detector, bn, state->local.ss_family, 0, previous_b, &ba, &bs)) {
if (route_ping_addr_encode(previous_b, &ba)) break;
observers++;
struct nat_pair observer_only = {.group = group->group_id, .observer = observer, .helper_priority = 4};
memcpy(observer_only.observer_endpoint, previous_b, sizeof(previous_b));
if (pair_after(observer_only, state->cursor) &&
(!single.group || pair_after(single, observer_only))) {
single = observer_only; single_address = ba; memcpy(single_key, bn->public_key, 32);
}
for (struct ll_entry *c = group->nodes->head; c; c = c->next) {
uint64_t helper = ((struct TOPO_GROUP_NODE *)c)->node_id;
if (helper == detector->instance->node_id || helper == observer ||
!control_route(detector, group->group_id, helper)) continue;
const struct TOPO_NODE *cn = group_node(detector, group->group_id, helper);
struct sockaddr_storage ca = {0}; uint8_t cs = 0, previous_c[ROUTE_ADDR_WIRE_SIZE] = {0};
int more_c = node_udp_endpoint(detector, cn, state->local.ss_family, 1, previous_c, &ca, &cs);
for (; more_c; more_c =
node_udp_endpoint(detector, cn, state->local.ss_family, 1, previous_c, &ca, &cs)) {
if (route_ping_addr_encode(previous_c, &ca)) break;
int old_contact = !state->cycle_history_complete;
for (unsigned i = 0; i < state->cycle_contact_cutoff; i++)
if (!memcmp(state->contacts[i], previous_c, 17)) { old_contact = 1; break; }
struct nat_pair candidate = {.group = group->group_id, .observer = observer, .helper = helper,
.helper_priority = 2 * !public_address(detector, &ca) + old_contact};
memcpy(candidate.observer_endpoint, previous_b, sizeof(previous_b));
memcpy(candidate.helper_endpoint, previous_c, sizeof(previous_c));
if (!pair_after(candidate, state->cursor)) continue;
if (best.group && !pair_after(best, candidate)) continue;
best = candidate; observer_address = ba; helper_address = ca; helper_socket = cs;
memcpy(observer_key, bn->public_key, 32); memcpy(helper_key, cn->public_key, 32);
}
}
}
}
}
if (!best.group && single.group && !state->cycle_mapping_seen) {
best = single; observer_address = single_address; memcpy(observer_key, single_key, 32);
}
if (!best.group) {
if (!observers || !state->cursor.group || !state->reason)
state->reason = observers ? NAT_REASON_NO_HELPER : NAT_REASON_NO_OBSERVER;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT candidate cycle exhausted socket=%s groups=%u observers=%u reason=%s",
state->socket->name, groups, observers, nat_check_reason_name(state->reason));
return -1;
}
state->current_pair = best;
state->group_id = best.group; state->observer = best.observer; state->helper = best.helper;
state->observer_address = observer_address; state->helper_address = helper_address; state->helper_socket = helper_socket;
memcpy(state->observer_key, observer_key, 32); memcpy(state->helper_key, helper_key, 32);
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT candidates socket=%s group=%016llx observer=%016llx endpoint=%s helper=%016llx candidate=%s priority=%u",
state->socket->name, (unsigned long long)best.group, (unsigned long long)best.observer,
sockaddr_storage_to_str(&observer_address).str, (unsigned long long)best.helper,
sockaddr_storage_to_str(&helper_address).str, best.helper_priority);
return 0;
}
static void publish(struct NAT_DETECTION *detector) {
if (detector->stopping || !detector->instance->topo_groups) return;
detector->publish_pending = topo_node_update_my_addresses(detector->instance) < 0;
if (detector->publish_pending) detector->publish_after = get_time_tb() + 10000;
if (detector->publish_pending) DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT profile publication failed; retry scheduled");
else chat_event_post(detector->instance, CHAT_EVT_LOCAL_SOCKETS, NULL, 0);
}
static struct nat_socket *socket_state(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, int create) {
if (!detector || !socket || socket->probe_only || detector->stopping) return NULL;
for (struct nat_socket *state = detector->sockets; state; state = state->next)
if (state->socket == socket) return state;
if (!create) return NULL;
struct nat_socket *state = u_calloc(1, sizeof(*state));
if (!state) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT endpoint state allocation failed socket=%s", socket->name); return NULL; }
state->detector = detector; state->socket = socket; state->local = socket->local_addr;
if (socket->is_tcp) state->local = socket->interface_addr;
state->due = get_time_tb() + 2500;
state->cold_until = state->due + NAT_ROUND_TB;
state->next = detector->sockets; detector->sockets = state;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT endpoint created socket=%s id=%u generation=%016llx protocol=%s local=%s",
socket->name, socket->sock_id, (unsigned long long)socket->generation,
socket->is_tcp ? "TCP" : "UDP", sockaddr_storage_to_str(&state->local).str);
return state;
}
/* История сохраняется один раз на новый IP. Перезапуск на том же IP:port её не очищает. */
static int history_table(struct NAT_DETECTION *detector) {
if (detector->history_table_ready) return 0;
sqlite3 *db = detector->instance->topo_sqlite_db;
if (!db) return -1;
if (sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS nat_contacts(local BLOB NOT NULL,remote BLOB NOT NULL,"
"PRIMARY KEY(local,remote)) WITHOUT ROWID", NULL, NULL, NULL) != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT contact history initialization failed: %s", sqlite3_errmsg(db)); return -1;
}
detector->history_table_ready = 1; return 0;
}
static int history_insert(struct nat_socket *state, const uint8_t remote[17]) {
uint8_t local[ROUTE_ADDR_WIRE_SIZE];
if (route_ping_addr_encode(local, &state->local) || history_table(state->detector)) return -1;
sqlite3_stmt *statement = NULL;
sqlite3 *db = state->detector->instance->topo_sqlite_db;
if (sqlite3_prepare_v2(db, "INSERT OR IGNORE INTO nat_contacts(local,remote) VALUES(?,?)",
-1, &statement, NULL) != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT contact write failed: %s", sqlite3_errmsg(db)); return -1;
}
sqlite3_bind_blob(statement, 1, local, sizeof(local), SQLITE_TRANSIENT);
sqlite3_bind_blob(statement, 2, remote, 17, SQLITE_TRANSIENT);
int result = sqlite3_step(statement); sqlite3_finalize(statement);
if (result != SQLITE_DONE) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT contact insert failed: %s", sqlite3_errmsg(db)); return -1; }
return 0;
}
static void history_mark_incomplete(struct nat_socket *state) {
static const uint8_t marker[17];
state->history_complete = 0; state->history_incomplete = 1;
/* family=0 — постоянный маркер неполной истории, не адрес помощника. */
if (history_insert(state, marker)) DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT incomplete history marker persistence failed");
}
static void history_load(struct nat_socket *state) {
if (state->history_loaded || state->socket->is_tcp) return;
struct NAT_DETECTION *detector = state->detector;
if (history_table(detector)) { state->history_complete = 0; return; }
uint8_t local[ROUTE_ADDR_WIRE_SIZE];
if (route_ping_addr_encode(local, &state->local)) return;
sqlite3_stmt *statement = NULL;
sqlite3 *db = detector->instance->topo_sqlite_db;
if (sqlite3_prepare_v2(db, "SELECT remote FROM nat_contacts WHERE local=?", -1, &statement, NULL) != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT history read failed: %s", sqlite3_errmsg(db)); return;
}
sqlite3_bind_blob(statement, 1, local, sizeof(local), SQLITE_TRANSIENT);
state->history_complete = !state->history_incomplete;
int result;
while ((result = sqlite3_step(statement)) == SQLITE_ROW) {
const uint8_t *remote = sqlite3_column_blob(statement, 0);
if (!remote || sqlite3_column_bytes(statement, 0) != 17 || (remote[0] != 4 && remote[0] != 6)) {
state->history_incomplete = 1; state->history_complete = 0; continue;
}
unsigned index;
for (index = 0; index < state->contact_count; index++) if (!memcmp(state->contacts[index], remote, 17)) break;
if (index < state->contact_count) continue;
if (state->contact_count == NAT_MAX_CONTACTS) { state->history_incomplete = 1; state->history_complete = 0; continue; }
memcpy(state->contacts[state->contact_count++], remote, 17);
}
if (result != SQLITE_DONE) {
state->history_complete = 0;
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT history query failed: %s", sqlite3_errmsg(db));
}
sqlite3_finalize(statement); state->history_loaded = 1;
/* Сохраняем также отправки, которые случились до открытия общей SQLite. */
for (unsigned i = 0; i < state->contact_count; i++)
if (history_insert(state, state->contacts[i])) { history_mark_incomplete(state); break; }
if (state->history_incomplete) history_mark_incomplete(state);
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT history loaded socket=%s contacts=%u complete=%u",
state->socket->name, state->contact_count, state->history_complete);
}
static int contacted(struct nat_socket *state, const struct sockaddr_storage *address) {
uint8_t remote[ROUTE_ADDR_WIRE_SIZE];
if (!state->history_complete || route_ping_addr_encode(remote, address)) return 1;
for (unsigned i = 0; i < state->contact_count; i++) if (!memcmp(state->contacts[i], remote, 17)) return 1;
return 0;
}
void nat_detection_note_send(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket,
const struct sockaddr_storage *destination) {
if (!detector || !socket || socket->is_tcp || socket->probe_only || socket->socks_udp || detector->stopping) return;
struct nat_socket *state = socket_state(detector, socket, 1);
if (!state) return;
history_load(state);
if ((state->phase == NAT_PREPARE_C || state->phase == NAT_PRE || state->phase == NAT_PRE_WAIT) && !state->pre_received &&
route_ping_addr_equal(destination, &state->helper_address, 0)) {
if (state->filter_clean) DEBUG_WARN(DEBUG_CATEGORY_NAT,
"NAT ingress contaminated by outgoing UDP socket=%s round=%016llx destination=%s",
socket->name, (unsigned long long)state->round, sockaddr_storage_to_str(destination).str);
state->filter_clean = 0;
}
uint8_t local[ROUTE_ADDR_WIRE_SIZE], remote[ROUTE_ADDR_WIRE_SIZE];
if (route_ping_addr_encode(local, &state->local) || route_ping_addr_encode(remote, destination)) return;
for (unsigned i = 0; i < state->contact_count; i++) if (!memcmp(state->contacts[i], remote, 17)) return;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT first UDP contact node=%016llx socket=%s id=%u generation=%016llx phase=%u group=%016llx"
" round=%016llx destination=%s reserved=%u cold_remaining_tb=%llu",
(unsigned long long)detector->instance->node_id, socket->name, socket->sock_id,
(unsigned long long)socket->generation, state->phase, (unsigned long long)state->group_id,
(unsigned long long)state->round, sockaddr_storage_to_str(destination).str,
state->reservation_fingerprint != 0,
(unsigned long long)(state->cold_until > get_time_tb() ? state->cold_until - get_time_tb() : 0));
if (state->contact_count == NAT_MAX_CONTACTS) {
if (state->history_complete) DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT contact history full socket=%s", socket->name);
if (!state->history_incomplete) history_mark_incomplete(state);
return;
}
memcpy(state->contacts[state->contact_count++], remote, 17);
if (!state->history_loaded || !detector->instance->topo_sqlite_db) { state->history_complete = 0; return; }
if (history_insert(state, remote)) history_mark_incomplete(state);
}
static int send_control(struct NAT_DETECTION *detector, uint64_t group_id, uint64_t destination, struct nat_wire *message) {
if (!control_route(detector, group_id, destination)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT control route unavailable group=%016llx peer=%016llx",
(unsigned long long)group_id, (unsigned long long)destination); return -1;
}
struct ll_entry *entry = queue_entry_new(0);
if (!entry) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT control queue allocation failed"); return -1; }
entry->dgram = u_malloc(1 + sizeof(*message));
if (!entry->dgram) { queue_entry_free(entry); DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT control allocation failed"); return -1; }
message->cmd = ETCP_RT_ID_NAT_DETECTION; message->version = NAT_WIRE_VERSION;
entry->dgram[0] = ETCP_RT_ID_NAT_DETECTION;
memcpy(entry->dgram + 1, message, sizeof(*message)); entry->len = 1 + sizeof(*message);
if (etcp_route_send(detector->instance, group_id, destination, entry, 0, ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT)) {
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT control send failed group=%016llx peer=%016llx command=%u",
(unsigned long long)group_id, (unsigned long long)destination, message->command); return -1;
}
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT control TX group=%016llx command=%u step=%u peer=%016llx owner=%016llx round=%016llx helper=%016llx",
(unsigned long long)group_id, message->command, message->step, (unsigned long long)destination,
(unsigned long long)read64(message->owner), (unsigned long long)read64(message->round),
(unsigned long long)read64(message->helper));
return 0;
}
/* Один бюджет ожидания для UDP-пробы, серий помощника и паузы доставки последнего пакета. */
static int probe_timeout_ms(struct nat_socket *state) {
int timeout = 1000;
struct ETCP_CONN *connection = topo_group_find_conn_for_node(
topo_groups_find(state->detector->instance->topo_groups, state->group_id), state->observer);
for (struct ETCP_LINK *link = connection ? connection->links : NULL; link; link = link->next)
if (link->rtt_last / 10 * 4 > timeout) timeout = link->rtt_last / 10 * 4;
return timeout;
}
static int make_message(struct nat_socket *state, struct nat_wire *message, uint8_t command) {
memset(message, 0, sizeof(*message)); message->command = command;
message->owner_socket = state->socket->sock_id; message->helper_socket = state->helper_socket;
write64(message->owner, state->detector->instance->node_id);
write64(message->generation, state->socket->generation); write64(message->round, state->round);
write64(message->helper, state->helper); write64(message->observer, state->observer); write64(message->token, state->token);
if (route_ping_addr_encode(message->target, &state->baseline)) return -1;
if (state->helper_address.ss_family && route_ping_addr_encode(message->primary, &state->helper_address)) return -1;
if (state->observer_address.ss_family && route_ping_addr_encode(message->observer_address, &state->observer_address)) return -1;
int timeout = probe_timeout_ms(state);
message->timeout_ms[0] = timeout >> 8; message->timeout_ms[1] = timeout;
return 0;
}
/* UDP-запрос коррелируется отдельным token; результат придёт через защищённый router, не через PONG. */
static int observe_control(struct ETCP_SOCKET *socket, const uint8_t key[32], const struct sockaddr_storage *target,
uint64_t group, uint64_t round, uint8_t step, uint64_t token, int timeout,
etcp_ping_callback_t callback, void *arg) {
uint8_t tag[NAT_QUERY_TAG_SIZE]; route_ping_make_tag(tag, socket, group, round);
tag[ROUTE_OBSERVE_TAG_SIZE] = step; write64(tag + ROUTE_OBSERVE_TAG_SIZE + 1, token);
return etcp_send_ping_to_socket(socket->instance, socket, key, target, timeout, callback, arg, tag, sizeof(tag),
ETCP_PING_FLAG_OBSERVE | ETCP_PING_FLAG_NO_REPLY);
}
static struct nat_binding *binding_find(struct NAT_DETECTION *detector, uint64_t group_id, uint64_t owner,
uint64_t generation, uint64_t round) {
for (struct nat_binding *binding = detector->bindings; binding; binding = binding->next)
if (binding->group_id == group_id && binding->owner == owner && binding->generation == generation && binding->round == round &&
binding->expires > get_time_tb()) return binding;
return NULL;
}
void nat_detection_record_observation(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket,
const uint8_t peer_key[32], const struct sockaddr_storage *source,
const uint8_t *tag, size_t length) {
if (!detector || detector->stopping) return;
if (!socket || socket->socks_udp || (length != ROUTE_OBSERVE_TAG_SIZE && length != NAT_QUERY_TAG_SIZE) ||
!tag || memcmp(tag, "NAT4UDP!", 8)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT observation query has invalid tag length=%zu", length); return;
}
uint64_t owner = sc_derive_node_id_from_pubkey(peer_key), generation = read64(tag + 9), round = read64(tag + 17);
uint64_t group_id = read64(tag + 25);
const struct TOPO_NODE *node = group_node(detector, group_id, owner);
if (!generation || !round || !node || memcmp(node->public_key, peer_key, 32)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT observation has invalid group/identity group=%016llx owner=%016llx",
(unsigned long long)group_id, (unsigned long long)owner); return;
}
int eligible = public_address(detector, source);
if (!eligible) DEBUG_WARN(DEBUG_CATEGORY_NAT,
"NAT observation rejected by receiver group=%016llx owner=%016llx round=%016llx source=%s reason=non_public_source",
(unsigned long long)group_id, (unsigned long long)owner, (unsigned long long)round, sockaddr_storage_to_str(source).str);
if (length == NAT_QUERY_TAG_SIZE) {
struct nat_wire report = {.command = NAT_OBSERVATION, .owner_socket = tag[8], .step = tag[ROUTE_OBSERVE_TAG_SIZE],
.reason = eligible ? NAT_REASON_NONE : NAT_REASON_INVALID_OBSERVATION};
write64(report.owner, owner); write64(report.generation, generation); write64(report.round, round);
write64(report.token, read64(tag + ROUTE_OBSERVE_TAG_SIZE + 1));
write64(report.observer, detector->instance->node_id); write64(report.helper, detector->instance->node_id);
if (route_ping_addr_encode(report.target, source) || send_control(detector, group_id, owner, &report))
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT routed observation delivery failed group=%016llx owner=%016llx round=%016llx",
(unsigned long long)group_id, (unsigned long long)owner, (unsigned long long)round);
}
if (!eligible) return;
struct nat_binding *binding = binding_find(detector, group_id, owner, generation, round);
if (!binding) {
unsigned count = 0;
for (struct nat_binding *item = detector->bindings; item; item = item->next) count++;
if (count >= NAT_MAX_BINDINGS) { DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT observation cache full"); return; }
binding = u_calloc(1, sizeof(*binding));
if (!binding) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT observation cache allocation failed"); return; }
binding->next = detector->bindings; detector->bindings = binding;
}
binding->group_id = group_id; binding->owner = owner; binding->generation = generation;
binding->round = round; binding->owner_socket = tag[8];
memcpy(binding->owner_key, peer_key, 32); binding->target = *source; binding->expires = get_time_tb() + NAT_ROUND_TB;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT observation group=%016llx owner=%016llx socket=%u generation=%016llx round=%016llx observed=%s via=%s",
(unsigned long long)group_id, (unsigned long long)owner, tag[8], (unsigned long long)generation, (unsigned long long)round,
sockaddr_storage_to_str(source).str, socket->name);
}
static void clear_profile(struct nat_socket *state, uint8_t reason) {
int changed = state->socket->address_profile.mapping || state->socket->address_profile.inbound ||
state->socket->nat_addr.ss_family;
memset(&state->socket->address_profile, 0, sizeof(state->socket->address_profile));
memset(&state->socket->nat_addr, 0, sizeof(state->socket->nat_addr));
state->completed_tb = 0;
state->cold_until = get_time_tb() + NAT_ROUND_TB + 2500;
memset(&state->measured_profile, 0, sizeof(state->measured_profile));
memset(&state->measured_address, 0, sizeof(state->measured_address));
memset(&state->observed_address, 0, sizeof(state->observed_address));
if (changed) {
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT profile invalidated socket=%s generation=%016llx reason=%s",
state->socket->name, (unsigned long long)state->socket->generation, nat_check_reason_name(reason));
publish(state->detector);
}
}
/* Измерение рабочего endpoint приоритетно; STUN того же пути даёт только предварительный EIM?. */
static void select_profile(struct nat_socket *state) {
const struct address_profile *current = &state->socket->address_profile;
struct address_profile selected = state->measured_profile;
struct sockaddr_storage selected_address = {0};
if (state->socket->is_tcp) {
selected_address = state->observed_address;
if (!selected.source && !public_address(state->detector, &state->local)) selected.source = ADDRESS_SOURCE_ETCP;
}
if (state->measured_profile.source == ADDRESS_SOURCE_ETCP) {
selected = state->measured_profile;
if (selected.mapping == ADDRESS_MAP_EIM) selected_address = state->measured_address;
}
if (!selected.mapping && state->observed_address.ss_family) {
selected.source = ADDRESS_SOURCE_ETCP; selected_address = state->observed_address;
}
struct stun_nat_result stun;
uint64_t now = get_time_tb();
if (!selected.mapping && !stun_nat_get_socket_result(state->detector->instance, state->socket, &stun) &&
(stun.status == STUN_NAT_COMPLETE || stun.status == STUN_NAT_PARTIAL) &&
stun.measured_at_tb && now >= stun.measured_at_tb && now - stun.measured_at_tb < STUN_NAT_TTL_TB &&
stun.server_count == 2 && stun.translated == 1 && (stun.mapping == STUN_MAP_MATCH || stun.mapping == STUN_MAP_EIM) &&
public_address(state->detector, &stun.servers[0]) && public_address(state->detector, &stun.servers[1]) &&
public_address(state->detector, &stun.mapped[0]) &&
(!selected_address.ss_family || route_ping_addr_equal(&selected_address, &stun.mapped[0], 0))) {
selected.mapping = ADDRESS_MAP_EIM; selected.inbound = ADDRESS_IN_UNKNOWN; selected.source = ADDRESS_SOURCE_STUN;
}
int address_changed = selected_address.ss_family != state->socket->nat_addr.ss_family ||
(selected_address.ss_family && !route_ping_addr_equal(&selected_address, &state->socket->nat_addr, 1));
if (selected.mapping == current->mapping && selected.inbound == current->inbound &&
selected.source == current->source && !address_changed) return;
if (selected.source == ADDRESS_SOURCE_STUN)
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT tentative EIM? socket=%s STUN_local=%s netif=%u evidence=%016llx"
" mapping=%s filtering=%s age_tb=%llu working_address=%s",
state->socket->name, sockaddr_storage_to_str(&stun.local).str, stun.key.netif_index,
(unsigned long long)stun.evidence_id, stun_nat_mapping_name(stun.mapping),
stun_nat_filtering_name(stun.filtering), (unsigned long long)(now - stun.measured_at_tb),
sockaddr_storage_to_str(&selected_address).str);
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT selected socket=%s generation=%016llx source=%s previous=%s mapping=%s inbound=%s"
" address_changed=%d reason=%s",
state->socket->name, (unsigned long long)state->socket->generation, address_profile_source_name(selected.source),
address_profile_source_name(current->source), address_mapping_name(selected.mapping), address_inbound_name(selected.inbound),
address_changed, nat_check_reason_name(state->reason));
state->socket->address_profile = selected;
state->socket->nat_addr = selected_address;
publish(state->detector);
}
/* Одно наблюдение подтверждает NAT-адрес, но не его независимость от IP назначения. */
static void remember_address(struct nat_socket *state, const struct sockaddr_storage *observed) {
if (route_ping_addr_equal(&state->local, observed, 1)) return;
if (route_ping_addr_equal(&state->observed_address, observed, 1)) return;
state->observed_address = *observed;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT address observed socket=%s local=%s external=%s mapping=%s scope=one_destination",
state->socket->name, sockaddr_storage_to_str(&state->local).str, sockaddr_storage_to_str(observed).str,
address_mapping_name(state->measured_profile.mapping));
select_profile(state);
}
static void owner_finish(struct nat_socket *state, uint8_t reason) {
/* Неудача входящего испытания не мешает сравнить mapping через уже известный публичный endpoint C. */
if ((reason == NAT_REASON_TIMEOUT || reason == NAT_REASON_CONTROL_LOST || reason == NAT_REASON_SEND_ERROR ||
reason == NAT_REASON_CONTACT_HISTORY) &&
(state->phase == NAT_PREPARE_C || state->phase == NAT_PRE || state->phase == NAT_POST) &&
get_time_tb() < state->cycle_deadline && public_address(state->detector, &state->helper_address) &&
!route_ping_addr_equal(&state->helper_address, &state->observer_address, 0) &&
!route_ping_addr_equal(&state->helper_address, &state->baseline, 0)) {
if (reason == NAT_REASON_CONTACT_HISTORY || !state->pre_received) state->filter_clean = 0;
state->phase = state->helper_seen.ss_family ? NAT_REPEAT_B : NAT_OBSERVE_C; state->retries = 0;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT mapping continues without ingress result socket=%s round=%016llx helper=%s reason=%s",
state->socket->name, (unsigned long long)state->round, sockaddr_storage_to_str(&state->helper_address).str,
nat_check_reason_name(reason));
if (owner_observe(state)) owner_finish(state, NAT_REASON_SEND_ERROR);
return;
}
uint64_t now = get_time_tb();
int next_pair = reason && state->phase && state->cycling &&
reason != NAT_REASON_CANCELLED && reason != NAT_REASON_ADDRESS_CHANGED;
etcp_cancel_pings(state->detector->instance, owner_ping_cb, state);
etcp_cancel_pings(state->detector->instance, owner_heartbeat_cb, state);
int pause = next_pair && now >= state->cycle_deadline;
if (next_pair && reason != NAT_REASON_BUDGET) state->cursor = state->current_pair;
state->active_ping = 0; state->heartbeat_active = 0; state->phase = NAT_IDLE; state->reason = reason;
if (next_pair) {
state->forced = 1; state->due = now + (pause ? NAT_RETRY_TB : 1000);
if (reason != NAT_REASON_BUDGET) state->helper_reselections++;
if (pause) DEBUG_INFO(DEBUG_CATEGORY_NAT,
"NAT candidate cycle paused socket=%s round=%016llx retry_current=%d resume_in_tb=%llu mapping=%s inbound=%s",
state->socket->name, (unsigned long long)state->round, reason == NAT_REASON_BUDGET,
(unsigned long long)NAT_RETRY_TB, address_mapping_name(state->measured_profile.mapping),
address_inbound_name(state->measured_profile.inbound));
} else {
state->cycling = 0; state->preferred_observer = 0; state->cursor = (struct nat_pair){0};
state->current_pair = (struct nat_pair){0};
int complete = state->measured_profile.mapping == ADDRESS_MAP_DEPENDENT ||
(state->measured_profile.mapping && state->measured_profile.inbound);
state->due = get_time_tb() + (reason == NAT_REASON_ADDRESS_CHANGED ? 2500 : complete ? NAT_REFRESH_TB : NAT_RETRY_TB);
}
if (reason) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT round incomplete socket=%s group=%016llx generation=%016llx round=%016llx reason=%s next_pair=%d",
state->socket->name, (unsigned long long)state->group_id, (unsigned long long)state->socket->generation,
(unsigned long long)state->round, nat_check_reason_name(reason), next_pair);
}
select_profile(state);
}
static void owner_apply(struct nat_socket *state, uint8_t mapping, uint8_t inbound) {
struct address_profile profile = {.mapping = mapping, .inbound = inbound, .source = ADDRESS_SOURCE_ETCP};
uint8_t partial_reason = inbound != ADDRESS_IN_UNKNOWN ? NAT_REASON_NONE : !state->filter_clean ? NAT_REASON_CONTACT_HISTORY :
mapping == ADDRESS_MAP_DEPENDENT ? NAT_REASON_DEPENDENT_MAPPING : NAT_REASON_TIMEOUT;
if (inbound == ADDRESS_IN_UNKNOWN && state->measured_profile.source == ADDRESS_SOURCE_ETCP &&
mapping == state->measured_profile.mapping && route_ping_addr_equal(&state->measured_address, &state->baseline, 1)) {
profile.inbound = state->measured_profile.inbound;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT incomplete ingress preserves previous result socket=%s round=%016llx inbound=%s reason=%s",
state->socket->name, (unsigned long long)state->round, address_inbound_name(profile.inbound),
nat_check_reason_name(partial_reason));
}
state->measured_profile = profile; state->measured_address = state->baseline;
state->cycle_mapping_seen = 1;
state->completed_tb = get_time_tb();
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT profile socket=%s id=%u generation=%016llx round=%016llx mapping=%s inbound=%s effective_inbound=%s"
" local=%s external=%s observer=%016llx helper=%016llx clean=%u partial_reason=%s",
state->socket->name, state->socket->sock_id, (unsigned long long)state->socket->generation,
(unsigned long long)state->round, address_mapping_name(mapping), address_inbound_name(inbound),
address_inbound_name(profile.inbound),
sockaddr_storage_to_str(&state->local).str, sockaddr_storage_to_str(&state->baseline).str,
(unsigned long long)state->observer, (unsigned long long)state->helper, state->filter_clean,
nat_check_reason_name(partial_reason));
int complete = inbound != ADDRESS_IN_UNKNOWN || mapping == ADDRESS_MAP_DEPENDENT;
owner_finish(state, complete ? NAT_REASON_NONE : partial_reason);
if (complete) state->reason = partial_reason;
}
static int owner_run(struct nat_socket *state, uint8_t step) {
if (random_id(&state->token)) return -1;
struct nat_wire message;
if (make_message(state, &message, NAT_RUN)) return -1;
message.step = step;
state->phase = step == NAT_STEP_PREPARE ? NAT_PREPARE_C :
step == NAT_STEP_PRE ? NAT_PRE : NAT_POST;
uint64_t budget = (uint64_t)probe_timeout_ms(state) * 10 *
(step == NAT_STEP_PREPARE ? 2 * NAT_PROBE_COUNT + 2 : NAT_PROBE_COUNT + 2);
state->deadline = get_time_tb() + budget;
if (state->deadline > state->cycle_deadline) state->deadline = state->cycle_deadline;
if (step == NAT_STEP_PRE) state->pre_started = get_time_tb();
return send_control(state->detector, state->group_id, state->observer, &message);
}
static int owner_observe(struct nat_socket *state) {
const struct sockaddr_storage *target = &state->observer_address;
const uint8_t *key = state->observer_key;
if (state->phase == NAT_OBSERVE_C || state->phase == NAT_REPEAT_C) { target = &state->helper_address; key = state->helper_key; }
int timeout = probe_timeout_ms(state);
state->deadline = state->cycle_deadline;
if (state->phase == NAT_OBSERVE_C && !state->retries) {
state->contact_cutoff = state->contact_count;
if (contacted(state, &state->helper_address)) state->filter_clean = 0;
}
if (random_id(&state->observation_token)) return -1;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT observe TX socket=%s phase=%u round=%016llx token=%016llx target=%s retries=%u",
state->socket->name, state->phase, (unsigned long long)state->round,
(unsigned long long)state->observation_token, sockaddr_storage_to_str(target).str, state->retries);
if (observe_control(state->socket, key, target, state->group_id, state->round, state->phase,
state->observation_token, timeout, owner_ping_cb, state)) return -1;
state->active_ping = 1; return 0;
}
/* Прямой контроль к B поддерживает mapping и позволяет отличать входящее испытание от общей недоступности UDP. */
static void owner_heartbeat_cb(int success, uint16_t rtt, void *arg, uint64_t nonce, const uint8_t *data, size_t length) {
(void)rtt; (void)nonce;
struct nat_socket *state = arg; state->heartbeat_active = 0;
if (!state->phase) return;
if (get_time_tb() >= state->cycle_deadline) { owner_finish(state, NAT_REASON_BUDGET); return; }
struct sockaddr_storage observed;
if (success && !route_ping_observation(data, length, state->socket, state->group_id, state->round, &observed) &&
public_address(state->detector, &observed)) {
if (state->baseline.ss_family && !route_ping_addr_equal(&state->baseline, &observed, 1)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT anchor mapping changed socket=%s round=%016llx previous=%s current=%s",
state->socket->name, (unsigned long long)state->round,
sockaddr_storage_to_str(&state->baseline).str, sockaddr_storage_to_str(&observed).str);
clear_profile(state, NAT_REASON_ADDRESS_CHANGED); owner_finish(state, NAT_REASON_ADDRESS_CHANGED); return;
}
state->heartbeat_received = get_time_tb();
DEBUG_TRACE(DEBUG_CATEGORY_NAT, "NAT anchor healthy socket=%s round=%016llx observed=%s",
state->socket->name, (unsigned long long)state->round, sockaddr_storage_to_str(&observed).str);
} else DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT anchor direct response unavailable socket=%s round=%016llx",
state->socket->name, (unsigned long long)state->round);
state->heartbeat_due = get_time_tb() + 10000;
}
static void owner_ping_cb(int success, uint16_t rtt, void *arg, uint64_t nonce, const uint8_t *data, size_t length) {
(void)rtt; (void)nonce;
struct nat_socket *state = arg;
state->active_ping = 0;
if (state->phase == NAT_IDLE) return;
if (get_time_tb() >= state->cycle_deadline) { owner_finish(state, NAT_REASON_BUDGET); return; }
struct sockaddr_storage observed;
if (!success || route_ping_observation(data, length, state->socket, state->group_id, state->round, &observed)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT observation incomplete socket=%s phase=%u round=%016llx attempt=%u reason=%s",
state->socket->name, state->phase, (unsigned long long)state->round, state->retries + 1,
success ? "invalid_observation" : "routed_report_timeout");
if (++state->retries < NAT_PROBE_COUNT) { state->due = get_time_tb() + 1000; return; }
owner_finish(state, success ? NAT_REASON_INVALID_OBSERVATION : NAT_REASON_TIMEOUT); return;
}
if (!public_address(state->detector, &observed)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT observation unusable socket=%s phase=%u round=%016llx observed=%s reason=non_public_source",
state->socket->name, state->phase, (unsigned long long)state->round, sockaddr_storage_to_str(&observed).str);
owner_finish(state, NAT_REASON_INVALID_OBSERVATION); return;
}
state->retries = 0;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT own observation socket=%s phase=%u round=%016llx observed=%s",
state->socket->name, state->phase, (unsigned long long)state->round, sockaddr_storage_to_str(&observed).str);
if (state->phase == NAT_OBSERVE_B) {
state->baseline = observed;
if ((state->measured_profile.mapping == ADDRESS_MAP_UNCHANGED && !route_ping_addr_equal(&state->local, &observed, 1)) ||
(state->measured_profile.mapping == ADDRESS_MAP_EIM &&
!route_ping_addr_equal(&state->measured_address, &observed, 1)))
clear_profile(state, NAT_REASON_ADDRESS_CHANGED);
remember_address(state, &observed);
if (!state->helper) { state->cycling = 0; owner_finish(state, NAT_REASON_NO_HELPER); return; }
if (owner_run(state, NAT_STEP_PREPARE)) owner_finish(state, NAT_REASON_CONTROL_LOST);
return;
}
if (state->phase == NAT_OBSERVE_C) {
state->helper_seen = observed;
if (route_ping_addr_equal(&state->baseline, &observed, 1) && state->filter_clean &&
state->pre_healthy && !state->pre_received) {
if (owner_run(state, NAT_STEP_POST)) owner_finish(state, NAT_REASON_CONTROL_LOST);
return;
}
state->phase = NAT_REPEAT_B;
} else if (state->phase == NAT_REPEAT_B) {
if (!route_ping_addr_equal(&observed, &state->baseline, 1)) {
clear_profile(state, NAT_REASON_ADDRESS_CHANGED); owner_finish(state, NAT_REASON_ADDRESS_CHANGED); return;
}
state->phase = NAT_REPEAT_C;
} else if (state->phase == NAT_REPEAT_C) {
if (!route_ping_addr_equal(&observed, &state->helper_seen, 1)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper-direction mapping unstable socket=%s round=%016llx previous=%s current=%s",
state->socket->name, (unsigned long long)state->round,
sockaddr_storage_to_str(&state->helper_seen).str, sockaddr_storage_to_str(&observed).str);
clear_profile(state, NAT_REASON_ADDRESS_CHANGED); owner_finish(state, NAT_REASON_ADDRESS_CHANGED); return;
}
int same = route_ping_addr_equal(&state->baseline, &state->helper_seen, 1);
uint8_t mapping = same ? (route_ping_addr_equal(&state->local, &state->baseline, 1) ?
ADDRESS_MAP_UNCHANGED : ADDRESS_MAP_EIM) : ADDRESS_MAP_DEPENDENT;
uint8_t inbound = state->filter_clean && state->pre_received ? ADDRESS_IN_OPEN :
state->filter_clean && state->pre_healthy && state->post_received && same &&
state->heartbeat_received >= state->pre_started &&
get_time_tb() - state->heartbeat_received <= (uint64_t)probe_timeout_ms(state) * 20 ? ADDRESS_IN_CONTACT : ADDRESS_IN_UNKNOWN;
owner_apply(state, mapping, inbound); return;
} else {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT observation in unexpected phase=%u", state->phase);
owner_finish(state, NAT_REASON_INVALID_OBSERVATION); return;
}
if (owner_observe(state)) owner_finish(state, NAT_REASON_SEND_ERROR);
}
void nat_detection_receive_ingress(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket,
const uint8_t peer_key[32], const struct sockaddr_storage *source,
const uint8_t *tag, size_t length) {
struct nat_socket *state = socket_state(detector, socket, 0);
if (!state || !tag || length != NAT_QUERY_TAG_SIZE) {
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT ingress ignored without pending endpoint/valid tag length=%zu", length); return;
}
uint8_t expected[ROUTE_OBSERVE_TAG_SIZE]; route_ping_make_tag(expected, socket, state->group_id, state->round);
uint8_t step = state->phase == NAT_PREPARE_C ? NAT_STEP_PREPARE :
(state->phase == NAT_PRE || state->phase == NAT_PRE_WAIT) ? NAT_STEP_PRE :
(state->phase == NAT_POST || state->phase == NAT_POST_WAIT) ? NAT_STEP_POST : 0;
if (!step || memcmp(tag, expected, sizeof(expected)) || tag[ROUTE_OBSERVE_TAG_SIZE] != step ||
read64(tag + ROUTE_OBSERVE_TAG_SIZE + 1) != state->token ||
sc_derive_node_id_from_pubkey(peer_key) != state->helper || memcmp(peer_key, state->helper_key, 32) ||
source->ss_family != state->local.ss_family) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT ingress correlation/source mismatch socket=%s phase=%u round=%016llx source=%s",
socket->name, state->phase, (unsigned long long)state->round, sockaddr_storage_to_str(source).str); return;
}
if (get_time_tb() >= state->cycle_deadline) { owner_finish(state, NAT_REASON_BUDGET); return; }
if (!public_address(detector, source)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper rejected by receiver socket=%s round=%016llx helper=%016llx source=%s reason=non_public_source",
socket->name, (unsigned long long)state->round, (unsigned long long)state->helper, sockaddr_storage_to_str(source).str);
owner_finish(state, NAT_REASON_NO_HELPER); return;
}
if ((step == NAT_STEP_POST || state->pre_received) && !route_ping_addr_equal(source, &state->helper_address, 1)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT ingress endpoint mismatch socket=%s phase=%u round=%016llx source=%s expected=%s",
socket->name, state->phase, (unsigned long long)state->round,
sockaddr_storage_to_str(source).str, sockaddr_storage_to_str(&state->helper_address).str); return;
}
if (route_ping_addr_equal(source, &state->observer_address, 0) || route_ping_addr_equal(source, &state->baseline, 0)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper rejected by receiver socket=%s round=%016llx helper=%016llx source=%s reason=%s",
socket->name, (unsigned long long)state->round, (unsigned long long)state->helper,
sockaddr_storage_to_str(source).str, route_ping_addr_equal(source, &state->baseline, 0) ? "hairpin" : "same_observer_ip");
owner_finish(state, NAT_REASON_NO_HELPER); return;
}
if (step == NAT_STEP_PRE || step == NAT_STEP_PREPARE) {
if (!state->pre_received) {
if (contacted(state, source)) {
state->helper_address = *source; state->filter_clean = 0;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT helper rejected by receiver socket=%s round=%016llx source=%s reason=previous_udp_contact",
socket->name, (unsigned long long)state->round, sockaddr_storage_to_str(source).str);
owner_finish(state, NAT_REASON_CONTACT_HISTORY); return;
}
state->filter_clean = !contacted(state, source); state->helper_address = *source;
}
state->pre_received = 1;
}
else {
uint8_t wire[ROUTE_ADDR_WIRE_SIZE];
if (!state->history_complete || route_ping_addr_encode(wire, source)) state->filter_clean = 0;
else for (unsigned i = 0; i < state->contact_cutoff; i++)
if (!memcmp(state->contacts[i], wire, 17)) state->filter_clean = 0;
if (!state->filter_clean) DEBUG_DEBUG(DEBUG_CATEGORY_NAT,
"NAT post-contact ingress cannot establish filtering socket=%s round=%016llx source=%s reason=contact_history",
socket->name, (unsigned long long)state->round, sockaddr_storage_to_str(source).str);
state->post_received = 1;
}
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT direct ingress socket=%s round=%016llx step=%u source=%s clean=%u",
socket->name, (unsigned long long)state->round, step, sockaddr_storage_to_str(source).str, state->filter_clean);
}
/* Дополнительный порт наблюдателя не публикуется и не участвует в NCD. */
static struct ETCP_SOCKET *helper_socket(struct NAT_DETECTION *detector, struct ETCP_SOCKET *source) {
struct nat_socket *state = socket_state(detector, source, 1);
if (!state) return NULL;
struct ETCP_SOCKET **slot = &state->helper_primary;
if (*slot) return *slot;
struct CFG_SERVER server; memset(&server, 0, sizeof(server));
server.ip = source->local_addr; server.netif_index = source->netif_index;
server.type = CFG_SERVER_TYPE_PRIVATE; server.mtu = source->mtu; server.fib = -1;
for (const struct CFG_SERVER *cfg = detector->instance->config->servers; cfg; cfg = cfg->next)
if (!strcmp(cfg->name, source->name)) { server.so_mark = cfg->so_mark; server.fib = cfg->fib; break; }
snprintf(server.name, sizeof(server.name), "nat-helper-%u", source->sock_id);
if (server.ip.ss_family == AF_INET) ((struct sockaddr_in *)&server.ip)->sin_port = 0;
else ((struct sockaddr_in6 *)&server.ip)->sin6_port = 0;
*slot = etcp_socket_add(detector->instance, &server);
if (!*slot) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT helper socket failed source=%s", source->name); return NULL; }
(*slot)->probe_only = 1;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT isolated helper socket source=%s local=%s helper=%s",
source->name, sockaddr_storage_to_str(&source->local_addr).str,
sockaddr_storage_to_str(&(*slot)->local_addr).str);
return *slot;
}
static void helper_remove(struct nat_probe *probe) {
struct NAT_DETECTION *detector = probe->detector;
struct nat_probe **cursor = &detector->probes;
while (*cursor && *cursor != probe) cursor = &(*cursor)->next;
if (*cursor) *cursor = probe->next;
probe->cancelled = 1;
etcp_cancel_pings(detector->instance, helper_ping_cb, probe);
u_free(probe);
}
static void helper_finish(struct nat_probe *probe, uint8_t reason) {
probe->message.command = NAT_RESULT; probe->message.reason = reason;
if (reason) DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper failed group=%016llx owner=%016llx round=%016llx step=%u reason=%s",
(unsigned long long)probe->group_id, (unsigned long long)read64(probe->message.owner),
(unsigned long long)read64(probe->message.round), probe->message.step, nat_check_reason_name(reason));
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT helper done owner=%016llx round=%016llx step=%u sent=%u received=%u errors=%u reason=%s",
(unsigned long long)read64(probe->message.owner), (unsigned long long)read64(probe->message.round),
probe->message.step, probe->message.sent, probe->message.received, probe->message.errors, nat_check_reason_name(reason));
if (send_control(probe->detector, probe->group_id, probe->reply, &probe->message))
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper result delivery failed");
helper_remove(probe);
}
/* Входящая проба несёт идентификаторы owner; OBSERVE с помощника — идентификаторы его собственного socket. */
static void ingress_tag(const struct nat_wire *message, uint64_t group_id, uint8_t tag[NAT_QUERY_TAG_SIZE]) {
memcpy(tag, "NAT4UDP!", 8); tag[8] = message->owner_socket;
memcpy(tag + 9, message->generation, 8); memcpy(tag + 17, message->round, 8); write64(tag + 25, group_id);
tag[ROUTE_OBSERVE_TAG_SIZE] = message->step;
memcpy(tag + ROUTE_OBSERVE_TAG_SIZE + 1, message->token, 8);
}
static void helper_ping_cb(int success, uint16_t rtt, void *arg, uint64_t nonce, const uint8_t *data, size_t length) {
(void)rtt; (void)nonce;
struct nat_probe *probe = arg;
if (probe->cancelled) return;
probe->active = 0;
uint8_t step = probe->message.step;
if (step == NAT_STEP_PREPARE && probe->finding_primary) {
struct ETCP_SOCKET *socket = find_socket(probe->detector, probe->io_socket, probe->io_generation);
struct sockaddr_storage mapped;
if (success && socket && !route_ping_observation(data, length, socket, probe->group_id,
read64(probe->message.round), &mapped) && public_address(probe->detector, &mapped)) {
if (route_ping_addr_encode(probe->message.primary, &mapped)) { helper_finish(probe, NAT_REASON_INVALID_OBSERVATION); return; }
probe->message.received++;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT helper endpoint measured group=%016llx owner=%016llx step=%u socket=%s mapped=%s fallback=%u",
(unsigned long long)probe->group_id, (unsigned long long)read64(probe->message.owner), step,
socket->name, sockaddr_storage_to_str(&mapped).str, probe->finding_primary);
helper_finish(probe, NAT_REASON_NONE); return;
}
if (success) { helper_finish(probe, NAT_REASON_INVALID_OBSERVATION); return; }
if (probe->message.sent >= NAT_PROBE_COUNT) { helper_finish(probe, NAT_REASON_TIMEOUT); return; }
} else {
if (success) DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT one-way ingress unexpectedly received PONG group=%016llx step=%u",
(unsigned long long)probe->group_id, step);
if (step == NAT_STEP_PREPARE && probe->message.sent >= NAT_PROBE_COUNT) {
probe->finding_primary = 1; probe->message.sent = 0;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT initial ingress series sent; discover helper endpoint through broker group=%016llx owner=%016llx",
(unsigned long long)probe->group_id, (unsigned long long)read64(probe->message.owner));
}
if (probe->message.sent >= NAT_PROBE_COUNT) { helper_finish(probe, NAT_REASON_NONE); return; }
}
probe->due = get_time_tb() + 1000;
}
static int helper_send(struct nat_probe *probe) {
struct NAT_DETECTION *detector = probe->detector;
struct sockaddr_storage target;
if (route_ping_addr_decode(probe->message.target, &target)) return -1;
int timeout = ((unsigned)probe->message.timeout_ms[0] << 8) | probe->message.timeout_ms[1];
if (timeout < 500 || timeout > NAT_ROUND_TB / 10) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper has invalid timeout_ms=%d", timeout); return -1;
}
int result;
if (probe->message.step == NAT_STEP_PREPARE && probe->finding_primary) {
struct ETCP_SOCKET *source = find_socket(detector, probe->source_socket, probe->source_generation);
if (!source) { DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper source generation disappeared"); return -1; }
struct ETCP_SOCKET *socket = helper_socket(detector, source);
if (!socket) return -1;
if (route_ping_addr_decode(probe->message.observer_address, &target)) return -1;
probe->io_socket = socket->sock_id; probe->io_generation = socket->generation;
result = observe_control(socket, probe->observer_key, &target, probe->group_id, read64(probe->message.round),
NAT_STEP_HELPER_OBSERVE, read64(probe->message.token), timeout, helper_ping_cb, probe);
} else {
struct ETCP_SOCKET *source = find_socket(detector, probe->source_socket, probe->source_generation);
if (!source || source->is_tcp || source->socks_udp) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper source is unavailable socket=%u", probe->source_socket); return -1;
}
struct ETCP_SOCKET *socket = helper_socket(detector, source);
if (!socket) return -1;
probe->io_socket = socket->sock_id; probe->io_generation = socket->generation;
uint8_t tag[NAT_QUERY_TAG_SIZE];
ingress_tag(&probe->message, probe->group_id, tag);
result = etcp_send_ping_to_socket(detector->instance, socket, probe->owner_key, &target, timeout,
helper_ping_cb, probe, tag, sizeof(tag), ETCP_PING_FLAG_ADDRESS_CHECK | ETCP_PING_FLAG_NO_REPLY);
}
if (result) { probe->message.errors++; return -1; }
probe->message.sent++; probe->active = 1; return 0;
}
/* Broker проверяет реальное наблюдение owner, затем делегирует выбранному владельцем помощнику через ту же группу. */
static void handle_run(struct NAT_DETECTION *detector, uint64_t group_id, uint64_t sender, struct nat_wire *message) {
uint64_t owner = read64(message->owner), observer = read64(message->observer), helper = read64(message->helper);
const struct TOPO_NODE *owner_node = group_node(detector, group_id, owner);
const struct TOPO_NODE *observer_node = group_node(detector, group_id, observer);
struct sockaddr_storage target;
uint8_t reason = NAT_REASON_INVALID_OBSERVATION;
if (!owner_node || !observer_node || owner == observer || owner == helper ||
route_ping_addr_decode(message->target, &target) || !public_address(detector, &target)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT run has invalid participants/target group=%016llx owner=%016llx observer=%016llx helper=%016llx",
(unsigned long long)group_id, (unsigned long long)owner, (unsigned long long)observer,
(unsigned long long)helper); goto reject;
}
if (sender == owner && observer == detector->instance->node_id) {
struct nat_binding *binding = binding_find(detector, group_id, owner, read64(message->generation), read64(message->round));
if (!binding || binding->owner_socket != message->owner_socket || !route_ping_addr_equal(&binding->target, &target, 1) ||
memcmp(binding->owner_key, owner_node->public_key, 32) || helper == observer) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT delegation rejected: stale own observation group=%016llx owner=%016llx",
(unsigned long long)group_id, (unsigned long long)owner); goto reject;
}
binding->helper = helper; binding->token = read64(message->token); binding->expires = get_time_tb() + NAT_ROUND_TB;
if (helper != detector->instance->node_id) {
if (!control_route(detector, group_id, helper) || send_control(detector, group_id, helper, message)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT delegation route failed group=%016llx helper=%016llx",
(unsigned long long)group_id, (unsigned long long)helper);
reason = NAT_REASON_CONTROL_LOST; goto reject;
}
return;
}
} else if (sender != observer || helper != detector->instance->node_id) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT run role mismatch group=%016llx sender=%016llx observer=%016llx helper=%016llx",
(unsigned long long)group_id, (unsigned long long)sender, (unsigned long long)observer,
(unsigned long long)helper); goto reject;
}
unsigned count = 0;
for (struct nat_probe *probe = detector->probes; probe; probe = probe->next) {
count++;
if (probe->group_id == group_id && read64(probe->message.owner) == owner &&
read64(probe->message.generation) == read64(message->generation) && read64(probe->message.round) == read64(message->round) &&
read64(probe->message.token) == read64(message->token)) {
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT duplicate helper run ignored"); return;
}
}
if (count >= NAT_MAX_PROBES) { DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper probe limit reached"); reason = NAT_REASON_SEND_ERROR; goto reject; }
struct sockaddr_storage observer_address;
struct ETCP_SOCKET *source = find_socket(detector, message->helper_socket, 0);
if (!source || source->is_tcp || source->probe_only || source->socks_udp ||
source->type == CFG_SERVER_TYPE_PRIVATE || source->type == CFG_SERVER_TYPE_LOCAL ||
source->local_addr.ss_family != target.ss_family ||
route_ping_addr_decode(message->observer_address, &observer_address) ||
observer_address.ss_family != target.ss_family || !public_address(detector, &observer_address)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT helper source is not a native socket group=%016llx socket=%u",
(unsigned long long)group_id, message->helper_socket);
reason = source && source->socks_udp ? NAT_REASON_PROXY : NAT_REASON_NO_HELPER; goto reject;
}
struct nat_probe *probe = u_calloc(1, sizeof(*probe));
if (!probe) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT helper probe allocation failed"); reason = NAT_REASON_SEND_ERROR; goto reject; }
probe->detector = detector; probe->message = *message; probe->group_id = group_id; probe->reply = sender;
memcpy(probe->owner_key, owner_node->public_key, 32); memcpy(probe->observer_key, observer_node->public_key, 32);
probe->source_socket = source->sock_id; probe->source_generation = source->generation;
probe->expires = get_time_tb() + NAT_ROUND_TB;
probe->next = detector->probes; detector->probes = probe;
if (helper_send(probe)) helper_finish(probe, NAT_REASON_SEND_ERROR);
return;
reject:
message->command = NAT_RESULT; message->reason = reason;
if (send_control(detector, group_id, sender, message)) DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT rejection delivery failed");
}
static void handle_result(struct nat_socket *state, const struct nat_wire *message) {
uint8_t step = state->phase == NAT_PRE ? NAT_STEP_PRE : state->phase == NAT_POST ? NAT_STEP_POST :
state->phase == NAT_PREPARE_C ? NAT_STEP_PREPARE : 0;
if (!step || message->step != step || read64(message->token) != state->token || read64(message->helper) != state->helper) {
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT stale helper result ignored socket=%s phase=%u", state->socket->name, state->phase); return;
}
if (get_time_tb() >= state->cycle_deadline) { owner_finish(state, NAT_REASON_BUDGET); return; }
if (message->reason || message->errors || !message->sent || message->received > message->sent) {
owner_finish(state, message->reason ? message->reason : NAT_REASON_SEND_ERROR); return;
}
if (step == NAT_STEP_PREPARE) {
struct sockaddr_storage primary;
if (route_ping_addr_decode(message->primary, &primary) || primary.ss_family != state->local.ss_family ||
!public_address(state->detector, &primary)) { owner_finish(state, NAT_REASON_INVALID_OBSERVATION); return; }
if (state->pre_received) primary = state->helper_address; /* Source полученной пробы важнее объявленного mapping помощника. */
int same_ip = route_ping_addr_equal(&state->observer_address, &primary, 0) ||
route_ping_addr_equal(&state->baseline, &primary, 0);
int old_contact = state->pre_received ? !state->filter_clean : contacted(state, &primary);
if (same_ip) {
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT try another pair socket=%s group=%016llx helper=%016llx endpoint=%s reason=%s",
state->socket->name, (unsigned long long)state->group_id, (unsigned long long)state->helper,
sockaddr_storage_to_str(&primary).str, "same_external_ip");
owner_finish(state, NAT_REASON_NO_HELPER); return;
}
state->helper_address = primary; state->filter_clean = !old_contact;
if (old_contact) DEBUG_INFO(DEBUG_CATEGORY_NAT,
"NAT known helper used for mapping only socket=%s round=%016llx helper=%s",
state->socket->name, (unsigned long long)state->round, sockaddr_storage_to_str(&primary).str);
if (state->pre_received || old_contact) {
state->phase = NAT_OBSERVE_C;
if (owner_observe(state)) owner_finish(state, NAT_REASON_SEND_ERROR);
return;
}
if (owner_run(state, NAT_STEP_PRE)) owner_finish(state, NAT_REASON_CONTROL_LOST);
return;
}
state->pre_healthy = step == NAT_STEP_PRE ? message->sent == NAT_PROBE_COUNT &&
state->heartbeat_received >= state->pre_started &&
get_time_tb() - state->heartbeat_received <= (uint64_t)probe_timeout_ms(state) * 20 : state->pre_healthy;
state->phase = step == NAT_STEP_PRE ? NAT_PRE_WAIT : NAT_POST_WAIT;
state->due = get_time_tb() + (uint64_t)probe_timeout_ms(state) * 10; /* Router может обогнать последнюю UDP-пробу. */
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT ingress series complete socket=%s round=%016llx step=%u sent=%u pre=%u post=%u clean=%u anchor=%u",
state->socket->name, (unsigned long long)state->round, step, message->sent,
state->pre_received, state->post_received, state->filter_clean, state->pre_healthy);
}
/* Отчёт привязан к точному UDP-запросу и подписан наблюдателем; старый этап не завершит новый запрос. */
static void receive_observation(struct NAT_DETECTION *detector, uint64_t group, uint64_t sender, const struct nat_wire *message) {
if (read64(message->owner) != detector->instance->node_id || read64(message->observer) != sender ||
read64(message->helper) != sender) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT observation report has invalid participants group=%016llx sender=%016llx",
(unsigned long long)group, (unsigned long long)sender); return;
}
struct ETCP_SOCKET *socket = find_socket(detector, message->owner_socket, read64(message->generation));
if (!socket) { DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT observation report for old socket ignored"); return; }
uint8_t data[ROUTE_ADDR_WIRE_SIZE + ROUTE_OBSERVE_TAG_SIZE];
memcpy(data, message->target, ROUTE_ADDR_WIRE_SIZE);
route_ping_make_tag(data + ROUTE_ADDR_WIRE_SIZE, socket, group, read64(message->round));
if (message->step == NAT_STEP_HELPER_OBSERVE) {
for (struct nat_probe *probe = detector->probes; probe; probe = probe->next) {
if (!probe->active || !probe->finding_primary || probe->group_id != group || probe->io_socket != socket->sock_id ||
probe->io_generation != socket->generation || read64(probe->message.round) != read64(message->round) ||
read64(probe->message.token) != read64(message->token) || read64(probe->message.observer) != sender) continue;
etcp_cancel_pings(detector->instance, helper_ping_cb, probe);
if (message->reason) { helper_finish(probe, message->reason); return; }
helper_ping_cb(1, 0, probe, 0, data, sizeof(data)); return;
}
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT stale helper observation report ignored"); return;
}
struct nat_socket *state = socket_state(detector, socket, 0);
if (!state || !state->active_ping || state->group_id != group || state->round != read64(message->round) ||
state->observation_token != read64(message->token) || state->phase != message->step ||
((state->phase == NAT_OBSERVE_C || state->phase == NAT_REPEAT_C) ? state->helper : state->observer) != sender) {
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT stale/foreign observation report ignored group=%016llx sender=%016llx step=%u",
(unsigned long long)group, (unsigned long long)sender, message->step); return;
}
if (get_time_tb() >= state->cycle_deadline) { owner_finish(state, NAT_REASON_BUDGET); return; }
etcp_cancel_pings(detector->instance, owner_ping_cb, state);
if (message->reason) { owner_finish(state, message->reason); return; }
owner_ping_cb(1, 0, state, 0, data, sizeof(data));
}
/* Потеря router-сессии отменяет только роли этого узла в данной группе, не общий транспорт. */
static void cancel_route(struct NAT_DETECTION *detector, uint64_t group_id, uint64_t peer) {
for (struct nat_socket *state = detector->sockets; state; state = state->next)
if (state->phase && state->group_id == group_id && (state->observer == peer || state->helper == peer))
owner_finish(state, NAT_REASON_CONTROL_LOST);
struct nat_probe *probe = detector->probes;
while (probe) {
struct nat_probe *next = probe->next;
if (probe->group_id == group_id && (probe->reply == peer || read64(probe->message.owner) == peer)) helper_remove(probe);
probe = next;
}
for (struct nat_binding *binding = detector->bindings; binding; binding = binding->next)
if (binding->group_id == group_id && (binding->owner == peer || binding->helper == peer)) binding->expires = 0;
}
/* SOURCE и group берём из проверенного router-заголовка, а не из ETCP next hop. */
static void receive_control(struct ETCP_CONN *from, struct ll_entry *entry) {
struct NAT_DETECTION *detector = from && from->instance ? from->instance->nat_det : NULL;
if (!detector || !entry || !entry->dgram || entry->len < ROUTER_SVC_HDR_SIZE) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT routed control invalid size=%zu", entry ? entry->len : 0);
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return;
}
uint64_t group_id, sender;
memcpy(&group_id, entry->dgram + ROUTER_SVC_GROUP_OFF, 8); memcpy(&sender, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
if (entry->len == ROUTER_SVC_HDR_SIZE) {
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT routed control closed group=%016llx peer=%016llx",
(unsigned long long)group_id, (unsigned long long)sender);
queue_dgram_free(entry); queue_entry_free(entry); cancel_route(detector, group_id, sender); return;
}
if (entry->dgram[ROUTER_SVC_FLAGS_OFF] != (ROUTER_FLAG_SIGNED | ROUTER_FLAG_ENCRYPTED) ||
!group_node(detector, group_id, sender)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT routed control rejected group=%016llx sender=%016llx size=%zu flags=%u",
(unsigned long long)group_id, (unsigned long long)sender, entry->len, entry->dgram[ROUTER_SVC_FLAGS_OFF]);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
if (entry->len == ROUTER_SVC_HDR_SIZE + sizeof(struct nat_recheck_wire)) {
struct nat_recheck_wire request; memcpy(&request, entry->dgram + ROUTER_SVC_PAYLOAD_OFF, sizeof(request));
queue_dgram_free(entry); queue_entry_free(entry);
struct ETCP_SOCKET *socket = find_socket(detector, request.socket_id, 0);
struct sockaddr_storage address = {0};
if (request.cmd != ETCP_RT_ID_NAT_DETECTION || request.command != NAT_RECHECK || request.version != NAT_WIRE_VERSION ||
!socket || socket->is_tcp || socket->probe_only || socket->socks_udp ||
socket->type == CFG_SERVER_TYPE_LOCAL || socket->type == CFG_SERVER_TYPE_PRIVATE ||
route_ping_addr_decode(request.address, &address) ||
(!route_ping_addr_equal(&address, &socket->local_addr, 1) && !route_ping_addr_equal(&address, &socket->nat_addr, 1))) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT failure recheck rejected group=%016llx peer=%016llx socket=%u: stale/invalid endpoint",
(unsigned long long)group_id, (unsigned long long)sender, request.socket_id); return;
}
schedule_failure_recheck(socket_state(detector, socket, 1), sender, 1); return;
}
if (entry->len != ROUTER_SVC_HDR_SIZE + sizeof(struct nat_wire)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT control invalid payload size=%zu", entry->len - ROUTER_SVC_HDR_SIZE);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
struct nat_wire message; memcpy(&message, entry->dgram + ROUTER_SVC_PAYLOAD_OFF, sizeof(message));
queue_dgram_free(entry); queue_entry_free(entry);
if (message.cmd != ETCP_RT_ID_NAT_DETECTION || message.version != NAT_WIRE_VERSION ||
!read64(message.owner) || !read64(message.observer) || !read64(message.helper) ||
!read64(message.generation) || !read64(message.round) || !read64(message.token) ||
(message.command != NAT_RUN && message.command != NAT_RESULT && message.command != NAT_OBSERVATION) ||
message.reason > NAT_REASON_DEPENDENT_MAPPING ||
(message.command != NAT_OBSERVATION && (message.step < NAT_STEP_PREPARE || message.step > NAT_STEP_POST))) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT control malformed command=%u version=%u step=%u", message.command, message.version, message.step);
return;
}
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT control RX group=%016llx command=%u step=%u sender=%016llx via=%016llx owner=%016llx round=%016llx",
(unsigned long long)group_id, message.command, message.step, (unsigned long long)sender,
(unsigned long long)from->peer_node_id, (unsigned long long)read64(message.owner), (unsigned long long)read64(message.round));
if (message.command == NAT_RUN) { handle_run(detector, group_id, sender, &message); return; }
if (message.command == NAT_OBSERVATION) { receive_observation(detector, group_id, sender, &message); return; }
uint64_t owner = read64(message.owner);
if (owner != detector->instance->node_id) {
struct nat_binding *binding = binding_find(detector, group_id, owner, read64(message.generation), read64(message.round));
if (!binding || binding->helper != sender || read64(message.helper) != sender || binding->token != read64(message.token) ||
read64(message.observer) != detector->instance->node_id) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT result has no matching delegation group=%016llx owner=%016llx sender=%016llx",
(unsigned long long)group_id, (unsigned long long)owner, (unsigned long long)sender); return;
}
if (send_control(detector, group_id, owner, &message)) DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT result forwarding failed");
return;
}
struct ETCP_SOCKET *socket = find_socket(detector, message.owner_socket, read64(message.generation));
struct nat_socket *state = socket_state(detector, socket, 0);
if (!state || state->group_id != group_id || state->round != read64(message.round) ||
state->observer != sender || read64(message.observer) != sender || state->phase == NAT_IDLE) {
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT stale/foreign owner response ignored group=%016llx", (unsigned long long)group_id); return;
}
handle_result(state, &message);
}
static void owner_start(struct nat_socket *state) {
struct NAT_DETECTION *detector = state->detector;
if (state->socket->is_tcp) {
select_profile(state); state->due = UINT64_MAX;
state->reason = state->socket->address_profile.source ? NAT_REASON_NONE : NAT_REASON_NO_OBSERVER;
return;
}
if (state->socket->socks_udp) {
state->reason = NAT_REASON_PROXY; state->due = UINT64_MAX;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT native endpoint not measured through SOCKS5 socket=%s", state->socket->name); return;
}
if (state->socket->type == CFG_SERVER_TYPE_LOCAL || state->socket->type == CFG_SERVER_TYPE_PRIVATE) {
state->due = UINT64_MAX; return;
}
uint64_t now = get_time_tb();
history_load(state);
if (!state->cycling) {
state->cycling = 1; state->cursor = (struct nat_pair){0};
state->cycle_contact_cutoff = state->contact_count; state->cycle_history_complete = state->history_complete;
state->cycle_mapping_seen = 0; state->reason = NAT_REASON_NONE;
state->cycle_deadline = now + NAT_ROUND_TB;
} else if (now >= state->cycle_deadline) {
state->cycle_deadline = now + NAT_ROUND_TB;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT candidate cycle resumed socket=%s contacts_at_start=%u current_contacts=%u",
state->socket->name, state->cycle_contact_cutoff, state->contact_count);
}
if (select_pair(state)) { owner_finish(state, state->reason); return; }
if (random_id(&state->round)) { owner_finish(state, NAT_REASON_SEND_ERROR); return; }
state->rounds++;
memset(&state->helper_seen, 0, sizeof(state->helper_seen)); memset(&state->baseline, 0, sizeof(state->baseline));
state->retries = 0; state->pre_received = 0; state->post_received = 0; state->filter_clean = 1; state->pre_healthy = 0;
state->heartbeat_due = get_time_tb(); state->heartbeat_received = 0; state->pre_started = 0;
state->deadline = state->cycle_deadline; state->reason = NAT_REASON_NONE;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT round start socket=%s group=%016llx id=%u generation=%016llx round=%016llx observer=%016llx helper=%016llx protocol=%s",
state->socket->name, (unsigned long long)state->group_id, state->socket->sock_id,
(unsigned long long)state->socket->generation, (unsigned long long)state->round,
(unsigned long long)state->observer, (unsigned long long)state->helper, state->socket->is_tcp ? "TCP" : "UDP");
state->phase = NAT_OBSERVE_B;
if (owner_observe(state)) owner_finish(state, NAT_REASON_SEND_ERROR);
}
static void detector_tick(void *arg) {
struct NAT_DETECTION *detector = arg; detector->timer = NULL;
if (detector->publish_pending && get_time_tb() >= detector->publish_after) publish(detector);
uint64_t now = get_time_tb();
for (struct ETCP_SOCKET *socket = detector->instance->etcp_sockets; socket; socket = socket->next)
if (!socket->probe_only) socket_state(detector, socket, 1);
for (struct nat_socket *state = detector->sockets; state; state = state->next) {
if (now >= state->select_due) { select_profile(state); state->select_due = now + 10000; }
if (state->phase && now >= state->deadline) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT phase deadline socket=%s round=%016llx phase=%u cycle_remaining_tb=%llu",
state->socket->name, (unsigned long long)state->round, state->phase,
(unsigned long long)(state->cycle_deadline > now ? state->cycle_deadline - now : 0));
owner_finish(state, now >= state->cycle_deadline ? NAT_REASON_BUDGET : NAT_REASON_TIMEOUT); continue;
}
if (!state->phase && now >= state->due && (detector->automatic || state->forced)) {
state->forced = 0; owner_start(state);
}
else if (state->phase && !state->socket->is_tcp) {
if ((state->phase == NAT_PRE_WAIT || state->phase == NAT_POST_WAIT) && now >= state->due) {
state->phase = state->phase == NAT_PRE_WAIT ? NAT_OBSERVE_C : NAT_REPEAT_B; state->retries = 0;
if (owner_observe(state)) { owner_finish(state, NAT_REASON_SEND_ERROR); continue; }
}
if (!state->heartbeat_active && state->baseline.ss_family && now >= state->heartbeat_due) {
if (route_ping_observe(state->socket, state->observer_key, &state->observer_address, state->group_id, state->round,
probe_timeout_ms(state), owner_heartbeat_cb, state)) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT anchor probe send failed socket=%s round=%016llx",
state->socket->name, (unsigned long long)state->round);
state->heartbeat_due = now + 10000;
} else state->heartbeat_active = 1;
}
}
if ((state->phase == NAT_OBSERVE_B || state->phase == NAT_OBSERVE_C || state->phase == NAT_REPEAT_C ||
state->phase == NAT_REPEAT_B) && !state->active_ping && now >= state->due) {
if (owner_observe(state)) owner_finish(state, NAT_REASON_SEND_ERROR);
}
}
struct nat_probe *probe = detector->probes;
while (probe) {
struct nat_probe *next = probe->next;
if (now >= probe->expires) helper_finish(probe, NAT_REASON_TIMEOUT);
else if (!probe->active && now >= probe->due && helper_send(probe)) helper_finish(probe, NAT_REASON_SEND_ERROR);
probe = next;
}
struct nat_binding **cursor = &detector->bindings;
while (*cursor) {
struct nat_binding *binding = *cursor;
if (now < binding->expires) { cursor = &binding->next; continue; }
*cursor = binding->next; u_free(binding);
}
detector->timer = uasync_set_timeout(detector->instance->ua, NAT_TICK_TB, detector, detector_tick, "nat_detection");
if (!detector->timer) DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT detector timer scheduling failed");
}
/* Отпечаток доступных endpoint не содержит timestamp/имя/профиль: обычный повтор NODEINFO не запускает пробы. */
static uint64_t candidate_fingerprint(struct nat_socket *state, int ready) {
struct NAT_DETECTION *detector = state->detector;
uint64_t total = 0;
for (struct ll_entry *e = detector->instance->topo_groups->group_list->head; e; e = e->next) {
struct TOPO_GROUP *group = (struct TOPO_GROUP *)e;
if (group->stopping || !group->local_node) continue;
uint64_t group_total = 0; unsigned candidates = 0, observers = 0;
for (struct ll_entry *n = group->nodes->head; n; n = n->next) {
uint64_t id = ((struct TOPO_GROUP_NODE *)n)->node_id;
if (id == detector->instance->node_id || (ready ? !control_route(detector, group->group_id, id) :
!topo_node_best_path((struct TOPO_GROUP_NODE *)n))) continue;
const struct TOPO_NODE *node = group_node(detector, group->group_id, id);
if (!node) continue;
struct sockaddr_storage endpoint = {0}; uint8_t socket_id = 0;
uint8_t bytes[16 + ROUTE_ADDR_WIRE_SIZE + 1] = {0};
write64(bytes, group->group_id); write64(bytes + 8, id);
struct sockaddr_storage observer_address = {0}; uint8_t observer_socket = 0;
observers += node_udp_endpoint(detector, node, state->local.ss_family, 0, NULL, &observer_address, &observer_socket);
candidates++;
uint8_t previous[ROUTE_ADDR_WIRE_SIZE] = {0};
int more = node_udp_endpoint(detector, node, state->local.ss_family, 1, previous, &endpoint, &socket_id);
for (; more; more =
node_udp_endpoint(detector, node, state->local.ss_family, 1, previous, &endpoint, &socket_id)) {
if (route_ping_addr_encode(previous, &endpoint)) break;
memcpy(bytes + 16, previous, sizeof(previous)); bytes[16 + ROUTE_ADDR_WIRE_SIZE] = socket_id;
uint64_t hash = UINT64_C(14695981039346656037);
for (unsigned i = 0; i < sizeof(bytes); i++) { hash ^= bytes[i]; hash *= UINT64_C(1099511628211); }
group_total += hash;
}
}
if (ready || (candidates >= 2 && observers)) total += group_total;
if (!ready) DEBUG_TRACE(DEBUG_CATEGORY_NAT, "NAT cold group candidates node=%016llx socket=%s group=%016llx candidates=%u observers=%u",
(unsigned long long)detector->instance->node_id, state->socket->name,
(unsigned long long)group->group_id, candidates, observers);
}
return total;
}
/* Ограниченное окно сохраняет свежие IP для первого ingress; изменение доступных endpoint обновляет резерв один раз. */
uint64_t nat_detection_probe_delay(struct NAT_DETECTION *detector) {
if (!detector || detector->stopping) return 0;
uint64_t now = get_time_tb(), delay = 0;
if (detector->reservation_dirty) {
detector->reservation_dirty = 0;
for (struct ETCP_SOCKET *socket = detector->instance->etcp_sockets; socket; socket = socket->next) {
if (socket->is_tcp || socket->probe_only || socket->socks_udp ||
socket->type == CFG_SERVER_TYPE_PRIVATE || socket->type == CFG_SERVER_TYPE_LOCAL) continue;
struct nat_socket *state = socket_state(detector, socket, 1);
if (!state) continue;
uint64_t fingerprint = candidate_fingerprint(state, 0);
if (fingerprint != state->reservation_fingerprint) {
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT cold reservation node=%016llx socket=%s previous=%016llx current=%016llx",
(unsigned long long)detector->instance->node_id, socket->name,
(unsigned long long)state->reservation_fingerprint, (unsigned long long)fingerprint);
state->reservation_fingerprint = fingerprint;
if (fingerprint) state->cold_until = now + NAT_ROUND_TB + 2500;
}
}
}
for (struct nat_socket *state = detector->sockets; state; state = state->next) {
if (state->socket->is_tcp || !state->reservation_fingerprint || state->cold_until <= now ||
(!detector->automatic && !state->phase && !state->forced) ||
(state->completed_tb && !state->phase && !state->cycling && !state->forced && state->due > now + 2500) ||
(state->measured_profile.source == ADDRESS_SOURCE_ETCP && state->measured_profile.mapping && state->measured_profile.inbound))
continue;
if (state->cold_until - now > delay) delay = state->cold_until - now;
}
return delay > 10000 ? 10000 : delay;
}
int nat_detection_ping_allowed(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket,
const uint8_t peer_key[32], const struct sockaddr_storage *source,
uint8_t flags, const uint8_t *tag, size_t length) {
if (!detector || !socket || socket->probe_only || (flags & (ETCP_PING_FLAG_ADDRESS_CHECK | ETCP_PING_FLAG_NO_REPLY))) return 1;
nat_detection_probe_delay(detector);
struct nat_socket *state = socket_state(detector, socket, 0);
if (!state || (!state->reservation_fingerprint && !state->available_fingerprint) || state->cold_until <= get_time_tb() ||
(!detector->automatic && !state->phase && !state->forced) ||
(state->completed_tb && !state->phase && !state->cycling && !state->forced && state->due > get_time_tb() + 2500) ||
(state->measured_profile.source == ADDRESS_SOURCE_ETCP && state->measured_profile.mapping && state->measured_profile.inbound)) return 1;
uint64_t peer = sc_derive_node_id_from_pubkey(peer_key);
if (!(flags & ETCP_PING_FLAG_OBSERVE)) {
history_load(state);
if (contacted(state, source)) return 1;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT background PING reply deferred socket=%s peer=%016llx source=%s",
socket->name, (unsigned long long)peer, sockaddr_storage_to_str(source).str);
return 0;
}
history_load(state);
if (contacted(state, source)) return 1; /* Уже известный IP не может испортить испытание незнакомого входа. */
if (!tag || length != ROUTE_OBSERVE_TAG_SIZE || memcmp(tag, "NAT4UDP!", 8)) return 1;
uint64_t group_id = read64(tag + 25), round = read64(tag + 17), owner = peer;
if (state->phase && state->group_id == group_id && state->round == round && (state->helper == peer || state->observer == peer)) return 1;
for (struct nat_binding *binding = detector->bindings; binding; binding = binding->next)
if (binding->group_id == group_id && binding->round == round && binding->helper == peer && binding->expires > get_time_tb()) {
owner = binding->owner; break;
}
if (owner <= detector->instance->node_id) return 1;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT observation deferred for cold owner priority socket=%s group=%016llx requester=%016llx owner=%016llx",
socket->name, (unsigned long long)group_id, (unsigned long long)peer, (unsigned long long)owner);
return 0;
}
/* Изменения нескольких групп объединяются; известный полный ETCP-профиль не перепроверяется из-за нового соседа. */
static void topology_wake(void *arg) {
struct NAT_DETECTION *detector = arg; detector->topology_wake = NULL;
for (struct nat_socket *state = detector->sockets; state; state = state->next) {
if (state->socket->is_tcp) continue;
uint64_t fingerprint = candidate_fingerprint(state, 1);
int changed = fingerprint != state->available_fingerprint; state->available_fingerprint = fingerprint;
if (state->phase) {
if (!control_route(detector, state->group_id, state->observer) || !control_route(detector, state->group_id, state->helper))
owner_finish(state, NAT_REASON_CONTROL_LOST);
continue;
}
if (!changed || !fingerprint || !detector->automatic || state->cycling) continue;
if (state->measured_profile.source == ADDRESS_SOURCE_ETCP && state->measured_profile.mapping && state->measured_profile.inbound)
continue;
state->cursor = (struct nat_pair){0};
state->due = get_time_tb() + 2500;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT available candidates changed socket=%s; early measurement scheduled", state->socket->name);
}
}
void nat_detection_topology_changed(struct NAT_DETECTION *detector) {
if (!detector || detector->stopping) return;
detector->reservation_dirty = 1;
if (detector->topology_wake) return;
detector->topology_wake = uasync_call_soon(detector->instance->ua, detector, topology_wake);
if (!detector->topology_wake) DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT topology wake scheduling failed");
}
void nat_detection_cancel_group(struct NAT_DETECTION *detector, uint64_t group_id) {
if (!detector || detector->stopping) return;
for (struct nat_socket *state = detector->sockets; state; state = state->next) {
if (state->group_id != group_id || (!state->phase && !state->cycling)) continue;
owner_finish(state, NAT_REASON_CANCELLED); state->forced = 0; state->group_id = 0; state->due = get_time_tb() + 2500;
}
struct nat_probe *probe = detector->probes;
while (probe) {
struct nat_probe *next = probe->next;
if (probe->group_id == group_id) helper_remove(probe);
probe = next;
}
struct nat_binding **cursor = &detector->bindings;
while (*cursor) {
struct nat_binding *binding = *cursor;
if (binding->group_id != group_id) { cursor = &binding->next; continue; }
*cursor = binding->next; u_free(binding);
}
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT group tasks cancelled group=%016llx", (unsigned long long)group_id);
nat_detection_topology_changed(detector);
}
void nat_detection_cancel_for_conn(struct NAT_DETECTION *detector, struct ETCP_CONN *connection) {
if (!detector || !connection || detector->stopping) return;
nat_detection_topology_changed(detector);
}
/* Сопоставление с текущим подписанным snapshot не зависит от remote_socket_id до завершения INIT. */
void nat_detection_connection_failed(struct NAT_DETECTION *detector, struct ETCP_CONN *connection, int reverse_done) {
if (!detector || detector->stopping || !detector->automatic || !connection || connection->links_up) return;
const struct TOPO_NODE *node = topo_node_registry_find(detector->instance->topo_groups, connection->peer_node_id);
if (!node || !node->timestamp || memcmp(node->public_key, connection->crypto_ctx.peer_public_key, 32)) return;
for (struct ETCP_LINK *link = connection->links; link; link = link->next) {
struct ETCP_SOCKET *socket = link->conn;
if (!socket || link->is_tcp || !link->init_retry_count || link->link_status || socket->probe_only || socket->socks_udp ||
socket->type == CFG_SERVER_TYPE_LOCAL || socket->type == CFG_SERVER_TYPE_PRIVATE) continue;
const struct address_profile *profile = NULL; uint8_t target_socket = 0, config_type = CFG_SERVER_TYPE_UNKNOWN, type = 0;
for (const struct TOPO_ADDR4 *a = node->v4_addrs; a; a = a->next) {
const struct sockaddr_in *target = (const struct sockaddr_in *)&link->remote_addr;
if (link->remote_addr.ss_family != AF_INET || !(a->protocol & TOPO_PROTO_UDP) ||
a->port != ntohs(target->sin_port) || memcmp(a->addr, &target->sin_addr, 4)) continue;
for (const struct TOPO_SOCKMETA4 *m = node->v4_sock_meta; m; m = m->next)
if (m->id == a->socket_id) { profile = &m->address_profile; config_type = m->config_type; break; }
target_socket = a->socket_id; type = a->type; break;
}
for (const struct TOPO_ADDR6 *a = node->v6_addrs; a; a = a->next) {
const struct sockaddr_in6 *target = (const struct sockaddr_in6 *)&link->remote_addr;
if (link->remote_addr.ss_family != AF_INET6 || !(a->protocol & TOPO_PROTO_UDP) ||
a->port != ntohs(target->sin6_port) || memcmp(a->addr, &target->sin6_addr, 16)) continue;
for (const struct TOPO_SOCKMETA6 *m = node->v6_sock_meta; m; m = m->next)
if (m->id == a->socket_id) { profile = &m->address_profile; config_type = m->config_type; break; }
target_socket = a->socket_id; type = a->type; break;
}
uint8_t target_class = sock_target_class(type, config_type, profile, public_address(detector, &link->remote_addr));
if (target_class != SOCK_TC_PUB && !(reverse_done && target_class == SOCK_TC_CONTACT)) continue;
struct nat_socket *state = socket_state(detector, socket, 1);
if (!schedule_failure_recheck(state, connection->peer_node_id, 0)) continue;
for (struct ll_entry *e = detector->instance->topo_groups->group_list->head; e; e = e->next) {
struct TOPO_GROUP *group = (struct TOPO_GROUP *)e;
if (!control_route(detector, group->group_id, connection->peer_node_id)) continue;
struct nat_recheck_wire request = {.cmd = ETCP_RT_ID_NAT_DETECTION, .command = NAT_RECHECK,
.version = NAT_WIRE_VERSION, .socket_id = target_socket};
if (route_ping_addr_encode(request.address, &link->remote_addr)) break;
struct ll_entry *packet = ll_alloc_lldgram(1 + sizeof(request));
if (!packet) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT failure recheck allocation failed"); break; }
packet->dgram[0] = ETCP_RT_ID_NAT_DETECTION; memcpy(packet->dgram + 1, &request, sizeof(request));
packet->len = 1 + sizeof(request);
if (etcp_route_send(detector->instance, group->group_id, connection->peer_node_id, packet, 0,
ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT))
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT failure recheck forwarding failed group=%016llx peer=%016llx",
(unsigned long long)group->group_id, (unsigned long long)connection->peer_node_id);
else DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT failure recheck sent group=%016llx peer=%016llx endpoint=%s",
(unsigned long long)group->group_id, (unsigned long long)connection->peer_node_id,
sockaddr_storage_to_str(&link->remote_addr).str);
break;
}
}
}
static void connection_status(struct ETCP_CONN *connection, int status, void *arg) {
struct NAT_DETECTION *detector = arg;
if (status == ETCP_CONN_STATUS_DOWN || status == ETCP_CONN_STATUS_DELETE || status == ETCP_CONN_STATUS_REINIT)
nat_detection_cancel_for_conn(detector, connection);
else if (status == ETCP_CONN_STATUS_UP) nat_detection_topology_changed(detector);
}
void nat_detection_link_ready(struct NAT_DETECTION *detector, struct ETCP_LINK *link) {
if (!detector || !link || !link->conn || link->conn->probe_only || detector->stopping) return;
struct nat_socket *state = socket_state(detector, link->conn, 1);
if (state && !state->phase && !state->completed_tb) state->due = get_time_tb() + 2500;
}
/* Одно TCP-наблюдение сравнивает только IP; нулевой порт не обещает доступный listener. */
void nat_detection_tcp_observed(struct NAT_DETECTION *detector, struct ETCP_LINK *link,
const struct sockaddr_storage *local, const struct sockaddr_storage *observed) {
if (!detector || detector->stopping || !link || !link->is_tcp || !link->conn || !local || !observed) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "TCP NAT observation has invalid socket/addresses"); return;
}
if (local->ss_family != observed->ss_family || !public_address(detector, observed) ||
!public_address(detector, &link->remote_addr)) {
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "TCP NAT observation ignored socket=%s local=%s observed=%s peer=%s reason=non_public_or_family",
link->conn->name, sockaddr_storage_to_str(local).str, sockaddr_storage_to_str(observed).str,
sockaddr_storage_to_str(&link->remote_addr).str); return;
}
struct nat_socket *state = socket_state(detector, link->conn, 1);
if (!state) return;
uint8_t mapping = public_address(detector, local) && route_ping_addr_equal(local, observed, 0) ?
ADDRESS_MAP_UNCHANGED : ADDRESS_MAP_UNKNOWN;
struct sockaddr_storage address = *observed;
if (address.ss_family == AF_INET) ((struct sockaddr_in *)&address)->sin_port = 0;
else ((struct sockaddr_in6 *)&address)->sin6_port = 0;
if (state->measured_profile.source == ADDRESS_SOURCE_ETCP && state->measured_profile.mapping == mapping &&
route_ping_addr_equal(&state->observed_address, &address, 1)) return;
state->measured_profile = (struct address_profile){.mapping = mapping, .source = ADDRESS_SOURCE_ETCP};
state->observed_address = address; state->completed_tb = get_time_tb(); state->due = UINT64_MAX; state->reason = NAT_REASON_NONE;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "TCP NAT presence socket=%s local=%s observed=%s NAT=%s type=UNKNOWN inbound=UNKNOWN",
link->conn->name, sockaddr_storage_to_str(local).str, sockaddr_storage_to_str(&address).str,
mapping == ADDRESS_MAP_UNCHANGED ? "no" : "yes");
select_profile(state);
}
void nat_detection_observed_by_peer(struct NAT_DETECTION *detector, struct ETCP_LINK *link,
const struct sockaddr_storage *observed) {
if (!detector || !link || !link->conn || link->is_tcp || link->conn->socks_udp || !observed->ss_family) return;
if (!public_address(detector, observed)) {
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT ignores LAN observation socket=%s peer=%016llx observed=%s",
link->conn->name, (unsigned long long)link->etcp->peer_node_id, sockaddr_storage_to_str(observed).str); return;
}
struct nat_socket *state = socket_state(detector, link->conn, 1);
if (!state) return;
if ((state->measured_profile.mapping == ADDRESS_MAP_UNCHANGED &&
!route_ping_addr_equal(&state->local, observed, 1)) ||
(state->measured_profile.mapping == ADDRESS_MAP_EIM &&
!route_ping_addr_equal(&state->measured_address, observed, 1))) {
if (state->phase) owner_finish(state, NAT_REASON_ADDRESS_CHANGED);
clear_profile(state, NAT_REASON_ADDRESS_CHANGED);
state->due = get_time_tb() + 2500;
}
if (link->conn->type != CFG_SERVER_TYPE_LOCAL && link->conn->type != CFG_SERVER_TYPE_PRIVATE) remember_address(state, observed);
}
void nat_detection_socket_removed(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket) {
if (!detector || !socket || detector->stopping) return;
if (!socket->probe_only) { detector->publish_pending = 1; detector->publish_after = get_time_tb(); }
struct nat_probe *probe = detector->probes;
while (probe) {
struct nat_probe *next = probe->next;
if ((probe->source_socket == socket->sock_id && probe->source_generation == socket->generation) ||
(probe->io_socket == socket->sock_id && probe->io_generation == socket->generation)) helper_remove(probe);
probe = next;
}
struct nat_socket **cursor = &detector->sockets;
while (*cursor && (*cursor)->socket != socket) cursor = &(*cursor)->next;
if (!*cursor) return;
struct nat_socket *state = *cursor; *cursor = state->next;
etcp_cancel_pings(detector->instance, owner_ping_cb, state);
etcp_cancel_pings(detector->instance, owner_heartbeat_cb, state);
struct ETCP_SOCKET *primary = state->helper_primary;
DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT endpoint removed socket=%s generation=%016llx", socket->name,
(unsigned long long)socket->generation);
u_free(state);
if (primary) etcp_socket_remove(primary);
}
static void socket_changed(struct ETCP_SOCKET *socket, int event, void *arg) {
struct NAT_DETECTION *detector = arg;
struct nat_socket *state = socket_state(detector, socket, 0);
if (!state || !(event & ETCP_SOCKET_EVENT_ADDR_CHANGED)) return;
clear_profile(state, NAT_REASON_ADDRESS_CHANGED);
nat_detection_socket_removed(detector, socket);
if (random_id(&socket->generation)) return;
socket_state(detector, socket, 1);
}
struct NAT_DETECTION *nat_detection_create(struct UTUN_INSTANCE *instance) {
if (!instance || !instance->topo_groups || !instance->router_conns ||
instance->router_bindings.callbacks[ETCP_RT_ID_NAT_DETECTION]) {
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT detector invalid instance/binding collision"); return NULL;
}
struct NAT_DETECTION *detector = u_calloc(1, sizeof(*detector));
if (!detector) { DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT detector allocation failed"); return NULL; }
detector->instance = instance; detector->automatic = 1; detector->reservation_dirty = 1;
if (etcp_router_bind(instance, ETCP_RT_ID_NAT_DETECTION, receive_control)) { u_free(detector); return NULL; }
etcp_add_conn_status_cbk(instance, connection_status, detector);
etcp_add_socket_cbk(instance, socket_changed, detector, ETCP_SOCKET_EVENT_ADDR_CHANGED);
detector->timer = uasync_set_timeout(instance->ua, NAT_TICK_TB, detector, detector_tick, "nat_detection");
if (!detector->timer) {
etcp_remove_conn_status_cbk(instance, connection_status, detector);
etcp_remove_socket_cbk(instance, socket_changed, detector);
etcp_router_unbind(instance, ETCP_RT_ID_NAT_DETECTION); u_free(detector);
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "NAT detector initial timer failed"); return NULL;
}
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT endpoint detector started: UDP mapping/inbound, TCP IP-only presence with port=0");
return detector;
}
void nat_detection_destroy(struct NAT_DETECTION *detector) {
if (!detector) return;
detector->stopping = 1;
if (detector->timer) uasync_cancel_timeout(detector->instance->ua, detector->timer);
if (detector->topology_wake) uasync_call_soon_cancel(detector->instance->ua, detector->topology_wake);
etcp_remove_conn_status_cbk(detector->instance, connection_status, detector);
etcp_remove_socket_cbk(detector->instance, socket_changed, detector);
etcp_router_unbind(detector->instance, ETCP_RT_ID_NAT_DETECTION);
while (detector->probes) helper_remove(detector->probes);
while (detector->sockets) {
struct nat_socket *state = detector->sockets; detector->sockets = state->next;
etcp_cancel_pings(detector->instance, owner_ping_cb, state);
etcp_cancel_pings(detector->instance, owner_heartbeat_cb, state);
struct ETCP_SOCKET *primary = state->helper_primary; u_free(state);
if (primary) etcp_socket_remove(primary);
}
while (detector->bindings) {
struct nat_binding *binding = detector->bindings; detector->bindings = binding->next; u_free(binding);
}
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT detector stopped; all endpoint rounds and helper probes cancelled");
u_free(detector);
}
void nat_detection_request_check_all(struct NAT_DETECTION *detector) {
if (!detector || detector->stopping) return;
for (struct ETCP_SOCKET *socket = detector->instance->etcp_sockets; socket; socket = socket->next) {
struct nat_socket *state = socket_state(detector, socket, 1);
if (!state) continue;
if (state->phase) owner_finish(state, NAT_REASON_CANCELLED);
state->cycling = 0; state->cursor = (struct nat_pair){0};
state->due = get_time_tb(); state->forced = 1; state->preferred_observer = 0;
}
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT endpoint recheck requested");
}
int nat_detection_request_check_socket(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, uint64_t observer) {
struct nat_socket *state = socket_state(detector, socket, 1);
if (!state || !observer) {
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT endpoint recheck has invalid endpoint/observer"); return -1;
}
if (state->phase) owner_finish(state, NAT_REASON_CANCELLED);
state->cycling = 0; state->cursor = (struct nat_pair){0};
state->preferred_observer = observer; state->due = get_time_tb(); state->forced = 1;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT endpoint recheck socket=%s observer=%016llx", socket->name, (unsigned long long)observer);
return 0;
}
void nat_detection_set_allow_local(struct NAT_DETECTION *detector, int allow) {
if (detector) detector->allow_local = !!allow;
}
void nat_detection_set_automatic(struct NAT_DETECTION *detector, int automatic) {
if (!detector) return;
if (automatic && !detector->automatic) {
uint64_t now = get_time_tb();
for (struct nat_socket *state = detector->sockets; state; state = state->next) {
if (state->phase || state->cycling ||
(state->measured_profile.source == ADDRESS_SOURCE_ETCP && state->measured_profile.mapping && state->measured_profile.inbound))
continue;
state->cold_until = now + NAT_ROUND_TB + 2500; state->due = now + 2500;
}
}
detector->automatic = !!automatic;
detector->reservation_dirty = 1;
DEBUG_INFO(DEBUG_CATEGORY_NAT, "NAT automatic endpoint measurements %s", automatic ? "enabled" : "disabled");
}
int nat_detection_get_status(struct NAT_DETECTION *detector, struct ETCP_SOCKET *socket, struct nat_check_status *status) {
struct nat_socket *state = socket_state(detector, socket, 0);
if (!state || !status) return -1;
*status = (struct nat_check_status){.generation = socket->generation, .round = state->round, .group_id = state->group_id,
.observer = state->observer,
.helper = state->helper, .rounds = state->rounds, .helper_reselections = state->helper_reselections,
.phase = state->phase, .reason = state->reason, .history_complete = state->history_complete, .cycling = state->cycling,
.measured_profile = state->measured_profile, .effective_profile = socket->address_profile};
return 0;
}