/** * @file enhanced_routing.c * @brief Enhanced routing system with bandwidth support and route types */ #include "routing.h" #include "connection.h" #include #include #include #include #include #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; }