Author SHA1 Message Date
Evgeny 9f13cf5459 route redesign + tests 2026-03-30 18:26:56 +03:00
Evgeny be6058c642 backup: before plan 1 fix for route_lib test (non-overlapping subnets) 2026-03-30 18:25:47 +03:00
Evgeny 27618217cc fix: update tests for new NODEINFO routing API
- Added route_node.o to test linking
- Simplified test_route_lib to avoid old API calls
- Minor fixes to build system

Branch: feature/nodeinfo-routing
2026-03-29 20:55:40 +03:00
Evgeny a93e34f85f feat: transition to NODEINFO based routing system
- Replaced old BGP_ROUTE_PACKET with NODEINFO packets
- Added paths list to NODEINFO_Q for multi-path routing
- Implemented process_nodeinfo, process_withdraw, add/remove_path
- Updated routing.c to use v_node_info->paths
- Fixed type inconsistencies and includes across route_* files
- Hash size for nodes queue increased to 256
- Version increment logic in build_my_nodeinfo

Branch: feature/nodeinfo-routing
2026-03-29 20:50:47 +03:00
13 changed files with 832 additions and 388 deletions

No files matched your search

+19 -1
View File
@@ -228,11 +228,29 @@ int queue_data_put(struct ll_queue* q, struct ll_entry* entry);
*/
int queue_data_put_first(struct ll_queue* q, struct ll_entry* entry);
/**
* @brief Добавляет элемент в конец очереди с явным указанием смещения индекса для hash.
* @param q очередь
* @param entry элемент
* @param index_offset смещение индекса (node_id) в data[]
* @param index_size размер индекса (8 для uint64_t node_id)
* @return 0 — успех, -1 — ошибка
*/
int queue_data_put_with_index(struct ll_queue* q, struct ll_entry* entry,
uint16_t index_offset, uint16_t index_size);
uint16_t index_offset, uint16_t index_size);
/**
* @brief Добавляет элемент в начало очереди с явным указанием смещения индекса для hash.
* @param q очередь
* @param entry элемент
* @param index_offset смещение индекса в data[]
* @param index_size размер индекса
* @return 0 — успех, -1 — ошибка
*/
int queue_data_put_first_with_index(struct ll_queue* q, struct ll_entry* entry,
uint16_t index_offset, uint16_t index_size);
/**
* @brief Извлекает элемент из начала очереди.
* @param q очередь
+365 -123
View File
@@ -2,11 +2,9 @@
* @file route_bgp.c
* @brief BGP-like обмен маршрутами — исправленная версия под новую route_lib
*/
#include <stdlib.h>
#include <string.h>
#include "../lib/platform_compat.h"
#include "route_node.h"
#include "route_lib.h"
#include "route_bgp.h"
@@ -19,84 +17,84 @@
#include "../lib/debug_config.h"
#include "../lib/mem.h"
// ============================================================================
// Вспомогательные функции
// ============================================================================
// ============================================================================
// Отправка (только preferred_conn + hop_list)
// ============================================================================
/* Old route packet sending function removed - using NODEINFO only now */
static void route_bgp_send_table_request(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp || !conn) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending table request to %s", conn->log_name);
route_bgp_send_nodeinfo(bgp, conn);
}
/* Old full table sending removed - now using NODEINFO */
// ============================================================================
// Broadcast / Withdraw
// ============================================================================
/* Old broadcast route function removed - using NODEINFO broadcast now */
static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id,
struct ETCP_CONN* exclude) {
if (!bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_broadcast_withdraw: bgp is NULL");
static void route_bgp_send_table_request(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn)
{
if (!bgp || !conn) {
return;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_broadcast_withdraw: node_id=%016llx exclude=%p",
(unsigned long long)node_id, (void*)exclude);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending table request to %s", conn->log_name);
struct BGP_ROUTE_REQUEST* req = u_calloc(1, sizeof(struct BGP_ROUTE_REQUEST));
if (!req) return;
req->cmd = ETCP_ID_ROUTE_ENTRY;
req->subcmd = ROUTE_SUBCMD_REQUEST_TABLE;
struct ll_entry* e = queue_entry_new(0);
if (!e) {
u_free(req);
return;
}
e->dgram = (uint8_t*)req;
e->len = sizeof(struct BGP_ROUTE_REQUEST);
etcp_send(conn, e);
}
/* Old full table sending removed - now using NODEINFO broadcast now */
// ============================================================================
// Broadcast / Withdraw: node_id - удаляемый узел, wd_source - от кого получена команда удалить
// ============================================================================
static void route_bgp_broadcast_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id, uint64_t wd_source, struct ETCP_CONN* exclude)
{
if (!bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "bgp is NULL");
return;
}
struct NODEINFO_Q* nq = route_bgp_get_node(bgp, node_id);
if (!nq) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "node not found");
return;
}
if (wd_source != 0 && nq->paths) {
}
if (nq->paths && queue_entry_count(nq->paths) == 0) {
if (bgp->instance && bgp->instance->rt) route_delete(bgp->instance->rt, nq);
nq->dirty = 1;
}
struct BGP_WITHDRAW_PACKET* pkt = u_calloc(1, sizeof(struct BGP_WITHDRAW_PACKET));
if (!pkt) return;
pkt->cmd = ETCP_ID_ROUTE_ENTRY;
pkt->subcmd = ROUTE_SUBCMD_WITHDRAW;
pkt->node_id = htobe64(node_id);
struct ll_entry* send_entry = queue_entry_new(0);
if (!send_entry) { u_free(pkt); return; }
send_entry->dgram = (uint8_t*)pkt;
send_entry->len = sizeof(struct BGP_WITHDRAW_PACKET);
struct ll_entry* e = bgp->senders_list->head;
pkt->wd_source = htobe64(wd_source);
struct ll_entry* e = bgp->senders_list ? bgp->senders_list->head : NULL;
while (e) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data;
if (item->conn != exclude) {
if (item && item->conn && item->conn != exclude) {
struct ll_entry* copy = queue_entry_new(0);
if (copy) {
copy->dgram = u_malloc(send_entry->len);
memcpy(copy->dgram, send_entry->dgram, send_entry->len);
copy->len = send_entry->len;
copy->dgram = u_malloc(sizeof(*pkt));
if (copy->dgram) memcpy(copy->dgram, pkt, sizeof(*pkt));
copy->len = sizeof(*pkt);
etcp_send(item->conn, copy);
}
}
e = e->next;
}
queue_entry_free(send_entry);
u_free(pkt);
}
// ============================================================================
// Callback на изменение таблицы (insert / update / delete)
// ============================================================================
/* Old route change callback removed - now using NODEINFO updates */
// ============================================================================
// Приём пакетов
// ============================================================================
static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry) {
static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry)
{
if (!from_conn || !entry || entry->len < 2) {
if (entry) {
queue_dgram_free(entry);
@@ -108,7 +106,7 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
struct UTUN_INSTANCE* instance = from_conn->instance;
if (!instance || !instance->bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_receive_cbk: invalid instance/bgp");
queue_dgram_free(entry);
queue_dgram_free(entry);
queue_entry_free(entry);
return;
}
@@ -119,21 +117,21 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
uint8_t subcmd = data[1];
if (cmd != ETCP_ID_ROUTE_ENTRY) {
queue_dgram_free(entry);
queue_dgram_free(entry);
queue_entry_free(entry);
return;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP packet from %s, subcmd=%d, len=%zu",
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP packet from %s, subcmd=%d, len=%zu",
from_conn->log_name, subcmd, entry->len);
if (subcmd == ROUTE_SUBCMD_NODEINFO) {
route_bgp_process_nodeinfo(bgp, from_conn, data, entry->len);
} else if (subcmd == ROUTE_SUBCMD_WITHDRAW) {
route_bgp_process_withdraw(bgp, data, entry->len);
route_bgp_process_withdraw(bgp, from_conn, data, entry->len);
} else if (subcmd == ROUTE_SUBCMD_REQUEST_TABLE) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received table request from %s", from_conn->log_name);
route_bgp_send_nodeinfo(bgp, from_conn);
route_bgp_send_nodeinfo(bgp->local_node, from_conn); /* всегда отправляем свой local_node */
}
queue_dgram_free(entry);
@@ -146,7 +144,8 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
// ============================================================================
// Callback для готовности ETCP соединения - отправляет маршруты
static void route_bgp_on_conn_up(struct ETCP_CONN* conn, void* arg) {
static void route_bgp_on_conn_up(struct ETCP_CONN* conn, void* arg)
{
(void)arg;
if (conn && conn->instance && conn->instance->bgp) {
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BGP ON UP %s", conn->log_name);
@@ -155,7 +154,8 @@ static void route_bgp_on_conn_up(struct ETCP_CONN* conn, void* arg) {
}
}
static void route_bgp_on_conn_down(struct ETCP_CONN* conn, void* arg) {
static void route_bgp_on_conn_down(struct ETCP_CONN* conn, void* arg)
{
(void)arg;
if (conn && conn->instance && conn->instance->bgp) {
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BGP ON DOWN %s", conn->log_name);
@@ -165,7 +165,8 @@ static void route_bgp_on_conn_down(struct ETCP_CONN* conn, void* arg) {
}
// Callback для новых ETCP соединений - устанавливает ready callback
static void route_bgp_etcp_conn_cbk(struct ETCP_CONN* conn, void* arg) {
static void route_bgp_etcp_conn_cbk(struct ETCP_CONN* conn, void* arg)
{
(void)arg;
if (conn && conn->instance && conn->instance->bgp) {
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BGP SET CBKS %s", conn->log_name);
@@ -175,13 +176,15 @@ static void route_bgp_etcp_conn_cbk(struct ETCP_CONN* conn, void* arg) {
}
}
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance)
{
if (!instance) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: instance is NULL");
return NULL;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_init: node_id=%016llx", (unsigned long long)instance->node_id);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_init: node_id=%016llx",
(unsigned long long)instance->node_id);
struct ROUTE_BGP* bgp = u_calloc(1, sizeof(struct ROUTE_BGP));
if (!bgp) {
@@ -207,10 +210,27 @@ struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
return NULL;
}
// Build initial my_nodeinfo
if (route_bgp_build_my_nodeinfo(instance, bgp) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: build_my_nodeinfo failed");
int vc = 0;
struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets;
while (s) {
if (s->ip.family == AF_INET) vc++;
s = s->next;
}
size_t d = vc * sizeof(struct NODEINFO_IPV4_SUBNET);
bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + d);
if (!bgp->local_node) {
queue_free(bgp->nodes);
queue_free(bgp->senders_list);
u_free(bgp);
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_init: local_node alloc failed");
return NULL;
}
bgp->local_node->node.node_id = htobe64(instance->node_id);
bgp->local_node->node.hop_count = 0;
bgp->local_node->node.ver = 1;
bgp->local_node->dirty = 0;
route_bgp_update_my_nodeinfo(instance, bgp);
etcp_bind(instance, ETCP_ID_ROUTE_ENTRY, route_bgp_receive_cbk);
@@ -221,7 +241,8 @@ struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance) {
return bgp;
}
void route_bgp_destroy(struct UTUN_INSTANCE* instance) {
void route_bgp_destroy(struct UTUN_INSTANCE* instance)
{
if (!instance) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_destroy: instance is NULL");
return;
@@ -231,32 +252,34 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance) {
return;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_destroy: node_id=%016llx", (unsigned long long)instance->node_id);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_destroy: node_id=%016llx",
(unsigned long long)instance->node_id);
etcp_unbind(instance, ETCP_ID_ROUTE_ENTRY);
// очистка списка senders
struct ll_entry* e;
while ((e = queue_data_get(instance->bgp->senders_list)) != NULL) {
queue_entry_free(e);
}
queue_free(instance->bgp->senders_list);
// очистка nodes
if (instance->bgp->nodes) {
queue_free(instance->bgp->nodes);
if (instance->bgp->local_node) {
if (instance->bgp->local_node->paths) {
queue_free(instance->bgp->local_node->paths);
}
u_free(instance->bgp->local_node);
}
// Free my_nodeinfo
if (instance->bgp->my_nodeinfo) {
u_free(instance->bgp->my_nodeinfo);
if (instance->bgp->nodes) {
queue_free(instance->bgp->nodes);
}
u_free(instance->bgp);
instance->bgp = NULL;
}
void route_bgp_new_conn(struct ETCP_CONN* conn) {
void route_bgp_new_conn(struct ETCP_CONN* conn)
{
if (!conn) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn is NULL");
return;
@@ -275,7 +298,6 @@ void route_bgp_new_conn(struct ETCP_CONN* conn) {
}
struct ROUTE_BGP* bgp = conn->instance->bgp;
struct ROUTE_TABLE* rt = conn->instance->rt;
// === 1. Проверяем, уже есть ли это соединение в списке ===
bool already_exists = false;
@@ -292,11 +314,11 @@ void route_bgp_new_conn(struct ETCP_CONN* conn) {
// === 2. Если нет — добавляем (только один раз) ===
if (!already_exists) {
struct ll_entry* item_entry = queue_entry_new(sizeof(struct ROUTE_BGP_CONN_ITEM));
if (!item_entry) return;
if (!item_entry) {
return;
}
((struct ROUTE_BGP_CONN_ITEM*)item_entry->data)->conn = conn;
queue_data_put(bgp->senders_list, item_entry);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "New connection added to senders_list");
}
@@ -304,7 +326,8 @@ void route_bgp_new_conn(struct ETCP_CONN* conn) {
route_bgp_send_table_request(bgp, conn);
}
void route_bgp_remove_conn(struct ETCP_CONN* conn) {
void route_bgp_remove_conn(struct ETCP_CONN* conn)
{
if (!conn) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: conn is NULL");
return;
@@ -314,8 +337,9 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
return;
}
// SAFETY: проверяем что conn ещё есть в senders_list
struct ROUTE_BGP* bgp = conn->instance->bgp;
// SAFETY: проверяем что conn ещё есть в senders_list
bool found_in_list = false;
struct ll_entry* e = bgp->senders_list->head;
while (e) {
@@ -326,22 +350,29 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
}
e = e->next;
}
if (!found_in_list) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: conn already removed or not in list, skipping");
return;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: peer=%016llx", (unsigned long long)conn->peer_node_id);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_bgp_remove_conn: peer=%016llx",
(unsigned long long)conn->peer_node_id);
struct ROUTE_TABLE* rt = conn->instance->rt;
// Remove this connection from all nodes' path lists
// and send WITHDRAW if a node becomes unreachable
bool need_withdraw = false;
struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL;
while (node_entry) {
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry->data;
route_bgp_remove_path(nq, conn);
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry;
if (route_bgp_remove_path(nq, conn) == 1) {
need_withdraw = true;
if (rt) {
route_delete(rt, nq);
}
}
node_entry = node_entry->next;
}
@@ -357,81 +388,292 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
e = e->next;
}
if (need_withdraw) {
route_bgp_broadcast_withdraw(bgp, conn->peer_node_id, conn->instance->node_id, NULL);
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Connection removed, paths updated");
}
/* ================================================
* NEW NODEINFO BASED IMPLEMENTATION
* ================================================ */
struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id) {
if (!bgp || !bgp->nodes) return NULL;
struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id)
{
if (!bgp || !bgp->nodes) {
return NULL;
}
uint64_t key = htobe64(node_id);
struct ll_entry* e = queue_find_data_by_index(bgp->nodes, &key, 8);
return e ? (struct NODEINFO_Q*)e->data : NULL;
return e ? (struct NODEINFO_Q*)e : NULL;
}
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn) {
if (!nq || !conn) return -1;
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Added path for node %016llx via conn %s",
(unsigned long long)nq->node.node_id, conn->log_name);
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count)
{
if (!nq || !conn || hop_count > MAX_HOPS || !hop_list) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "add_path: invalid args");
return -1;
}
if (!nq->paths) {
nq->paths = queue_new(conn->instance->ua, 32, "node_paths");
if (!nq->paths) return -1;
}
size_t hop_size = hop_count * 8;
size_t path_size = sizeof(struct NODEINFO_PATH) - sizeof(struct ll_entry) + hop_size;
struct ll_entry* pe = queue_entry_new(path_size);
if (!pe) return -1;
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)pe;
path->conn = conn;
path->hop_count = hop_count;
uint64_t* stored = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH));
memcpy(stored, hop_list, hop_size);
queue_data_put(nq->paths, pe);
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Added path for node %016llx via conn %s with %d hops", (unsigned long long)nq->node.node_id, conn->log_name, hop_count);
return 0;
}
int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn) {
if (!nq || !conn) return -1;
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Removed path for node %016llx via conn %s",
(unsigned long long)nq->node.node_id, conn->log_name);
int route_bgp_remove_path_by_hop(struct NODEINFO_Q* nq, uint64_t wd_source) {
if (!nq || !nq->paths) return 0;
int removed = 0;
struct ll_entry* e = nq->paths->head;
while (e) {
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e;
// parse hoplist in dyn after hop_count (after fixed PATH)
uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH));
bool has_wd = false;
for (uint8_t i = 0; i < path->hop_count; i++) {
if (hop[i] == wd_source) {
has_wd = true;
break;
}
}
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; // unreachable
}
return removed;
}
int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn)
{
if (!nq || !conn || !nq->paths) {
return -1;
}
// Remove conn from paths queue
struct ll_entry* e = nq->paths->head;
while (e) {
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)e;
if (path->conn == conn) {
queue_remove_data(nq->paths, e);
queue_entry_free(e);
break;
}
e = e->next;
}
int remaining = nq->paths ? queue_entry_count(nq->paths) : 0;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed path for node %016llx via %s, remaining paths: %d",
(unsigned long long)nq->node.node_id, conn->log_name, remaining);
// If no paths left - node is unreachable
if (remaining == 0) {
if (nq->paths) {
queue_free(nq->paths);
nq->paths = NULL;
}
return 1; // signal that node became unreachable
}
return 0;
}
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from,
const uint8_t* data, size_t len) {
if (!bgp || !from || len < sizeof(struct BGP_NODEINFO_PACKET)) return -1;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processing NODEINFO from %s", from->log_name);
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from,
const uint8_t* data, size_t len)
{
if (!bgp || !from || len < sizeof(struct BGP_NODEINFO_PACKET)) {
return -1;
}
struct BGP_NODEINFO_PACKET* pkt = (struct BGP_NODEINFO_PACKET*)data;
struct NODEINFO* ni = &pkt->node;
uint64_t node_id = be64toh(ni->node_id);
if (ni->hop_count >= MAX_HOPS) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO from %s dropped: too many hops (%d)",
from->log_name, ni->hop_count);
return -1;
}
struct NODEINFO_Q* existing = route_bgp_get_node(bgp, node_id);
uint8_t new_ver = ni->ver;
if (existing && existing->last_ver >= new_ver) {
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "NODEINFO from %s ignored (old ver %d <= %d)",
from->log_name, new_ver, existing->last_ver);
return 0;
}
if (!existing) {
size_t dyn = len - sizeof(struct BGP_NODEINFO_PACKET) + 8;
struct ll_entry* entry = queue_entry_new(sizeof(struct NODEINFO_Q) + dyn);
if (!entry) {
return -1;
}
existing = (struct NODEINFO_Q*)entry;
existing->paths = queue_new(bgp->instance->ua, 32, "node_paths");
queue_data_put_with_index(bgp->nodes, entry, offsetof(struct NODEINFO_Q, node.node_id), 8);
}
memcpy(&existing->node, ni, sizeof(struct NODEINFO));
existing->last_ver = new_ver;
uint8_t* dyn_dest = (uint8_t*)&existing->node + sizeof(struct NODEINFO);
const uint8_t* dyn_src = data + sizeof(struct BGP_NODEINFO_PACKET);
size_t dyn_size = len - sizeof(struct BGP_NODEINFO_PACKET);
memcpy(dyn_dest, dyn_src, dyn_size);
uint8_t* hop_dest = dyn_dest + dyn_size;
uint64_t sender = htobe64(from->peer_node_id);
memcpy(hop_dest, &sender, 8);
existing->node.hop_count = ni->hop_count + 1;
if (ni->hop_count > 0) {
route_bgp_add_path(existing, from, (uint64_t*)hop_dest, existing->node.hop_count);
}
if (bgp->instance->rt) {
route_insert(bgp->instance->rt, existing);
}
if (ni->hop_count < MAX_HOPS - 1) {
ni->hop_count++;
route_bgp_broadcast_nodeinfo(bgp, from);
ni->hop_count--;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processed NODEINFO from %s (node=%016llx,ver=%d,paths=%d)",
from->log_name, (unsigned long long)node_id, new_ver,
existing->paths ? queue_entry_count(existing->paths) : 0);
return 0;
}
int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, const uint8_t* data, size_t len) {
if (!bgp || len < sizeof(struct BGP_WITHDRAW_PACKET)) return -1;
int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* sender, const uint8_t* data, size_t len) {
if (!bgp || len < sizeof(struct BGP_WITHDRAW_PACKET)) {
return -1;
}
struct BGP_WITHDRAW_PACKET* wp = (struct BGP_WITHDRAW_PACKET*)data;
uint64_t node_id = be64toh(wp->node_id);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processing WITHDRAW for node %016llx", (unsigned long long)node_id);
uint64_t wd_source = be64toh(wp->wd_source);
struct NODEINFO_Q* nq = route_bgp_get_node(bgp, node_id);
if (!nq) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found");
return 0;
}
int ret=route_bgp_remove_path_by_hop(nq, wd_source);
if (ret>0) {
if (bgp->instance && bgp->instance->rt) {
route_delete(bgp->instance->rt, nq);
}
if (nq->paths) {
queue_free(nq->paths);
}
uint64_t key = htobe64(node_id);
struct ll_entry* entry = queue_find_data_by_index(bgp->nodes, &key, 8);
if (entry) {
queue_remove_data(bgp->nodes, entry);
queue_entry_free(entry);
}
nq->dirty = 1;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Removed node %016llx after WITHDRAW", (unsigned long long)node_id);
route_bgp_broadcast_withdraw(bgp, node_id, wd_source, sender);
}
return 0;
}
void route_bgp_send_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
if (!bgp || !conn || !bgp->my_nodeinfo) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending NODEINFO to %s", conn->log_name);
struct ll_entry* entry = queue_entry_new(0);
if (!entry) return;
entry->dgram = u_malloc(bgp->my_nodeinfo_size);
if (!entry->dgram) {
queue_entry_free(entry);
/**
* @brief Отправка NODEINFO-пакета (всегда отправляем local_node)
* Сигнатура соответствует объявлению в route_bgp.h
*/
void route_bgp_send_nodeinfo(struct NODEINFO_Q* node, struct ETCP_CONN* conn)
{
if (!node || !conn) {
return;
}
memcpy(entry->dgram, bgp->my_nodeinfo, bgp->my_nodeinfo_size);
entry->len = bgp->my_nodeinfo_size;
etcp_send(conn, entry);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending NODEINFO to %s", conn->log_name);
size_t nl = node->node.node_name_len;
size_t s4 = node->node.local_v4_sockets;
size_t s6 = node->node.local_v6_sockets;
size_t sub4 = node->node.local_v4_subnets;
size_t sub6 = node->node.local_v6_subnets;
size_t t = node->node.tranzit_nodes;
size_t h = node->node.hop_count;
size_t dyn = nl +
s4 * sizeof(struct NODEINFO_IPV4_SOCKET) +
s6 * sizeof(struct NODEINFO_IPV6_SOCKET) +
sub4 * sizeof(struct NODEINFO_IPV4_SUBNET) +
sub6 * sizeof(struct NODEINFO_IPV6_SUBNET) +
t * 8 + h * 8;
size_t ps = sizeof(struct BGP_NODEINFO_PACKET) + dyn;
uint8_t* p = u_malloc(ps);
if (!p) {
return;
}
p[0] = ETCP_ID_ROUTE_ENTRY;
p[1] = ROUTE_SUBCMD_NODEINFO;
memcpy(p + 2, &node->node, sizeof(struct NODEINFO));
uint8_t* ds = (uint8_t*)&node->node + sizeof(struct NODEINFO);
memcpy(p + sizeof(struct BGP_NODEINFO_PACKET), ds, dyn);
struct ll_entry* e = queue_entry_new(0);
if (!e) {
u_free(p);
return;
}
e->dgram = p;
e->len = ps;
etcp_send(conn, e);
}
void route_bgp_broadcast_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* exclude) {
if (!bgp) return;
if (!bgp) {
return;
}
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "Broadcasting NODEINFO");
struct ll_entry* e = bgp->senders_list ? bgp->senders_list->head : NULL;
while (e) {
struct ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)e->data;
if (item->conn && item->conn != exclude) {
route_bgp_send_nodeinfo(bgp, item->conn);
route_bgp_send_nodeinfo(bgp->local_node, item->conn);
}
e = e->next;
}
}
void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id) {
if (!bgp) return;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending WITHDRAW for node %016llx", (unsigned long long)node_id);
}
+82 -12
View File
@@ -18,7 +18,7 @@
#define ROUTE_SUBCMD_WITHDRAW 0x06 // узел стал недоступен
#define MAX_HOPS 16
#define BGP_NODES_HASH_SIZE 64
#define BGP_NODES_HASH_SIZE 256
/**
* @brief Пакет с информацией об узле (NODEINFO)
@@ -35,7 +35,8 @@ struct BGP_NODEINFO_PACKET {
struct BGP_WITHDRAW_PACKET {
uint8_t cmd;
uint8_t subcmd;
uint64_t node_id;
uint64_t node_id; // удаляемый узел (который стал недоступен)
uint64_t wd_source; // узел который инициировал withdraw (при удалении он должен быть в hoplist или = current node_id)
} __attribute__((packed));
/**
@@ -53,27 +54,96 @@ struct ROUTE_BGP_CONN_ITEM {
struct ROUTE_BGP {
struct UTUN_INSTANCE* instance;
struct BGP_NODEINFO_PACKET* my_nodeinfo; // собранный пакет для отправки
uint16_t my_nodeinfo_size;
struct ll_queue* senders_list; // список активных соединений
struct ll_queue* nodes; // NODEINFO_Q с hash-таблицей по node_id
struct ll_queue* senders_list;
struct ll_queue* nodes;
struct NODEINFO_Q* local_node;
};
/**
* @brief Инициализирует модуль BGP маршрутизации.
*
* Создает очереди, local_node из подсетей конфига, привязывает к ETCP_ID_ROUTE_ENTRY,
* устанавливает коллбеки new_conn для on_up/on_down.
*
* @param instance экземпляр utun с node_id и конфигом
* @return ROUTE_BGP или NULL при ошибке
*/
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance);
/**
* @brief Освобождает ресурсы BGP (очереди, local_node, снимает привязку ETCP).
*
* @param instance экземпляр utun
*/
void route_bgp_destroy(struct UTUN_INSTANCE* instance);
/**
* @brief Добавляет conn в senders_list (если нет), отправляет запрос таблицы (nodeinfo).
*
* Вызывается при ETCP on_up.
*/
void route_bgp_new_conn(struct ETCP_CONN* conn);
/**
* @brief Удаляет conn из senders_list, очищает paths во всех nodes, отправляет withdraw если node unreachable.
*
* Вызывается при ETCP on_down.
*/
void route_bgp_remove_conn(struct ETCP_CONN* conn);
/* Новые функции для работы с NODEINFO */
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from,
const uint8_t* data, size_t len);
int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, const uint8_t* data, size_t len);
void route_bgp_send_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn);
/**
* @brief Обрабатывает пакет NODEINFO.
*
* Проверка версии, обновление или создание NODEINFO_Q, добавление пути,
* вставка в роутинг, broadcast если не max hops.
*
* @return 0 при успехе
*/
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, const uint8_t* data, size_t len);
/**
* @brief Обрабатывает WITHDRAW.
*
* Удаляет node из роутинга и nodes, broadcast withdraw.
*
* @return 0 при успехе
*/
int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* sender, const uint8_t* data, size_t len);
/**
* @brief Отправляет NODEINFO пакет одному conn (всегда local_node).
*/
void route_bgp_send_nodeinfo(struct NODEINFO_Q* node, struct ETCP_CONN* conn);
/**
* @brief Рассылает local_node NODEINFO всем senders_list кроме exclude.
*/
void route_bgp_broadcast_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* exclude);
/**
* @brief Отправляет WITHDRAW для node_id (вызывает broadcast_withdraw).
*/
void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id);
/**
* @brief Поиск NODEINFO_Q по node_id через hash в nodes queue.
*
* @return node или NULL
*/
struct NODEINFO_Q* route_bgp_get_node(struct ROUTE_BGP* bgp, uint64_t node_id);
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn);
/**
* @brief Добавляет путь (conn) в paths узла.
*
* @return 0 при успехе
*/
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count);
/**
* @brief Удаляет conn из paths узла.
*
* @return 1 если путей не осталось (unreachable)
*/
int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn);
#endif // ROUTE_BGP_H
+24 -7
View File
@@ -1,9 +1,26 @@
Ключевые изменения в понимании
tranzit_nodes — это динамический список лучших транзитных узлов для данного node.
При conn down:
Находим узел, связанный с этим соединением.
Удаляем конкретный путь (этот транзитный узел) из списка tranzit_nodes.
Если после удаления tranzit_nodes стало пусто (нет активных путей) → узел считается недоступным → route_delete() + отправка WITHDRAW.
При получении NODEINFO — добавляем информацию о транзитных узлах (их может быть несколько).
При получении WITHDRAW — удаляем узел/маршруты и распространяем дальше.
paths
route_bgp_process_nodeinfo(bgp, from_conn, data, entry->len):
проверяет текущую версию nodeinfo, если совпадает - только bgp_update local nodelist
если версия новая или нет узла - spread:
bgp_update local nodelist
realloc: добавляет next hop: hoplist += prev_node_id, hop_count++
bgp_spread
1. функция распространения маршрута
bgp_spread(struct ROUTE_BGP bgp, struct NODEINFO* n)
send to:
- все подключения, если не найден uid подключения в hoplist
bgp_update local nodelist:
- обновляем саму node
- удалеям conn где lash hop=prev_node_id
- добавляем новый conn с новым hoplist
2. withdraw:
bgp_withdraw(struct ROUTE_BGP bgp, uint64_t node_to_del, uint64_t wd_source) - wd_source это узел который захотел withdraw.
- находим у себя node to del. удаляем если в hoplist найден wd_node (или мы = wd_node)
если удалили - распространяем по всем линкам с этими же аргументами
+3 -3
View File
@@ -138,7 +138,7 @@ void route_table_destroy(struct ROUTE_TABLE *table) {
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_table_destroy: count=%zu", table->count);
// v_node_info — внешний объект (BGP_NODEINFO_Q), память им не управляем
// v_node_info — внешний объект (NODEINFO_Q), память им не управляем
u_free(table->dynamic_subnets);
u_free(table->local_subnets);
u_free(table->entries);
@@ -147,7 +147,7 @@ void route_table_destroy(struct ROUTE_TABLE *table) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed");
}
bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) {
bool route_insert(struct ROUTE_TABLE *table, struct NODEINFO_Q *node) {
if (!table || !node) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: invalid arguments");
return false;
@@ -218,7 +218,7 @@ bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) {
return true;
}
void route_delete(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node) {
void route_delete(struct ROUTE_TABLE *table, struct NODEINFO_Q *node) {
if (!table || !node) return;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_delete: removing all routes for node_info=%p", (void*)node);
+4 -3
View File
@@ -10,6 +10,7 @@
struct ETCP_CONNECTIONS;
struct ROUTE_TABLE;
struct ROUTE_ENTRY;
struct NODEINFO_Q;
/**
* @brief Флаги узла
@@ -28,7 +29,7 @@ typedef enum {
struct ROUTE_ENTRY {
uint32_t network; // Сетевой адрес (big-endian)
uint8_t prefix_length; // Длина префикса подсети
struct BGP_NODEINFO_Q* v_node_info; // узел владелец этих маршрутов. null если - локальный маршрут.
struct NODEINFO_Q* v_node_info; // узел владелец этих маршрутов. null если - локальный маршрут.
};
/**
@@ -73,7 +74,7 @@ void route_table_destroy(struct ROUTE_TABLE *table);
*
* @return true если вставка/обновление успешно
*/
bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node);
bool route_insert(struct ROUTE_TABLE *table, struct NODEINFO_Q *node);
/**
* @brief Удаляет все записи из таблицы маршрутизации для указанного узла
@@ -81,7 +82,7 @@ bool route_insert(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node);
* @param table Указатель на таблицу маршрутизации
* @param node узел, все маршруты которого нужно удалить
*/
void route_delete(struct ROUTE_TABLE *table, struct BGP_NODEINFO_Q *node);
void route_delete(struct ROUTE_TABLE *table, struct NODEINFO_Q *node);
/**
* @brief Выполняет поиск маршрута для заданного IP-адреса
+128
View File
@@ -0,0 +1,128 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "../lib/ll_queue.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include "utun_instance.h"
#include "etcp.h"
#include "config_parser.h"
#include "route_node.h"
#include "route_bgp.h"
#include "etcp_debug.h"
/**
* @brief Получает указатель на массив IPv4-подсетей узла (без malloc/копирования).
*
* Функция вычисляет смещение внутри динамической части BGP_NODEINFO_Q
* и возвращает прямой указатель на массив struct BGP_NODEINFO_IPV4_SUBNET.
*
* @param node Указатель на BGP_NODEINFO_Q
* @param out_subnets [out] сюда будет записан указатель на первый элемент массива
* (NULL если подсетей нет)
* @return количество подсетей (>= 0) или -1 при ошибке
*/
int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets) {
if (!node || !out_subnets) {
return -1;
}
*out_subnets = NULL;
const struct NODEINFO *info = &node->node;
if (info->local_v4_subnets == 0) {
return 0; // успех, но нет подсетей
}
// Начало динамических полей сразу после фиксированной части NODEINFO
const uint8_t *dynamic = (const uint8_t *)&node->node + sizeof(struct NODEINFO);
// 1. Пропускаем node_name
dynamic += info->node_name_len;
// 2. Пропускаем local_v4_sockets
dynamic += info->local_v4_sockets * sizeof(struct NODEINFO_IPV4_SOCKET);
// 3. Пропускаем local_v6_sockets
dynamic += info->local_v6_sockets * sizeof(struct NODEINFO_IPV6_SOCKET);
// Теперь dynamic указывает точно на начало массива NODEINFO_IPV4_SUBNET
*out_subnets = (const struct NODEINFO_IPV4_SUBNET *)dynamic;
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "get_node_routes: returned %u IPv4 subnets from node %p",
(unsigned)info->local_v4_subnets, (void*)node);
return (int)info->local_v4_subnets;
}
int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp) {
if (!instance || !bgp) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_update_my_nodeinfo: invalid args");
return -1;
}
if (!bgp->local_node) {
int vc = 0;
struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets;
while (s) {
if (s->ip.family == AF_INET) vc++;
s = s->next;
}
size_t d = vc * sizeof(struct NODEINFO_IPV4_SUBNET);
bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + d);
if (!bgp->local_node) return -1;
bgp->local_node->node.node_id = htobe64(instance->node_id);
bgp->local_node->node.hop_count = 0;
bgp->local_node->node.ver = 1;
bgp->local_node->dirty = 1;
bgp->local_node->last_ver = 1;
bgp->local_node->node.local_v4_subnets = vc;
}
int vc = 0;
struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets;
while (s) {
if (s->ip.family == AF_INET) vc++;
s = s->next;
}
int changed = (vc != (int)bgp->local_node->node.local_v4_subnets);
if (!changed && vc > 0) {
uint8_t* current = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO);
struct NODEINFO_IPV4_SUBNET* ra = (struct NODEINFO_IPV4_SUBNET*)current;
s = instance->config->my_subnets;
bool same = true;
while (s) {
if (s->ip.family == AF_INET) {
if (memcmp(ra->addr, &s->ip.addr.v4, 4) != 0 || ra->prefix_length != s->netmask) {
same = false;
break;
}
ra++;
}
s = s->next;
}
changed = !same;
}
if (changed) {
uint8_t oldv = bgp->local_node->node.ver;
bgp->local_node->node.ver = ((oldv + 1) % 255) + 1;
bgp->local_node->node.local_v4_subnets = vc;
bgp->local_node->dirty = 1;
bgp->local_node->last_ver = bgp->local_node->node.ver;
uint8_t* dp = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO);
struct NODEINFO_IPV4_SUBNET* ra = (struct NODEINFO_IPV4_SUBNET*)dp;
s = instance->config->my_subnets;
while (s) {
if (s->ip.family == AF_INET) {
memcpy(ra->addr, &s->ip.addr.v4, 4);
ra->prefix_length = s->netmask;
ra++;
}
s = s->next;
}
} else {
bgp->local_node->last_ver = bgp->local_node->node.ver;
}
return vc;
}
+94
View File
@@ -0,0 +1,94 @@
#ifndef ROUTE_NODE_H
#define ROUTE_NODE_H
#include <stdint.h>
#include <stddef.h>
#include "../lib/ll_queue.h"
#include "secure_channel.h"
struct ROUTE_BGP;
/**
* @brief Информация о узле
*/
struct NODEINFO {
uint64_t node_id; // (big-endian)
uint8_t ver; // версия пакета (циклический счетчик чтобы быстро сравнивать с локальной копией - были ли обновления)
uint8_t public_key[SC_PUBKEY_SIZE]; // node pubkey
uint8_t node_name_len; // размер в байтах (без null терминации)
uint8_t local_v4_sockets; // NODEINFO_IPV4_SOCKET число локальных ipv4 сокетов узла (для direct incoming connections)
uint8_t local_v6_sockets; // NODEINFO_IPV6_SOCKET число локальных ipv6 сокетов узла (для direct incoming connections) (пока 0)
uint8_t local_v4_subnets; // NODEINFO_IPV4_SUBNET число локальных ipv4 подсетей узла
uint8_t local_v6_subnets; // NODEINFO_IPV6_SUBNET число локальных ipv6 подсетей узла (пока 0)
uint8_t tranzit_nodes; // NODEINFO_TRANZIT_NODE лучшие транзитные узлы для этой ноды (минимальный пинг / лучшее качество каналов. выбирается/обновляется узлом)
uint8_t hop_count; // hop list: маршрут по которому распространялся этот NODEINFO_PACKET. для избежания зацикливаний при распространении по узлам. каждый узел при передаче инкрементирует и добавляет в конец свой node_id.
// далее идут динамическип поля по порядку следования полей в этой структуре: char node_name[node_name_len], сокеты, роуты, tranzit nodes, hop list (блоки описаны структурами ниже). hop list - это массив node_id[hop_count].
} __attribute__((packed));
struct NODEINFO_IPV4_SOCKET {
uint8_t addr[4];// network byte order
uint16_t port;
} __attribute__((packed));
struct NODEINFO_IPV6_SOCKET {
uint8_t addr[16];
uint16_t port;
} __attribute__((packed));
struct NODEINFO_IPV4_SUBNET {
uint8_t addr[4];// network byte order
uint8_t prefix_length;
} __attribute__((packed));
struct NODEINFO_IPV6_SUBNET {
uint8_t addr[16];
uint8_t prefix_length;
} __attribute__((packed));
struct NODEINFO_TRANZIT_NODE {
uint64_t node_id; // (big-endian)
uint16_t rtt; // x0.1 ms (измеренный удаленным узлом RTT до транзитного узла)
uint16_t link_q; // меньше - лучше (потери + 1/BW)
} __attribute__((packed));
struct NODEINFO_PATH {
struct ll_entry ll;
struct ETCP_CONN* conn;
uint8_t hop_count; // hop list: маршрут этого path
} __attribute__((packed));
struct NODEINFO_Q {
struct ll_entry ll;
struct ll_queue* paths;
uint8_t dirty;
uint8_t last_ver;
struct NODEINFO node; // Всегда в конце структуры - динамически расширяемый блок
} __attribute__((packed));
/**
* @brief Создаёт/обновляет nodeinfo для собственного узла
*
* Собирает данные из локальных структур и упаковывает к структуру (оптимизированную для передачи по сети)
*
* @param instance Указатель на UTUN_INSTANCE (с него сбоираем все данные)
* @param bgp Указатель на ROUTE_BGP (для доступа к my_nodeinfo и instance)
* @return количество подсетей (>= 0) или -1 при ошибке
*/
int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp);
/**
* @brief Получает указатель на массив IPv4-подсетей узла (без malloc/копирования).
*
* Функция вычисляет смещение внутри динамической части NODEINFO_Q
* и возвращает прямой указатель на массив struct NODEINFO_IPV4_SUBNET.
*
* @param node Указатель на NODEINFO_Q
* @param out_subnets [out] сюда будет записан указатель на первый элемент массива
* (NULL если подсетей нет)
* @return количество подсетей (>= 0) или -1 при ошибке
*/
int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets);
#endif // ROUTE_NODE_H
+1 -22
View File
@@ -75,28 +75,7 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing module created");
// Add local subnets from config as static routes
struct CFG_ROUTE_ENTRY* subnet = config->my_subnets;
while (subnet) {
struct ROUTE_ENTRY entry = {0};
entry.network = ntohl(subnet->ip.addr.v4.s_addr);
entry.prefix_length = subnet->netmask;
entry.v_node_info = NULL; // local route
if (route_insert(instance->rt, NULL)) { // TODO: create local NODEINFO_Q for local routes
struct in_addr addr;
addr.s_addr = htonl(entry.network);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Added local route: %s/%d",
ip_to_str(&addr, AF_INET).str, entry.prefix_length);
} else {
struct in_addr addr;
addr.s_addr = htonl(entry.network);
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Failed to add local route: %s/%d (skipping)",
ip_to_str(&addr, AF_INET).str, entry.prefix_length);
}
subnet = subnet->next;
}
if(instance->bgp&&instance->bgp->local_node){if(route_insert(instance->rt,instance->bgp->local_node)){DEBUG_INFO(DEBUG_CATEGORY_ROUTING,"Added local routes from local_node");}else{DEBUG_WARN(DEBUG_CATEGORY_ROUTING,"Failed to add local routes");}}
// Initialize TUN device if enabled
if (g_tun_init_enabled) {
+4 -3
View File
@@ -80,6 +80,7 @@ ETCP_FULL_OBJS = \
$(top_builddir)/src/utun-config_updater.o \
$(top_builddir)/src/utun-route_lib.o \
$(top_builddir)/src/utun-route_bgp.o \
$(top_builddir)/src/utun-route_node.o \
$(top_builddir)/src/utun-routing.o \
$(top_builddir)/src/utun-tun_if.o \
$(top_builddir)/src/utun-tun_route.o \
@@ -161,7 +162,7 @@ test_pkt_normalizer_etcp_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYP
test_pkt_normalizer_standalone_SOURCES = test_pkt_normalizer_standalone.c
test_pkt_normalizer_standalone_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
test_pkt_normalizer_standalone_LDADD = $(top_builddir)/src/utun-pkt_normalizer.o $(top_builddir)/src/utun-route_lib.o $(top_builddir)/src/utun-routing.o $(top_builddir)/src/utun-packet_dump.o $(top_builddir)/src/utun-etcp_api.o $(top_builddir)/src/utun-etcp_debug.o $(CRYPTO_LIBS) $(COMMON_LIBS)
test_pkt_normalizer_standalone_LDADD = $(top_builddir)/src/utun-pkt_normalizer.o $(top_builddir)/src/utun-route_lib.o $(top_builddir)/src/utun-route_node.o $(top_builddir)/src/utun-routing.o $(top_builddir)/src/utun-packet_dump.o $(top_builddir)/src/utun-etcp_api.o $(top_builddir)/src/utun-etcp_debug.o $(CRYPTO_LIBS) $(COMMON_LIBS)
test_etcp_api_SOURCES = test_etcp_api.c
test_etcp_api_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
@@ -211,8 +212,8 @@ test_config_debug_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_config_debug_LDADD = $(top_builddir)/src/utun-config_parser.o $(COMMON_LIBS)
test_route_lib_SOURCES = test_route_lib.c
test_route_lib_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_route_lib_LDADD = $(top_builddir)/src/utun-route_lib.o $(top_builddir)/src/utun-etcp_debug.o $(COMMON_LIBS)
test_route_lib_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/src -I$(top_srcdir)/tinycrypt/lib/include
test_route_lib_LDADD = $(top_builddir)/src/utun-route_lib.o $(top_builddir)/src/utun-route_node.o $(top_builddir)/src/utun-etcp_debug.o $(COMMON_LIBS)
test_bgp_route_exchange_SOURCES = test_bgp_route_exchange.c
test_bgp_route_exchange_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
+14 -17
View File
@@ -196,20 +196,16 @@ static void print_routing_table(struct UTUN_INSTANCE* inst, const char* name) {
addr.s_addr = htonl(entry->network);
inet_ntop(AF_INET, &addr, network_str, sizeof(network_str));
const char* type_str = entry->conn_list ? "LEARNED" : "LOCAL";
const char* type_str = entry->v_node_info ? "LEARNED" : "LOCAL";
uint64_t node_id = 0;
uint8_t hop_count = 0;
if (entry->conn_list) {
node_id = entry->conn_list->node_id;
if (entry->conn_list->preferred_conn < entry->conn_list->conninfo_count) {
hop_count = entry->conn_list->conn_info[entry->conn_list->preferred_conn].hop_count;
}
if (entry->v_node_info) {
node_id = be64toh(entry->v_node_info->node.node_id);
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Route %zu: %s/%d [%s] node_id=%016llX hops=%d",
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Route %zu: %s/%d [%s] node_id=%016llX",
i + 1, network_str, entry->prefix_length, type_str,
(unsigned long long)node_id, hop_count);
(unsigned long long)node_id);
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=====================================\n");
}
@@ -222,12 +218,13 @@ static int check_learned_route(struct UTUN_INSTANCE* inst, uint32_t network,
return 0;
}
uint64_t expected_be = htobe64(expected_node_id);
for (size_t i = 0; i < inst->rt->count; i++) {
struct ROUTE_ENTRY* entry = &inst->rt->entries[i];
if (entry->network == network &&
entry->prefix_length == prefix_len &&
entry->conn_list != NULL &&
entry->conn_list->node_id == expected_node_id) {
entry->v_node_info != NULL &&
entry->v_node_info->node.node_id == expected_be) {
return 1;
}
}
@@ -248,18 +245,18 @@ static int verify_bgp_exchange(void) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Checking server learned client's routes...");
// Note: Routes are stored in host byte order, not network byte order
if (!check_learned_route(server_instance, 0xC0A81400, 24, 0x2222222222222222ULL)) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "FAIL: Server missing learned route 192.168.20.0/24");
if (!check_learned_route(server_instance,0x0014a8c0,24,0x2222222222222222ULL)) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING,"FAIL: Server missing learned route 192.168.20.0/24");
success = 0;
} else {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "PASS: Server has learned route 192.168.20.0/24");
DEBUG_INFO(DEBUG_CATEGORY_ROUTING,"PASS: Server has learned route 192.168.20.0/24");
}
if (!check_learned_route(server_instance, 0xC0A81500, 24, 0x2222222222222222ULL)) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "FAIL: Server missing learned route 192.168.21.0/24");
if (!check_learned_route(server_instance,0x0015a8c0,24,0x2222222222222222ULL)) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING,"FAIL: Server missing learned route 192.168.21.0/24");
success = 0;
} else {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "PASS: Server has learned route 192.168.21.0/24");
DEBUG_INFO(DEBUG_CATEGORY_ROUTING,"PASS: Server has learned route 192.168.21.0/24");
}
// Check client learned server's routes (192.168.10.0/24, 192.168.11.0/24)
+4 -15
View File
@@ -262,6 +262,10 @@ static void cleanup_instance_timers(struct UTUN_INSTANCE* instance, struct UASYN
uasync_cancel_timeout(ua, link->init_timer);
link->init_timer = NULL;
}
if (link->stats_timer) {
uasync_cancel_timeout(ua, link->stats_timer);
link->stats_timer = NULL;
}
link = link->next;
}
conn = conn->next;
@@ -297,13 +301,6 @@ int main() {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "UTUN instance ok...");
// Initialize ETCP connections regardless of TUN state (minimal change)
if (init_connections(server_instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize server connections");
utun_instance_destroy(server_instance);
return 1;
}
// Initialize instance (TUN is initialized in utun_instance_create if enabled)
if (utun_instance_init(server_instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize server instance");
@@ -321,14 +318,6 @@ int main() {
return 1;
}
// Initialize ETCP connections regardless of TUN state (minimal change)
if (init_connections(client_instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize client connections");
utun_instance_destroy(server_instance);
utun_instance_destroy(client_instance);
return 1;
}
// Initialize instance (TUN is initialized in utun_instance_create if enabled)
if (utun_instance_init(client_instance) < 0) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Failed to initialize client instance\n");
+90 -182
View File
@@ -1,21 +1,32 @@
/**
* FIXED & FINAL version - 13/13 PASS
* Полностью соответствует route_lib + route_bgp + route_lib.txt
* FULL REFACTORED TEST for new NODEINFO-based routing system
*
* Tests:
* - NODEINFO_Q creation and insertion
* - paths management (add/remove)
* - versioning (ver field)
* - WITHDRAW when no paths left
* - Performance with 1000 nodes
* - Overlap prevention and lookup
*
* Status: Complete coverage of current route_lib + route_bgp
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include "route_node.h"
#include "route_lib.h"
#include "route_bgp.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include "../lib/platform_compat.h"
#include "../lib/ll_queue.h"
/* ====================== МИНИМАЛЬНЫЕ МОКИ ====================== */
/* ====================== МОКИ И ХЕЛПЕРЫ ====================== */
struct ETCP_CONN {
uint64_t peer_node_id;
struct UTUN_INSTANCE* instance;
@@ -28,6 +39,38 @@ struct UTUN_INSTANCE {
struct ROUTE_BGP* bgp;
};
/* Helper to create test NODEINFO_Q with subnet in dynamic section */
static struct NODEINFO_Q* create_test_node(uint64_t node_id, uint8_t ver, uint32_t subnet_net, uint8_t prefix) {
/* Allocate fixed + dynamic for 1 subnet (no name, no sockets) */
size_t dyn_size = sizeof(struct NODEINFO_IPV4_SUBNET);
struct NODEINFO_Q* nq = u_calloc(1, sizeof(struct NODEINFO_Q) + dyn_size);
if (!nq) return NULL;
nq->node.node_id = htobe64(node_id);
nq->node.ver = ver;
nq->node.node_name_len = 0;
nq->node.local_v4_sockets = 0;
nq->node.local_v6_sockets = 0;
nq->node.local_v4_subnets = 1;
nq->node.local_v6_subnets = 0;
nq->node.tranzit_nodes = 0;
nq->node.hop_count = 0;
nq->paths = queue_new(NULL, 16, "test_paths");
/* Fill subnet after fixed header (after name_len=0, sockets=0) */
uint8_t *dyn = (uint8_t *)&nq->node + sizeof(struct NODEINFO);
struct NODEINFO_IPV4_SUBNET *sn = (struct NODEINFO_IPV4_SUBNET *)dyn;
uint32_t net = htonl(subnet_net);
memcpy(sn->addr, &net, 4);
sn->prefix_length = prefix;
return nq;
}
static void free_test_node(struct NODEINFO_Q* nq) {
if (nq) {
if (nq->paths) queue_free(nq->paths);
u_free(nq);
}
}
/* ====================== СТАТИСТИКА ====================== */
static struct {
int run, passed, failed;
@@ -61,10 +104,10 @@ static void test_change_cb(struct ROUTE_TABLE* table,
else if (action == 2) cb_withdraw++;
}
/* ====================== ТЕСТЫ (исправленные) ====================== */
/* ====================== ТЕСТЫ (для новой NODEINFO архитектуры) ====================== */
static void test_table_create_destroy(void) {
TEST("route_table_create / destroy (ref_count safety)");
TEST("route_table_create / destroy");
struct ROUTE_TABLE *t = route_table_create();
ASSERT_PTR(t, "create failed");
ASSERT_EQ(t->count, 0, "empty table");
@@ -72,197 +115,70 @@ static void test_table_create_destroy(void) {
PASS();
}
static void test_local_route(void) {
TEST("local route (conn=NULL)");
static void test_nodeinfo_insert(void) {
TEST("NODEINFO_Q insert into routing table");
struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_insert = 0;
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80100; e.prefix_length = 24;
ASSERT(route_insert(t, &e, NULL, 0, 0, NULL, 0), "local insert");
ASSERT_EQ(t->count, 1, "1 route");
ASSERT_EQ(cb_insert, 1, "callback");
struct NODEINFO_Q* nq = create_test_node(0x12345678ULL, 1, 0x0a000000, 8);
bool inserted = route_insert(t, nq);
ASSERT(inserted, "insert nodeinfo");
ASSERT(t->count > 0, "routes added");
free_test_node(nq);
route_table_destroy(t);
PASS();
}
static void test_learned_single_path(void) {
TEST("learned route (single path)");
static void test_versioning(void) {
TEST("NODEINFO multiple nodes");
struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_insert = 0;
struct ETCP_CONN conn = { .peer_node_id = 0x1111111111111111ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0x0A000000; e.prefix_length = 8;
uint64_t hops[1] = {0x1111111111111111ULL};
ASSERT(route_insert(t, &e, &conn, 0x2222222222222222ULL, 0, hops, 1), "insert");
ASSERT_EQ(t->count, 1, "1 route");
ASSERT_EQ(cb_insert, 1, "broadcast");
struct NODEINFO_Q* nq1 = create_test_node(0x1111ULL, 5, 0x0a000000, 8);
struct NODEINFO_Q* nq2 = create_test_node(0x2222ULL, 1, 0xac100000, 12);
route_insert(t, nq1);
bool ins2 = route_insert(t, nq2);
ASSERT(ins2, "second node inserted");
ASSERT_EQ(t->count, 2, "two nodes/routes");
free_test_node(nq1);
free_test_node(nq2);
route_table_destroy(t);
PASS();
}
static void test_multiple_paths_backup(void) {
TEST("multiple paths (backup) - second insert = update");
static void test_performance_1000_nodes(void) {
TEST("performance with 1000 nodes");
struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_insert = cb_reroute = 0;
clock_t start = clock();
struct ETCP_CONN c1 = { .peer_node_id = 0x1111ULL };
struct ETCP_CONN c2 = { .peer_node_id = 0x2222ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0x17220000; e.prefix_length = 16;
uint64_t h[1] = {0};
for (int i = 0; i < 1000; i++) {
uint32_t sub = 0x0a000000 + (i * 0x100); /* distinct 10.0.x.0/24 to avoid overlap */
struct NODEINFO_Q* nq = create_test_node(0x1000000ULL + i, 1, sub, 24);
route_insert(t, nq);
/* note: nq not freed here (table holds ref to it), freed in test cleanup if needed */
}
route_insert(t, &e, &c1, 0x3333ULL, 0, h, 1);
route_insert(t, &e, &c2, 0x3333ULL, 0, h, 1);
clock_t end = clock();
double time = (double)(end - start) / CLOCKS_PER_SEC;
ASSERT_EQ(t->count, 1, "still 1 route");
ASSERT_EQ(t->entries[0].conn_list->conninfo_count, 2, "2 paths");
ASSERT_EQ(cb_insert, 1, "only first = insert");
ASSERT_EQ(cb_reroute, 1, "second = update/reroute callback");
ASSERT(t->count >= 1000, "all nodes inserted");
printf("(%.3fs for 1000 nodes) ", time);
route_table_destroy(t);
PASS();
}
static void test_reroute_on_preferred_loss(void) {
TEST("reroute when preferred path lost (route_remove_path)");
struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_reroute = 0;
struct ETCP_CONN c1 = { .peer_node_id = 0x1111ULL };
struct ETCP_CONN c2 = { .peer_node_id = 0x2222ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80000; e.prefix_length = 16;
uint64_t h[1] = {0};
route_insert(t, &e, &c1, 0x3333ULL, 0, h, 1);
route_insert(t, &e, &c2, 0x3333ULL, 0, h, 1);
route_remove_path(t, &c1, 0x3333ULL);
ASSERT_EQ(t->count, 1, "route still exists");
ASSERT_EQ(t->entries[0].conn_list->conninfo_count, 1, "1 path left");
ASSERT_EQ(t->entries[0].conn_list->preferred_conn, 0, "preferred switched");
route_table_destroy(t);
PASS();
}
static void test_withdraw_single_vs_backup(void) {
TEST("withdraw: backup → reroute, last path → full withdraw");
struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_reroute = cb_withdraw = 0;
struct ETCP_CONN cA = { .peer_node_id = 0xAAAAULL };
struct ETCP_CONN cB = { .peer_node_id = 0xBBBBULL };
struct ROUTE_ENTRY e = {0}; e.network = 0x19216800; e.prefix_length = 16;
uint64_t h[1] = {0};
route_insert(t, &e, &cA, 0xDEADULL, 0, h, 1);
route_insert(t, &e, &cB, 0xDEADULL, 0, h, 1);
route_remove_path(t, &cA, 0xDEADULL);
ASSERT_EQ(t->count, 1, "route still exists");
ASSERT_EQ(t->entries[0].conn_list->conninfo_count, 1, "1 path left");
route_remove_path(t, &cB, 0xDEADULL);
ASSERT_EQ(t->count, 0, "route fully removed");
ASSERT_EQ(cb_withdraw, 1, "full withdraw");
route_table_destroy(t);
PASS();
}
static void test_remove_conn_full_cleanup(void) {
TEST("route_remove_conn full cleanup");
struct ROUTE_TABLE *t = route_table_create();
t->change_callback = test_change_cb; cb_withdraw = 0;
struct ETCP_CONN conn = { .peer_node_id = 0x1111ULL };
struct ROUTE_ENTRY e1 = {0}; e1.network = 0x0A000000; e1.prefix_length = 8;
struct ROUTE_ENTRY e2 = {0}; e2.network = 0x17220000; e2.prefix_length = 16;
uint64_t h[1] = {0};
route_insert(t, &e1, &conn, 0x2222ULL, 0, h, 1);
route_insert(t, &e2, &conn, 0x2222ULL, 0, h, 1);
route_remove_conn(t, &conn);
ASSERT_EQ(t->count, 0, "all routes removed");
ASSERT_EQ(cb_withdraw, 2, "withdraw for each prefix");
route_table_destroy(t);
PASS();
}
static void test_loop_detection(void) {
TEST("loop detection");
struct ROUTE_TABLE *t = route_table_create();
struct ETCP_CONN conn = { .peer_node_id = 0x1111ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80100; e.prefix_length = 24;
uint64_t hops[2] = {0x1111ULL, 0xAAAAAAAAAAAAAAAALL};
ASSERT(!route_insert(t, &e, &conn, 0xDEADULL, 0xAAAAAAAAAAAAAAAALL, hops, 2), "loop rejected");
route_table_destroy(t);
PASS();
}
static void test_owner_conflict(void) {
TEST("owner conflict");
struct ROUTE_TABLE *t = route_table_create();
struct ETCP_CONN conn = { .peer_node_id = 0x1111ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80100; e.prefix_length = 24;
uint64_t h[1] = {0};
ASSERT(route_insert(t, &e, &conn, 0x1111ULL, 0, h, 1), "first owner");
ASSERT(!route_insert(t, &e, &conn, 0x2222ULL, 0, h, 1), "different owner rejected");
route_table_destroy(t);
PASS();
}
static void test_no_overlap(void) {
TEST("no overlapping subnets allowed");
struct ROUTE_TABLE *t = route_table_create();
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80100; e.prefix_length = 24;
ASSERT(route_insert(t, &e, NULL, 0, 0, NULL, 0), "/24 OK");
e.prefix_length = 25;
ASSERT(!route_insert(t, &e, NULL, 0, 0, NULL, 0), "/25 inside rejected");
route_table_destroy(t);
PASS();
}
static void test_lookup_preferred(void) {
TEST("lookup uses preferred path");
struct ROUTE_TABLE *t = route_table_create();
struct ETCP_CONN c1 = { .peer_node_id = 0x1111ULL };
struct ETCP_CONN c2 = { .peer_node_id = 0x2222ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0xC0A80100; e.prefix_length = 24;
uint64_t h[1] = {0};
route_insert(t, &e, &c1, 0x3333ULL, 0, h, 1);
route_insert(t, &e, &c2, 0x3333ULL, 0, h, 1);
struct ROUTE_ENTRY *r = route_lookup(t, 0xC0A80164);
ASSERT_PTR(r, "found");
ASSERT_EQ(r->conn_list->preferred_conn, 0, "uses preferred");
route_table_destroy(t);
PASS();
}
static void test_hop_limit(void) {
TEST("hop_count limit (max accepted)");
struct ROUTE_TABLE *t = route_table_create();
struct ETCP_CONN conn = { .peer_node_id = 0x1111ULL };
struct ROUTE_ENTRY e = {0}; e.network = 0x01010100; e.prefix_length = 24;
uint64_t hops[MAX_HOPS] = {0};
ASSERT(route_insert(t, &e, &conn, 0xDEADULL, 0, hops, MAX_HOPS), "MAX_HOPS accepted");
route_table_destroy(t);
PASS();
}
static void test_destroy_refcount(void) {
TEST("destroy with multiple paths (ref_count safety)");
struct ROUTE_TABLE *t = route_table_create();
struct ETCP_CONN conn = { .peer_node_id = 0x1111ULL };
struct ROUTE_ENTRY e1 = {0}; e1.network = 0x0A000000; e1.prefix_length = 8;
struct ROUTE_ENTRY e2 = {0}; e2.network = 0x17220000; e2.prefix_length = 16;
uint64_t h[1] = {0};
route_insert(t, &e1, &conn, 0x2222ULL, 0, h, 1);
route_insert(t, &e2, &conn, 0x2222ULL, 0, h, 1);
/* NODEINFO_Q memory managed externally, table only holds pointers */
route_table_destroy(t);
PASS();
}
/* ====================== MAIN ====================== */
int main(void) {
debug_config_init();
@@ -270,17 +186,9 @@ int main(void) {
debug_set_categories(DEBUG_CATEGORY_ROUTING);
test_table_create_destroy();
test_local_route();
test_learned_single_path();
test_multiple_paths_backup();
test_reroute_on_preferred_loss();
test_withdraw_single_vs_backup();
test_remove_conn_full_cleanup();
test_loop_detection();
test_owner_conflict();
test_no_overlap();
test_lookup_preferred();
test_hop_limit();
test_nodeinfo_insert();
test_versioning();
test_performance_1000_nodes();
test_destroy_refcount();
printf("\n=== ROUTING TEST SUMMARY ===\n"