Trace radio mesh packet paths and original stop conditions

This commit is contained in:
evgeny committed 2026-10-26 14:57:41 +01:00
1 parent 5587c60603
commit c2908a6c58
2 files changed
+89

No files matched your search

+4
View File
@@ -955,6 +955,10 @@ test_radio_headless_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
test_radio_mesh_SOURCES = test_radio_mesh.c
test_radio_mesh_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/chat -I$(top_srcdir)/src/dm -I$(top_srcdir)/src/call -I$(top_srcdir)/src/radio -I$(top_srcdir)/src/routing_layer -I$(top_srcdir)/src/transport_layer -I$(top_srcdir)/lib
test_radio_mesh_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
if !OS_WINDOWS
test_radio_mesh_CFLAGS += -DUTUN_TEST_MESH_OBSERVE
test_radio_mesh_LDFLAGS = -Wl,--wrap=etcp_bind -Wl,--wrap=etcp_send
endif
test_stcp_traffic_SOURCES = test_stcp_traffic.c
test_stcp_traffic_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
+85
View File
@@ -19,6 +19,7 @@
#include "chat_event.h"
#include "member_sync.h"
#include "../radio/radio.h"
#include "../radio/radio_proto.h"
#include "../routing_layer/topo_node_sqlite.h"
#include "../routing_layer/topo_group.h"
#include "../routing_layer/topo_node.h"
@@ -90,6 +91,76 @@ struct mesh_shared {
};
static struct mesh_shared g_sh;
static struct rx_evt g_rx[4];
static const char* g_names[4] = {"A", "B", "D", "C"};
static int mesh_node_index(uint64_t id) {
for (int i = 0; i < 4; ++i) if (g_sh.nid[i] == id) return i;
return -1;
}
#ifdef UTUN_TEST_MESH_OBSERVE
/* Наблюдаем raw ETCP до дедупликации, не меняя данные/результат или владение entry. */
static etcp_recv_fn g_radio_recv;
static unsigned int g_wire_tx[4][4], g_wire_rx[4][4];
extern int __real_etcp_bind(struct UTUN_INSTANCE* inst, uint8_t id, etcp_recv_fn callback);
extern int __real_etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry);
static void mesh_wire(const char* direction, struct ETCP_CONN* conn, struct ll_entry* entry) {
if (!conn || !entry || entry->len < 1 + RADIO_HDR_SIZE || entry->dgram[0] != ETCP_ID_RADIO) return;
const uint8_t* p = entry->dgram + 1;
uint64_t group, src; uint16_t stream, seq;
memcpy(&group, p + RADIO_OFF_GROUP, 8); memcpy(&src, p + RADIO_OFF_SRC, 8);
memcpy(&stream, p + RADIO_OFF_STREAM, 2); memcpy(&seq, p + RADIO_OFF_SEQ, 2);
int self = mesh_node_index(conn->instance->node_id), peer = mesh_node_index(conn->peer_node_id);
if (self >= 0 && peer >= 0) {
if (!strcmp(direction, "TX")) ++g_wire_tx[self][peer]; else ++g_wire_rx[self][peer];
}
DEBUG_INFO(DEBUG_CATEGORY_DEBUG,
"mesh wire %s self=%s peer=%s group=%016llx src=%016llx stream=%u seq=%u fin=%d hops=%u",
direction, self < 0 ? "?" : g_names[self], peer < 0 ? "?" : g_names[peer],
(unsigned long long)group, (unsigned long long)src, stream, seq, !!(p[RADIO_OFF_FLAGS] & RADIO_FLAG_FIN),
p[RADIO_OFF_HOPS]);
}
static void mesh_radio_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
mesh_wire("RX", conn, entry);
g_radio_recv(conn, entry);
}
int __wrap_etcp_bind(struct UTUN_INSTANCE* inst, uint8_t id, etcp_recv_fn callback) {
if (id == ETCP_ID_RADIO) {
if (g_radio_recv && g_radio_recv != callback) {
DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "mesh: unexpected different radio callback"); abort();
}
g_radio_recv = callback;
return __real_etcp_bind(inst, id, mesh_radio_recv);
}
return __real_etcp_bind(inst, id, callback);
}
int __wrap_etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry) {
mesh_wire("TX", conn, entry);
return __real_etcp_send(conn, entry);
}
#endif
static void mesh_snapshot(struct mesh_ctx* t, const char* reason) {
uint64_t gid = strtoull(CH_ID, NULL, 10);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "mesh snapshot reason=%s phase=%d tick=%d tb=%llu", reason, t->phase, t->ticks,
(unsigned long long)get_time_tb());
for (int i = 0; i < 4; ++i) {
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "mesh node=%s id=%016llx frames=%d fin=%d seq_ok=%d next_seq=%u dup=%u",
g_names[i], (unsigned long long)g_sh.nid[i], g_rx[i].frames, g_rx[i].fin_seen,
g_rx[i].seq_ok, g_rx[i].expect_seq, radio_dup_dropped(t->inst[i], gid));
#ifdef UTUN_TEST_MESH_OBSERVE
for (int j = 0; j < 4; ++j) if (g_wire_tx[i][j] || g_wire_rx[i][j])
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "mesh edge self=%s peer=%s tx=%u rx=%u",
g_names[i], g_names[j], g_wire_tx[i][j], g_wire_rx[i][j]);
#endif
}
}
static void mesh_tail_stop(void* arg) { uasync_stop(arg); }
static int G_PASSED = 0, G_FAILED = 0, G_TOTAL = 0;
#define TEST(n) do { G_TOTAL++; printf(" %-58s", n); fflush(stdout); } while(0)
@@ -345,6 +416,7 @@ static void mesh_tick(void* arg) {
struct UTUN_INSTANCE* D = t->inst[IDX_D];
struct UTUN_INSTANCE* C = t->inst[IDX_C];
uint64_t gid = strtoull(CH_ID, NULL, 10);
enum mesh_phase previous = t->phase;
switch (t->phase) {
case P_WAIT_CONN:
@@ -383,11 +455,14 @@ static void mesh_tick(void* arg) {
t->phase = P_DONE;
break;
case P_DONE:
mesh_snapshot(t, "original_stop");
t->result = 1;
uasync_stop(t->ua);
return;
}
if (previous != t->phase) mesh_snapshot(t, "phase_change");
if (++t->ticks > MAX_TICKS) {
fprintf(stderr, "test: timeout in phase %d (B_rx=%d/%d fin=%d seqok=%d D_rx=%d/%d fin=%d seqok=%d C_rx=%d/%d fin=%d seqok=%d dupC=%u)\n",
(int)t->phase,
@@ -407,6 +482,7 @@ static void mesh_tick(void* arg) {
int main(int argc, char** argv) {
(void)argc; (void)argv;
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR);
debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO);
if (getenv("UTUN_TEST_DEBUG")) {
debug_set_category_level_by_name("radio", "debug");
debug_set_category_level_by_name("bgp", "info");
@@ -457,6 +533,15 @@ int main(int argc, char** argv) {
uint64_t gid = strtoull(CH_ID, NULL, 10);
uint32_t dup_c = radio_dup_dropped(t.inst[IDX_C], gid);
/* Диагностический хвост не меняет сохранённый результат исходной проверки. */
if (getenv("UTUN_TEST_MESH_TAIL")) {
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "mesh observe tail=500ms original_ok=%d original_dupC=%u", ok, dup_c);
void* tail = uasync_set_timeout(ua, 5000, ua, mesh_tail_stop, "mesh_diagnostic_tail");
if (!tail) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "mesh: tail timer allocation failed"); abort(); }
uasync_mainloop(ua);
mesh_snapshot(&t, "after_diagnostic_tail");
}
if (ok && dup_c == 0) {
/* кадры не задвоились, но и дедупликация не сработала — топология не дала дублей */
printf("WARN: dupC=0 — diamond не породил дублей, кэш не проверен\n");