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.
414 lines
15 KiB
414 lines
15 KiB
// tests/test_routing_full.c - Comprehensive routing table tests |
|
#include "routing.h" |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <assert.h> |
|
#include <arpa/inet.h> |
|
#include <sys/time.h> |
|
|
|
#define TEST_ASSERT(cond, msg) do { \ |
|
if (!(cond)) { \ |
|
fprintf(stderr, "FAIL: %s\n", msg); \ |
|
return -1; \ |
|
} \ |
|
} while(0) |
|
|
|
#define TEST_PASS(msg) printf(" ✓ %s\n", msg) |
|
|
|
// Helper: Convert IP string to uint32_t |
|
static uint32_t ip_to_uint32(const char* ip) { |
|
struct in_addr addr; |
|
inet_pton(AF_INET, ip, &addr); |
|
return ntohl(addr.s_addr); |
|
} |
|
|
|
// Helper: Convert uint32_t to IP string |
|
static const char* uint32_to_ip(uint32_t ip, char* buf) { |
|
struct in_addr addr; |
|
addr.s_addr = htonl(ip); |
|
return inet_ntop(AF_INET, &addr, buf, INET_ADDRSTRLEN); |
|
} |
|
|
|
// Test 1: Basic create and destroy |
|
static int test_routing_table_create_destroy(void) { |
|
printf("\n=== Test 1: Create and Destroy ===\n"); |
|
|
|
struct routing_table* table = routing_table_create(); |
|
TEST_ASSERT(table != NULL, "Table creation failed"); |
|
TEST_ASSERT(table->entries != NULL, "Entries should be allocated initially"); |
|
TEST_ASSERT(table->count == 0, "Count should be 0"); |
|
TEST_ASSERT(table->capacity == 100, "Capacity should be 100 (INITIAL_ROUTE_CAPACITY)"); |
|
TEST_PASS("Table created successfully"); |
|
|
|
routing_table_destroy(table); |
|
TEST_PASS("Table destroyed successfully"); |
|
|
|
return 0; |
|
} |
|
|
|
// Test 2: Insert and lookup basic routes |
|
static int test_routing_insert_lookup_basic(void) { |
|
printf("\n=== Test 2: Insert and Lookup Basic Routes ===\n"); |
|
|
|
struct routing_table* table = routing_table_create(); |
|
TEST_ASSERT(table != NULL, "Table creation failed"); |
|
|
|
// Insert default route |
|
struct route_entry default_route = { |
|
.network = 0, // 0.0.0.0 |
|
.prefix_length = 0, |
|
.next_hop_ip = ip_to_uint32("192.168.1.1"), |
|
.type = ROUTE_TYPE_STATIC, |
|
.flags = ROUTE_FLAG_ACTIVE |
|
}; |
|
|
|
bool result = routing_table_insert(table, &default_route); |
|
TEST_ASSERT(result == true, "Failed to insert default route"); |
|
TEST_ASSERT(table->count == 1, "Count should be 1"); |
|
TEST_PASS("Default route inserted"); |
|
|
|
// Insert LAN route |
|
struct route_entry lan_route = { |
|
.network = ip_to_uint32("192.168.1.0"), |
|
.prefix_length = 24, |
|
.next_hop_ip = 0, // Directly connected |
|
.type = ROUTE_TYPE_LOCAL, |
|
.flags = ROUTE_FLAG_ACTIVE |
|
}; |
|
|
|
result = routing_table_insert(table, &lan_route); |
|
TEST_ASSERT(result == true, "Failed to insert LAN route"); |
|
TEST_ASSERT(table->count == 2, "Count should be 2"); |
|
TEST_PASS("LAN route inserted"); |
|
|
|
// Test lookup for LAN IP |
|
struct route_entry found_route; |
|
uint32_t test_ip = ip_to_uint32("192.168.1.100"); |
|
result = routing_table_lookup(table, test_ip, &found_route); |
|
TEST_ASSERT(result == true, "Failed to lookup LAN IP"); |
|
TEST_ASSERT(found_route.network == lan_route.network, "Wrong route found"); |
|
TEST_PASS("LAN IP lookup correct"); |
|
|
|
// Test lookup for external IP (should use default) |
|
uint32_t external_ip = ip_to_uint32("8.8.8.8"); |
|
result = routing_table_lookup(table, external_ip, &found_route); |
|
TEST_ASSERT(result == true, "Failed to lookup external IP"); |
|
TEST_ASSERT(found_route.network == default_route.network, "Default route not found"); |
|
TEST_PASS("Default route lookup correct"); |
|
|
|
routing_table_destroy(table); |
|
TEST_PASS("Table destroyed"); |
|
|
|
return 0; |
|
} |
|
|
|
// Test 3: Longest Prefix Match |
|
static int test_routing_longest_prefix_match(void) { |
|
printf("\n=== Test 3: Longest Prefix Match ===\n"); |
|
|
|
struct routing_table* table = routing_table_create(); |
|
TEST_ASSERT(table != NULL, "Table creation failed"); |
|
|
|
// Insert multiple overlapping routes |
|
struct route_entry route_24 = { |
|
.network = ip_to_uint32("10.0.0.0"), |
|
.prefix_length = 24, |
|
.next_hop_ip = ip_to_uint32("192.168.1.1"), |
|
.type = ROUTE_TYPE_STATIC |
|
}; |
|
routing_table_insert(table, &route_24); |
|
|
|
struct route_entry route_16 = { |
|
.network = ip_to_uint32("10.0.0.0"), |
|
.prefix_length = 16, |
|
.next_hop_ip = ip_to_uint32("192.168.1.2"), |
|
.type = ROUTE_TYPE_STATIC |
|
}; |
|
routing_table_insert(table, &route_16); |
|
|
|
struct route_entry route_8 = { |
|
.network = ip_to_uint32("10.0.0.0"), |
|
.prefix_length = 8, |
|
.next_hop_ip = ip_to_uint32("192.168.1.3"), |
|
.type = ROUTE_TYPE_STATIC |
|
}; |
|
routing_table_insert(table, &route_8); |
|
|
|
TEST_ASSERT(table->count == 3, "Should have 3 routes"); |
|
TEST_PASS("3 overlapping routes inserted"); |
|
|
|
// Lookup should find /24 route (longest prefix) |
|
struct route_entry found; |
|
uint32_t test_ip = ip_to_uint32("10.0.0.50"); |
|
bool result = routing_table_lookup(table, test_ip, &found); |
|
TEST_ASSERT(result == true, "Lookup failed"); |
|
TEST_ASSERT(found.prefix_length == 24, "Should find /24 route"); |
|
TEST_ASSERT(found.next_hop_ip == route_24.next_hop_ip, "Wrong next hop"); |
|
TEST_PASS("Longest prefix match works"); |
|
|
|
routing_table_destroy(table); |
|
return 0; |
|
} |
|
|
|
// Test 4: Delete routes |
|
static int test_routing_delete(void) { |
|
printf("\n=== Test 4: Delete Routes ===\n"); |
|
|
|
struct routing_table* table = routing_table_create(); |
|
|
|
// Insert 3 routes |
|
struct route_entry routes[3] = { |
|
{ip_to_uint32("10.0.1.0"), 24, ip_to_uint32("192.168.1.1"), NULL, ROUTE_TYPE_STATIC}, |
|
{ip_to_uint32("10.0.2.0"), 24, ip_to_uint32("192.168.1.2"), NULL, ROUTE_TYPE_STATIC}, |
|
{ip_to_uint32("10.0.3.0"), 24, ip_to_uint32("192.168.1.3"), NULL, ROUTE_TYPE_STATIC} |
|
}; |
|
|
|
for (int i = 0; i < 3; i++) { |
|
routing_table_insert(table, &routes[i]); |
|
} |
|
TEST_ASSERT(table->count == 3, "Should have 3 routes"); |
|
TEST_PASS("3 routes inserted"); |
|
|
|
// Delete middle route |
|
bool result = routing_table_delete(table, routes[1].network, routes[1].prefix_length, routes[1].next_hop_ip); |
|
TEST_ASSERT(result == true, "Delete failed"); |
|
TEST_ASSERT(table->count == 2, "Count should be 2"); |
|
TEST_PASS("Route deleted"); |
|
|
|
// Verify deleted route not found, but other routes still work |
|
struct route_entry found; |
|
result = routing_table_lookup(table, ip_to_uint32("10.0.1.50"), &found); |
|
TEST_ASSERT(result == true, "Should find remaining route 1"); |
|
TEST_ASSERT(found.network == routes[0].network, "Should find route 1"); |
|
|
|
result = routing_table_lookup(table, ip_to_uint32("10.0.3.50"), &found); |
|
TEST_ASSERT(result == true, "Should find remaining route 3"); |
|
TEST_ASSERT(found.network == routes[2].network, "Should find route 3"); |
|
TEST_PASS("Remaining routes functional"); |
|
|
|
routing_table_destroy(table); |
|
return 0; |
|
} |
|
|
|
// Test 5: Route types and validation |
|
static int test_routing_types_validation(void) { |
|
printf("\n=== Test 5: Route Types and Validation ===\n"); |
|
|
|
struct routing_table* table = routing_table_create(); |
|
|
|
// Insert different route types |
|
struct route_entry static_route = { |
|
.network = ip_to_uint32("192.168.1.0"), |
|
.prefix_length = 24, |
|
.next_hop_ip = ip_to_uint32("10.0.0.1"), |
|
.next_hop = NULL, |
|
.type = ROUTE_TYPE_STATIC |
|
}; |
|
routing_table_insert(table, &static_route); |
|
|
|
struct route_entry local_route = { |
|
.network = ip_to_uint32("192.168.2.0"), |
|
.prefix_length = 24, |
|
.next_hop_ip = 0, |
|
.next_hop = NULL, |
|
.type = ROUTE_TYPE_LOCAL |
|
}; |
|
routing_table_insert(table, &local_route); |
|
|
|
struct route_entry dynamic_route = { |
|
.network = ip_to_uint32("192.168.3.0"), |
|
.prefix_length = 24, |
|
.next_hop_ip = ip_to_uint32("10.0.0.2"), |
|
.next_hop = NULL, |
|
.type = ROUTE_TYPE_DYNAMIC |
|
}; |
|
routing_table_insert(table, &dynamic_route); |
|
TEST_ASSERT(table->count == 3, "Should have 3 routes"); |
|
TEST_PASS("Different route types inserted"); |
|
|
|
// Validate routes |
|
bool result = routing_validate_route(table, static_route.network, |
|
static_route.prefix_length, ROUTE_TYPE_STATIC); |
|
TEST_ASSERT(result == true, "Static route validation failed"); |
|
TEST_PASS("Route validation works"); |
|
|
|
routing_table_destroy(table); |
|
return 0; |
|
} |
|
|
|
// Test 6: Statistics |
|
static int test_routing_statistics(void) { |
|
printf("\n=== Test 6: Statistics ===\n"); |
|
|
|
struct routing_table* table = routing_table_create(); |
|
|
|
// Initial stats |
|
TEST_ASSERT(table->stats.total_routes == 0, "Initial total_routes should be 0"); |
|
TEST_ASSERT(table->stats.routes_added == 0, "Initial routes_added should be 0"); |
|
TEST_PASS("Initial stats zero"); |
|
|
|
// Add routes |
|
struct route_entry route = { |
|
.network = ip_to_uint32("10.0.0.0"), |
|
.prefix_length = 24, |
|
.next_hop_ip = ip_to_uint32("192.168.1.1"), |
|
.type = ROUTE_TYPE_STATIC |
|
}; |
|
routing_table_insert(table, &route); |
|
|
|
TEST_ASSERT(table->stats.total_routes == 1, "total_routes should be 1"); |
|
TEST_ASSERT(table->stats.routes_added == 1, "routes_added should be 1"); |
|
TEST_ASSERT(table->stats.static_routes == 1, "static_routes should be 1"); |
|
TEST_PASS("Stats updated on insert"); |
|
|
|
// Perform lookups |
|
struct route_entry found; |
|
for (int i = 0; i < 5; i++) { |
|
routing_table_lookup(table, ip_to_uint32("10.0.0.100"), &found); |
|
} |
|
|
|
TEST_ASSERT(table->stats.lookup_count == 5, "lookup_count should be 5"); |
|
TEST_ASSERT(table->stats.hit_count == 5, "hit_count should be 5"); |
|
TEST_ASSERT(table->stats.routes_lookup_hits == 5, "routes_lookup_hits should be 5"); |
|
TEST_PASS("Lookup stats tracked"); |
|
|
|
routing_table_destroy(table); |
|
return 0; |
|
} |
|
|
|
// Test 7: Edge cases |
|
static int test_routing_edge_cases(void) { |
|
printf("\n=== Test 7: Edge Cases ===\n"); |
|
|
|
struct routing_table* table = routing_table_create(); |
|
|
|
// Lookup in empty table |
|
struct route_entry found; |
|
bool result = routing_table_lookup(table, ip_to_uint32("10.0.0.1"), &found); |
|
TEST_ASSERT(result == false, "Lookup in empty table should fail"); |
|
TEST_PASS("Empty table lookup handled"); |
|
|
|
// Delete non-existent route |
|
result = routing_table_delete(table, ip_to_uint32("10.0.0.0"), 24, 0); |
|
TEST_ASSERT(result == false, "Delete non-existent should fail"); |
|
TEST_PASS("Delete non-existent handled"); |
|
|
|
// Insert with prefix 32 |
|
struct route_entry host_route = { |
|
.network = ip_to_uint32("10.0.0.100"), |
|
.prefix_length = 32, |
|
.next_hop_ip = ip_to_uint32("192.168.1.1"), |
|
.type = ROUTE_TYPE_STATIC |
|
}; |
|
result = routing_table_insert(table, &host_route); |
|
TEST_ASSERT(result == true, "Failed to insert /32 route"); |
|
TEST_ASSERT(table->count == 1, "Should have 1 route"); |
|
TEST_PASS("/32 route inserted"); |
|
|
|
// Insert with prefix 0 (default) |
|
struct route_entry default_route = { |
|
.network = 0, |
|
.prefix_length = 0, |
|
.next_hop_ip = ip_to_uint32("192.168.1.1"), |
|
.type = ROUTE_TYPE_STATIC |
|
}; |
|
result = routing_table_insert(table, &default_route); |
|
TEST_ASSERT(result == true, "Failed to insert default route"); |
|
TEST_ASSERT(table->count == 2, "Should have 2 routes"); |
|
TEST_PASS("Default route inserted"); |
|
|
|
routing_table_destroy(table); |
|
return 0; |
|
} |
|
|
|
// Test 8: Performance test (basic) |
|
static int test_routing_performance(void) { |
|
printf("\n=== Test 8: Performance ===\n"); |
|
|
|
struct routing_table* table = routing_table_create(); |
|
|
|
// Insert 1000 routes |
|
struct timeval start, end; |
|
gettimeofday(&start, NULL); |
|
|
|
for (int i = 0; i < 1000; i++) { |
|
struct route_entry route = { |
|
.network = htonl((10 << 24) | i), // 10.0.i.0 |
|
.prefix_length = 24, |
|
.next_hop_ip = ip_to_uint32("192.168.1.1"), |
|
.type = ROUTE_TYPE_STATIC |
|
}; |
|
routing_table_insert(table, &route); |
|
} |
|
|
|
gettimeofday(&end, NULL); |
|
long insert_time_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); |
|
printf(" Inserted 1000 routes in %ld us (%ld ns/op)\n", insert_time_us, insert_time_us * 1000 / 1000); |
|
TEST_PASS("Bulk insert completed"); |
|
|
|
// Perform 10000 lookups |
|
gettimeofday(&start, NULL); |
|
struct route_entry found; |
|
|
|
for (int i = 0; i < 10000; i++) { |
|
uint32_t ip = htonl((10 << 24) | (i % 1000)); // Random IPs in 10.0.x.x range |
|
routing_table_lookup(table, ip, &found); |
|
} |
|
|
|
gettimeofday(&end, NULL); |
|
long lookup_time_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); |
|
printf(" Performed 10000 lookups in %ld us (%ld ns/op)\n", lookup_time_us, lookup_time_us * 1000 / 10000); |
|
TEST_PASS("Bulk lookup completed"); |
|
|
|
routing_table_destroy(table); |
|
TEST_PASS("Performance test passed"); |
|
|
|
return 0; |
|
} |
|
|
|
// Main test runner |
|
int main(void) { |
|
printf("╔═══════════════════════════════════════════════════════════════╗\n"); |
|
printf("║ uTun Routing Module Comprehensive Tests ║\n"); |
|
printf("╚═══════════════════════════════════════════════════════════════╝\n"); |
|
|
|
int tests_passed = 0; |
|
int total_tests = 0; |
|
|
|
struct test_case { |
|
const char* name; |
|
int (*func)(void); |
|
} test_cases[] = { |
|
{"Create/Destroy", test_routing_table_create_destroy}, |
|
{"Insert/Lookup Basic", test_routing_insert_lookup_basic}, |
|
{"Longest Prefix Match", test_routing_longest_prefix_match}, |
|
{"Delete Routes", test_routing_delete}, |
|
{"Route Types & Validation", test_routing_types_validation}, |
|
{"Statistics", test_routing_statistics}, |
|
{"Edge Cases", test_routing_edge_cases}, |
|
{"Performance", test_routing_performance}, |
|
{NULL, NULL} |
|
}; |
|
|
|
for (int i = 0; test_cases[i].func != NULL; i++) { |
|
total_tests++; |
|
printf("\n[TEST %d/%d] Running: %s\n", i + 1, 8, test_cases[i].name); |
|
|
|
if (test_cases[i].func() == 0) { |
|
tests_passed++; |
|
printf("[TEST %d/%d] ✓ PASSED\n", i + 1, 8); |
|
} else { |
|
printf("[TEST %d/%d] ✗ FAILED\n", i + 1, 8); |
|
} |
|
} |
|
|
|
printf("\n╔═══════════════════════════════════════════════════════════════╗\n"); |
|
printf("║ TEST SUMMARY ║\n"); |
|
printf("╠═══════════════════════════════════════════════════════════════╣\n"); |
|
printf("║ Tests Passed: %-3d / %-3d ║\n", tests_passed, total_tests); |
|
printf("║ Coverage: Routing Module Core Functions ║\n"); |
|
printf("╚═══════════════════════════════════════════════════════════════╝\n"); |
|
|
|
return (tests_passed == total_tests) ? 0 : 1; |
|
} |