From 79d8789410dc35073888a55ee23ca1fdb76b0033 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 17 Oct 2026 17:47:18 +0200 Subject: [PATCH] nat: prioritize mapping candidates and label unknown NAT explicitly --- src/routing_layer/nat_detection.c | 10 +++++++--- tools/chatgui/src/memberlistmodel.cpp | 2 +- tools/chatgui/src/nodespage.cpp | 16 +++++++++++++--- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/routing_layer/nat_detection.c b/src/routing_layer/nat_detection.c index ce2d9320..3ce0248b 100644 --- a/src/routing_layer/nat_detection.c +++ b/src/routing_layer/nat_detection.c @@ -48,6 +48,7 @@ struct nat_wire { struct nat_pair { uint64_t group, observer, helper; + uint8_t helper_priority; /* Сначала публичный endpoint C, затем приватный, затем один наблюдатель. */ 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]; }; @@ -223,6 +224,8 @@ static int node_udp_endpoint(struct NAT_DETECTION *detector, const struct TOPO_N } 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; @@ -255,7 +258,7 @@ static int select_pair(struct nat_socket *state) { node_udp_endpoint(detector, bn, state->local.ss_family, 0, previous_b, &ba, &bs)) { if (!state->socket->is_tcp && route_ping_addr_encode(previous_b, &ba)) break; observers++; - struct nat_pair observer_only = {.group = group->group_id, .observer = observer}; + struct nat_pair observer_only = {.group = group->group_id, .observer = observer, .helper_priority = 2}; memcpy(observer_only.observer_endpoint, previous_b, sizeof(previous_b)); if (!state->socket->is_tcp && pair_after(observer_only, state->cursor) && (!single.group || pair_after(single, observer_only))) { @@ -271,7 +274,8 @@ static int select_pair(struct nat_socket *state) { for (; more_c; more_c = !state->socket->is_tcp && node_udp_endpoint(detector, cn, state->local.ss_family, 1, previous_c, &ca, &cs)) { if (!state->socket->is_tcp && route_ping_addr_encode(previous_c, &ca)) break; - struct nat_pair candidate = {.group = group->group_id, .observer = observer, .helper = helper}; + struct nat_pair candidate = {.group = group->group_id, .observer = observer, .helper = helper, + .helper_priority = !public_address(detector, &ca)}; memcpy(candidate.observer_endpoint, previous_b, sizeof(previous_b)); memcpy(candidate.helper_endpoint, previous_c, sizeof(previous_c)); if (state->using_fallback) { @@ -1286,7 +1290,7 @@ static void owner_start(struct nat_socket *state) { } if (!state->cycling) { state->cycling = 1; state->cursor = (struct nat_pair){0}; state->fallback = (struct nat_pair){0}; - state->using_fallback = 0; state->prefer_fresh = 1; + state->using_fallback = 0; state->prefer_fresh = state->measured_profile.mapping != ADDRESS_MAP_UNKNOWN; state->cycle_deadline = get_time_tb() + NAT_ROUND_TB; } history_load(state); diff --git a/tools/chatgui/src/memberlistmodel.cpp b/tools/chatgui/src/memberlistmodel.cpp index a229ab9a..e976067e 100644 --- a/tools/chatgui/src/memberlistmodel.cpp +++ b/tools/chatgui/src/memberlistmodel.cpp @@ -61,7 +61,7 @@ QVariant MemberListModel::data(const QModelIndex& index, int role) const { QString sub; if (it->rtt >= 0) sub += QString("rtt=%1ms").arg(it->rtt / 10); if (it->natType >= 0) { - static const char* nat[] = {"NET","DIR","EIM","DEP","?","CONTACT"}; + static const char* nat[] = {"NET","DIR","EIM","DEP","NAT type=UNKNOWN","CONTACT"}; const char* nt = (it->natType <= 5) ? nat[it->natType] : "?"; if (!sub.isEmpty()) sub += " "; sub += nt; diff --git a/tools/chatgui/src/nodespage.cpp b/tools/chatgui/src/nodespage.cpp index 871b56d1..9500efbd 100644 --- a/tools/chatgui/src/nodespage.cpp +++ b/tools/chatgui/src/nodespage.cpp @@ -12,6 +12,7 @@ #include #include "../../../lib/platform_compat.h" +#include "../../../src/routing_layer/topo_node.h" static QString hex16(uint64_t v) { return QString("%1").arg(v, 16, 16, QChar('0')).toUpper(); @@ -49,7 +50,15 @@ QString NodesPage::protoStr(int protocol) { } static QString addrTypeStr(int t) { - switch (t) { case 0: return "NETIF"; case 1: return "DIRECT"; case 2: return "NAT_EIM"; case 3: return "NAT_DEPENDENT"; case 4: return "UNKNOWN"; case 5: return "CONTACT_REQUIRED"; default: return QString("?%1").arg(t); } + switch (t) { + case ADDR_TYPE_NETIF: return "NETIF"; + case ADDR_TYPE_DIRECT: return "DIRECT"; + case ADDR_TYPE_NAT_EIM: return "NAT type=EIM"; + case ADDR_TYPE_NAT_STRICT: return "NAT type=STRICT"; + case ADDR_TYPE_NAT: return "NAT type=UNKNOWN"; + case ADDR_TYPE_CONTACT: return "CONTACT_REQUIRED"; + default: return QString("?%1").arg(t); + } } NodesPage::NodesPage(DbManager* db, QWidget* parent) @@ -199,7 +208,7 @@ void NodesPage::refreshNodes() { sqlite3_stmt* na = nullptr; sqlite3_prepare_v2(d, - "SELECT family, protocol, address, port, rtt, addr_type" + "SELECT family, protocol, address, port, rtt, addr_type, address_kind" " FROM node_addresses WHERE node_id=? ORDER BY family, port", -1, &na, nullptr); if (na) { @@ -214,13 +223,14 @@ void NodesPage::refreshNodes() { int port = sqlite3_column_int(na, 3); int rtt = sqlite3_column_int(na, 4); int addrType = sqlite3_column_int(na, 5); + int kind = sqlite3_column_int(na, 6); if (addr && addrSz > 0) { dump += QString(" %1:%2 %3 %4 rtt=%5ms\n") .arg(addrStr(addr, family)) .arg(port) .arg(protoStr(proto)) - .arg(addrTypeStr(addrType)) + .arg(kind == TOPO_ADDR_INTERFACE ? QStringLiteral("INTERFACE") : addrTypeStr(addrType)) .arg(rtt > 0 ? QString::number(rtt / 10) : "-"); } ac++;