db: accept TCP IP-only addresses while preserving stored projections

This commit is contained in:
evgeny committed 2026-10-17 19:58:54 +02:00
1 parent 2c7c3c9e8b
commit d2a8e6fda7
2 files changed
+56 -2

No files matched your search

+28 -2
View File
@@ -250,12 +250,38 @@ 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'",
if (sqlite3_prepare_v2(db, "SELECT sql 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;
}
int exists = sqlite3_step(schema) == SQLITE_ROW;
char *zero_port_schema = NULL;
if (exists) {
const char *ddl = (const char *)sqlite3_column_text(schema, 0);
const char *strict_port = ddl ? strstr(ddl, "port > 0") : NULL;
if (strict_port) {
zero_port_schema = sqlite3_mprintf("%.*sport >= 0%s", (int)(strict_port - ddl), ddl, strict_port + strlen("port > 0"));
if (!zero_port_schema) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "address zero-port schema allocation failed"); sqlite3_finalize(schema); return -1;
}
}
}
sqlite3_finalize(schema); schema = NULL;
if (zero_port_schema) {
int failed = sqlite3_exec(db, "SAVEPOINT address_zero_port; ALTER TABLE node_addresses RENAME TO address_previous_ports",
NULL, NULL, NULL) != SQLITE_OK ||
sqlite3_exec(db, zero_port_schema, NULL, NULL, NULL) != SQLITE_OK ||
sqlite3_exec(db, "INSERT INTO node_addresses SELECT * FROM address_previous_ports;"
"DROP TABLE address_previous_ports; RELEASE address_zero_port", NULL, NULL, NULL) != SQLITE_OK;
sqlite3_free(zero_port_schema);
if (failed) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "address zero-port schema rebuild failed: %s", sqlite3_errmsg(db));
if (sqlite3_exec(db, "ROLLBACK TO address_zero_port; RELEASE address_zero_port", NULL, NULL, NULL) != SQLITE_OK)
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "address zero-port schema rollback failed: %s", sqlite3_errmsg(db));
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "address schema now accepts TCP IP-only port=0; stored addresses preserved");
}
if (exists) {
int compatible = sqlite3_prepare_v2(db,
"SELECT nat_mapping,nat_inbound,nat_source,address_kind FROM node_addresses LIMIT 0",
@@ -303,7 +329,7 @@ int topo_node_sqlite_init(sqlite3* db) {
" family INTEGER NOT NULL CHECK(family IN (4, 6)),"
" protocol INTEGER NOT NULL DEFAULT 1,"
" address BLOB NOT NULL,"
" port INTEGER NOT NULL CHECK(port > 0 AND port <= 65535),"
" port INTEGER NOT NULL CHECK(port >= 0 AND port <= 65535),"
" rtt INTEGER,"
" addr_type INTEGER DEFAULT 0,"
" config_type INTEGER DEFAULT 0,"
+28
View File
@@ -36,8 +36,36 @@ static struct TOPO_NODE* record(struct UTUN_INSTANCE* inst, uint64_t timestamp,
return ni;
}
/* Старая БД запрещала port=0; перестройка CHECK сохраняет адреса и восстанавливает индексы. */
static void test_zero_port_schema(void) {
sqlite3 *db = NULL; assert(sqlite3_open(":memory:", &db) == SQLITE_OK);
assert(sqlite3_exec(db, "CREATE TABLE node_addresses("
"id INTEGER PRIMARY KEY,node_id INTEGER,family INTEGER,protocol INTEGER,address BLOB,"
"port INTEGER CHECK(port > 0 AND port <= 65535),rtt INTEGER,addr_type INTEGER,config_type INTEGER,"
"nat_mapping INTEGER,nat_inbound INTEGER,nat_source INTEGER,address_kind INTEGER,socket_id INTEGER,"
"options BLOB,created_at INTEGER);"
"CREATE UNIQUE INDEX idx_na_unique ON node_addresses(node_id,family,socket_id,protocol,address,port,address_kind);"
"INSERT INTO node_addresses VALUES(7,1,4,2,X'CB00712C',443,15,4,2,0,0,1,1,3,X'0102',123)",
NULL, NULL, NULL) == SQLITE_OK);
assert(!topo_node_sqlite_init(db));
sqlite3_stmt *statement = NULL;
assert(sqlite3_prepare_v2(db, "SELECT port,rtt,nat_source,length(options),created_at FROM node_addresses WHERE id=7",
-1, &statement, NULL) == SQLITE_OK);
assert(sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_int(statement, 0) == 443);
assert(sqlite3_column_int(statement, 1) == 15 && sqlite3_column_int(statement, 2) == ADDRESS_SOURCE_ETCP);
assert(sqlite3_column_int(statement, 3) == 2 && sqlite3_column_int(statement, 4) == 123);
sqlite3_finalize(statement);
assert(sqlite3_exec(db, "UPDATE node_addresses SET port=0 WHERE id=7", NULL, NULL, NULL) == SQLITE_OK);
assert(sqlite3_exec(db, "INSERT INTO node_addresses SELECT 8,node_id,family,protocol,address,port,rtt,"
"addr_type,config_type,nat_mapping,nat_inbound,nat_source,address_kind,socket_id,options,created_at"
" FROM node_addresses WHERE id=7", NULL, NULL, NULL) == SQLITE_CONSTRAINT);
assert(!topo_node_sqlite_init(db));
sqlite3_close(db);
}
/* Новый wire и SQL сохраняют адреса, протоколы, классификацию и REALITY; смена формата оставляет bootstrap. */
static void test_compact_addresses(struct UTUN_INSTANCE *inst) {
test_zero_port_schema();
sqlite3 *saved_db = inst->topo_sqlite_db;
assert(sqlite3_open(":memory:", &inst->topo_sqlite_db) == SQLITE_OK);
sqlite3 *db = inst->topo_sqlite_db;