From 546e0c185d9b49f56914d5bdba68314ead917582 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 28 Sep 2026 14:20:55 +0300 Subject: [PATCH] Fix WITHDRAW removing nodes with surviving paths --- src/routing_layer/topo_group.c | 12 ++++++++---- tests/test_group_exchange.c | 13 +++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/routing_layer/topo_group.c b/src/routing_layer/topo_group.c index 21ddba5d..f5f98078 100644 --- a/src/routing_layer/topo_group.c +++ b/src/routing_layer/topo_group.c @@ -1065,7 +1065,7 @@ int topo_group_add_path(struct TOPO_GROUP_NODE* nq, struct ETCP_CONN* conn, uint return 0; } -/* Удаляет пути, содержащие wd_source в hop_list; 1 — если путей не осталось. */ +/* Удаляет пути, содержащие wd_source в hop_list; возвращает число удалённых путей. */ static int topo_group_remove_path_by_hop(struct TOPO_GROUP_NODE* nq, uint64_t wd_source) { if (!nq || !nq->paths) return 0; int removed = 0; @@ -1077,7 +1077,7 @@ static int topo_group_remove_path_by_hop(struct TOPO_GROUP_NODE* nq, uint64_t wd if (has_wd) { struct ll_entry* next = e->next; queue_remove_data(nq->paths, e); queue_entry_free(e); removed++; e = next; continue; } e = e->next; } - if (removed > 0 && nq->paths && queue_entry_count(nq->paths) == 0) { queue_free(nq->paths); nq->paths = NULL; return 1; } + if (removed > 0 && queue_entry_count(nq->paths) == 0) { queue_free(nq->paths); nq->paths = NULL; } return removed; } @@ -1314,8 +1314,12 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(group, node_id); if (!nq) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found"); return 0; } - int ret = topo_group_remove_path_by_hop(nq, wd_source); - if (ret == 1) { + int removed = topo_group_remove_path_by_hop(nq, wd_source); + int remaining = nq->paths ? queue_entry_count(nq->paths) : 0; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "WITHDRAW group=%016llx node=%016llx source=%016llx removed=%d remaining=%d", + (unsigned long long)group->group_id, (unsigned long long)node_id, + (unsigned long long)wd_source, removed, remaining); + if (removed > 0 && remaining == 0) { nq->conn_presence = 0; nq->conn_up = 0; topo_fire_nodeinfo_cbk(group->instance, group, nq); if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance && group->instance->rt) route_delete(group->instance->rt, nq); diff --git a/tests/test_group_exchange.c b/tests/test_group_exchange.c index 42f8ea38..fa4ecf46 100644 --- a/tests/test_group_exchange.c +++ b/tests/test_group_exchange.c @@ -126,6 +126,19 @@ int main(void) { nodeinfo(a, conn, &node, &keys, a_id, 1); assert(topo_node_find_by_id(a, node.node_id)->last_timestamp == 1); + /* Отзыв альтернативы не должен уничтожать сохранившийся прямой путь. */ + struct ETCP_CONN relay = { .instance = inst, .peer_node_id = 0x1234, .links_up = 1 }; + uint64_t relay_hops[] = { node.node_id, relay.peer_node_id }; + struct TOPO_GROUP_NODE* routed = topo_node_find_by_id(a, node.node_id); + assert(topo_group_add_path(routed, &relay, relay_hops, 2, 10) == 0); + struct TOPOMSG_WITHDRAW_PKT alternate_wd = { .node_id = node.node_id, .wd_source = relay.peer_node_id }; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "regression: withdraw one of two paths node=%016llx paths=%d", + (unsigned long long)node.node_id, queue_entry_count(routed->paths)); + assert(topo_group_process_withdraw(a, &relay, (const uint8_t*)&alternate_wd, sizeof(alternate_wd)) == 0); + assert(topo_node_find_by_id(a, node.node_id) == routed); + assert(queue_entry_count(routed->paths) == 1 && topo_group_find_conn_for_node(a, node.node_id) == conn); + assert(topo_group_process_withdraw(a, &relay, (const uint8_t*)&alternate_wd, sizeof(alternate_wd)) == 0); + assert(topo_node_find_by_id(a, node.node_id) == routed && queue_entry_count(routed->paths) == 1); etcp_fire_conn_status(conn, ETCP_CONN_STATUS_REINIT); assert(topo_group_find_conn_for_node(a, node.node_id) == conn); /* REINIT preserves known paths */ assert(peer(a)->local_epoch != a_id && peer(b)->local_epoch != b_id);