Browse Source

nodeinfo: 2 адреса на сокет (до/после NAT) + типы адресов local/public/nat/nat_eim/nat_strict

- topo_node: сокет рекламирует INTERFACE (до NAT) + NAT (после NAT, EIM);
  strict/direct отдельным адресом не передаются
- topo_node_sqlite: addr_type_classify различает NAT_VERIFIED_*; добавлен
  ADDR_TYPE_NAT (тип неизвестен), fallback больше не даёт DIRECT по IP
- chat_member: бейдж NAT-типа по приоритету (public>nat_eim>nat>nat_strict>local)
  вместо MIN(addr_type)
- GUI (desktop+android): адреса мембера с socket_id и ролью (local/public/nat/nat_eim/nat_strict)
v2
evgeny 3 weeks ago
parent
commit
7bba39ddcb
  1. 18
      src/chat/chat_member.c
  2. 7
      src/routing_layer/conn_mgr_monitor.c
  3. 25
      src/routing_layer/topo_node.c
  4. 1
      src/routing_layer/topo_node.h
  5. 15
      src/routing_layer/topo_node_sqlite.c
  6. 4
      src/transport_layer/etcp_connections.c
  7. 7
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt
  8. 2
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt
  9. 11
      tools/chatgui-android/jni_bridge/android_jni_bridge.c
  10. 19
      tools/chatgui/src/accountlist.cpp
  11. 1
      tools/chatgui/src/accountlist.h
  12. 4
      tools/chatgui/src/memberlistmodel.cpp
  13. 2
      tools/chatgui/src/nodespage.cpp
  14. 3
      tools/chatgui/transport/node_config.cpp

18
src/chat/chat_member.c

@ -170,8 +170,13 @@ int chat_core_get_member_list(const char* ch_id, uint8_t** out, int* count) {
}
snprintf(sql, sizeof(sql),
"SELECT node_id, MIN(rtt), MIN(addr_type) FROM node_addresses"
" WHERE node_id IN (%s) AND rtt > 0 GROUP BY node_id", id_list);
"SELECT node_id, MIN(rtt),"
" CASE WHEN MAX(addr_type=1) THEN 1"
" WHEN MAX(addr_type=2) THEN 2"
" WHEN MAX(addr_type=4) THEN 4"
" WHEN MAX(addr_type=3) THEN 3"
" ELSE 0 END"
" FROM node_addresses WHERE node_id IN (%s) AND rtt > 0 GROUP BY node_id", id_list);
u_free(id_list);
if (sqlite3_prepare_v2(g_cc.db, sql, -1, &st, NULL) == SQLITE_OK) {
@ -262,8 +267,13 @@ int chat_core_get_single_member(const char* ch_id, uint64_t node_id, uint8_t* ou
/* RTT / NAT */
snprintf(sql, sizeof(sql),
"SELECT MIN(rtt), MIN(addr_type) FROM node_addresses"
" WHERE node_id=? AND rtt > 0");
"SELECT MIN(rtt),"
" CASE WHEN MAX(addr_type=1) THEN 1"
" WHEN MAX(addr_type=2) THEN 2"
" WHEN MAX(addr_type=4) THEN 4"
" WHEN MAX(addr_type=3) THEN 3"
" ELSE 0 END"
" FROM node_addresses WHERE node_id=? AND rtt > 0");
if (sqlite3_prepare_v2(g_cc.db, sql, -1, &st, NULL) == SQLITE_OK) {
sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id);
if (sqlite3_step(st) == SQLITE_ROW) {

7
src/routing_layer/conn_mgr_monitor.c

@ -193,7 +193,12 @@ void cm_candidate_ping_timer_cb(void* arg) {
if (cm_has_active_conn(mgr->group, nid)) {
struct ETCP_CONN* dc = topo_group_find_conn_for_node(mgr->group, nid);
struct TOPO_NODE* ni = topo_node_registry_find(mgr->instance->topo_groups, nq->node_id);
if (ni && dc) etcp_send_ping(mgr->instance, ni->public_key, &dc->links->remote_addr, CONN_PROBE_TIMEOUT_MS, cm_bg_ping_noop_cb, NULL, NULL, 0);
if (ni && dc && dc->links) {
if (dc->links->is_tcp)
etcp_send_tcp_ping(mgr->instance, ni->public_key, &dc->links->remote_addr, CONN_PROBE_TIMEOUT_MS, cm_bg_ping_noop_cb, NULL);
else
etcp_send_ping(mgr->instance, ni->public_key, &dc->links->remote_addr, CONN_PROBE_TIMEOUT_MS, cm_bg_ping_noop_cb, NULL, NULL, 0);
}
} else route_connectivity_probe_node(mgr->instance, mgr->group, nq);
}
mgr->candidate_ping_timer = uasync_set_timeout(mgr->instance->ua, CONN_MGR_CANDIDATE_PING_TB, mgr, cm_candidate_ping_timer_cb, "conn_mgr_cand_ping");

25
src/routing_layer/topo_node.c

@ -938,19 +938,26 @@ int topo_node_update_my_addresses(struct UTUN_INSTANCE* instance) {
struct sockaddr_in* local_sin = (struct sockaddr_in*)&e_sock->local_addr;
struct sockaddr_in* if_sin = (struct sockaddr_in*)&e_sock->interface_addr;
struct sockaddr_in* nat_sin = (struct sockaddr_in*)&e_sock->nat_addr;
int nat_verified = (e_sock->nat_addr.ss_family == AF_INET && nat_sin->sin_addr.s_addr != 0 && e_sock->nat_type != NAT_VERIFIED_STRICT);
{ struct TOPO_ADDR4* a = memory_pool_alloc(instance->topo_groups->v4_addr_pool);
if (nat_verified) {
memcpy(a->addr, &nat_sin->sin_addr.s_addr, 4); a->port = ntohs(nat_sin->sin_port);
a->type = TOPO_ADDR_NAT; a->socket_id = e_sock->sock_id;
} else {
/* адрес ДО NAT (локальный/интерфейсный) — рекламируем всегда */
int use_local = (local_sin->sin_addr.s_addr != 0);
memcpy(a->addr, use_local ? &local_sin->sin_addr.s_addr : &if_sin->sin_addr.s_addr, 4);
a->port = ntohs(use_local ? local_sin->sin_port : if_sin->sin_port);
uint32_t pre_ip = use_local ? local_sin->sin_addr.s_addr : if_sin->sin_addr.s_addr;
uint16_t pre_port = ntohs(use_local ? local_sin->sin_port : if_sin->sin_port);
{ struct TOPO_ADDR4* a = memory_pool_alloc(instance->topo_groups->v4_addr_pool);
memcpy(a->addr, &pre_ip, 4); a->port = pre_port;
a->type = TOPO_ADDR_INTERFACE; a->socket_id = e_sock->sock_id;
}
a->protocol = TOPO_PROTO_UDP;
a->next = v4_head; v4_head = a; }
/* адрес ПОСЛЕ NAT — дополнительно, если NAT определён (EIM).
DIRECT (nat==интерфейс) и STRICT не рекламируем отдельным адресом. */
if (e_sock->nat_addr.ss_family == AF_INET && nat_sin->sin_addr.s_addr != 0
&& e_sock->nat_type != NAT_VERIFIED_STRICT
&& e_sock->nat_type != NAT_VERIFIED_DIRECT) {
struct TOPO_ADDR4* a = memory_pool_alloc(instance->topo_groups->v4_addr_pool);
memcpy(a->addr, &nat_sin->sin_addr.s_addr, 4); a->port = ntohs(nat_sin->sin_port);
a->type = TOPO_ADDR_NAT; a->socket_id = e_sock->sock_id;
a->protocol = TOPO_PROTO_UDP;
a->next = v4_head; v4_head = a;
}
} else if (e_sock->local_addr.ss_family == AF_INET6) {
{ struct TOPO_SOCKMETA6* sm6 = memory_pool_alloc(instance->topo_groups->v6_sock_meta_pool);
sm6->id = e_sock->sock_id; sm6->config_type = e_sock->type; sm6->nat_type = e_sock->nat_type;

1
src/routing_layer/topo_node.h

@ -49,6 +49,7 @@ struct UTUN_INSTANCE;
#define ADDR_TYPE_DIRECT 1
#define ADDR_TYPE_NAT_EIM 2
#define ADDR_TYPE_NAT_STRICT 3
#define ADDR_TYPE_NAT 4 /* за NAT, тип неизвестен */
#define PROBE_STATUS_NONE 0
#define PROBE_STATUS_IN_PROGRESS 1

15
src/routing_layer/topo_node_sqlite.c

@ -62,13 +62,16 @@ static int addr_type_from_ip(int family, const uint8_t* addr) {
}
/* Итоговая классификация: конфиг + автоопределение + IP (всё локально). */
static int addr_type_classify(int config_type, int nat_type, int family, const uint8_t* ip) {
static int addr_type_classify(int config_type, int nat_type) {
if (config_type == CFG_SERVER_TYPE_PUBLIC) return ADDR_TYPE_DIRECT;
if (config_type == CFG_SERVER_TYPE_PRIVATE || config_type == CFG_SERVER_TYPE_LOCAL) return ADDR_TYPE_NETIF;
if (nat_type == NAT_TYPE_EIM) return ADDR_TYPE_NAT_EIM;
if (nat_type == NAT_TYPE_STRICT) return ADDR_TYPE_NAT_STRICT;
if (nat_type == NAT_TYPE_DIRECT) return ADDR_TYPE_DIRECT;
return addr_type_from_ip(family, ip);
if (nat_type == NAT_VERIFIED_EIM) return ADDR_TYPE_NAT_EIM;
if (nat_type == NAT_VERIFIED_STRICT) return ADDR_TYPE_NAT_STRICT;
if (nat_type == NAT_VERIFIED_DIRECT) return ADDR_TYPE_DIRECT;
return ADDR_TYPE_NAT; /* за NAT, тип неизвестен */
}
static void smeta4_lookup(struct TOPO_SOCKMETA4* head, uint8_t sid, uint8_t* cfg, uint8_t* nat) {
@ -115,8 +118,8 @@ int topo_node_sqlite_addrs_put(sqlite3* db, uint64_t node_id, struct TOPO_NODE*
int written = 0;
for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) {
uint8_t cfg, nat; smeta4_lookup(ni->v4_sock_meta, a->socket_id, &cfg, &nat);
int at = (a->type == TOPO_ADDR_INTERFACE) ? ADDR_TYPE_NETIF
: addr_type_classify(cfg, nat, 4, a->addr);
int at = (a->type == TOPO_ADDR_INTERFACE) ? addr_type_from_ip(4, a->addr)
: addr_type_classify(cfg, nat);
sqlite3_bind_int64(is, 1, (sqlite3_int64)node_id);
sqlite3_bind_int(is, 2, 4);
sqlite3_bind_int(is, 3, (int)a->protocol);
@ -136,7 +139,7 @@ int topo_node_sqlite_addrs_put(sqlite3* db, uint64_t node_id, struct TOPO_NODE*
for (const struct TOPO_ADDR6* a = ni->v6_addrs; a; a = a->next) {
uint8_t cfg, nat; smeta6_lookup(ni->v6_sock_meta, a->socket_id, &cfg, &nat);
int at = (a->type == TOPO_ADDR_INTERFACE) ? ADDR_TYPE_NETIF
: addr_type_classify(cfg, nat, 6, a->addr);
: addr_type_classify(cfg, nat);
sqlite3_bind_int64(is, 1, (sqlite3_int64)node_id);
sqlite3_bind_int(is, 2, 6);
sqlite3_bind_int(is, 3, (int)a->protocol);
@ -747,7 +750,7 @@ int topo_node_sqlite_nodeinfo_updated(sqlite3* db, uint64_t node_id) {
}
}
static const int at2type[] = { [ADDR_TYPE_NETIF]=1, [ADDR_TYPE_DIRECT]=1, [ADDR_TYPE_NAT_EIM]=2, [ADDR_TYPE_NAT_STRICT]=1 };
static const int at2type[] = { [ADDR_TYPE_NETIF]=1, [ADDR_TYPE_DIRECT]=1, [ADDR_TYPE_NAT_EIM]=2, [ADDR_TYPE_NAT_STRICT]=1, [ADDR_TYPE_NAT]=2 };
int base_type = (best_at < 0) ? 0 : at2type[best_at];
sqlite3_stmt* s = NULL;

4
src/transport_layer/etcp_connections.c

@ -1470,9 +1470,9 @@ int etcp_send_ping(struct UTUN_INSTANCE* instance, const uint8_t* peer_pubkey_bi
return -1;
}
struct ETCP_SOCKET* e_sock = instance->etcp_sockets;
while (e_sock && e_sock->local_addr.ss_family != addr->ss_family) e_sock = e_sock->next;
while (e_sock && (e_sock->is_tcp || e_sock->local_addr.ss_family != addr->ss_family)) e_sock = e_sock->next;
if (!e_sock) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "no socket for addr_family=%d", addr->ss_family);
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "no UDP socket for addr_family=%d", addr->ss_family);
return -2;
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "ping N1 [%s]", sockaddr_storage_to_str(addr).str);

7
tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt

@ -87,7 +87,11 @@ data class MemberDetail(val nodeId: Long, val name: String = "",
val bgpNodes: Int = 0)
data class MemberAddr(val family: Int = 0, val protocol: String = "", val address: String = "",
val port: Int = 0, val socketId: Int = 0, val rtt: Int = 0)
val port: Int = 0, val socketId: Int = 0, val addrType: Int = 0, val rtt: Int = 0) {
val roleLabel: String get() = when (addrType) {
0 -> "local"; 1 -> "public"; 2 -> "nat_eim"; 3 -> "nat_strict"; 4 -> "nat"; else -> "?$addrType"
}
}
data class LinkStatus(val linkId: Int = 0, val localFamily: Int = 0, val localAddr: String = "", val localPort: Int = 0,
val remoteFamily: Int = 0, val remoteAddr: String = "", val remotePort: Int = 0,
@ -428,6 +432,7 @@ class ChatRepository {
address = a.optString("ad", ""),
port = a.optInt("po", 0),
socketId = a.optInt("si", 0),
addrType = a.optInt("at", 0),
rtt = a.optInt("rt", 0)
))
}

2
tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/MemberListScreen.kt

@ -325,7 +325,7 @@ private fun MemberDetailCard(
Text("Addresses:", fontSize = 13.sp, fontWeight = FontWeight.Medium)
detail.addresses.forEach { a ->
Text(
text = "${a.protocol} ${a.address}:${a.port}",
text = "[sock${a.socketId}] ${a.protocol} ${a.address}:${a.port} ${a.roleLabel}",
fontFamily = FontFamily.Monospace,
fontSize = 13.sp,
color = Color.DarkGray

11
tools/chatgui-android/jni_bridge/android_jni_bridge.c

@ -673,8 +673,8 @@ char* utun_bridge_get_member_detail_json(const char* channel_id, uint64_t node_i
snprintf(buf + pos, sizeof(buf) - pos, ",\"adr\":["); pos += strlen(buf + pos);
if (sqlite3_prepare_v2(db,
"SELECT family, protocol, address, port, socket_id, COALESCE(rtt,0)"
" FROM node_addresses WHERE node_id=? ORDER BY family, port",
"SELECT family, protocol, address, port, socket_id, addr_type, COALESCE(rtt,0)"
" FROM node_addresses WHERE node_id=? ORDER BY socket_id, addr_type, family, port",
-1, &st, NULL) == SQLITE_OK) {
sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id);
int af = 1;
@ -685,7 +685,8 @@ char* utun_bridge_get_member_detail_json(const char* channel_id, uint64_t node_i
int addr_len = sqlite3_column_bytes(st, 2);
int port = sqlite3_column_int(st, 3);
int sid = sqlite3_column_int(st, 4);
int rtt = sqlite3_column_int(st, 5);
int at = sqlite3_column_int(st, 5);
int rtt = sqlite3_column_int(st, 6);
char addr_str[64] = "";
if (addr && addr_len >= (family == 4 ? 4 : 16)) {
@ -700,8 +701,8 @@ char* utun_bridge_get_member_detail_json(const char* channel_id, uint64_t node_i
const char* pn;
switch (proto) { case 1: pn = "UDP"; break; case 2: pn = "TCP"; break; default: pn = "UDP/TCP"; break; }
pos += (size_t)snprintf(buf + pos, sizeof(buf) - pos,
"%s{\"fm\":%d,\"pt\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"si\":%d,\"rt\":%d}",
af ? "" : ",", family, pn, addr_str[0] ? addr_str : "?", port, sid, rtt);
"%s{\"fm\":%d,\"pt\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"si\":%d,\"at\":%d,\"rt\":%d}",
af ? "" : ",", family, pn, addr_str[0] ? addr_str : "?", port, sid, at, rtt);
af = 0;
}
sqlite3_finalize(st);

19
tools/chatgui/src/accountlist.cpp

@ -31,6 +31,10 @@ static void memberDetailCb(const uint8_t* data, int len) {
g_detailTarget->onMemberDetailData(nodeId, data, len);
}
static QString addrRoleStr(int t) {
switch (t) { case 0: return "local"; case 1: return "public"; case 2: return "nat_eim"; case 3: return "nat_strict"; case 4: return "nat"; default: return QString("?%1").arg(t); }
}
QString AccountList::addrStr(const void* addr, int family) {
if (family == 4) {
const uint8_t* a = (const uint8_t*)addr;
@ -233,7 +237,7 @@ void AccountList::showMemberDetail(quint64 nodeId) {
sqlite3_stmt* na = nullptr;
sqlite3_prepare_v2(d,
"SELECT family, protocol, address, port, socket_id FROM node_addresses WHERE node_id=?",
"SELECT family, protocol, address, port, socket_id, addr_type FROM node_addresses WHERE node_id=?",
-1, &na, nullptr);
if (na) {
sqlite3_bind_int64(na, 1, (sqlite3_int64)nodeId);
@ -246,6 +250,7 @@ void AccountList::showMemberDetail(quint64 nodeId) {
if (adr && sz > 0) a.addr = QByteArray((const char*)adr, sz);
a.port = sqlite3_column_int(na, 3);
a.socket_id = sqlite3_column_int(na, 4);
a.addr_type = sqlite3_column_int(na, 5);
if (!a.addr.isEmpty()) m_detailAddrs.append(a);
}
sqlite3_finalize(na);
@ -253,7 +258,8 @@ void AccountList::showMemberDetail(quint64 nodeId) {
std::sort(m_detailAddrs.begin(), m_detailAddrs.end(),
[](const DetailAddress& a, const DetailAddress& b) {
if (a.ifname != b.ifname) return a.ifname < b.ifname;
if (a.socket_id != b.socket_id) return a.socket_id < b.socket_id;
if (a.addr_type != b.addr_type) return a.addr_type < b.addr_type;
if (a.addr != b.addr) return a.addr < b.addr;
if (a.protocol != b.protocol) return a.protocol < b.protocol;
return a.port < b.port;
@ -333,7 +339,8 @@ void AccountList::onMemberDetailData(uint64_t nodeId, const uint8_t* data, int l
}
std::sort(m_detailAddrs.begin(), m_detailAddrs.end(),
[](const DetailAddress& a, const DetailAddress& b) {
if (a.ifname != b.ifname) return a.ifname < b.ifname;
if (a.socket_id != b.socket_id) return a.socket_id < b.socket_id;
if (a.addr_type != b.addr_type) return a.addr_type < b.addr_type;
if (a.addr != b.addr) return a.addr < b.addr;
if (a.protocol != b.protocol) return a.protocol < b.protocol;
return a.port < b.port;
@ -365,11 +372,13 @@ void AccountList::updateDetailDisplay() {
for (const auto& a : m_detailAddrs) {
QString proto;
switch (a.protocol) { case 1: proto = "UDP"; break; case 2: proto = "TCP"; break; default: proto = "?"; break; }
lines.append(QString(" %1 %2 %3:%4")
lines.append(QString(" [sock%1] %2 %3 %4:%5 %6")
.arg(a.socket_id)
.arg(proto)
.arg(a.ifname)
.arg(addrStr(a.addr.constData(), a.family))
.arg(a.port));
.arg(a.port)
.arg(addrRoleStr(a.addr_type)));
}
}

1
tools/chatgui/src/accountlist.h

@ -19,6 +19,7 @@ struct DetailAddress {
QByteArray addr;
int port;
int socket_id;
int addr_type;
QString ifname;
};

4
tools/chatgui/src/memberlistmodel.cpp

@ -39,8 +39,8 @@ 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","STR"};
const char* nt = (it->natType <= 3) ? nat[it->natType] : "?";
static const char* nat[] = {"NET","DIR","EIM","STR","NAT"};
const char* nt = (it->natType <= 4) ? nat[it->natType] : "?";
if (!sub.isEmpty()) sub += " ";
sub += nt;
}

2
tools/chatgui/src/nodespage.cpp

@ -47,7 +47,7 @@ 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_STRICT"; default: return QString("?%1").arg(t); }
switch (t) { case 0: return "NETIF"; case 1: return "DIRECT"; case 2: return "NAT_EIM"; case 3: return "NAT_STRICT"; case 4: return "NAT"; default: return QString("?%1").arg(t); }
}
NodesPage::NodesPage(DbManager* db, QWidget* parent)

3
tools/chatgui/transport/node_config.cpp

@ -30,7 +30,8 @@ static const QSet<QString> SERVER_KEYS = {
};
static const QSet<QString> CLIENT_KEYS = {
"peer_public_key", "link", "keepalive"
"peer_public_key", "link", "keepalive",
"reality", "server_name", "short_id", "public_key", "version", "fingerprint"
};
static const QSet<QString> CONTROL_KEYS = {

Loading…
Cancel
Save