diff --git a/src/routing_layer/topo_node_sqlite.c b/src/routing_layer/topo_node_sqlite.c index 9b864fcc..35e17d4f 100644 --- a/src/routing_layer/topo_node_sqlite.c +++ b/src/routing_layer/topo_node_sqlite.c @@ -62,11 +62,13 @@ static int addr_type_from_ip(int family, const uint8_t* addr) { } /* Итоговая классификация: конфиг + автоопределение + IP (всё локально). */ -static int addr_type_classify(int config_type, int global, const struct address_profile *profile) { +static int addr_type_classify(int kind, int config_type, int global, const struct address_profile *profile) { if (config_type == CFG_SERVER_TYPE_PRIVATE || config_type == CFG_SERVER_TYPE_LOCAL) return ADDR_TYPE_NETIF; if (!profile) return ADDR_TYPE_NAT; if (profile->mapping == ADDRESS_MAP_EIM) return ADDR_TYPE_NAT_EIM; if (profile->mapping == ADDRESS_MAP_DEPENDENT) return ADDR_TYPE_NAT_STRICT; + /* Публичный адрес интерфейса известен без определения входящих и не становится NAT из-за UNKNOWN. */ + if (global && kind == TOPO_ADDR_INTERFACE && profile->mapping == ADDRESS_MAP_UNKNOWN) return ADDR_TYPE_NETIF; if (!global) return config_type == CFG_SERVER_TYPE_NAT ? ADDR_TYPE_NAT : ADDR_TYPE_NETIF; if (profile->mapping == ADDRESS_MAP_UNCHANGED && profile->inbound == ADDRESS_IN_OPEN) return ADDR_TYPE_DIRECT; if (profile->mapping == ADDRESS_MAP_UNCHANGED && profile->inbound == ADDRESS_IN_CONTACT) return ADDR_TYPE_CONTACT; @@ -109,7 +111,7 @@ static int address_row_put(sqlite3_stmt *statement, struct TOPO_NODE *node, uint uint8_t protocol, uint8_t config, const struct address_profile *profile) { static const struct address_profile unknown = {0}; if (!profile) profile = &unknown; - int summary = addr_type_classify(config, addr_type_from_ip(family, address) == ADDR_TYPE_DIRECT, profile); + int summary = addr_type_classify(kind, config, addr_type_from_ip(family, address) == ADDR_TYPE_DIRECT, profile); struct TOPO_ADDR_REALITY_OPTS reality; int has_reality = (protocol & TOPO_PROTO_REALITY) && reality_opts_build(node, socket_id, &reality); sqlite3_bind_int64(statement, 1, (sqlite3_int64)node_id); diff --git a/tests/test_node_snapshot.c b/tests/test_node_snapshot.c index 8f17eb16..4d865952 100644 --- a/tests/test_node_snapshot.c +++ b/tests/test_node_snapshot.c @@ -83,6 +83,16 @@ static void test_compact_addresses(struct UTUN_INSTANCE *inst) { assert(sqlite3_column_int(statement, 4) == ADDR_TYPE_CONTACT && sqlite3_column_int(statement, 5) == TOPO_ADDR_REALITY_OPTS_SIZE); assert(sqlite3_step(statement) == SQLITE_DONE); sqlite3_finalize(statement); + /* Публичный IP интерфейса не становится NAT UNKNOWN из-за отсутствия измерения входящих. */ + struct address_profile known = node->v4_sock_meta->address_profile; + node->v4_sock_meta->address_profile = (struct address_profile){0}; + assert(!topo_node_sqlite_addrs_put(db, inst->node_id, node)); + assert(sqlite3_prepare_v2(db, "SELECT addr_type FROM node_addresses WHERE family=4", -1, &statement, NULL) == SQLITE_OK); + assert(sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_int(statement, 0) == ADDR_TYPE_NETIF); + sqlite3_finalize(statement); + node->v4_sock_meta->address_profile = known; + assert(!topo_node_sqlite_addrs_put(db, inst->node_id, node)); + /* Две старые колонки отмечают прежний формат. Миграция не теряет IP/port, REALITY и максимум timestamp. */ assert(sqlite3_exec(db, "ALTER TABLE node_addresses ADD COLUMN nat_evidence INTEGER DEFAULT 1;" "ALTER TABLE node_addresses ADD COLUMN nat_checked INTEGER DEFAULT 1", NULL, NULL, NULL) == SQLITE_OK); diff --git a/tools/chatgui/src/nodespage.cpp b/tools/chatgui/src/nodespage.cpp index 9500efbd..37860c76 100644 --- a/tools/chatgui/src/nodespage.cpp +++ b/tools/chatgui/src/nodespage.cpp @@ -208,7 +208,7 @@ void NodesPage::refreshNodes() { sqlite3_stmt* na = nullptr; sqlite3_prepare_v2(d, - "SELECT family, protocol, address, port, rtt, addr_type, address_kind" + "SELECT family, protocol, address, port, rtt, addr_type, address_kind, nat_mapping, nat_inbound" " FROM node_addresses WHERE node_id=? ORDER BY family, port", -1, &na, nullptr); if (na) { @@ -224,13 +224,20 @@ void NodesPage::refreshNodes() { int rtt = sqlite3_column_int(na, 4); int addrType = sqlite3_column_int(na, 5); int kind = sqlite3_column_int(na, 6); + int mapping = sqlite3_column_int(na, 7), inbound = sqlite3_column_int(na, 8); if (addr && addrSz > 0) { + QString label = addrTypeStr(addrType); + if (kind == TOPO_ADDR_INTERFACE) { + label = QStringLiteral("INTERFACE"); + if (mapping != ADDRESS_MAP_UNKNOWN || inbound != ADDRESS_IN_UNKNOWN) + label += QString(" mapping=%1 inbound=%2").arg(address_mapping_name(mapping), address_inbound_name(inbound)); + } dump += QString(" %1:%2 %3 %4 rtt=%5ms\n") .arg(addrStr(addr, family)) .arg(port) .arg(protoStr(proto)) - .arg(kind == TOPO_ADDR_INTERFACE ? QStringLiteral("INTERFACE") : addrTypeStr(addrType)) + .arg(label) .arg(rtt > 0 ? QString::number(rtt / 10) : "-"); } ac++;