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