/** * @file enhanced_routing.c * @brief Enhanced routing system with bandwidth support and route types */ #include "routing.h" #include "etcp_connections.h" #include "../lib/debug_config.h" #include #include #include #include #include #define INITIAL_ROUTE_CAPACITY 100 #define ROUTE_EXPANSION_FACTOR 2 #define MAX_SUBNET_VALIDATION_RANGES 50 // Parse subnet string 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 = ntohl(addr.s_addr); *prefix_length = (uint8_t)prefix; return 0; } 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; // First compare by prefix length (longest prefix first) 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; } // 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; } struct routing_table *routing_table_create(void) { struct routing_table *table = calloc(1, sizeof(struct routing_table)); if (!table) { DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_create: failed to allocate memory for routing table"); return NULL; } table->capacity = INITIAL_ROUTE_CAPACITY; table->entries = calloc(table->capacity, sizeof(struct route_entry)); if (!table->entries) { DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_create: failed to allocate memory for %d route entries", table->capacity); 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) { DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_create: failed to allocate memory for subnet validation arrays"); free(table->entries); free(table->dynamic_subnets); free(table->local_subnets); free(table); return NULL; } return table; } void routing_table_destroy(struct routing_table *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(struct routing_table *table, const struct route_entry *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); 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; struct route_entry *new_entries = realloc(table->entries, new_capacity * sizeof(struct route_entry)); if (!new_entries) { DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_insert: failed to expand routing table to %zu entries", new_capacity); return false; } table->entries = new_entries; table->capacity = new_capacity; } // 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.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 table->stats.routes_added++; 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; } } return false; } bool routing_table_lookup(struct routing_table *table, uint32_t dest_ip, struct route_entry *best_route) { if (!table || !best_route) { DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "routing_table_lookup: invalid parameters (table=%p, best_route=%p)", table, best_route); return false; } table->stats.lookup_count++; // Find longest prefix match 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 -> %s", 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++; table->stats.routes_lookup_hits++; return true; } } } // No route found char ip_str[16]; ip_to_string(dest_ip, ip_str); DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "routing_table_lookup: no route found for destination IP %s", ip_str); return false; } bool routing_validate_route(struct routing_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: table->stats.routes_lookup_misses++; 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(struct routing_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; // 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); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Added subnet validation range: %s/%d", ip_str, prefix_length); return true; } bool routing_add_dynamic_subnet(struct routing_table *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(struct routing_table *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 struct routing_table *table, uint32_t network, uint8_t prefix_length, struct route_entry **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(struct route_entry)); 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 struct routing_table *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 struct route_entry *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"; } } 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; }