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.
306 lines
11 KiB
306 lines
11 KiB
#include "route_node.h" |
|
#include "route_lib.h" |
|
#include "etcp_debug.h" |
|
#include "../lib/debug_config.h" |
|
#include "../lib/mem.h" |
|
#include "../lib/platform_compat.h" |
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <stdint.h> |
|
|
|
#define INITIAL_CAPACITY 100 |
|
|
|
// ============================================================================ |
|
// Вспомогательные функции |
|
// ============================================================================ |
|
|
|
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; |
|
} |
|
|
|
// ============================================================================ |
|
// Поиск (LPM) |
|
// ============================================================================ |
|
|
|
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)) { |
|
left = mid + 1; |
|
} else { |
|
right = mid; |
|
} |
|
} |
|
return left; |
|
} |
|
|
|
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 { |
|
right = mid - 1; |
|
} |
|
} |
|
|
|
// fallback проверка предыдущей записи |
|
if (!best && left > 0) { |
|
struct ROUTE_ENTRY *entry = &table->entries[left - 1]; |
|
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 (simplified), capacity %d", INITIAL_CAPACITY); |
|
return table; |
|
} |
|
|
|
void route_table_destroy(struct ROUTE_TABLE *table) { |
|
if (!table) return; |
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_table_destroy: count=%zu", table->count); |
|
|
|
// v_node_info — внешний объект (NODEINFO_Q), память им не управляем |
|
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, struct NODEINFO_Q *node) { |
|
if (!table || !node) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: invalid arguments"); |
|
return false; |
|
} |
|
|
|
// === Вызов сторонней функции (теперь без malloc) === |
|
const struct NODEINFO_IPV4_SUBNET *subnets = NULL; |
|
int cnt = get_node_routes(node, &subnets); |
|
|
|
if (cnt <= 0) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_insert: get_node_routes returned %d subnets", cnt); |
|
return false; |
|
} |
|
|
|
size_t count = (size_t)cnt; |
|
|
|
// === Проверка пересечений === |
|
for (size_t i = 0; i < count; i++) { |
|
uint32_t network; |
|
memcpy(&network, subnets[i].addr, 4); // addr уже в network byte order (big-endian) |
|
uint8_t prefix = subnets[i].prefix_length; |
|
|
|
if (check_route_overlap_in_table(network, prefix, |
|
table->entries, table->count)) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_insert: overlap detected for %s/%d", |
|
ip_to_string(network).a, prefix); |
|
return false; |
|
} |
|
} |
|
|
|
// === Реаллокация при необходимости === |
|
if (table->count + count > table->capacity) { |
|
size_t new_cap = table->capacity * 2; |
|
while (new_cap < table->count + count) new_cap *= 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; |
|
} |
|
|
|
// === Вставка каждого префикса (таблица остаётся отсортированной) === |
|
for (size_t i = 0; i < count; i++) { |
|
uint32_t network; |
|
memcpy(&network, subnets[i].addr, 4); |
|
uint8_t prefix_length = subnets[i].prefix_length; |
|
|
|
int pos = binary_search_insert_pos(table->entries, table->count, network, prefix_length); |
|
|
|
if (pos < (int)table->count) { |
|
memmove(&table->entries[pos + 1], &table->entries[pos], |
|
(table->count - pos) * sizeof(struct ROUTE_ENTRY)); |
|
} |
|
|
|
struct ROUTE_ENTRY *e = &table->entries[pos]; |
|
e->network = network; |
|
e->prefix_length = prefix_length; |
|
e->v_node_info = node; |
|
|
|
table->count++; |
|
if (node && node->node.hop_count == 0) table->stats.local_routes++; else table->stats.learned_routes++; |
|
} |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_insert: added %zu route(s) for node_info=%p", |
|
count, (void*)node); |
|
return true; |
|
} |
|
|
|
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); |
|
|
|
size_t i = 0; |
|
size_t removed = 0; |
|
while (i < table->count) { |
|
if (table->entries[i].v_node_info == node) { |
|
if (i < table->count - 1) { |
|
memmove(&table->entries[i], &table->entries[i + 1], |
|
(table->count - i - 1) * sizeof(struct ROUTE_ENTRY)); |
|
} |
|
table->count--; |
|
if (node && node->node.hop_count == 0) table->stats.local_routes--; else table->stats.learned_routes--; |
|
removed++; |
|
continue; |
|
} |
|
i++; |
|
} |
|
|
|
if (removed > 0) { |
|
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_delete: removed %zu route(s)", removed); |
|
} |
|
} |
|
|
|
struct ROUTE_ENTRY* route_lookup(struct ROUTE_TABLE *table, uint32_t dest_ip) { |
|
if (!table) 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_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: FOUND %s/%d (node_info=%p)", |
|
ip_to_string(result->network).a, result->prefix_length, |
|
(void*)result->v_node_info); |
|
} 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 (simplified) ==="); |
|
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Count: %zu / Capacity: %zu", table->count, table->capacity); |
|
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Learned routes: %lu", 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->v_node_info && entry->v_node_info->node.hop_count != 0) { |
|
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " v_node_info=%p", (void*)entry->v_node_info); |
|
} else { |
|
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " LOCAL"); |
|
} |
|
} |
|
} |
|
|
|
int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_length) { |
|
if (!subnet_str || !network || !prefix_length) return -1; |
|
|
|
char buf[64]; |
|
strncpy(buf, subnet_str, sizeof(buf) - 1); |
|
buf[sizeof(buf) - 1] = '\0'; |
|
|
|
char *slash = strchr(buf, '/'); |
|
if (!slash) return -1; |
|
*slash = '\0'; |
|
|
|
if (inet_pton(AF_INET, buf, network) != 1) return -1; |
|
*network = ntohl(*network); // приводим к big-endian как требует структура |
|
|
|
int p = atoi(slash + 1); |
|
if (p < 0 || p > 32) return -1; |
|
*prefix_length = (uint8_t)p; |
|
|
|
return 0; |
|
}
|
|
|