#include #include #include #include "../lib/debug_config.h" #include "../lib/mem.h" #include "../lib/platform_compat.h" #include "utun_instance.h" #include "route_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) { 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); network = ntohl(network); 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); network = ntohl(network); 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; }