Exercise group creation rollback radio retry and BGP subnets

This commit is contained in:
evgeny committed 2026-10-24 15:48:58 +02:00
1 parent 4363a424d0
commit 6b413d67a6
2 files changed
+63 -1

No files matched your search

+26
View File
@@ -10,6 +10,7 @@
#include "etcp_api.h"
#include "etcp_router.h"
#include "route_crypto.h"
#include "route_lib.h"
#include "node_conn_direct.h"
#include "../lib/debug_config.h"
#include "test_utils.h"
@@ -183,6 +184,30 @@ static void metric_jitter(void) {
assert(delivered == before); /* RTT is telemetry, not routing state. */
}
/* Новый набор подсетей обязан заменять старый на всех узлах, включая пустой набор. */
static void subnet_replacement(void) {
struct CFG_ROUTE_ENTRY subnet = {.ip.family = AF_INET, .netmask = 24};
for (int phase = 0; phase < 3; phase++) {
subnet.ip.addr.v4.s_addr = htonl(phase == 0 ? 0x0a630000 : 0x0a640000);
nodes[0]->config->my_subnets = phase < 2 ? &subnet : NULL;
assert(topo_group_update_my_nodeinfo(nodes[0], groups[0]) == (phase < 2));
nodes[0]->config->my_subnets = NULL;
for (struct ll_entry* e = groups[0]->senders_list->head; e; e = e->next) {
struct TOPO_GROUP_CONN_ITEM* p = (struct TOPO_GROUP_CONN_ITEM*)e->data;
assert(topo_group_send_nodeinfo(groups[0], groups[0]->local_node, p->conn) == 0);
}
settle(); verify();
for (int i = 0; i < N; i++) {
struct ROUTE_ENTRY* old = route_lookup(nodes[i]->rt, 0x0a630001);
struct ROUTE_ENTRY* current = route_lookup(nodes[i]->rt, 0x0a640001);
assert((old != NULL) == (phase == 0) && (current != NULL) == (phase == 1));
if (old) assert(old->v_node_info->node_id == nodes[0]->node_id);
if (current) assert(current->v_node_info->node_id == nodes[0]->node_id);
}
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP IPv4 subnet replacement and removal passed on all four nodes");
}
static void service_receive(struct ETCP_CONN* conn, struct ll_entry* e) {
if (e->len > ROUTER_SVC_HDR_SIZE) {
assert(conn->instance == nodes[3]);
@@ -336,6 +361,7 @@ int main(void) {
create();
unsigned triangle = edge(0, 1) | edge(1, 2) | edge(0, 2) | edge(2, 3);
change(triangle); settle(); verify();
subnet_replacement();
metric_jitter();
/* One-sided REINIT with routes retained, then simultaneous REINIT at all peers. */
etcp_fire_conn_status(conns[0][1], ETCP_CONN_STATUS_REINIT); settle(); verify();
+37 -1
View File
@@ -27,9 +27,12 @@ struct wire_fixture {
#ifdef __linux__
static unsigned fail_at, attempts, inside_pool;
static int track_allocations;
static int lifecycle_faults;
static int allocation_fails(const char *where) {
if (!track_allocations || (!strstr(where, "topo_node.c:") && !strstr(where, "topo_node_sqlite.c:"))) return 0;
if (!track_allocations || (!strstr(where, "topo_node.c:") && !strstr(where, "topo_node_sqlite.c:") &&
!(lifecycle_faults && (strstr(where, "topo_group.c:") || strstr(where, "conn_mgr_core.c:") ||
strstr(where, "broadcast.c:") || strstr(where, "radio.c:"))))) return 0;
attempts++;
if (attempts != fail_at) return 0;
DEBUG_WARN(DEBUG_CATEGORY_BGP, "[fault] NODEINFO allocation %u rejected at %s", attempts, where);
@@ -338,6 +341,39 @@ static void test_local_allocation_failures(struct wire_fixture *f) {
assert(route_lookup(f->inst.rt, 0x0a020001)->v_node_info == old && !route_lookup(f->inst.rt, 0x0a030001));
}
fail_at = 0;
/* Ошибка публикации radio не должна делать следующую такую же команду no-op. */
struct TOPO_GROUP chat = {.instance = &f->inst, .group_id = 44, .group_type = TOPO_GROUP_TYPE_CHAT};
assert(topo_group_update_my_nodeinfo(&f->inst, &chat) == 0);
struct TOPO_GROUP_NODE* radio_before = chat.local_node;
attempts = 0; fail_at = 1; track_allocations = 1;
topo_group_set_radio(&chat, 1);
track_allocations = 0; fail_at = 0;
assert(!chat.radio_active && chat.local_node == radio_before && !chat.local_node->radio);
topo_group_set_radio(&chat, 1);
assert(chat.radio_active && chat.local_node->radio);
topo_nodeq_free_group_fields(&f->groups, chat.local_node); u_free(chat.local_node);
/* Ни local_node, ни один из обязательных контекстов не может отсутствовать у возвращённой группы. */
f->groups.group_list = queue_new(f->inst.ua, 16, 0, 8, "creation_faults"); assert(f->groups.group_list);
attempts = 0; lifecycle_faults = 1; track_allocations = 1;
struct TOPO_GROUP* created = topo_groups_create_group(&f->groups, 43, TOPO_GROUP_TYPE_UTUN, NULL);
track_allocations = 0;
assert(created && created->local_node && created->conn_mgr && created->broadcast && created->radio);
unsigned creation_allocations = attempts;
topo_groups_remove_group(&f->groups, 43); uasync_poll(f->inst.ua, 0);
for (unsigned nth = 1; nth <= creation_allocations; nth++) {
size_t before = live_allocations(), routes = f->inst.rt->count;
uint32_t before_refs = node->group_ref_count;
attempts = 0; fail_at = nth; track_allocations = 1;
created = topo_groups_create_group(&f->groups, 43, TOPO_GROUP_TYPE_UTUN, NULL);
track_allocations = 0; uasync_poll(f->inst.ua, 0);
assert(!created && !topo_groups_find(&f->groups, 43));
assert(node->group_ref_count == before_refs && f->inst.rt->count == routes && live_allocations() == before);
}
lifecycle_faults = 0; fail_at = 0;
queue_free(f->groups.group_list); f->groups.group_list = NULL;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "[PASS] group creation: %u allocation failures roll back all resources; radio retry preserved",
creation_allocations);
topo_nodeq_free_group_fields(&f->groups, old); u_free(old); f->group.local_node = NULL;
topo_node_registry_unref(&f->groups, f->inst.node_id);
assert(!f->groups.node_registry->head); queue_free(f->groups.node_registry); f->groups.node_registry = NULL;