member detail: structured display with addresses, connections, BGP summary and socket names

- Extend MEMBER_DETAIL_LINK_SIZE to 63B with 20B sock_name per link
- Replace link_count with links_up/links_down (two uint16) in sock snap
- Add BGP hop list collection via topo_node_best_hop_list, post as CHAT_EVT_BGP_INFO
- Add direct RTT summary text via same event
- Rework AccountList detail panel: header + QTextEdit with monospace layout
- Display protocol (TCP/UDP), socket name line, colored dots for connections
- Show [up/down] link counts for self listen sockets
- Remove * prefix from link count display in Android
This commit is contained in:
evgeny committed 2026-08-11 16:05:35 +03:00
1 parent 5a42741d06
commit fec3ea38a7
8 files changed
+217 -77

No files matched your search

+1
View File
@@ -44,6 +44,7 @@ extern "C" {
#define CHAT_EVT_MEMBER_LIST 23 /* [ch_id_len:1][ch_id:var][count:2][member:79B]* — полный список мемберов канала */
#define CHAT_EVT_MEMBER_UPDATED 24 /* [ch_id_len:1][ch_id:var][count:1][member:79B] — обновление одного мембера */
#define CHAT_EVT_MEMBER_REMOVED 25 /* [ch_id_len:1][ch_id:var][node_id:8] — мембер удалён из канала */
#define CHAT_EVT_BGP_INFO 26 /* текст: "rtt:XXms hop:X via bgp: [XXXX] name1 [XXXX] name2" */
typedef void (*chat_event_handler_fn)(int type, const uint8_t* data, int len);
+65 -5
View File
@@ -16,6 +16,8 @@
#include "../transport_layer/etcp_connections.h"
#include "../transport_layer/stcp_link.h"
#include "../transport_layer/pkt_normalizer.h"
#include "../routing_layer/topo_node.h"
#include "../routing_layer/topo_group.h"
#include <string.h>
static const char* nat_type_str(uint8_t t) {
@@ -303,17 +305,18 @@ void chat_core_collect_conn_metrics_trampoline(void* arg) {
}
/* ── Live-снапшот для панели деталей участника ──
* packet: [node_id:8][flags:1][own_tcp_active:2][link_count:1](link:43B)*[sock_count:1](sock:48B)*
* packet: [node_id:8][flags:1][own_tcp_active:2][link_count:1](link:63B)*[sock_count:1](sock:48B)*
* flags: bit0=conn_present bit1=conn_up(links_up) bit2=conn_initialized
* link: [is_tcp:1][link_state:1][link_status:1][tcp_ready:1][is_server:1]
* [local_family:1][local_addr:16][local_port:2]
* [remote_family:1][remote_addr:16][remote_port:2]
* sock: [sock_id:4][port:2][link_count:4][ifname:38]
* [sock_name:20]
* sock: [sock_id:4][port:2][links_up:2][links_down:2][ifname:38]
* Собирается в worker-потоке — читает живые структуры UTUN_INSTANCE безопасно. */
#define MEMBER_DETAIL_MAX_LINKS 16
#define MEMBER_DETAIL_MAX_SOCKS 16
#define MEMBER_DETAIL_LINK_SIZE 43
#define MEMBER_DETAIL_LINK_SIZE 63
#define MEMBER_DETAIL_SOCK_SIZE 48
/* Повторяет Qt: name.lastIndexOf("_v") → name.mid(3, idx - 3) */
@@ -416,6 +419,11 @@ static void collect_member_detail(uint64_t node_id) {
}
*p++ = rf; memcpy(p, ra, 16); p += 16; memcpy(p, &rp, 2); p += 2;
}
{ char sn[20] = {0};
if (lk->conn) strncpy(sn, lk->conn->name, 19);
memcpy(p, sn, 20); p += 20;
}
}
*p++ = sock_count;
@@ -425,16 +433,68 @@ static void collect_member_detail(uint64_t node_id) {
const struct sockaddr_storage* sa = s->interface_addr.ss_family ? &s->interface_addr : &s->local_addr;
if (sa->ss_family == AF_INET) sp = ntohs(((const struct sockaddr_in*)sa)->sin_port);
else if (sa->ss_family == AF_INET6) sp = ntohs(((const struct sockaddr_in6*)sa)->sin6_port);
uint32_t lc = s->is_tcp ? 0 : (s->links_queue ? (uint32_t)queue_entry_count(s->links_queue) : 0);
uint16_t lu = 0, ld = 0;
{ struct ll_entry* e = inst->connections ? inst->connections->head : NULL;
for (; e; e = e->next) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)e->data;
if (!ce || !ce->conn) continue;
for (struct ETCP_LINK* lk = ce->conn->links; lk; lk = lk->next)
if (lk->conn == s) { if (lk->link_state == 3) lu++; else ld++; }
}
}
{ struct ll_entry* e = inst->tcp_connections ? inst->tcp_connections->head : NULL;
for (; e; e = e->next) {
struct tcp_conn_entry* te = (struct tcp_conn_entry*)e->data;
if (!te || !te->etcp_conn) continue;
for (struct ETCP_LINK* lk = te->etcp_conn->links; lk; lk = lk->next)
if (lk->conn == s) { if (lk->link_state == 3) lu++; else ld++; }
}
}
char ifname[38] = {0}; sock_ifname(s->name, ifname, sizeof(ifname));
memcpy(p, &sid, 4); p += 4;
memcpy(p, &sp, 2); p += 2;
memcpy(p, &lc, 4); p += 4;
memcpy(p, &lu, 2); p += 2;
memcpy(p, &ld, 2); p += 2;
memcpy(p, ifname, 38); p += 38;
}
chat_event_post(CHAT_EVT_MEMBER_DETAIL, buf, (int)(p - buf));
u_free(buf);
/* summary text: direct RTT or BGP */
if (conn && conn->links_up) {
uint16_t rtt = conn->links ? conn->links->rtt_last : 0;
if (rtt > 0) {
char tbuf[32];
int off = snprintf(tbuf, sizeof(tbuf), "direct, rtt:%ums", rtt / 10);
chat_event_post(CHAT_EVT_BGP_INFO, (const uint8_t*)tbuf, off);
}
} else if (conn && !conn->links_up && node_id != inst->node_id) {
struct ll_queue* gl = inst->topo_groups ? inst->topo_groups->group_list : NULL;
for (struct ll_entry* ge = gl ? gl->head : NULL; ge; ge = ge->next) {
struct TOPO_GROUP* group = (struct TOPO_GROUP*)ge->data;
if (!group) continue;
struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, node_id);
if (!nq) continue;
uint8_t hc = 0;
uint64_t* hops = topo_node_best_hop_list(nq, &hc, NULL);
if (hc > 0 && hops) {
char tbuf[512] = {0};
uint16_t cr = topo_get_chain_rtt(nq);
int off = snprintf(tbuf, sizeof(tbuf), "rtt:%ums hop:%u via bgp:", cr / 10, hc);
for (uint8_t hi = 0; hi < hc && off < (int)sizeof(tbuf) - 24; hi++) {
char hn[32] = {0};
get_node_name(hops[hi], hn, sizeof(hn));
uint16_t hid = (uint16_t)(hops[hi] & 0xFFFF);
off += snprintf(tbuf + off, sizeof(tbuf) - (size_t)off,
" [%04X]%s", hid, hn[0] ? " " : "");
if (hn[0]) off += snprintf(tbuf + off, sizeof(tbuf) - (size_t)off, "%s", hn);
}
chat_event_post(CHAT_EVT_BGP_INFO, (const uint8_t*)tbuf, off);
}
break;
}
}
}
void chat_core_collect_member_detail_trampoline(void* arg) {