Browse Source

Fix WITHDRAW removing nodes with surviving paths

proxy
evgeny 3 days ago
parent
commit
546e0c185d
  1. 12
      src/routing_layer/topo_group.c
  2. 13
      tests/test_group_exchange.c

12
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; 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) { static int topo_group_remove_path_by_hop(struct TOPO_GROUP_NODE* nq, uint64_t wd_source) {
if (!nq || !nq->paths) return 0; if (!nq || !nq->paths) return 0;
int removed = 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; } 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; 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; 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); 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; } if (!nq) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found"); return 0; }
int ret = topo_group_remove_path_by_hop(nq, wd_source); int removed = topo_group_remove_path_by_hop(nq, wd_source);
if (ret == 1) { 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; nq->conn_presence = 0; nq->conn_up = 0;
topo_fire_nodeinfo_cbk(group->instance, group, nq); 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); if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance && group->instance->rt) route_delete(group->instance->rt, nq);

13
tests/test_group_exchange.c

@ -126,6 +126,19 @@ int main(void) {
nodeinfo(a, conn, &node, &keys, a_id, 1); nodeinfo(a, conn, &node, &keys, a_id, 1);
assert(topo_node_find_by_id(a, node.node_id)->last_timestamp == 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); 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(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); assert(peer(a)->local_epoch != a_id && peer(b)->local_epoch != b_id);

Loading…
Cancel
Save