Browse Source

Add NAT detection with burst ping, test timing, fix port conflicts

- route_ping.c: burst mode - send next ping immediately on response
- route_ping.h: add target_ipv4/target_port/pubkey to BGP_PING_REQUEST
- test_nat_detection.c: add test for ping without embedded pubkey
- test_etcp_simple_traffic.c: use dynamic ports + fix timebase units
- Makefile.am: show test execution time in ms
congestion
Evgeny 6 months ago
parent
commit
7cdb84c7a2
  1. 70
      src/route_ping.c
  2. 5
      src/route_ping.h
  3. 9
      tests/Makefile.am
  4. 77
      tests/test_etcp_simple_traffic.c
  5. 53
      tests/test_nat_detection.c

70
src/route_ping.c

@ -99,8 +99,14 @@ static void route_ping_cb(int success, uint16_t rtt, void* arg, uint64_t nonce,
series->rtt_sum += rtt; series->rtt_sum += rtt;
} }
if (series->count_sent < series->count_total && series->req->reply_conn) { if (series->count_sent < series->count_total && series->req->reply_conn) {
if (success) {
// Ответ получен — следующий пинг сразу (burst mode)
route_ping_next(series);
} else {
// Таймаут — ждем interval_ms перед retry
struct UTUN_INSTANCE* inst = series->req->reply_conn->instance; struct UTUN_INSTANCE* inst = series->req->reply_conn->instance;
series->next_timer = uasync_set_timeout(inst->ua, series->interval_ms * 10, series, route_ping_next); series->next_timer = uasync_set_timeout(inst->ua, series->interval_ms * 10, series, route_ping_next);
}
return; return;
} }
// серия завершена // серия завершена
@ -236,7 +242,7 @@ int route_ping_send_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64
return -3; return -3;
} }
e->dgram = (uint8_t*)req_pkt; e->dgram = (uint8_t*)req_pkt;
e->len = sizeof(struct BGP_PING_REQUEST); e->len = offsetof(struct BGP_PING_REQUEST, target_ipv4);
int ret = etcp_send(to_conn, e); int ret = etcp_send(to_conn, e);
if (ret != 0) { if (ret != 0) {
@ -275,8 +281,7 @@ int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, u
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr: invalid args"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr: invalid args");
return -1; return -1;
} }
size_t extra = 6 + (pubkey ? SC_PUBKEY_SIZE : 0); size_t pkt_size = pubkey ? sizeof(struct BGP_PING_REQUEST) : offsetof(struct BGP_PING_REQUEST, pubkey);
size_t pkt_size = sizeof(struct BGP_PING_REQUEST) + extra;
struct BGP_PING_REQUEST* req_pkt = u_calloc(1, pkt_size); struct BGP_PING_REQUEST* req_pkt = u_calloc(1, pkt_size);
if (!req_pkt) { if (!req_pkt) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr: alloc failed"); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr: alloc failed");
@ -289,16 +294,13 @@ int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, u
req_pkt->count = count; req_pkt->count = count;
req_pkt->interval_ms = interval_ms; req_pkt->interval_ms = interval_ms;
req_pkt->timeout_ms = timeout_ms; req_pkt->timeout_ms = timeout_ms;
req_pkt->target_ipv4[0] = (target_ip >> 24) & 0xFF;
uint8_t* tail = (uint8_t*)req_pkt + sizeof(struct BGP_PING_REQUEST); req_pkt->target_ipv4[1] = (target_ip >> 16) & 0xFF;
tail[0] = (target_ip >> 24) & 0xFF; req_pkt->target_ipv4[2] = (target_ip >> 8) & 0xFF;
tail[1] = (target_ip >> 16) & 0xFF; req_pkt->target_ipv4[3] = target_ip & 0xFF;
tail[2] = (target_ip >> 8) & 0xFF; req_pkt->target_port = target_port;
tail[3] = target_ip & 0xFF;
tail[4] = (target_port >> 8) & 0xFF;
tail[5] = target_port & 0xFF;
if (pubkey) { if (pubkey) {
memcpy(tail + 6, pubkey, SC_PUBKEY_SIZE); memcpy(req_pkt->pubkey, pubkey, SC_PUBKEY_SIZE);
} }
struct ll_entry* e = queue_entry_new(0); struct ll_entry* e = queue_entry_new(0);
@ -352,8 +354,10 @@ void route_ping_destroy_pending(struct ROUTE_BGP* bgp) {
} }
void route_ping_handle_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) { void route_ping_handle_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) {
if (!bgp || !from_conn || !data || len < sizeof(struct BGP_PING_REQUEST)) { const size_t base_len = offsetof(struct BGP_PING_REQUEST, target_ipv4);
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_handle_req: invalid args"); const size_t addr_len = offsetof(struct BGP_PING_REQUEST, pubkey);
if (!bgp || !from_conn || !data || (len != base_len && len != addr_len && len != sizeof(struct BGP_PING_REQUEST))) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_handle_req: invalid args len=%zu", len);
return; return;
} }
const struct BGP_PING_REQUEST* req_pkt = (const struct BGP_PING_REQUEST*)data; const struct BGP_PING_REQUEST* req_pkt = (const struct BGP_PING_REQUEST*)data;
@ -367,29 +371,34 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, c
struct NODEINFO_Q* target = NULL; struct NODEINFO_Q* target = NULL;
const uint8_t* embedded_pubkey = NULL; const uint8_t* embedded_pubkey = NULL;
if (len >= sizeof(struct BGP_PING_REQUEST) + 6) { if (len == sizeof(struct BGP_PING_REQUEST) || len == addr_len) {
// Custom target address provided (for NAT ping) // Extended packet with custom target (with or without pubkey)
const uint8_t* tail = data + sizeof(struct BGP_PING_REQUEST); custom_socket.addr[0] = req_pkt->target_ipv4[0];
custom_socket.addr[0] = tail[0]; custom_socket.addr[1] = req_pkt->target_ipv4[1];
custom_socket.addr[1] = tail[1]; custom_socket.addr[2] = req_pkt->target_ipv4[2];
custom_socket.addr[2] = tail[2]; custom_socket.addr[3] = req_pkt->target_ipv4[3];
custom_socket.addr[3] = tail[3]; custom_socket.port = req_pkt->target_port;
custom_socket.port = (tail[4] << 8) | tail[5];
custom_socket.type = 0; custom_socket.type = 0;
custom_socket.id = 0; custom_socket.id = 0;
sockets_v4 = &custom_socket; sockets_v4 = &custom_socket;
sock_count_v4 = 1; sock_count_v4 = 1;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_ping_handle_req: using custom target %d.%d.%d.%d:%u for node %016llx", DEBUG_INFO(DEBUG_CATEGORY_BGP, "route_ping_handle_req: using custom target %d.%d.%d.%d:%u for node %016llx",
tail[0], tail[1], tail[2], tail[3], custom_socket.port, req_pkt->target_ipv4[0], req_pkt->target_ipv4[1], req_pkt->target_ipv4[2], req_pkt->target_ipv4[3],
(unsigned long long)req_pkt->node_id); req_pkt->target_port, (unsigned long long)req_pkt->node_id);
// Check for embedded pubkey // Check if embedded pubkey is non-zero (only when full packet length)
if (len >= sizeof(struct BGP_PING_REQUEST) + 6 + SC_PUBKEY_SIZE) { if (len == sizeof(struct BGP_PING_REQUEST)) {
embedded_pubkey = tail + 6; int pubkey_zero = 1;
for (int i = 0; i < SC_PUBKEY_SIZE; i++) {
if (req_pkt->pubkey[i] != 0) { pubkey_zero = 0; break; }
}
if (!pubkey_zero) {
embedded_pubkey = req_pkt->pubkey;
}
} }
// For custom target, we still need target node for pubkey lookup if not embedded // For custom target, we still need target node for pubkey lookup if not embedded
target = route_bgp_get_node(bgp, req_pkt->node_id); target = route_bgp_get_node(bgp, req_pkt->node_id);
} else { } else if (len == base_len) {
// No custom target - look up nodeinfo for advertised sockets // Base packet without custom target - use nodeinfo sockets
target = route_bgp_get_node(bgp, req_pkt->node_id); target = route_bgp_get_node(bgp, req_pkt->node_id);
if (!target) { if (!target) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "route_ping_handle_req: node %016llx not found", DEBUG_WARN(DEBUG_CATEGORY_BGP, "route_ping_handle_req: node %016llx not found",
@ -407,6 +416,9 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, c
} }
sock_count_v4 = get_node_v4_sockets(target, &sockets_v4); sock_count_v4 = get_node_v4_sockets(target, &sockets_v4);
sock_count_v6 = get_node_v6_sockets(target, &sockets_v6); sock_count_v6 = get_node_v6_sockets(target, &sockets_v6);
} else {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "route_ping_handle_req: unexpected packet size %zu", len);
return;
} }
if ((sock_count_v4 <= 0 || !sockets_v4) && (sock_count_v6 <= 0 || !sockets_v6)) { if ((sock_count_v4 <= 0 || !sockets_v4) && (sock_count_v6 <= 0 || !sockets_v6)) {

5
src/route_ping.h

@ -12,10 +12,13 @@ struct BGP_PING_REQUEST {
uint8_t cmd; // ETCP_ID_ROUTE_ENTRY uint8_t cmd; // ETCP_ID_ROUTE_ENTRY
uint8_t subcmd; // ROUTE_SUBCMD_PING_REQ uint8_t subcmd; // ROUTE_SUBCMD_PING_REQ
uint64_t request_id; // для корреляции uint64_t request_id; // для корреляции
uint64_t node_id; // целевой узел (должен быть известен в nodes) uint64_t node_id; // целевой узел
uint8_t count; // число пингов uint8_t count; // число пингов
uint16_t interval_ms; // интервал между пингами uint16_t interval_ms; // интервал между пингами
uint16_t timeout_ms; // таймаут одного пинга uint16_t timeout_ms; // таймаут одного пинга
uint8_t target_ipv4[4]; // custom target IP (0 = use nodeinfo sockets)
uint16_t target_port; // custom target port
uint8_t pubkey[SC_PUBKEY_SIZE]; // pubkey for encryption if target not in nodes
} __attribute__((packed)); } __attribute__((packed));
struct BGP_PING_RESPONSE { struct BGP_PING_RESPONSE {

9
tests/Makefile.am

@ -291,11 +291,16 @@ check-local:
total=$$((total + 1)); \ total=$$((total + 1)); \
logfile="$(TEST_LOG_DIR)/$$test.log"; \ logfile="$(TEST_LOG_DIR)/$$test.log"; \
if test -f "./$$test"; then \ if test -f "./$$test"; then \
start=$$(date +%s%N); \
if "./$$test" > "$$logfile" 2>&1; then \ if "./$$test" > "$$logfile" 2>&1; then \
echo "[PASS] $$test"; \ end=$$(date +%s%N); \
elapsed_ms=$$(( (end - start) / 1000000 )); \
echo "[PASS] $$test ($${elapsed_ms}ms)"; \
passed=$$((passed + 1)); \ passed=$$((passed + 1)); \
else \ else \
echo "[FAIL] $$test (see $$logfile)"; \ end=$$(date +%s%N); \
elapsed_ms=$$(( (end - start) / 1000000 )); \
echo "[FAIL] $$test ($${elapsed_ms}ms) (see $$logfile)"; \
failed=$$((failed + 1)); \ failed=$$((failed + 1)); \
if test -z "$$failed_tests"; then \ if test -z "$$failed_tests"; then \
failed_tests="$$test"; \ failed_tests="$$test"; \

77
tests/test_etcp_simple_traffic.c

@ -23,7 +23,7 @@
#include "../lib/ll_queue.h" #include "../lib/ll_queue.h"
#include "../lib/debug_config.h" #include "../lib/debug_config.h"
#define TEST_TIMEOUT_MS 5000 // 5 seconds for packet transmission #define TEST_TIMEOUT_MS 50000 // 5 seconds in uasync timebase (0.1ms units)
#define PACKET_SIZE 100 // Test packet size #define PACKET_SIZE 100 // Test packet size
static struct UTUN_INSTANCE* server_instance = NULL; static struct UTUN_INSTANCE* server_instance = NULL;
@ -34,45 +34,21 @@ static struct UASYNC* ua = NULL;
static char temp_dir[] = "/tmp/utun_test_XXXXXX"; static char temp_dir[] = "/tmp/utun_test_XXXXXX";
static char server_config_path[256]; static char server_config_path[256];
static char client_config_path[256]; static char client_config_path[256];
static int server_port = 0;
static int client_port = 0;
// Server config content // Create temp config files with dynamic ports to avoid conflicts
static const char* server_config_content =
"[global]\n"
"my_node_id=0x1111111111111111\n"
"my_private_key=67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb\n"
"my_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n"
"tun_ip=10.99.0.1/24\n"
"tun_ifname=tun99\n"
"\n"
"[server: test]\n"
"addr=127.0.0.1:9001\n"
"type=public\n";
// Client config content
static const char* client_config_content =
"[global]\n"
"my_node_id=0x2222222222222222\n"
"my_private_key=4813d31d28b7e9829247f488c6be7672f2bdf61b2508333128e386d1759afed2\n"
"my_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n"
"tun_ip=10.99.0.2/24\n"
"tun_ifname=tun98\n"
"\n"
"[server: test]\n"
"addr=127.0.0.1:9002\n"
"type=public\n"
"\n"
"[client: test_client]\n"
"keepalive=1\n"
"peer_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n"
"link=test:127.0.0.1:9001\n";
// Create temp config files
static int create_temp_configs(void) { static int create_temp_configs(void) {
if (test_mkdtemp(temp_dir) != 0) { if (test_mkdtemp(temp_dir) != 0) {
fprintf(stderr, "Failed to create temp directory\n"); fprintf(stderr, "Failed to create temp directory\n");
return -1; return -1;
} }
// Use PID-based ports to avoid conflicts with parallel tests or leftovers
int base_port = 40000 + (getpid() % 20000);
server_port = base_port;
client_port = base_port + 1;
snprintf(server_config_path, sizeof(server_config_path), "%s/server.conf", temp_dir); snprintf(server_config_path, sizeof(server_config_path), "%s/server.conf", temp_dir);
snprintf(client_config_path, sizeof(client_config_path), "%s/client.conf", temp_dir); snprintf(client_config_path, sizeof(client_config_path), "%s/client.conf", temp_dir);
@ -81,7 +57,18 @@ static int create_temp_configs(void) {
fprintf(stderr, "Failed to create server config file\n"); fprintf(stderr, "Failed to create server config file\n");
return -1; return -1;
} }
fprintf(f, "%s", server_config_content); fprintf(f,
"[global]\n"
"my_node_id=0x1111111111111111\n"
"my_private_key=67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb\n"
"my_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n"
"tun_ip=10.99.0.1/24\n"
"tun_ifname=tun99\n"
"\n"
"[server: test]\n"
"addr=127.0.0.1:%d\n"
"type=public\n",
server_port);
fclose(f); fclose(f);
f = fopen(client_config_path, "w"); f = fopen(client_config_path, "w");
@ -90,7 +77,23 @@ static int create_temp_configs(void) {
test_unlink(server_config_path); test_unlink(server_config_path);
return -1; return -1;
} }
fprintf(f, "%s", client_config_content); fprintf(f,
"[global]\n"
"my_node_id=0x2222222222222222\n"
"my_private_key=4813d31d28b7e9829247f488c6be7672f2bdf61b2508333128e386d1759afed2\n"
"my_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n"
"tun_ip=10.99.0.2/24\n"
"tun_ifname=tun98\n"
"\n"
"[server: test]\n"
"addr=127.0.0.1:%d\n"
"type=public\n"
"\n"
"[client: test_client]\n"
"keepalive=1\n"
"peer_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n"
"link=test:127.0.0.1:%d\n",
client_port, server_port);
fclose(f); fclose(f);
return 0; return 0;
@ -341,7 +344,7 @@ static void monitor_and_send(void* arg) {
static void test_timeout(void* arg) { static void test_timeout(void* arg) {
(void)arg; (void)arg;
if (!test_completed) { if (!test_completed) {
printf("\n=== TIMEOUT: Packet not received within %d seconds ===\n", TEST_TIMEOUT_MS/1000); printf("\n=== TIMEOUT: Packet not received within %d seconds ===\n", TEST_TIMEOUT_MS/10000);
test_completed = 2; // Timeout/failure test_completed = 2; // Timeout/failure
if (packet_timeout_id) { if (packet_timeout_id) {
uasync_cancel_timeout(server_instance->ua, packet_timeout_id); uasync_cancel_timeout(server_instance->ua, packet_timeout_id);
@ -483,7 +486,7 @@ int main() {
printf("Starting connection and packet transmission...\n"); printf("Starting connection and packet transmission...\n");
int elapsed = 0; int elapsed = 0;
int poll_interval = 5; int poll_interval = 50; // 5ms in uasync timebase (0.1ms units)
int check_counter = 0; int check_counter = 0;
while (!test_completed && elapsed < TEST_TIMEOUT_MS + 1000) { while (!test_completed && elapsed < TEST_TIMEOUT_MS + 1000) {
if (ua) uasync_poll(ua, poll_interval); if (ua) uasync_poll(ua, poll_interval);

53
tests/test_nat_detection.c

@ -217,10 +217,7 @@ int main(void) {
// 1. Wait for links C1->S and C2->S to initialize // 1. Wait for links C1->S and C2->S to initialize
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Waiting for ETCP links to initialize..."); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Waiting for ETCP links to initialize...");
// Give some time for connections to establish before checking // Wait for links to be initialized instead of fixed 100 iterations
for (int i = 0; i < 100; i++) {
uasync_poll(ua, 10);
}
while (!test_timed_out) { while (!test_timed_out) {
if (has_initialized_link(inst_c1) && has_initialized_link(inst_c2)) { if (has_initialized_link(inst_c1) && has_initialized_link(inst_c2)) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Links C1->S and C2->S initialized"); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Links C1->S and C2->S initialized");
@ -392,6 +389,54 @@ int main(void) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "STUN check PASSED: recv_ip=%08x recv_port=%u", DEBUG_INFO(DEBUG_CATEGORY_BGP, "STUN check PASSED: recv_ip=%08x recv_port=%u",
(unsigned)nat_ping_result.recv_ip, (unsigned)nat_ping_result.recv_port); (unsigned)nat_ping_result.recv_ip, (unsigned)nat_ping_result.recv_port);
// 7. Test route_ping_send_req_addr WITHOUT embedded pubkey (C2 should resolve pubkey from node_id)
// First wait for C2 to learn C1's nodeinfo via BGP exchange from S
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Waiting for C2 to learn C1 nodeinfo...");
bgp_wait_cycles = 0;
while (!test_timed_out && bgp_wait_cycles < 500) {
if (inst_c2->bgp && route_bgp_get_node(inst_c2->bgp, NODE_ID_C1) != NULL) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "C2 learned C1 nodeinfo");
break;
}
uasync_poll(ua, 10);
bgp_wait_cycles++;
}
if (!inst_c2->bgp || route_bgp_get_node(inst_c2->bgp, NODE_ID_C1) == NULL) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: C2 did not learn C1 nodeinfo in time");
goto cleanup;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Testing route ping without embedded pubkey...");
memset(&nat_ping_result, 0, sizeof(nat_ping_result));
ret = route_ping_send_req_addr(inst_s->bgp, conn_sc2, NODE_ID_C1,
node_c1->nat_ip, node_c1->nat_port,
3, 10, 200, 3000, nat_ping_resp_cb, NULL,
NULL);
if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_ping_send_req_addr (no pubkey) failed: %d", ret);
goto cleanup;
}
bgp_wait_cycles = 0;
while (!test_timed_out && bgp_wait_cycles < 500 && !nat_ping_result.done) {
uasync_poll(ua, 10);
bgp_wait_cycles++;
}
if (!nat_ping_result.done) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: no-pubkey ping timeout");
goto cleanup;
}
if (!nat_ping_result.success || nat_ping_result.count_ok == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: no-pubkey ping failed success=%d ok=%u",
nat_ping_result.success, (unsigned)nat_ping_result.count_ok);
goto cleanup;
}
if (nat_ping_result.recv_ip == 0 || nat_ping_result.recv_port == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "FAIL: no-pubkey ping STUN fields zero");
goto cleanup;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "No-pubkey ping check PASSED");
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT detection test PASSED"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT detection test PASSED");
test_result = 0; test_result = 0;

Loading…
Cancel
Save