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
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"); |
|
} |
|
} |
|
}
|
|
|