Retain stable NAT results and compact the address database

This commit is contained in:
evgeny committed 2026-10-12 17:04:59 +02:00
1 parent e47ae480f5
commit db8a5ee2df
2 files changed
+80 -42

No files matched your search

+37 -9
View File
@@ -121,9 +121,7 @@ static int address_row_put(sqlite3_stmt *statement, struct TOPO_NODE *node, uint
if (has_reality) sqlite3_bind_blob(statement, 10, &reality, sizeof(reality), SQLITE_STATIC);
else sqlite3_bind_null(statement, 10);
sqlite3_bind_int(statement, 11, kind); sqlite3_bind_int(statement, 12, profile->inbound);
sqlite3_bind_int64(statement, 13, (sqlite3_int64)profile->evidence_id);
sqlite3_bind_int64(statement, 14, (sqlite3_int64)profile->checked_at);
sqlite3_bind_int(statement, 15, profile->source);
sqlite3_bind_int(statement, 13, profile->source);
int result = sqlite3_step(statement);
sqlite3_reset(statement);
if (result != SQLITE_DONE) {
@@ -147,8 +145,8 @@ int topo_node_sqlite_addrs_put(sqlite3 *db, uint64_t node_id, struct TOPO_NODE *
if (sqlite3_step(statement) != SQLITE_DONE) goto fail;
sqlite3_finalize(statement); statement = NULL;
if (sqlite3_prepare_v2(db, "INSERT INTO node_addresses(node_id,family,protocol,address,port,addr_type,config_type,"
"nat_mapping,socket_id,options,address_kind,nat_inbound,nat_evidence,nat_checked,nat_source)"
" VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", -1, &statement, NULL) != SQLITE_OK) goto fail;
"nat_mapping,socket_id,options,address_kind,nat_inbound,nat_source)"
" VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)", -1, &statement, NULL) != SQLITE_OK) goto fail;
int written = 0;
for (const struct TOPO_ADDR4 *address = node->v4_addrs; address; address = address->next) {
uint8_t config;
@@ -244,6 +242,7 @@ int topo_node_sqlite_init(sqlite3* db) {
/* Проекция адресов производная. Старый формат пересоздаём, не интерпретируя NAT_TYPE_* как новый профиль. */
sqlite3_stmt *schema = NULL;
int legacy_profile = 0;
if (sqlite3_prepare_v2(db, "SELECT 1 FROM sqlite_master WHERE type='table' AND name='node_addresses'",
-1, &schema, NULL) != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "address schema inspection failed: %s", sqlite3_errmsg(db)); return -1;
@@ -252,7 +251,7 @@ int topo_node_sqlite_init(sqlite3* db) {
sqlite3_finalize(schema); schema = NULL;
if (exists) {
int compatible = sqlite3_prepare_v2(db,
"SELECT nat_mapping,nat_inbound,nat_evidence,nat_checked,nat_source,address_kind FROM node_addresses LIMIT 0",
"SELECT nat_mapping,nat_inbound,nat_source,address_kind FROM node_addresses LIMIT 0",
-1, &schema, NULL);
sqlite3_finalize(schema);
if (compatible == SQLITE_ERROR) {
@@ -262,6 +261,17 @@ int topo_node_sqlite_init(sqlite3* db) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "old address projection reset; signed NODEINFO will rebuild endpoint profiles");
} else if (compatible != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "address schema read failed: %s", sqlite3_errmsg(db)); return -1;
} else {
if (sqlite3_prepare_v2(db, "SELECT count(*) FROM pragma_table_info('node_addresses')"
" WHERE name IN ('nat_evidence','nat_checked')", -1, &schema, NULL) != SQLITE_OK ||
sqlite3_step(schema) != SQLITE_ROW) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "address profile schema inspection failed: %s", sqlite3_errmsg(db));
sqlite3_finalize(schema); return -1;
}
int fields = sqlite3_column_int(schema, 0);
sqlite3_finalize(schema); schema = NULL;
if (fields == 1) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "incomplete legacy NAT profile schema"); return -1; }
legacy_profile = fields == 2;
}
}
@@ -292,8 +302,6 @@ int topo_node_sqlite_init(sqlite3* db) {
" config_type INTEGER DEFAULT 0,"
" nat_mapping INTEGER NOT NULL DEFAULT 0,"
" nat_inbound INTEGER NOT NULL DEFAULT 0,"
" nat_evidence INTEGER NOT NULL DEFAULT 0,"
" nat_checked INTEGER NOT NULL DEFAULT 0,"
" nat_source INTEGER NOT NULL DEFAULT 0,"
" address_kind INTEGER NOT NULL DEFAULT 0,"
" socket_id INTEGER NOT NULL DEFAULT 0,"
@@ -331,6 +339,24 @@ int topo_node_sqlite_init(sqlite3* db) {
return -1;
}
/* Старые подписи непригодны; адреса и максимальные версии остаются для bootstrap и новой публикации. */
if (legacy_profile) {
const char *migration = "SAVEPOINT compact_nat_profile;"
"ALTER TABLE node_addresses DROP COLUMN nat_evidence;"
"ALTER TABLE node_addresses DROP COLUMN nat_checked;"
"UPDATE node_addresses SET nat_mapping=0,nat_inbound=0,nat_source=0,"
"addr_type=CASE WHEN addr_type=0 OR config_type IN (3,4) THEN 0 ELSE 4 END;"
"UPDATE node_snapshots SET record=X'';"
"RELEASE compact_nat_profile;";
if (sqlite3_exec(db, migration, NULL, NULL, NULL) != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "compact NAT profile migration failed: %s", sqlite3_errmsg(db));
if (sqlite3_exec(db, "ROLLBACK TO compact_nat_profile; RELEASE compact_nat_profile", NULL, NULL, NULL) != SQLITE_OK)
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "compact NAT profile migration rollback failed: %s", sqlite3_errmsg(db));
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT profile compacted to mapping/inbound/source; bootstrap addresses and node versions preserved");
}
/* migration: добавить колонку options в уже существующую node_addresses (idempotent) */
{
sqlite3_stmt* cs = NULL;
@@ -1033,7 +1059,9 @@ struct TOPO_NODE* topo_node_sqlite_snapshot_load(sqlite3* db, struct TOPO_GROUPS
struct TOPO_GROUP group = { .instance = groups->instance };
const uint8_t* data = sqlite3_column_blob(stmt, 0);
int len = sqlite3_column_bytes(stmt, 0);
if (topo_node_deserialize(&group, data, (size_t)len, &ni, NULL, NULL, NULL, NULL) < 0 ||
if (!len) {
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "snapshot invalidated by format change node=%016llx", (unsigned long long)node_id);
} else if (topo_node_deserialize(&group, data, (size_t)len, &ni, NULL, NULL, NULL, NULL) < 0 ||
!ni || ni->node_id != node_id || topo_node_verify(ni) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid persisted snapshot node=%016llx", (unsigned long long)node_id);
topo_node_destroy(groups, ni); ni = NULL;