From 509368b76de2c1cbefc4532cd81b15d7bdb841be Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 3 Nov 2026 20:35:38 +0100 Subject: [PATCH] Test radio invitation isolation and active invitation revocation --- tests/test_chat_group_isolation.c | 56 +++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/tests/test_chat_group_isolation.c b/tests/test_chat_group_isolation.c index 4f39d25b4..2821b587b 100644 --- a/tests/test_chat_group_isolation.c +++ b/tests/test_chat_group_isolation.c @@ -9,7 +9,10 @@ #include "config_updater.h" #include "broadcast.h" #include "chat/member_sync.h" +#include "chat/chat_event.h" #include "radio/radio.h" +#include "radio/radio_call.h" +#include "ntp_time.h" #include "member_files/private.h" #include "member_files/disk.h" #include "routing_layer/topo_group.h" @@ -46,6 +49,8 @@ static struct { pthread_cond_t disk_condition; int disk_started, disk_released; uint64_t block_request; + unsigned call_incoming[2][2], call_finished[2]; + uint8_t call_reason[2]; } fixture = { .disk_mutex = PTHREAD_MUTEX_INITIALIZER, .disk_condition = PTHREAD_COND_INITIALIZER }; /* Лог содержит и проверяемый эффект, и результат; отрицательный тест не обрывает соседние проверки. */ @@ -317,6 +322,52 @@ static void file_response(void* arg, const struct mf_message* response) { mf_status_text(response->status), response->data_len); } +static void invitation_event(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int bytes) { + if (type != CHAT_EVT_RADIO_CALL || bytes != 27) return; + unsigned node = inst == fixture.nodes[0] ? 0 : 1; + uint64_t group; memcpy(&group, data + 8, 8); + if (data[24] == RADIO_CALL_INCOMING && group >= 111 && group <= 112) fixture.call_incoming[node][group - 111]++; + if (data[24] == RADIO_CALL_FINISHED) { fixture.call_finished[node]++; fixture.call_reason[node] = data[25]; } +} + +static int invitation_isolation(void) { + struct UTUN_INSTANCE* server = fixture.nodes[0]; struct UTUN_INSTANCE* client = fixture.nodes[1]; + REQUIRE(sqlite3_exec(server->topo_sqlite_db, "UPDATE peers_112 SET admitted=1", NULL, NULL, NULL) == SQLITE_OK); + for (unsigned i = 0; i < 2; i++) { REQUIRE(!radio_call_init(fixture.nodes[i])); chat_event_set_handler(fixture.nodes[i], invitation_event); } + struct radio_call_request request = {.inst = client, .id = 7000, .group = 112, .peer = server->node_id, .action = RADIO_CALL_START}; + radio_call_execute(&request); + uint64_t deadline = get_time_tb() + 10000; + while (!fixture.call_incoming[0][1] && get_time_tb() < deadline) uasync_poll(fixture.ua, 1); + REQUIRE(fixture.call_incoming[0][1] == 1 && radio_call_busy(server)); + check("admitted radio invitation reaches B", 1); + struct ETCP_ROUTER_CONN* route = etcp_router_conn_get(server, 112, client->node_id, ETCP_RT_ID_RADIO_CALL); REQUIRE(route); + struct SVC_ROUTE_HDR saved = {.cmd = ETCP_ID_SVC_ROUTE, .group_id = 112, .src_node_id = client->node_id, + .dst_node_id = server->node_id, .svc_id = ETCP_RT_ID_RADIO_CALL, .reset_id = route->peer_reset_id, + .peer_reset_id = route->reset_id, .seq = route->rx_seq}; + char sql[160]; snprintf(sql, sizeof(sql), "UPDATE peers_112 SET admitted=0 WHERE node_id=%lld", (long long)client->node_id); + REQUIRE(sqlite3_exec(server->topo_sqlite_db, sql, NULL, NULL, NULL) == SQLITE_OK); + topo_group_revalidate_members(fixture.groups[0][1]); + check("revocation stops incoming radio invitation", !radio_call_busy(server) && fixture.call_finished[0] == 1 && + fixture.call_reason[0] == RADIO_CALL_UNAVAILABLE); + uint8_t payload[18] = {1}; // RC_INVITE: command, id, expiry_us, reason. + uint64_t id = 7001; int64_t expires = ntp_time_get_us(client) + 2000000; + memcpy(payload + 1, &id, 8); memcpy(payload + 9, &expires, 8); + REQUIRE(!send_routed(&saved, payload, sizeof(payload), ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT)); + check("revoked signed invitation creates no incoming call", !radio_call_busy(server) && fixture.call_incoming[0][1] == 1); + struct radio_call_request denied = {.inst = server, .id = 7001, .group = 112, .peer = client->node_id, .action = RADIO_CALL_START}; + radio_call_execute(&denied); + check("local radio invitation obeys current group admission", !radio_call_busy(server) && fixture.call_finished[0] == 2 && + fixture.call_reason[0] == RADIO_CALL_UNAVAILABLE); + request.action = RADIO_CALL_CANCEL; radio_call_execute(&request); + request.id = 7002; request.group = 111; request.action = RADIO_CALL_START; radio_call_execute(&request); + deadline = get_time_tb() + 10000; + while (!fixture.call_incoming[0][0] && get_time_tb() < deadline) uasync_poll(fixture.ua, 1); + check("radio invitation in neighbouring A still works", fixture.call_incoming[0][0] == 1 && radio_call_busy(server) && + fixture.transport == instance_find_conn(client, server->node_id) && fixture.transport->links_up); + for (unsigned i = 0; i < 2; i++) { radio_call_destroy(fixture.nodes[i]); chat_event_set_handler(fixture.nodes[i], NULL); } + return 0; +} + static int file_isolation(unsigned running) { struct UTUN_INSTANCE* server = fixture.nodes[0]; struct UTUN_INSTANCE* client = fixture.nodes[1]; @@ -401,7 +452,7 @@ int main(void) { debug_set_category_level(DEBUG_CATEGORY_BGP, DEBUG_LEVEL_INFO); debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_DEBUG); } - if (setup() || run()) fixture.failed++; + if (setup() || run() || invitation_isolation()) fixture.failed++; else { for (unsigned i = 0; i < 2; i++) if (member_files_init(fixture.nodes[i])) fixture.failed++; if (!fixture.failed) { @@ -423,7 +474,8 @@ int main(void) { pthread_mutex_unlock(&fixture.disk_mutex); for (unsigned i = 0; i < 2; i++) if (fixture.handles[i]) conn_mgr_close(fixture.handles[i]); for (unsigned i = 0; i < 2; i++) if (fixture.nodes[i]) { - member_files_destroy(fixture.nodes[i]); member_sync_destroy(fixture.nodes[i]); utun_instance_destroy(fixture.nodes[i]); + radio_call_destroy(fixture.nodes[i]); member_files_destroy(fixture.nodes[i]); + member_sync_destroy(fixture.nodes[i]); utun_instance_destroy(fixture.nodes[i]); } if (fixture.ua) uasync_destroy(fixture.ua, 0); pthread_cond_destroy(&fixture.disk_condition); pthread_mutex_destroy(&fixture.disk_mutex);