Browse Source
- Add etcp_find_free_local_link_id() function to allocate unique link IDs - Modify etcp_link_new() to auto-assign local_link_id, fail if none available - Update INIT_REQUEST (0x02/0x04) to send local_link_id after keepalive - Update INIT_RESPONSE (0x03/0x05) to include local_link_id - Parse remote_link_id from incoming handshake packets - Update protocol documentation in doc/etcp_protocol.txt - Add comprehensive unit test test_etcp_link_id.c New packet format: INIT_REQUEST: [code][node_id(8)][mtu(2)][keepalive(2)][link_id(1)][pubkey(64)] INIT_RESPONSE: [code][node_id(8)][mtu(2)][link_id(1)]nodeinfo-routing-update
8 changed files with 372 additions and 20 deletions
@ -0,0 +1,17 @@
|
||||
Архитектура etcp: |
||||
|
||||
## etcp_connections: |
||||
- обслуживает encrypt/decrypt и установку защищенного подключения. |
||||
- для одного etcp соединения может испольоваться несколько подключений одновременно (load balancing / filover) |
||||
|
||||
## etcp_loadbalancer: |
||||
- выбирает через какой маршрут отправить пакет |
||||
- ограничивает rate отправки чтобы не забивать очереди каналов |
||||
|
||||
## etcp: |
||||
- обеспечивает ретрансмиссии при передаче и сборку в правильной последовательности при приёме |
||||
|
||||
## etcp_metric: |
||||
- обеспечивает обновление метрик каналов etcp_connections |
||||
- вызывается при получении ack |
||||
|
||||
Binary file not shown.
@ -0,0 +1,279 @@
|
||||
// test_etcp_link_id.c - Unit test for etcp_find_free_local_link_id function
|
||||
// Tests: empty connection, full allocation, random deletion/addition cycles
|
||||
|
||||
#include "../src/etcp_connections.h" |
||||
#include "../src/etcp.h" |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <time.h> |
||||
#include <assert.h> |
||||
|
||||
// Mock ETCP_LINK structure for testing (minimal version)
|
||||
struct TEST_LINK { |
||||
struct TEST_LINK* next; |
||||
uint8_t local_link_id; |
||||
}; |
||||
|
||||
// Helper function to create a mock ETCP_CONN with empty links list
|
||||
static struct ETCP_CONN* create_test_conn(void) { |
||||
struct ETCP_CONN* conn = calloc(1, sizeof(struct ETCP_CONN)); |
||||
if (!conn) { |
||||
fprintf(stderr, "Failed to allocate test connection\n"); |
||||
exit(1); |
||||
} |
||||
conn->links = NULL; |
||||
return conn; |
||||
} |
||||
|
||||
// Helper function to add a link with specific id to connection
|
||||
static void add_link_with_id(struct ETCP_CONN* conn, uint8_t id) { |
||||
struct ETCP_LINK* link = calloc(1, sizeof(struct ETCP_LINK)); |
||||
if (!link) { |
||||
fprintf(stderr, "Failed to allocate test link\n"); |
||||
exit(1); |
||||
} |
||||
link->local_link_id = id; |
||||
link->next = conn->links; |
||||
conn->links = link; |
||||
} |
||||
|
||||
// Helper function to remove link with specific id
|
||||
static int remove_link_with_id(struct ETCP_CONN* conn, uint8_t id) { |
||||
struct ETCP_LINK** pp = &conn->links; |
||||
while (*pp) { |
||||
if ((*pp)->local_link_id == id) { |
||||
struct ETCP_LINK* to_remove = *pp; |
||||
*pp = (*pp)->next; |
||||
free(to_remove); |
||||
return 1; // Success
|
||||
} |
||||
pp = &(*pp)->next; |
||||
} |
||||
return 0; // Not found
|
||||
} |
||||
|
||||
// Helper function to count links
|
||||
static int count_links(struct ETCP_CONN* conn) { |
||||
int count = 0; |
||||
struct ETCP_LINK* link = conn->links; |
||||
while (link) { |
||||
count++; |
||||
link = link->next; |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
// Helper function to free all links
|
||||
static void free_all_links(struct ETCP_CONN* conn) { |
||||
struct ETCP_LINK* link = conn->links; |
||||
while (link) { |
||||
struct ETCP_LINK* next = link->next; |
||||
free(link); |
||||
link = next; |
||||
} |
||||
conn->links = NULL; |
||||
} |
||||
|
||||
// Test 1: NULL argument should return -1
|
||||
static void test_null_argument(void) { |
||||
printf("Test 1: NULL argument... "); |
||||
int result = etcp_find_free_local_link_id(NULL); |
||||
assert(result == -1); |
||||
printf("PASSED\n"); |
||||
} |
||||
|
||||
// Test 2: Empty connection should return 0
|
||||
static void test_empty_connection(void) { |
||||
printf("Test 2: Empty connection... "); |
||||
struct ETCP_CONN* conn = create_test_conn(); |
||||
int result = etcp_find_free_local_link_id(conn); |
||||
assert(result == 0); |
||||
free(conn); |
||||
printf("PASSED\n"); |
||||
} |
||||
|
||||
// Test 3: Fill all 256, randomly delete and add back (30 cycles)
|
||||
static void test_random_deletion_addition(void) { |
||||
printf("Test 3: Random deletion/addition cycles (30 iterations)...\n"); |
||||
|
||||
for (int cycle = 0; cycle < 30; cycle++) { |
||||
struct ETCP_CONN* conn = create_test_conn(); |
||||
uint8_t used[256] = {0}; |
||||
|
||||
// Fill all 256 ids
|
||||
for (int i = 0; i < 256; i++) { |
||||
add_link_with_id(conn, i); |
||||
used[i] = 1; |
||||
} |
||||
|
||||
assert(count_links(conn) == 256); |
||||
|
||||
// Random number of links to delete (30-100)
|
||||
int num_to_delete = 30 + (rand() % 71); |
||||
int deleted_ids[100]; |
||||
int deleted_count = 0; |
||||
|
||||
// Randomly delete links
|
||||
while (deleted_count < num_to_delete) { |
||||
int id = rand() % 256; |
||||
if (used[id] && remove_link_with_id(conn, id)) { |
||||
used[id] = 0; |
||||
deleted_ids[deleted_count++] = id; |
||||
} |
||||
} |
||||
|
||||
assert(count_links(conn) == 256 - num_to_delete); |
||||
|
||||
// Add back the same number of links
|
||||
int added_ids[100]; |
||||
int added_count = 0; |
||||
int prev_id = -1; |
||||
|
||||
while (added_count < num_to_delete) { |
||||
int new_id = etcp_find_free_local_link_id(conn); |
||||
|
||||
// Verify the id is actually free
|
||||
assert(new_id >= 0 && new_id < 256); |
||||
assert(used[new_id] == 0); |
||||
|
||||
// Verify ids are returned in ascending order (filling gaps from smallest)
|
||||
assert(new_id > prev_id); |
||||
prev_id = new_id; |
||||
|
||||
// Add the link
|
||||
add_link_with_id(conn, new_id); |
||||
used[new_id] = 1; |
||||
added_ids[added_count++] = new_id; |
||||
} |
||||
|
||||
assert(count_links(conn) == 256); |
||||
|
||||
// Verify all ids are marked as used
|
||||
for (int i = 0; i < 256; i++) { |
||||
assert(used[i] == 1); |
||||
} |
||||
|
||||
// Verify no duplicates
|
||||
uint8_t check[256] = {0}; |
||||
struct ETCP_LINK* link = conn->links; |
||||
while (link) { |
||||
assert(check[link->local_link_id] == 0); // No duplicate
|
||||
check[link->local_link_id] = 1; |
||||
link = link->next; |
||||
} |
||||
|
||||
// Cleanup
|
||||
free_all_links(conn); |
||||
free(conn); |
||||
|
||||
if ((cycle + 1) % 10 == 0) { |
||||
printf(" Completed %d cycles...\n", cycle + 1); |
||||
} |
||||
} |
||||
|
||||
printf("Test 3: PASSED (all 30 cycles)\n"); |
||||
} |
||||
|
||||
// Test 4: All 256 occupied should return -1
|
||||
static void test_all_occupied(void) { |
||||
printf("Test 4: All 256 occupied... "); |
||||
struct ETCP_CONN* conn = create_test_conn(); |
||||
|
||||
// Fill all 256
|
||||
for (int i = 0; i < 256; i++) { |
||||
add_link_with_id(conn, i); |
||||
} |
||||
|
||||
int result = etcp_find_free_local_link_id(conn); |
||||
assert(result == -1); |
||||
|
||||
free_all_links(conn); |
||||
free(conn); |
||||
printf("PASSED\n"); |
||||
} |
||||
|
||||
// Test 5: Delete specific ids (5, 10, 100), should return 5
|
||||
static void test_specific_deletion(void) { |
||||
printf("Test 5: Delete ids 5, 10, 100... "); |
||||
struct ETCP_CONN* conn = create_test_conn(); |
||||
|
||||
// Fill all 256
|
||||
for (int i = 0; i < 256; i++) { |
||||
add_link_with_id(conn, i); |
||||
} |
||||
|
||||
// Delete specific ids
|
||||
assert(remove_link_with_id(conn, 5)); |
||||
assert(remove_link_with_id(conn, 10)); |
||||
assert(remove_link_with_id(conn, 100)); |
||||
|
||||
// Should return 5 (smallest free)
|
||||
int result = etcp_find_free_local_link_id(conn); |
||||
assert(result == 5); |
||||
|
||||
free_all_links(conn); |
||||
free(conn); |
||||
printf("PASSED\n"); |
||||
} |
||||
|
||||
// Test 6: Delete id 0, should return 0
|
||||
static void test_delete_zero(void) { |
||||
printf("Test 6: Delete id 0... "); |
||||
struct ETCP_CONN* conn = create_test_conn(); |
||||
|
||||
// Fill all 256
|
||||
for (int i = 0; i < 256; i++) { |
||||
add_link_with_id(conn, i); |
||||
} |
||||
|
||||
// Delete id 0
|
||||
assert(remove_link_with_id(conn, 0)); |
||||
|
||||
// Should return 0
|
||||
int result = etcp_find_free_local_link_id(conn); |
||||
assert(result == 0); |
||||
|
||||
free_all_links(conn); |
||||
free(conn); |
||||
printf("PASSED\n"); |
||||
} |
||||
|
||||
// Test 7: Delete all, should return 0
|
||||
static void test_delete_all(void) { |
||||
printf("Test 7: Delete all... "); |
||||
struct ETCP_CONN* conn = create_test_conn(); |
||||
|
||||
// Fill all 256
|
||||
for (int i = 0; i < 256; i++) { |
||||
add_link_with_id(conn, i); |
||||
} |
||||
|
||||
// Delete all
|
||||
free_all_links(conn); |
||||
|
||||
// Should return 0
|
||||
int result = etcp_find_free_local_link_id(conn); |
||||
assert(result == 0); |
||||
|
||||
free(conn); |
||||
printf("PASSED\n"); |
||||
} |
||||
|
||||
int main(void) { |
||||
printf("=== ETCP Link ID Unit Tests ===\n\n"); |
||||
|
||||
// Initialize random seed
|
||||
srand(time(NULL)); |
||||
|
||||
test_null_argument(); |
||||
test_empty_connection(); |
||||
test_random_deletion_addition(); |
||||
test_all_occupied(); |
||||
test_specific_deletion(); |
||||
test_delete_zero(); |
||||
test_delete_all(); |
||||
|
||||
printf("\n=== All tests PASSED ===\n"); |
||||
return 0; |
||||
} |
||||
Binary file not shown.
Loading…
Reference in new issue