From 02f39f6089fc6044ca566275ff0e4490c35d0498 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 13 Feb 2026 18:17:20 +0300 Subject: [PATCH] Fix: Update route_lib.c to match route_lib.h interface - Implemented route_table_lookup() returning ROUTE_ARRAY* with cache support - Implemented route_table_delete() to remove routes by connection - Implemented route_update_quality() with simple formula - Added internal ROUTE_ARRAY management functions (create/destroy/add) - Implemented LRU cache for route lookups (10 entries) - Fixed route_table_insert() to actually insert entries - Removed duplicate parse_subnet() from utun.c - Added helper function declarations to route_lib.h Build: Project compiles without errors --- src/route_lib.c | 501 +++++++++++++++++++++++++++++++++++------------- src/route_lib.h | 43 +++++ src/utun.c | 14 -- 3 files changed, 411 insertions(+), 147 deletions(-) diff --git a/src/route_lib.c b/src/route_lib.c index 32061a68..371f2147 100644 --- a/src/route_lib.c +++ b/src/route_lib.c @@ -15,8 +15,27 @@ #define INITIAL_ROUTE_CAPACITY 100 #define ROUTE_EXPANSION_FACTOR 2 #define MAX_SUBNET_VALIDATION_RANGES 50 +#define ROUTE_CACHE_SIZE 10 -// Parse subnet string +/** + * @brief Структура кеша маршрутов (LRU) + */ +struct ROUTE_CACHE { + struct ROUTE_ARRAY *entries[ROUTE_CACHE_SIZE]; /**< Массив кешированных результатов */ + uint32_t keys[ROUTE_CACHE_SIZE]; /**< IP-адреса (ключи) */ + uint64_t access_time[ROUTE_CACHE_SIZE]; /**< Время последнего доступа */ + int count; /**< Текущее количество записей */ +}; + +static struct ROUTE_CACHE g_route_cache = {0}; + +/** + * @brief Парсит строку подсети в сетевой адрес и длину префикса + * @param subnet_str Строка подсети (например, "192.168.1.0/24") + * @param network Указатель для сохранения сетевого адреса + * @param prefix_length Указатель для сохранения длины префикса + * @return 0 при успехе, -1 при ошибке + */ int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_length) { if (!subnet_str || !network || !prefix_length) return -1; char ip[64]; @@ -30,36 +49,163 @@ int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_leng return 0; } +/** + * @brief Создает массив маршрутов + * @param capacity Максимальное количество маршрутов + * @return Указатель на созданный массив или NULL + */ +static struct ROUTE_ARRAY* route_array_create(uint32_t capacity) { + struct ROUTE_ARRAY *array = calloc(1, sizeof(struct ROUTE_ARRAY) + + capacity * sizeof(struct ROUTE_ENTRY*)); + if (!array) { + DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_array_create: failed to allocate memory"); + return NULL; + } + array->capacity = capacity; + array->routes = 0; + array->ip = 0; + return array; +} + +/** + * @brief Освобождает память массива маршрутов + * @param array Указатель на массив + */ +static void route_array_destroy(struct ROUTE_ARRAY *array) { + if (!array) return; + // entries[] содержит только указатели на ROUTE_ENTRY из таблицы, + // их освобождать не нужно - они принадлежат таблице + free(array); +} + +/** + * @brief Добавляет маршрут в массив + * @param array Массив маршрутов + * @param entry Указатель на запись маршрута + * @return true при успехе, false если массив полон + */ +static bool route_array_add(struct ROUTE_ARRAY *array, struct ROUTE_ENTRY *entry) { + if (!array || !entry) return false; + if (array->routes >= array->capacity) return false; + array->entries[array->routes++] = entry; + return true; +} +/** + * @brief Пересчитывает метрику quality на основе других метрик + * @param entry Указатель на запись маршрута + * @return true при успехе, false при ошибке + */ +bool route_update_quality(struct ROUTE_ENTRY *entry) { + if (!entry) return false; + + // Простая формула: latency * 2 + loss * 10 + hops * 50 + // Меньше значение = лучше маршрут + entry->metrics.metric = entry->metrics.latency_ms * 2 + + entry->metrics.packet_loss_rate * 10 + + entry->metrics.hop_count * 50; + + return true; +} -static int compare_routes(const void *a, const void *b) { - const struct ROUTE_ENTRY *route_a = (const struct ROUTE_ENTRY *)a; - const struct ROUTE_ENTRY *route_b = (const struct ROUTE_ENTRY *)b; +/** + * @brief Сравнивает два маршрута для сортировки (лучший маршрут - меньшее значение) + */ +static int compare_routes_by_quality(const void *a, const void *b) { + const struct ROUTE_ENTRY *route_a = *(const struct ROUTE_ENTRY **)a; + const struct ROUTE_ENTRY *route_b = *(const struct ROUTE_ENTRY **)b; - // First compare by prefix length (longest prefix first) + // Сначала сравниваем по quality (меньше - лучше) + if (route_a->metrics.metric != route_b->metrics.metric) { + return (int)(route_a->metrics.metric - route_b->metrics.metric); + } + + // Затем по длине префикса (больше - лучше) if (route_b->prefix_length != route_a->prefix_length) { return route_b->prefix_length - route_a->prefix_length; } - // Then by network - if (route_a->network != route_b->network) { - return route_a->network < route_b->network ? -1 : 1; - } + return 0; +} + +/** + * @brief Ищет запись в кеше по IP + */ +static struct ROUTE_ARRAY* cache_lookup(uint32_t dest_ip) { + uint64_t current_time = (uint64_t)time(NULL) * 1000000; - // Finally by metrics (best route first) - if (route_a->metrics.bandwidth_kbps != route_b->metrics.bandwidth_kbps) { - return route_b->metrics.bandwidth_kbps - route_a->metrics.bandwidth_kbps; + for (int i = 0; i < g_route_cache.count; i++) { + if (g_route_cache.keys[i] == dest_ip && g_route_cache.entries[i]) { + g_route_cache.access_time[i] = current_time; + DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Cache hit for IP %u", dest_ip); + return g_route_cache.entries[i]; + } } + return NULL; +} + +/** + * @brief Добавляет результат в кеш (LRU замещение) + */ +static void cache_insert(uint32_t dest_ip, struct ROUTE_ARRAY *result) { + uint64_t current_time = (uint64_t)time(NULL) * 1000000; + int insert_idx = -1; - if (route_a->metrics.latency_ms != route_b->metrics.latency_ms) { - return route_a->metrics.latency_ms - route_b->metrics.latency_ms; + // Ищем пустое место или самую старую запись + if (g_route_cache.count < ROUTE_CACHE_SIZE) { + insert_idx = g_route_cache.count++; + } else { + // LRU: находим запись с самым старым access_time + uint64_t oldest_time = g_route_cache.access_time[0]; + insert_idx = 0; + for (int i = 1; i < ROUTE_CACHE_SIZE; i++) { + if (g_route_cache.access_time[i] < oldest_time) { + oldest_time = g_route_cache.access_time[i]; + insert_idx = i; + } + } + // Освобождаем старую запись + if (g_route_cache.entries[insert_idx]) { + route_array_destroy(g_route_cache.entries[insert_idx]); + } } - if (route_a->metrics.packet_loss_rate != route_b->metrics.packet_loss_rate) { - return route_a->metrics.packet_loss_rate - route_b->metrics.packet_loss_rate; - } + g_route_cache.keys[insert_idx] = dest_ip; + g_route_cache.entries[insert_idx] = result; + g_route_cache.access_time[insert_idx] = current_time; - return route_a->metrics.hop_count - route_b->metrics.hop_count; + DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Cache insert for IP %u at slot %d", dest_ip, insert_idx); +} + +/** + * @brief Преобразует IP-адрес в строку + * @param ip IP-адрес (в host byte order) + * @param buffer Буфер для строки (минимум 16 байт) + * @return Указатель на buffer или NULL + */ +static char *ip_to_string(uint32_t ip, char *buffer) { + if (!buffer) return NULL; + + struct in_addr addr; + addr.s_addr = htonl(ip); + strncpy(buffer, inet_ntoa(addr), 15); + buffer[15] = '\0'; + return buffer; +} + +/** + * @brief Преобразует тип маршрута в строку + * @param type Тип маршрута + * @return Строковое представление типа + */ +static const char *route_type_to_string(route_type_t type) { + switch (type) { + case ROUTE_TYPE_STATIC: return "STATIC"; + case ROUTE_TYPE_DYNAMIC: return "DYNAMIC"; + case ROUTE_TYPE_LOCAL: return "LOCAL"; + case ROUTE_TYPE_LEARNED: return "LEARNED"; + default: return "UNKNOWN"; + } } struct ROUTE_TABLE *route_table_create(void) { @@ -72,7 +218,7 @@ struct ROUTE_TABLE *route_table_create(void) { table->capacity = INITIAL_ROUTE_CAPACITY; table->entries = calloc(table->capacity, sizeof(struct ROUTE_ENTRY)); if (!table->entries) { - DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate memory for %d route entries", table->capacity); + DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate memory for %zu route entries", table->capacity); free(table); return NULL; } @@ -106,6 +252,51 @@ void route_table_destroy(struct ROUTE_TABLE *table) { free(table); } +/** + * @brief Валидирует маршрут на соответствие разрешенным подсетям + */ +static bool route_validate_route(struct ROUTE_TABLE *table, uint32_t network, + uint8_t prefix_length, route_type_t route_type) { + if (!table) return false; + + uint32_t *validation_ranges = NULL; + size_t range_count = 0; + + switch (route_type) { + case ROUTE_TYPE_STATIC: + case ROUTE_TYPE_LEARNED: + return true; // No validation for static or learned routes + case ROUTE_TYPE_DYNAMIC: + validation_ranges = table->dynamic_subnets; + range_count = table->dynamic_subnet_count; + break; + case ROUTE_TYPE_LOCAL: + validation_ranges = table->local_subnets; + range_count = table->local_subnet_count; + break; + default: + return false; + } + + if (range_count == 0) return true; // No validation ranges configured + + // Check if network falls within any validation range + for (size_t i = 0; i < range_count; i += 2) { + uint32_t range_network = validation_ranges[i]; + uint8_t range_prefix = (uint8_t)validation_ranges[i + 1]; + + uint32_t mask = (range_prefix == 0) ? 0 : (0xFFFFFFFFU << (32 - range_prefix)); + if ((network & mask) == (range_network & mask)) { + // Check if this route is more specific than or equal to the validation range + if (prefix_length >= range_prefix) { + return true; + } + } + } + + return false; +} + bool route_table_insert(struct ROUTE_TABLE *table, const struct ROUTE_ENTRY *entry) { if (!table || !entry) return false; @@ -115,11 +306,9 @@ bool route_table_insert(struct ROUTE_TABLE *table, const struct ROUTE_ENTRY *ent ip_to_string(entry->network, ip_str); DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Ignoring invalid route: %s/%d (type: %s)", ip_str, entry->prefix_length, route_type_to_string(entry->type)); - return false; } - // Check if we need to expand the table if (table->count >= table->capacity) { size_t new_capacity = table->capacity * ROUTE_EXPANSION_FACTOR; @@ -129,110 +318,170 @@ bool route_table_insert(struct ROUTE_TABLE *table, const struct ROUTE_ENTRY *ent return false; } + // Zero new memory + memset(new_entries + table->capacity, 0, (new_capacity - table->capacity) * sizeof(struct ROUTE_ENTRY)); table->entries = new_entries; table->capacity = new_capacity; } + // Проверяем, не существует ли уже такой маршрут (по network + prefix_length + next_hop) + for (size_t i = 0; i < table->count; i++) { + if (table->entries[i].network == entry->network && + table->entries[i].prefix_length == entry->prefix_length && + table->entries[i].next_hop == entry->next_hop) { + // Обновляем существующий маршрут + table->entries[i] = *entry; + table->entries[i].last_update = (uint64_t)time(NULL) * 1000000; + if (!(table->entries[i].flags & ROUTE_FLAG_ACTIVE)) { + table->entries[i].flags |= ROUTE_FLAG_ACTIVE; + } + return true; + } + } + // Set timestamps and ensure route is active struct ROUTE_ENTRY new_entry = *entry; - new_entry.created_time = new_entry.last_update = (uint64_t)time(NULL) * 1000000; // Convert to microseconds + new_entry.created_time = new_entry.last_update = (uint64_t)time(NULL) * 1000000; // Ensure route is active by default if (!(new_entry.flags & ROUTE_FLAG_ACTIVE)) { new_entry.flags |= ROUTE_FLAG_ACTIVE; } - + // Insert at the end (order doesn't matter, lookup will sort) + table->entries[table->count++] = new_entry; - // Insert and maintain sorted order - size_t insert_pos = table->count; - for (size_t i = 0; i < table->count; i++) { - if (compare_routes(&new_entry, &table->entries[i]) < 0) { - insert_pos = i; - break; - } + // Update stats + if (new_entry.type == ROUTE_TYPE_LOCAL) { + table->stats.local_routes++; + } else if (new_entry.type == ROUTE_TYPE_LEARNED) { + table->stats.learned_routes++; } - return false; + char ip_str[16]; + ip_to_string(new_entry.network, ip_str); + DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Route inserted: %s/%d (%s)", + ip_str, new_entry.prefix_length, route_type_to_string(new_entry.type)); + + return true; } -bool route_table_lookup(struct ROUTE_TABLE *table, uint32_t dest_ip, struct ROUTE_ENTRY *best_route) { - if (!table || !best_route) { - DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_table_lookup: invalid parameters (table=%p, best_route=%p)", table, best_route); - return false; - } - - // Find longest prefix match +void route_table_delete(struct ROUTE_TABLE *table, struct ETCP_SOCKET* conn) { + if (!table || !conn) return; + + size_t removed = 0; + size_t write_idx = 0; + for (size_t i = 0; i < table->count; i++) { - const struct ROUTE_ENTRY *entry = &table->entries[i]; - - // Check if route is valid and matches destination - if (entry->flags & ROUTE_FLAG_ACTIVE) { - uint32_t mask = (entry->prefix_length == 0) ? 0 : (0xFFFFFFFFU << (32 - entry->prefix_length)); - if ((dest_ip & mask) == (entry->network & mask)) { - *best_route = *entry; - best_route->last_update = (uint64_t)time(NULL) * 1000000; - - char ip_str[16]; - ip_to_string(dest_ip, ip_str); - DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Found route for %s: %s/%d", - ip_str, ip_to_string(entry->network, ip_str), entry->prefix_length); - - return true; + if (table->entries[i].next_hop == conn) { + // Обновляем статистику + if (table->entries[i].type == ROUTE_TYPE_LOCAL) { + table->stats.local_routes--; + } else if (table->entries[i].type == ROUTE_TYPE_LEARNED) { + table->stats.learned_routes--; + } + removed++; + } else { + // Сохраняем запись + if (write_idx != i) { + table->entries[write_idx] = table->entries[i]; } + write_idx++; } } - - // No route found - char ip_str[16]; - ip_to_string(dest_ip, ip_str); - DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_table_lookup: no route found for destination IP %s", ip_str); - return false; + table->count = write_idx; + + // Очищаем освободившиеся записи + for (size_t i = write_idx; i < write_idx + removed && i < table->capacity; i++) { + memset(&table->entries[i], 0, sizeof(struct ROUTE_ENTRY)); + } + + // Инвалидируем кеш + for (int i = 0; i < g_route_cache.count; i++) { + if (g_route_cache.entries[i]) { + route_array_destroy(g_route_cache.entries[i]); + g_route_cache.entries[i] = NULL; + } + } + g_route_cache.count = 0; + + DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Removed %zu routes for connection %p", removed, (void*)conn); } -bool route_validate_route(struct ROUTE_TABLE *table, uint32_t network, uint8_t prefix_length, route_type_t route_type) { - if (!table) return false; +struct ROUTE_ARRAY* route_table_lookup(struct ROUTE_TABLE *table, uint32_t dest_ip) { + if (!table) return NULL; - uint32_t *validation_ranges = NULL; - size_t range_count = 0; + table->stats.routes_lookup_hits++; - switch (route_type) { - case ROUTE_TYPE_STATIC: - case ROUTE_TYPE_LEARNED: - return true; // No validation for static or learned routes - case ROUTE_TYPE_DYNAMIC: - validation_ranges = table->dynamic_subnets; - range_count = table->dynamic_subnet_count; - break; - case ROUTE_TYPE_LOCAL: - validation_ranges = table->local_subnets; - range_count = table->local_subnet_count; - break; - default: - return false; + // Сначала проверяем кеш + struct ROUTE_ARRAY *cached = cache_lookup(dest_ip); + if (cached) { + return cached; } - - if (range_count == 0) return true; // No validation ranges configured - - // Check if network falls within any validation range - for (size_t i = 0; i < range_count; i += 2) { - uint32_t range_network = validation_ranges[i]; - uint8_t range_prefix = (uint8_t)validation_ranges[i + 1]; - - uint32_t mask = (range_prefix == 0) ? 0 : (0xFFFFFFFFU << (32 - range_prefix)); - if ((network & mask) == (range_network & mask)) { - // Check if this route is more specific than or equal to the validation range - if (prefix_length >= range_prefix) { - return true; + + // Создаем новый результат + struct ROUTE_ARRAY *result = route_array_create(table->count); + if (!result) { + return NULL; + } + result->ip = dest_ip; + + // Ищем все подходящие маршруты (longest prefix match) + for (size_t i = 0; i < table->count; i++) { + struct ROUTE_ENTRY *entry = &table->entries[i]; + + // Проверяем что маршрут активен + if (!(entry->flags & ROUTE_FLAG_ACTIVE)) { + continue; + } + + // Проверяем совпадение по подсети + uint32_t mask = (entry->prefix_length == 0) ? 0 : + (0xFFFFFFFFU << (32 - entry->prefix_length)); + + if ((dest_ip & mask) == (entry->network & mask)) { + // Добавляем в результат + if (!route_array_add(result, entry)) { + DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Route array full, skipping additional routes"); + break; } } } - - return false; + + if (result->routes == 0) { + // Нет маршрутов + table->stats.routes_lookup_misses++; + route_array_destroy(result); + + char ip_str[16]; + ip_to_string(dest_ip, ip_str); + DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "No route found for destination IP %s", ip_str); + return NULL; + } + + // Обновляем quality для всех найденных маршрутов + for (uint32_t i = 0; i < result->routes; i++) { + route_update_quality(result->entries[i]); + } + + // Сортируем по quality (лучший первый) + qsort(result->entries, result->routes, sizeof(struct ROUTE_ENTRY*), + compare_routes_by_quality); + + // Сохраняем в кеш + cache_insert(dest_ip, result); + + char ip_str[16]; + ip_to_string(dest_ip, ip_str); + DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Found %u routes for %s (best quality: %u)", + result->routes, ip_str, result->entries[0]->metrics.metric); + + return result; } -static bool enhanced_route_add_subnet_range(struct ROUTE_TABLE *table, uint32_t network, uint8_t prefix_length, uint32_t **ranges, size_t *count) { +static bool enhanced_route_add_subnet_range(struct ROUTE_TABLE *table, uint32_t network, + uint8_t prefix_length, uint32_t **ranges, size_t *count) { if (!table || !ranges || !count) return false; if (*count >= MAX_SUBNET_VALIDATION_RANGES - 2) return false; @@ -256,18 +505,18 @@ static bool enhanced_route_add_subnet_range(struct ROUTE_TABLE *table, uint32_t return true; } - - bool route_add_dynamic_subnet(struct ROUTE_TABLE *table, uint32_t network, uint8_t prefix_length) { - return enhanced_route_add_subnet_range(table, network, prefix_length, &table->dynamic_subnets, &table->dynamic_subnet_count); + return enhanced_route_add_subnet_range(table, network, prefix_length, + &table->dynamic_subnets, &table->dynamic_subnet_count); } bool route_add_local_subnet(struct ROUTE_TABLE *table, uint32_t network, uint8_t prefix_length) { - return enhanced_route_add_subnet_range(table, network, prefix_length, &table->local_subnets, &table->local_subnet_count); + return enhanced_route_add_subnet_range(table, network, prefix_length, + &table->local_subnets, &table->local_subnet_count); } bool route_get_all_routes(const struct ROUTE_TABLE *table, uint32_t network, uint8_t prefix_length, - struct ROUTE_ENTRY **routes, size_t *count) { + struct ROUTE_ENTRY **routes, size_t *count) { if (!table || !routes || !count) return false; *routes = NULL; @@ -294,7 +543,6 @@ bool route_get_all_routes(const struct ROUTE_TABLE *table, uint32_t network, uin for (size_t i = 0; i < table->count; i++) { if (table->entries[i].network == network && table->entries[i].prefix_length == prefix_length) { (*routes)[result_index] = table->entries[i]; - result_index++; } } @@ -308,53 +556,40 @@ void route_table_print(const struct ROUTE_TABLE *table) { DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=== Routing Table ==="); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Total routes: %zu", table->count); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Dynamic subnets: %zu, Local subnets: %zu", - table->dynamic_subnet_count / 2, table->local_subnet_count / 2); + table->dynamic_subnet_count / 2, table->local_subnet_count / 2); + DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Local routes: %lu, Learned routes: %lu", + table->stats.local_routes, table->stats.learned_routes); + DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Cache hits: %lu, Misses: %lu", + table->stats.routes_lookup_hits - table->stats.routes_lookup_misses, + table->stats.routes_lookup_misses); if (table->count > 0) { DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routes:"); for (size_t i = 0; i < table->count; i++) { - const struct ROUTE_ENTRY *entry = &table->entries[i]; - char network_str[16], next_hop_str[16]; + const struct ROUTE_ENTRY *entry = &table->entries[i]; + char network_str[16]; ip_to_string(entry->network, network_str); - DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d -> %s [%s]", - i + 1, network_str, entry->prefix_length, next_hop_str, - route_type_to_string(entry->type)); + DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d -> %p [%s] (quality: %u)", + i + 1, network_str, entry->prefix_length, + (void*)entry->next_hop, + route_type_to_string(entry->type), + entry->metrics.metric); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Bandwidth: %uK, Loss: %.2f%%, Latency: %ums, Hops: %d", - entry->metrics.bandwidth_kbps, - entry->metrics.packet_loss_rate / 100.0, - entry->metrics.latency_ms, - entry->metrics.hop_count); + entry->metrics.bandwidth_kbps, + entry->metrics.packet_loss_rate / 100.0, + entry->metrics.latency_ms, + entry->metrics.hop_count); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Flags: %s%s%s%s", - (entry->flags & ROUTE_FLAG_ACTIVE) ? "ACTIVE " : "", - (entry->flags & ROUTE_FLAG_VALIDATED) ? "VALIDATED " : "", - (entry->flags & ROUTE_FLAG_ADVERTISED) ? "ADVERTISED " : "", - (entry->flags & ROUTE_FLAG_LEARNED) ? "LEARNED" : ""); + (entry->flags & ROUTE_FLAG_ACTIVE) ? "ACTIVE " : "", + (entry->flags & ROUTE_FLAG_VALIDATED) ? "VALIDATED " : "", + (entry->flags & ROUTE_FLAG_ADVERTISED) ? "ADVERTISED " : "", + (entry->flags & ROUTE_FLAG_LEARNED) ? "LEARNED" : ""); } } else { DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "No routes configured."); } } - -const char *route_type_to_string(route_type_t type) { - switch (type) { - case ROUTE_TYPE_STATIC: return "STATIC"; - case ROUTE_TYPE_DYNAMIC: return "DYNAMIC"; - case ROUTE_TYPE_LOCAL: return "LOCAL"; - case ROUTE_TYPE_LEARNED: return "LEARNED"; - default: return "UNKNOWN"; - } -} - -char *ip_to_string(uint32_t ip, char *buffer) { - if (!buffer) return NULL; - - struct in_addr addr; - addr.s_addr = htonl(ip); - strncpy(buffer, inet_ntoa(addr), 15); - buffer[15] = '\0'; - return buffer; -} diff --git a/src/route_lib.h b/src/route_lib.h index b52e7d70..c33010ce 100644 --- a/src/route_lib.h +++ b/src/route_lib.h @@ -137,4 +137,47 @@ bool route_update_quality(struct ROUTE_ENTRY *entry); */ void route_table_print(const struct ROUTE_TABLE *table); +/* + * @brief Парсит строку подсети в сетевой адрес и длину префикса + * + * @param subnet_str Строка подсети (например, "192.168.1.0/24") + * @param network Указатель для сохранения сетевого адреса + * @param prefix_length Указатель для сохранения длины префикса + * @return 0 при успехе, -1 при ошибке + */ +int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_length); + +/** + * @brief Добавляет диапазон динамических подсетей для валидации + * + * @param table Указатель на таблицу маршрутизации + * @param network Сетевой адрес + * @param prefix_length Длина префикса + * @return true при успехе, false при ошибке + */ +bool route_add_dynamic_subnet(struct ROUTE_TABLE *table, uint32_t network, uint8_t prefix_length); + +/** + * @brief Добавляет диапазон локальных подсетей для валидации + * + * @param table Указатель на таблицу маршрутизации + * @param network Сетевой адрес + * @param prefix_length Длина префикса + * @return true при успехе, false при ошибке + */ +bool route_add_local_subnet(struct ROUTE_TABLE *table, uint32_t network, uint8_t prefix_length); + +/** + * @brief Получает все маршруты для указанной подсети + * + * @param table Указатель на таблицу маршрутизации + * @param network Сетевой адрес + * @param prefix_length Длина префикса + * @param routes Указатель для сохранения массива маршрутов (выделяется calloc) + * @param count Указатель для сохранения количества маршрутов + * @return true при успехе, false при ошибке + */ +bool route_get_all_routes(const struct ROUTE_TABLE *table, uint32_t network, uint8_t prefix_length, + struct ROUTE_ENTRY **routes, size_t *count); + #endif // ROUTE_LIB_H diff --git a/src/utun.c b/src/utun.c index ed8c074d..857f4aaf 100644 --- a/src/utun.c +++ b/src/utun.c @@ -55,20 +55,6 @@ typedef struct { -// Parse subnet string -static int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_length) { - if (!subnet_str || !network || !prefix_length) return -1; - char ip[64]; - int prefix; - if (sscanf(subnet_str, "%[^/]/%d", ip, &prefix) != 2) return -1; - if (prefix < 0 || prefix > 32) return -1; - struct in_addr addr; - if (inet_pton(AF_INET, ip, &addr) != 1) return -1; - *network = addr.s_addr; - *prefix_length = (uint8_t)prefix; - return 0; -} - // Extract destination IPv4 address from packet static uint32_t get_dest_ip(const uint8_t *packet, size_t len) { if (len < 20) return 0; // Minimum IPv4 header size