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

#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;
}