You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

724 lines
28 KiB

#include "route_lib.h"
#include "etcp.h"
#include "etcp_debug.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include "../lib/platform_compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define INITIAL_CAPACITY 100
#define CONN_INFO_INIT_SIZE 4
// ============================================================================
// Вспомогательные функции (без изменений)
// ============================================================================
static uint32_t prefix_to_mask(uint8_t prefix) {
if (prefix == 0) return 0;
return ~((1U << (32 - prefix)) - 1);
}
static bool routes_overlap(uint32_t net1, uint8_t pre1, uint32_t net2, uint8_t pre2) {
uint32_t mask1 = prefix_to_mask(pre1);
uint32_t mask2 = prefix_to_mask(pre2);
if ((net1 & mask2) == net2 && pre1 >= pre2) return true;
if ((net2 & mask1) == net1 && pre2 >= pre1) return true;
return false;
}
static bool check_route_overlap_in_table(uint32_t network, uint8_t prefix,
const struct ROUTE_ENTRY *entries, size_t count) {
for (size_t i = 0; i < count; i++) {
if (routes_overlap(network, prefix, entries[i].network, entries[i].prefix_length)) {
return true;
}
}
return false;
}
static struct NODE_CONNS_INFO* find_node_conns(struct ROUTE_TABLE *table, uint64_t node_id) {
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].conn_list && table->entries[i].conn_list->node_id == node_id) {
return table->entries[i].conn_list;
}
}
return NULL;
}
// ============================================================================
// NODE_CONNS_INFO — исправленный realloc + preferred_conn
// ============================================================================
static struct NODE_CONNS_INFO* node_conns_info_create(uint64_t node_id, uint8_t init_size) {
size_t total_size = sizeof(struct NODE_CONNS_INFO) + init_size * sizeof(struct NODE_CONN_INFO);
struct NODE_CONNS_INFO *info = u_calloc(1, total_size);
if (!info) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "node_conns_info_create: alloc failed");
return NULL;
}
info->node_id = node_id;
info->conninfo_count = 0;
info->conninfo_memsize = init_size; // исправлена опечатка
info->ref_count = 1;
info->preferred_conn = 0;
info->flags = ROUTE_FLAG_ACTIVE;
return info;
}
static struct NODE_CONNS_INFO* node_conns_info_add(struct ROUTE_TABLE *table,
struct NODE_CONNS_INFO *info,
uint32_t endpoint_ip, uint16_t endpoint_port,
const uint8_t *public_key,
struct ETCP_CONN *conn,
uint64_t *hop_list, uint8_t hop_count,
bool *out_needs_reroute) {
if (!info) return NULL;
if (out_needs_reroute) *out_needs_reroute = false;
// === 1. Обновление существующего подключения (решение проблемы #2) ===
for (uint8_t i = 0; i < info->conninfo_count; i++) {
if (info->conn_info[i].conn_id == conn) {
struct NODE_CONN_INFO *ci = &info->conn_info[i];
// освобождаем старый hop_list
if (ci->hop_list) {
u_free(ci->hop_list);
ci->hop_list = NULL;
}
ci->endpoint_ip = endpoint_ip;
ci->endpoint_port = endpoint_port;
ci->hop_count = hop_count;
if (hop_count > 0 && hop_list) {
ci->hop_list = u_malloc(hop_count * sizeof(uint64_t));
if (ci->hop_list)
memcpy(ci->hop_list, hop_list, hop_count * sizeof(uint64_t));
}
if (public_key)
memcpy(ci->public_key, public_key, 64);
// если обновился preferred_conn — нужен reroute
if (i == info->preferred_conn && out_needs_reroute)
*out_needs_reroute = true;
return info;
}
}
// === 2. Добавление нового подключения (оригинальная логика) ===
if (info->conninfo_count >= info->conninfo_memsize) {
uint8_t new_size = info->conninfo_memsize * 2;
size_t new_total = sizeof(struct NODE_CONNS_INFO) + new_size * sizeof(struct NODE_CONN_INFO);
struct NODE_CONNS_INFO *new_info = u_realloc(info, new_total);
if (!new_info) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "node_conns_info_add: realloc failed");
return NULL;
}
new_info->conninfo_memsize = new_size;
// обновляем все указатели в таблице
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].conn_list == info)
table->entries[i].conn_list = new_info;
}
info = new_info;
}
struct NODE_CONN_INFO *ci = &info->conn_info[info->conninfo_count];
ci->endpoint_ip = endpoint_ip;
ci->endpoint_port = endpoint_port;
ci->conn_id = conn;
ci->hop_count = hop_count;
ci->hop_list = NULL;
if (hop_count > 0 && hop_list) {
ci->hop_list = u_malloc(hop_count * sizeof(uint64_t));
if (ci->hop_list)
memcpy(ci->hop_list, hop_list, hop_count * sizeof(uint64_t));
}
if (public_key)
memcpy(ci->public_key, public_key, 64);
info->conninfo_count++;
return info;
}
static bool node_conns_info_remove(struct NODE_CONNS_INFO *info, struct ETCP_CONN *conn) {
if (!info || !conn) return false;
for (uint8_t i = 0; i < info->conninfo_count; i++) {
if (info->conn_info[i].conn_id == conn) {
if (info->conn_info[i].hop_list) {
u_free(info->conn_info[i].hop_list);
}
bool was_preferred = (i == info->preferred_conn);
if (i < info->conninfo_count - 1) {
memmove(&info->conn_info[i], &info->conn_info[i + 1],
(info->conninfo_count - i - 1) * sizeof(struct NODE_CONN_INFO));
}
info->conninfo_count--;
if (info->preferred_conn > i) info->preferred_conn--;
if (info->preferred_conn >= info->conninfo_count && info->conninfo_count > 0) {
info->preferred_conn = 0;
}
return was_preferred; // нужен reroute?
}
}
return false;
}
static void node_conns_info_destroy(struct NODE_CONNS_INFO *info) {
if (!info) return;
for (uint8_t i = 0; i < info->conninfo_count; i++) {
if (info->conn_info[i].hop_list) u_free(info->conn_info[i].hop_list);
}
u_free(info);
}
// ============================================================================
// Поиск
// ============================================================================
static int binary_search_insert_pos(struct ROUTE_ENTRY *entries, size_t count,
uint32_t network, uint8_t prefix_length) {
int left = 0;
int right = (int)count;
while (left < right) {
int mid = left + (right - left) / 2;
struct ROUTE_ENTRY *e = &entries[mid];
if (e->network < network ||
(e->network == network && e->prefix_length > prefix_length)) { // longer prefix first
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
static struct ROUTE_ENTRY* find_exact(struct ROUTE_TABLE *table,
uint32_t network, uint8_t prefix) {
if (!table) return NULL;
int left = 0, right = (int)table->count;
while (left < right) {
int mid = left + (right - left) / 2;
struct ROUTE_ENTRY *e = &table->entries[mid];
if (e->network < network || (e->network == network && e->prefix_length >= prefix)) {
left = mid + 1;
} else {
right = mid;
}
}
if (left < (int)table->count &&
table->entries[left].network == network &&
table->entries[left].prefix_length == prefix) {
return &table->entries[left];
}
if (left > 0 && table->entries[left-1].network == network &&
table->entries[left-1].prefix_length == prefix) {
return &table->entries[left-1];
}
return NULL;
}
static struct ROUTE_ENTRY* binary_search_lpm(struct ROUTE_TABLE *table, uint32_t dest_ip) {
if (table->count == 0) return NULL;
int left = 0;
int right = (int)table->count - 1;
struct ROUTE_ENTRY *best = NULL;
uint8_t best_prefix = 0;
while (left <= right) {
int mid = left + (right - left) / 2;
struct ROUTE_ENTRY *entry = &table->entries[mid];
uint32_t mask = prefix_to_mask(entry->prefix_length);
if ((dest_ip & mask) == (entry->network & mask)) {
if (entry->prefix_length > best_prefix) {
best_prefix = entry->prefix_length;
best = entry;
}
if (mid + 1 < (int)table->count) {
struct ROUTE_ENTRY *next = &table->entries[mid + 1];
uint32_t next_mask = prefix_to_mask(next->prefix_length);
if ((dest_ip & next_mask) == (next->network & next_mask)) {
left = mid + 1;
} else {
break;
}
} else {
break;
}
} else if (dest_ip > entry->network) {
left = mid + 1;
} else {
if (mid > 0) {
struct ROUTE_ENTRY *prev = &table->entries[mid - 1];
uint32_t prev_mask = prefix_to_mask(prev->prefix_length);
if ((dest_ip & prev_mask) == (prev->network & prev_mask)) {
if (prev->prefix_length > best_prefix) {
best_prefix = prev->prefix_length;
best = prev;
}
}
}
right = mid - 1;
}
}
if (!best && left > 0 && left <= (int)table->count) {
int check = left - 1;
if (check >= 0) {
struct ROUTE_ENTRY *entry = &table->entries[check];
uint32_t mask = prefix_to_mask(entry->prefix_length);
if ((dest_ip & mask) == (entry->network & mask)) {
best = entry;
}
}
}
return best;
}
// ============================================================================
// Основные функции
// ============================================================================
struct ROUTE_TABLE *route_table_create(void) {
struct ROUTE_TABLE *table = u_calloc(1, sizeof(struct ROUTE_TABLE));
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_table_create: alloc failed");
return NULL;
}
table->entries = u_calloc(INITIAL_CAPACITY, sizeof(struct ROUTE_ENTRY));
if (!table->entries) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_table_create: alloc entries failed");
u_free(table);
return NULL;
}
table->capacity = INITIAL_CAPACITY;
table->count = 0;
table->dynamic_subnet_count = 0;
table->local_subnet_count = 0;
memset(&table->stats, 0, sizeof(table->stats));
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table created, capacity %d", INITIAL_CAPACITY);
return table;
}
void route_table_destroy(struct ROUTE_TABLE *table) {
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_table_destroy: table is NULL");
return;
}
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_table_destroy: count=%zu", table->count);
// Правильно освобождаем NODE_CONNS_INFO с учётом ref_count
// (один info может быть у нескольких префиксов)
for (size_t i = 0; i < table->count; i++) {
struct NODE_CONNS_INFO *info = table->entries[i].conn_list;
if (info) {
table->entries[i].conn_list = NULL; // защита от повторного использования
if (--info->ref_count == 0) {
node_conns_info_destroy(info);
}
}
}
// Освобождаем динамические/локальные подсети (если они были выделены)
u_free(table->dynamic_subnets);
u_free(table->local_subnets);
u_free(table->entries);
u_free(table);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed");
}
/* ====================== НОВАЯ ОСНОВНАЯ ФУНКЦИЯ ====================== */
bool route_insert(struct ROUTE_TABLE *table,
const struct ROUTE_ENTRY *entry,
struct ETCP_CONN *conn,
uint64_t node_id,
uint64_t my_node_id,
uint64_t *hop_list,
uint8_t hop_count) {
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: table is NULL");
return false;
}
if (!entry) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: entry is NULL");
return false;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_insert: network=%s prefix=%d conn=%p node_id=%016llx",
ip_to_string(entry->network).a, entry->prefix_length, (void*)conn, (unsigned long long)node_id);
// === 0. Loop prevention — ВСЕГДА (исправление проблемы #3) ===
if (conn && my_node_id && hop_list && hop_count > 0) {
for (uint8_t i = 0; i < hop_count; i++) {
if (hop_list[i] == my_node_id) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Loop detected: %s/%d", ip_to_string(entry->network).a, entry->prefix_length);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: REJECTED (loop detected)");
return false;
}
}
}
uint64_t peer_node_id=0;
if (conn) {
peer_node_id=conn->peer_node_id;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "peer: %s %016llx", conn->log_name, (unsigned long long)peer_node_id);
}
else DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "no peer");
// === 1. Точный матч префикса ===
struct ROUTE_ENTRY *existing = find_exact(table, entry->network, entry->prefix_length);
if (existing) {
bool existing_local = (existing->conn_list == NULL);
bool new_local = (conn == NULL);
if (existing_local != new_local ||
(!existing_local && existing->conn_list->node_id != node_id)) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Route conflict (different owner): %s/%d", ip_to_string(entry->network).a, entry->prefix_length);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: REJECTED (conflict)");
return false;
}
// === LOCAL route update ===
if (new_local) {
existing->last_update = get_time_tb();
if (table->change_callback)
table->change_callback(table, existing, 1, peer_node_id, table->change_callback_arg);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: UPDATED local route %s/%d", ip_to_string(entry->network).a, entry->prefix_length);
return true;
}
// === LEARNED route: обновляем hop_list или добавляем новый путь (исправление проблемы #2) ===
struct NODE_CONNS_INFO *info = existing->conn_list;
bool needs_reroute = false; // не используется здесь, но нужен для сигнатуры
info = node_conns_info_add(table, info, 0, 0, NULL, conn, hop_list, hop_count, &needs_reroute);
if (!info) {
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: FAILED (node_conns_info_add)");
return false;
}
existing->conn_list = info;
existing->last_update = get_time_tb();
if (table->change_callback)
table->change_callback(table, existing, 1, peer_node_id, table->change_callback_arg);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_insert: UPDATED learned route %s/%d node_id=%016llx",
ip_to_string(entry->network).a, entry->prefix_length, (unsigned long long)node_id);
return true;
}
// === 2. Новый префикс ===
if (check_route_overlap_in_table(entry->network, entry->prefix_length,
table->entries, table->count)) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Route overlap rejected: %s/%d", ip_to_string(entry->network).a, entry->prefix_length);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: REJECTED (overlap)");
return false;
}
// === 3. Реаллокация таблицы при необходимости ===
if (table->count >= table->capacity) {
uint32_t new_cap = table->capacity * 2;
struct ROUTE_ENTRY *new_entries = u_realloc(table->entries, new_cap * sizeof(struct ROUTE_ENTRY));
if (!new_entries) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: realloc failed");
return false;
}
table->entries = new_entries;
table->capacity = new_cap;
}
// === 4. Поиск позиции для вставки (сортировка) ===
int pos = binary_search_insert_pos(table->entries, table->count,
entry->network, entry->prefix_length);
// === 5. Создаём новую запись ===
struct ROUTE_ENTRY new_entry = *entry;
new_entry.created_time = get_time_tb();
new_entry.last_update = get_time_tb();
new_entry.conn_list = NULL;
if (conn) {
// === LEARNED route ===
uint64_t owner_node_id = node_id;
struct NODE_CONNS_INFO *node_info = find_node_conns(table, owner_node_id);
if (!node_info) {
node_info = node_conns_info_create(owner_node_id, CONN_INFO_INIT_SIZE);
if (!node_info) return false;
} else {
node_info->ref_count++;
}
new_entry.conn_list = node_info;
bool dummy = false;
node_info = node_conns_info_add(table, node_info, 0, 0, NULL, conn, hop_list, hop_count, &dummy);
if (!node_info) return false;
new_entry.conn_list = node_info;
} else {
// === LOCAL route ===
table->stats.local_routes++;
}
// === 6. Вставка в отсортированный массив ===
if (pos < (int)table->count) {
memmove(&table->entries[pos + 1], &table->entries[pos],
(table->count - pos) * sizeof(struct ROUTE_ENTRY));
}
table->entries[pos] = new_entry;
table->count++;
// === 7. Callback ===
if (table->change_callback) {
table->change_callback(table, &table->entries[pos], 0, peer_node_id, table->change_callback_arg);
}
// === 8. Отладочное сообщение ===
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Route %s: %s/%d (node %016llx)",
conn ? "learned" : "local", ip_to_string(entry->network).a, entry->prefix_length,
(unsigned long long)node_id);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_insert: INSERTED %s %s/%d",
conn ? "learned" : "local", ip_to_string(entry->network).a, entry->prefix_length);
return true;
}
/* ====================== НОВАЯ ФУНКЦИЯ ====================== */
void route_remove_conn(struct ROUTE_TABLE *table, struct ETCP_CONN *conn) {
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_remove_conn: table is NULL");
return;
}
if (!conn) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_remove_conn: conn is NULL");
return;
}
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_remove_conn: conn=%p", (void*)conn);
struct NODE_CONNS_INFO* affected[512];
bool needs_reroute[512] = {0};
size_t aff_count = 0;
// Pass 1: удаляем conn (один раз на node_id)
for (size_t i = 0; i < table->count; i++) {
struct NODE_CONNS_INFO *info = table->entries[i].conn_list;
if (!info) continue;
bool has_conn = false;
for (uint8_t j = 0; j < info->conninfo_count; j++) {
if (info->conn_info[j].conn_id == conn) { has_conn = true; break; }
}
if (!has_conn) continue;
bool already = false;
for (size_t k = 0; k < aff_count; k++) {
if (affected[k] == info) { already = true; break; }
}
if (already) continue;
bool rer = node_conns_info_remove(info, conn);
affected[aff_count] = info;
needs_reroute[aff_count] = rer;
aff_count++;
if (aff_count >= 512) break;
}
// Pass 2: удаляем записи без оставшихся подключений
size_t j = 0;
for (size_t i = 0; i < table->count; i++) {
struct ROUTE_ENTRY *e = &table->entries[i];
if (e->conn_list && e->conn_list->conninfo_count == 0) {
if (table->change_callback)
table->change_callback(table, e, 2, 0, table->change_callback_arg);
struct NODE_CONNS_INFO *info = e->conn_list;
e->conn_list = NULL;
if (--info->ref_count == 0)
node_conns_info_destroy(info);
continue;
}
if (i != j) table->entries[j] = table->entries[i];
j++;
}
table->count = j;
// Pass 3: reroute только если изменился preferred_conn
for (size_t i = 0; i < table->count; i++) {
struct ROUTE_ENTRY *e = &table->entries[i];
if (!e->conn_list) continue;
for (size_t k = 0; k < aff_count; k++) {
if (affected[k] == e->conn_list && needs_reroute[k]) {
e->last_update = get_time_tb();
if (table->change_callback)
table->change_callback(table, e, 1, 0, table->change_callback_arg);
break;
}
}
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_conn: processed %zu nodes", aff_count);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_remove_conn: DONE");
}
/* ====================== НОВАЯ ФУНКЦИЯ ДЛЯ WITHDRAW ====================== */
bool route_remove_path(struct ROUTE_TABLE *table,
struct ETCP_CONN *conn,
uint64_t node_id) {
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_remove_path: table is NULL");
return false;
}
if (!conn) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_remove_path: conn is NULL");
return false;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_path: node_id=%016llx conn=%p",
(unsigned long long)node_id, (void*)conn);
struct NODE_CONNS_INFO* info = find_node_conns(table, node_id);
if (!info) {
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_remove_path: node not found");
return false;
}
bool was_preferred = node_conns_info_remove(info, conn);
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_remove_path: removed, was_preferred=%d remaining_conns=%d",
was_preferred, info->conninfo_count);
if (info->conninfo_count == 0) {
// Последний путь к узлу — полный withdraw
route_delete(table, node_id); // уже вызывает callback(2) и очищает память
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_path: WITHDRAW node_id=%016llx", (unsigned long long)node_id);
return true;
} else if (was_preferred) {
// Изменился preferred_conn — уведомляем о reroute для всех префиксов этого узла
for (size_t i = 0; i < table->count; i++) {
struct ROUTE_ENTRY *e = &table->entries[i];
if (e->conn_list == info) {
e->last_update = get_time_tb();
if (table->change_callback)
table->change_callback(table, e, 1, 0, table->change_callback_arg);
}
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_remove_path: REROUTE node_id=%016llx", (unsigned long long)node_id);
}
return false;
}
void route_delete(struct ROUTE_TABLE *table, uint64_t node_id) {
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_delete: table is NULL");
return;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_delete: node_id=%016llx", (unsigned long long)node_id);
size_t i = 0;
while (i < table->count) {
struct ROUTE_ENTRY *entry = &table->entries[i];
bool deleted = false;
if (entry->conn_list && entry->conn_list->node_id == node_id) {
for (uint8_t j = 0; j < entry->conn_list->conninfo_count; j++) {
if (entry->conn_list->conn_info[j].hop_list) {
u_free(entry->conn_list->conn_info[j].hop_list);
}
}
entry->conn_list->conninfo_count = 0;
entry->conn_list->ref_count--;
if (entry->conn_list->ref_count == 0) {
node_conns_info_destroy(entry->conn_list);
}
entry->conn_list = NULL;
if (table->change_callback) {
table->change_callback(table, entry, 2, 0, table->change_callback_arg);
}
if (i < table->count - 1) {
memmove(&table->entries[i], &table->entries[i + 1],
(table->count - i - 1) * sizeof(struct ROUTE_ENTRY));
}
table->count--;
deleted = true;
}
if (!deleted) {
i++;
}
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routes deleted for node %016llx", (unsigned long long)node_id);
}
struct ROUTE_ENTRY* route_lookup(struct ROUTE_TABLE *table, uint32_t dest_ip) {
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_lookup: table is NULL");
return NULL;
}
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: dest_ip=%s", ip_to_string(dest_ip).a);
struct ROUTE_ENTRY *result = binary_search_lpm(table, dest_ip);
if (result) {
table->stats.routes_lookup_hits++;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_lookup: FOUND %s/%d node_id=%016llx",
ip_to_string(result->network).a, result->prefix_length,
result->conn_list ? (unsigned long long)result->conn_list->node_id : 0);
} else {
table->stats.routes_lookup_misses++;
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: NOT FOUND");
}
return result;
}
void route_table_print(const struct ROUTE_TABLE *table) {
if (!table) return;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=== Routing Table ===");
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Count: %zu, Capacity: %zu", table->count, table->capacity);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Local routes: %lu, Learned: %lu",
table->stats.local_routes, table->stats.learned_routes);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Lookup hits: %lu, misses: %lu",
table->stats.routes_lookup_hits, table->stats.routes_lookup_misses);
for (size_t i = 0; i < table->count; i++) {
const struct ROUTE_ENTRY *entry = &table->entries[i];
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d", i + 1, ip_to_string(entry->network).a, entry->prefix_length);
if (entry->conn_list) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " node_id=%016llx, conns=%d, ref=%d",
(unsigned long long)entry->conn_list->node_id,
entry->conn_list->conninfo_count,
entry->conn_list->ref_count);
} else {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " LOCAL");
}
}
}