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.
 
 
 
 
 
 

286 lines
11 KiB

#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include "../lib/platform_compat.h"
#include "utun_instance.h"
#include "topo_node.h"
#include "route_lib.h"
#include "etcp_debug.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) {
if (pre1 == pre2 && net1 == net2) 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, "alloc failed");
return NULL;
}
table->entries = u_calloc(INITIAL_CAPACITY, sizeof(struct ROUTE_ENTRY));
if (!table->entries) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "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, "count=%zu", table->count);
// v_node_info — внешний объект (TOPO_NODEQ), память им не управляем
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 TOPO_NODEQ *node) {
if (!table || !node) { DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "invalid arguments"); return false; }
if (!node->subnets || !node->subnets->v4_subnets) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "node has no v4 subnets");
return false;
}
const struct TOPO_SUBNET4* sub = node->subnets->v4_subnets;
int cnt = 0; for (const struct TOPO_SUBNET4* s = sub; s; s = s->next) cnt++;
if (cnt == 0) { DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "0 subnets"); return false; }
size_t count = (size_t)cnt;
for (const struct TOPO_SUBNET4* s = sub; s; s = s->next) {
uint32_t network; memcpy(&network, s->addr, 4); network = ntohl(network);
if (check_route_overlap_in_table(network, s->prefix_length, table->entries, table->count)) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "overlap detected for %s/%d", ip_to_string(network).a, s->prefix_length);
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, "realloc failed"); return false; }
table->entries = new_entries; table->capacity = new_cap;
}
for (const struct TOPO_SUBNET4* s = sub; s; s = s->next) {
uint32_t network; memcpy(&network, s->addr, 4); network = ntohl(network);
int pos = binary_search_insert_pos(table->entries, table->count, network, s->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 = s->prefix_length; e->v_node_info = node;
table->count++;
if (node->hop_count == 0) table->stats.local_routes++; else table->stats.learned_routes++;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "added %zu route(s) for node=%016llx",
count, (unsigned long long)node->node->node_id);
return true;
}
void route_delete(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node) {
if (!table || !node) return;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "removing all routes for node=%016llx", (unsigned long long)node->node->node_id);
size_t i = 0, 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->hop_count == 0) table->stats.local_routes--; else table->stats.learned_routes--;
removed++; continue;
}
i++;
}
if (removed > 0) DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "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, "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, "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, "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->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;
}
int is_local_subnet(uint32_t ip) {
uint8_t* bip=(uint8_t*)&ip;
uint8_t first_octet = bip[0];
uint8_t second_octet = bip[1];
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "check ip %d.%d : ", first_octet, second_octet);
if (first_octet == 0) return 1;
if (first_octet == 10) return 1;
if (first_octet == 127) return 1;
if (first_octet == 169 && second_octet == 254) return 1;
if (first_octet >= 224 && first_octet <= 239) return 1;
if (first_octet == 172 && second_octet >= 16 && second_octet <= 31) return 1;
if (first_octet == 192 && second_octet == 168) return 1;
if (ip==0xffffffff) return 1;
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "result: global ip %d.%d : ", first_octet, second_octet);
return 0;
}