From 145dd1e5abed7fdae2ba83f515598596815d207e Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 15:50:17 +0200 Subject: [PATCH] Prepare BGP paths before committing received node state --- src/routing_layer/topo_group.c | 62 +++++++++++++++++++++------------- src/routing_layer/topo_node.c | 3 +- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/routing_layer/topo_group.c b/src/routing_layer/topo_group.c index e36aa090..5696381d 100644 --- a/src/routing_layer/topo_group.c +++ b/src/routing_layer/topo_group.c @@ -52,7 +52,7 @@ static int topo_tx_control(struct TOPO_GROUP_CONN_ITEM* peer, const void* data, static int topo_tx_node(struct TOPO_GROUP_CONN_ITEM* peer, uint64_t node_id); static int topo_tx_table(struct TOPO_GROUP_CONN_ITEM* peer); static int topo_control_wait(struct ETCP_CONN* conn, const void* data, size_t size); -static void topo_group_routes_changed(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, int event); +static int topo_group_routes_changed(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, int event); static void topo_group_prune_paths(struct TOPO_GROUP_CONN_ITEM* peer, int all); static void topo_exchange_watch(void* arg); static void topo_tx_fail(struct TOPO_GROUP_CONN_ITEM* peer); @@ -1191,8 +1191,9 @@ int topo_group_remove_path(struct TOPO_GROUP_NODE* nq, struct ETCP_CONN* conn) { } /* Одна точка обновления доступности, потребителей и анонсов после изменения путей. */ -static void topo_group_routes_changed(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, int event) { +static int topo_group_routes_changed(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, int event) { uint64_t id = node->node_id; + int result = 0; struct TOPO_NODEPATH* best = topo_node_best_path(node); node->conn_presence &= ~(NCONN_BGP | NCONN_DIRECT); node->conn_up &= ~(NCONN_BGP | NCONN_DIRECT); @@ -1211,9 +1212,11 @@ static void topo_group_routes_changed(struct TOPO_GROUP* group, struct TOPO_GROU (unsigned long long)(best ? best->conn->peer_node_id : 0), best ? best->hop_count : 0); if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance->rt) { route_delete(group->instance->rt, node); - if (best && node->subnets && node->subnets->v4_subnets && !route_insert(group->instance->rt, node)) + if (best && node->subnets && node->subnets->v4_subnets && !route_insert(group->instance->rt, node)) { + result = -1; DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "BGP IPv4 routes rejected: group=%016llx node=%016llx; previous routes removed", (unsigned long long)group->group_id, (unsigned long long)id); + } } topo_fire_nodeinfo_cbk(group->instance, group, node); if (!node->paths) { @@ -1226,6 +1229,7 @@ static void topo_group_routes_changed(struct TOPO_GROUP* group, struct TOPO_GROU topo_recovery_changed(group); conn_mgr_routes_changed(group->conn_mgr); multicast_routes_changed(group->instance); + return result; } /* TABLE_COMPLETE подтверждает только NODEINFO текущего обмена. Сбой обмена @@ -1374,9 +1378,9 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from if (result < 0) return -1; struct TOPO_GROUP_CONN_ITEM* peer = topo_group_peer(group, from->peer_node_id); ((struct TOPO_NODEPATH*)nodeinfo1->paths->tail)->exchange = peer ? peer->local_epoch : 0; - topo_group_routes_changed(group, nodeinfo1, TOPO_NODE_EVENT_UPDATE); + result = topo_group_routes_changed(group, nodeinfo1, TOPO_NODE_EVENT_UPDATE); topo_group_peer_progressed(group, from->peer_node_id); - return 0; + return result; } uint64_t incoming_timestamp = new_ni->timestamp; if (!nodeinfo1) { @@ -1391,9 +1395,22 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from nodeinfo1 = (struct TOPO_GROUP_NODE*)qe; memset((uint8_t*)nodeinfo1 + sizeof(struct ll_entry), 0, sizeof(*nodeinfo1) - sizeof(struct ll_entry)); } + /* Подготовить новый путь до изменения записи/подсетей. OOM сохраняет прежний узел целиком. */ + uint64_t hop_list[MAX_HOPS]; + if (new_hop_count) memcpy(hop_list, new_hop_list, new_hop_count * sizeof(*hop_list)); + hop_list[new_hop_count] = from->peer_node_id; + struct TOPO_GROUP_NODE prepared = {0}; + if (topo_group_add_path(&prepared, from, hop_list, new_hop_count + 1, incoming_cumulative_rtt) < 0) { + topo_node_destroy(group->instance->topo_groups, new_ni); + struct TOPO_GROUP_NODE rejected = { .subnets = new_subnets }; + topo_nodeq_free_group_fields(group->instance->topo_groups, &rejected); + if (is_new_node) queue_entry_free(&nodeinfo1->ll); + u_free(new_hop_list); return -1; + } struct TOPO_NODE* stored = topo_node_registry_store(group->instance->topo_groups, new_ni); if (!stored) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node registry update failed"); + topo_nodeq_clear_paths(&prepared); topo_node_destroy(group->instance->topo_groups, new_ni); struct TOPO_GROUP_NODE rejected = { .subnets = new_subnets }; topo_nodeq_free_group_fields(group->instance->topo_groups, &rejected); @@ -1409,24 +1426,24 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from DEBUG_INFO(DEBUG_CATEGORY_BGP, "node added to group: node=%016llx grp=%016llx type=%u ch=%s (bgp)", (unsigned long long)node_id, (unsigned long long)group->group_id, group->group_type, group->channel_id); - nodeinfo1->paths = paths; nodeinfo1->last_ver = new_ver; + /* Остальные соседи сохраняются; после store больше не нужны выделения памяти. */ + struct TOPO_NODEPATH* incoming = (struct TOPO_NODEPATH*)prepared.paths->head; + struct TOPO_GROUP_CONN_ITEM* peer = topo_group_peer(group, from->peer_node_id); + incoming->exchange = peer ? peer->local_epoch : 0; + if (paths) { + struct ll_entry* e; + while ((e = queue_data_get(paths))) { + if (((struct TOPO_NODEPATH*)e)->conn == from) queue_entry_free(e); + else queue_data_put(prepared.paths, e); + } + queue_free(paths); + } + nodeinfo1->paths = prepared.paths; nodeinfo1->last_ver = new_ver; nodeinfo1->last_timestamp = incoming_timestamp; nodeinfo1->radio = (ni->flags & TOPO_FLAG_RADIO) ? 1 : 0; nodeinfo1->phone_status = ni->flags & (TOPO_FLAG_PHONE_KNOWN | TOPO_FLAG_PHONE_BUSY); - uint64_t hop_list[MAX_HOPS]; - if (new_hop_count > 0) memcpy(hop_list, new_hop_list, new_hop_count * 8); - hop_list[new_hop_count] = from->peer_node_id; - uint8_t extended_count = new_hop_count + 1; - u_free(new_hop_list); - - if (topo_group_add_path(nodeinfo1, from, hop_list, extended_count, incoming_cumulative_rtt) < 0) { - if (is_new_node) topo_nodeq_remove_node(group, nodeinfo1); - return -1; - } - struct TOPO_GROUP_CONN_ITEM* peer = topo_group_peer(group, from->peer_node_id); - ((struct TOPO_NODEPATH*)nodeinfo1->paths->tail)->exchange = peer ? peer->local_epoch : 0; if (node_id != group->instance->node_id) { sqlite3* sdb = group->instance->topo_sqlite_db; if (sdb) { @@ -1448,12 +1465,9 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from if (group->instance->topo_groups->node_updated_cb) group->instance->topo_groups->node_updated_cb(group->instance, node_id, ni->public_key, ni->ed25519_public_key); - topo_group_routes_changed(group, nodeinfo1, is_new_node ? TOPO_NODE_EVENT_NEW : TOPO_NODE_EVENT_UPDATE); + int result = topo_group_routes_changed(group, nodeinfo1, is_new_node ? TOPO_NODE_EVENT_NEW : TOPO_NODE_EVENT_UPDATE); - int socks_changed = 1; - if (!is_new_node) socks_changed = 1; - - if (node_id != group->instance->node_id && (is_new_node || socks_changed)) { + if (node_id != group->instance->node_id) { struct TOPO_NODE* tni = topo_node_registry_find(group->instance->topo_groups, nodeinfo1->node_id); int v4a = tni ? topo_list_count((struct _topo_head*)tni->v4_addrs) : 0; int v6a = tni ? topo_list_count((struct _topo_head*)tni->v6_addrs) : 0; @@ -1464,7 +1478,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from topo_group_peer_progressed(group, from->peer_node_id); node_conn_direct_update_node(group->instance, node_id); - return 0; + return result; } /* Обрабатывает WITHDRAW: удаляет узел/пути, чистит роутинг, форвардит withdraw. */ diff --git a/src/routing_layer/topo_node.c b/src/routing_layer/topo_node.c index 54bf1712..bd959907 100644 --- a/src/routing_layer/topo_node.c +++ b/src/routing_layer/topo_node.c @@ -1102,7 +1102,8 @@ int topo_node_init_self(struct UTUN_INSTANCE* instance) { topo_node_destroy(instance->topo_groups, ni); return -1; } if (!topo_node_registry_store(instance->topo_groups, ni)) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "cannot register local identity"); return -1; + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "cannot register local identity"); + topo_node_destroy(instance->topo_groups, ni); return -1; } return 0; }