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.
 
 
 
 
 
 

405 lines
14 KiB

/**
* @file enhanced_routing.c
* @brief Enhanced routing system with bandwidth support and route types
*/
#include "routing.h"
#include "connection.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <time.h>
#define INITIAL_ROUTE_CAPACITY 100
#define ROUTE_EXPANSION_FACTOR 2
#define MAX_SUBNET_VALIDATION_RANGES 50
static int compare_routes(const void *a, const void *b) {
const route_entry_t *route_a = (const route_entry_t *)a;
const route_entry_t *route_b = (const route_entry_t *)b;
// First compare by network
if (route_a->network != route_b->network) {
return route_a->network < route_b->network ? -1 : 1;
}
// Then by prefix length (longest prefix first)
if (route_b->prefix_length != route_a->prefix_length) {
return route_b->prefix_length - route_a->prefix_length;
}
// 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;
}
if (route_a->metrics.latency_ms != route_b->metrics.latency_ms) {
return route_a->metrics.latency_ms - route_b->metrics.latency_ms;
}
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;
}
return route_a->metrics.hop_count - route_b->metrics.hop_count;
}
routing_table_t *routing_table_create(void) {
routing_table_t *table = calloc(1, sizeof(routing_table_t));
if (!table) return NULL;
table->capacity = INITIAL_ROUTE_CAPACITY;
table->entries = calloc(table->capacity, sizeof(route_entry_t));
if (!table->entries) {
free(table);
return NULL;
}
// Initialize subnet validation arrays
table->dynamic_subnets = calloc(MAX_SUBNET_VALIDATION_RANGES, sizeof(uint32_t) * 2);
table->local_subnets = calloc(MAX_SUBNET_VALIDATION_RANGES, sizeof(uint32_t) * 2);
if (!table->dynamic_subnets || !table->local_subnets) {
free(table->entries);
free(table->dynamic_subnets);
free(table->local_subnets);
free(table);
return NULL;
}
return table;
}
void routing_table_destroy(routing_table_t *table) {
if (!table) return;
// Free route entries
free(table->entries);
// Free validation ranges
free(table->dynamic_subnets);
free(table->local_subnets);
free(table);
}
bool routing_table_insert(routing_table_t *table, const route_entry_t *entry) {
if (!table || !entry) return false;
// Validate the route
if (!routing_validate_route(table, entry->network, entry->prefix_length, entry->type)) {
char ip_str[16];
ip_to_string(entry->network, ip_str);
printf("[ROUTING] Ignoring invalid route: %s/%d (type: %s)\n",
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;
route_entry_t *new_entries = realloc(table->entries, new_capacity * sizeof(route_entry_t));
if (!new_entries) return false;
table->entries = new_entries;
table->capacity = new_capacity;
}
// Set timestamps and ensure route is active
route_entry_t new_entry = *entry;
new_entry.created_time = new_entry.last_update = (uint64_t)time(NULL) * 1000000; // Convert to microseconds
new_entry.last_used = 0;
// Ensure route is active by default
if (!(new_entry.flags & ROUTE_FLAG_ACTIVE)) {
new_entry.flags |= ROUTE_FLAG_ACTIVE;
}
// 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;
}
}
// Shift entries to make room
if (insert_pos < table->count) {
memmove(&table->entries[insert_pos + 1], &table->entries[insert_pos],
(table->count - insert_pos) * sizeof(route_entry_t));
}
table->entries[insert_pos] = new_entry;
table->count++;
table->stats.total_routes++;
switch (new_entry.type) {
case ROUTE_TYPE_STATIC: table->stats.static_routes++; break;
case ROUTE_TYPE_DYNAMIC: table->stats.dynamic_routes++; break;
case ROUTE_TYPE_LOCAL: table->stats.local_routes++; break;
case ROUTE_TYPE_LEARNED: table->stats.learned_routes++; break;
}
char ip_str[16];
ip_to_string(entry->network, ip_str);
printf("[ROUTING] Added route: %s/%d -> %s (type: %s, bandwidth: %uK)\n",
ip_str, entry->prefix_length,
ip_to_string(entry->next_hop_ip, ip_str),
route_type_to_string(entry->type), entry->metrics.bandwidth_kbps);
return true;
}
bool routing_table_delete(routing_table_t *table, uint32_t network, uint8_t prefix_length, uint32_t source_node_id) {
if (!table) return false;
for (size_t i = 0; i < table->count; i++) {
route_entry_t *entry = &table->entries[i];
if (entry->network == network &&
entry->prefix_length == prefix_length &&
entry->next_hop_ip == source_node_id) {
route_type_t route_type = entry->type;
// Shift remaining entries
if (i < table->count - 1) {
memmove(entry, &table->entries[i + 1],
(table->count - i - 1) * sizeof(route_entry_t));
}
table->count--;
table->stats.total_routes--;
switch (route_type) {
case ROUTE_TYPE_STATIC: table->stats.static_routes--; break;
case ROUTE_TYPE_DYNAMIC: table->stats.dynamic_routes--; break;
case ROUTE_TYPE_LOCAL: table->stats.local_routes--; break;
case ROUTE_TYPE_LEARNED: table->stats.learned_routes--; break;
}
char ip_str[16];
ip_to_string(network, ip_str);
printf("[ROUTING] Deleted route: %s/%d (source: %u)\n",
ip_str, prefix_length, source_node_id);
return true;
}
}
return false;
}
bool routing_table_lookup(routing_table_t *table, uint32_t dest_ip, route_entry_t *best_route) {
if (!table || !best_route) return false;
table->stats.lookup_count++;
// Find longest prefix match
for (size_t i = 0; i < table->count; i++) {
const route_entry_t *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);
printf("[ROUTING] Found route for %s: %s/%d -> %s\n",
ip_str, ip_to_string(entry->network, ip_str), entry->prefix_length,
ip_to_string(entry->next_hop_ip, ip_str));
table->stats.hit_count++;
return true;
}
}
}
return false;
}
bool routing_validate_route(routing_table_t *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:
table->stats.miss_count++;
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 enhanced_routing_add_subnet_range(routing_table_t *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;
// Expand array if needed
if (*ranges == NULL) {
*ranges = calloc(MAX_SUBNET_VALIDATION_RANGES, sizeof(uint32_t));
if (!*ranges) return false;
*count = 0;
}
// Add network and prefix length as a pair
(*ranges)[*count] = network;
(*ranges)[*count + 1] = prefix_length;
*count += 2;
char ip_str[16];
ip_to_string(network, ip_str);
printf("[ROUTING] Added subnet validation range: %s/%d\n", ip_str, prefix_length);
return true;
}
bool routing_add_dynamic_subnet(routing_table_t *table, uint32_t network, uint8_t prefix_length) {
return enhanced_routing_add_subnet_range(table, network, prefix_length, &table->dynamic_subnets, &table->dynamic_subnet_count);
}
bool routing_add_local_subnet(routing_table_t *table, uint32_t network, uint8_t prefix_length) {
return enhanced_routing_add_subnet_range(table, network, prefix_length, &table->local_subnets, &table->local_subnet_count);
}
bool routing_get_all_routes(const routing_table_t *table, uint32_t network, uint8_t prefix_length,
route_entry_t **routes, size_t *count) {
if (!table || !routes || !count) return false;
*routes = NULL;
*count = 0;
// Count matching routes
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].network == network && table->entries[i].prefix_length == prefix_length) {
(*count)++;
}
}
if (*count == 0) return true; // No routes found, but not an error
// Allocate result array
*routes = calloc(*count, sizeof(route_entry_t));
if (!*routes) {
*count = 0;
return false;
}
// Copy matching routes
size_t result_index = 0;
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++;
}
}
return true;
}
void routing_table_print(const routing_table_t *table) {
if (!table) return;
printf("\n=== Routing Table ===\n");
printf("Total routes: %zu\n", table->count);
printf("Dynamic subnets: %zu, Local subnets: %zu\n",
table->dynamic_subnet_count / 2, table->local_subnet_count / 2);
if (table->count > 0) {
printf("\nRoutes:\n");
for (size_t i = 0; i < table->count; i++) {
const route_entry_t *entry = &table->entries[i];
char network_str[16], next_hop_str[16];
ip_to_string(entry->network, network_str);
ip_to_string(entry->next_hop_ip, next_hop_str);
printf(" %zu: %s/%d -> %s [%s]\n",
i + 1, network_str, entry->prefix_length, next_hop_str,
route_type_to_string(entry->type));
printf(" Bandwidth: %uK, Loss: %.2f%%, Latency: %ums, Hops: %d\n",
entry->metrics.bandwidth_kbps,
entry->metrics.packet_loss_rate / 100.0,
entry->metrics.latency_ms,
entry->metrics.hop_count);
printf(" Flags: %s%s%s%s\n",
(entry->flags & ROUTE_FLAG_ACTIVE) ? "ACTIVE " : "",
(entry->flags & ROUTE_FLAG_VALIDATED) ? "VALIDATED " : "",
(entry->flags & ROUTE_FLAG_ADVERTISED) ? "ADVERTISED " : "",
(entry->flags & ROUTE_FLAG_LEARNED) ? "LEARNED" : "");
printf("\n");
}
} else {
printf("\nNo routes configured.\n");
}
}
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";
}
}
void routing_get_stats(const routing_table_t *table, routing_stats_t *stats) {
if (!table || !stats) return;
*stats = table->stats;
}
char *ip_to_string(uint32_t ip, char *buffer) {
if (!buffer) return NULL;
struct in_addr addr;
addr.s_addr = ip;
strncpy(buffer, inet_ntoa(addr), 15);
buffer[15] = '\0';
return buffer;
}