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.
 
 
 
 
 
 

523 lines
23 KiB

// test_etcp_link_stress.c - Multilink test: добавляет линки в 5 вариантах (V1/V2 one-way,
// V3a/b/c коллизия), проверяет per-link трафик и инвариант «нет реинита пока жив хотя бы один линк».
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../lib/platform_compat.h"
#include "test_utils.h"
#include "etcp.h"
#include "etcp_api.h"
#include "etcp_connections.h"
#include "etcp_dump.h"
#include "../src/config_parser.h"
#include "../src/utun_instance.h"
#include "routing.h"
#include "../src/tun_if.h"
#include "secure_channel.h"
#include "../lib/u_async.h"
#include "../lib/ll_queue.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#define PACKET_SIZE 64
#define TRAFFIC_PACKETS 200
#define LINK_UP_TIMEOUT_MS 8000
#define TRAFFIC_TIMEOUT_MS 8000
#define LINK_DOWN_TIMEOUT_MS 8000
#define COLLISION_TIMEOUT_MS 15000
enum { COL_A = 0, COL_B, COL_C };
static struct UTUN_INSTANCE* server_instance = NULL;
static struct UTUN_INSTANCE* client_instance = NULL;
static struct UASYNC* ua = NULL;
static char temp_dir[] = "/tmp/utun_test_XXXXXX";
static char server_config_path[256];
static char client_config_path[256];
static int server_port = 0;
static int client_port = 0;
static int next_port = 0;
static uint32_t packets_sent = 0;
static uint32_t packets_received = 0;
// globals for generic poll_until conditions
static struct ETCP_LINK* g_link = NULL;
static struct ETCP_SOCKET* g_sock = NULL;
static struct ETCP_CONN* g_conn = NULL;
static uint16_t g_port = 0;
static int traffic_corrupt = 0;
static int create_temp_configs(void) {
if (test_mkdtemp(temp_dir) != 0) { fprintf(stderr, "Failed to create temp directory\n"); return -1; }
int base_port = 50000 + (getpid() % 15000);
server_port = base_port;
client_port = base_port + 1;
next_port = base_port + 100;
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);
FILE* f = fopen(server_config_path, "w");
if (!f) { fprintf(stderr, "Failed to create server config file\n"); return -1; }
fprintf(f,
"[global]\n"
"my_node_id=0x1111111111111111\n"
"my_private_key=38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68\n"
"my_public_key=ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a\n"
"tun_ip=10.99.0.1/24\n"
"tun_ifname=tun99\n"
"keepalive_interval=100\n"
"keepalive_adaptive=0\n"
"\n"
"[server: udp0]\n"
"addr=127.0.0.1:%d\n"
"type=public\n"
"\n"
"[allowed_keys]\n"
"allow_all=1\n",
server_port);
fclose(f);
f = fopen(client_config_path, "w");
if (!f) { fprintf(stderr, "Failed to create client config file\n"); test_unlink(server_config_path); return -1; }
fprintf(f,
"[global]\n"
"my_node_id=0x2222222222222222\n"
"my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n"
"my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n"
"tun_ip=10.99.0.2/24\n"
"tun_ifname=tun98\n"
"keepalive_interval=100\n"
"keepalive_adaptive=0\n"
"\n"
"[server: udp0]\n"
"addr=127.0.0.1:%d\n"
"type=public\n"
"\n"
"[client: test_client]\n"
"keepalive=1\n"
"peer_public_key=ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a\n"
"link=udp0:127.0.0.1:%d\n"
"\n"
"[allowed_keys]\n"
"allow_all=1\n",
client_port, server_port);
fclose(f);
return 0;
}
static void cleanup_temp_configs(void) {
if (server_config_path[0]) test_unlink(server_config_path);
if (client_config_path[0]) test_unlink(client_config_path);
if (temp_dir[0]) test_rmdir(temp_dir);
}
// ===== helpers =====
static struct ETCP_CONN* get_conn(struct UTUN_INSTANCE* inst) {
if (!inst || !inst->connections || !inst->connections->head) return NULL;
struct conn_queue_entry* ce = (struct conn_queue_entry*)inst->connections->head->data;
return ce ? ce->conn : NULL;
}
static int conn_up(struct UTUN_INSTANCE* inst) {
struct ETCP_CONN* conn = get_conn(inst);
return conn && conn->initialized && conn->links_up;
}
static int count_links(struct ETCP_CONN* conn) {
int n = 0;
for (struct ETCP_LINK* l = conn ? conn->links : NULL; l; l = l->next) n++;
return n;
}
static int count_links_up(struct ETCP_CONN* conn) {
int n = 0;
for (struct ETCP_LINK* l = conn ? conn->links : NULL; l; l = l->next) if (l->link_status) n++;
return n;
}
static void mk_addr(struct sockaddr_storage* out, uint16_t port) {
memset(out, 0, sizeof(*out));
struct sockaddr_in* sin = (struct sockaddr_in*)out;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sin->sin_port = htons(port);
}
static int alloc_port(void) { return next_port++; }
static struct ETCP_SOCKET* add_udp_socket(struct UTUN_INSTANCE* inst, uint16_t port, const char* name) {
struct CFG_SERVER cfg; memset(&cfg, 0, sizeof(cfg));
snprintf(cfg.name, sizeof(cfg.name), "%s", name);
cfg.type = CFG_SERVER_TYPE_PUBLIC;
cfg.mtu = PACKET_DATA_MAX_MTU;
mk_addr(&cfg.ip, port);
return etcp_socket_add(inst, &cfg);
}
static struct ETCP_LINK* find_link_by_remote_port(struct ETCP_CONN* conn, uint16_t port) {
for (struct ETCP_LINK* l = conn ? conn->links : NULL; l; l = l->next) {
if (l->remote_addr.ss_family == AF_INET &&
ntohs(((struct sockaddr_in*)&l->remote_addr)->sin_port) == port)
return l;
}
return NULL;
}
static struct ETCP_LINK* first_link_on_socket(struct ETCP_SOCKET* sock) {
if (!sock || !sock->links_queue || !sock->links_queue->head) return NULL;
struct link_queue_entry* lqe = (struct link_queue_entry*)sock->links_queue->head->data;
return lqe ? lqe->link : NULL;
}
static uint64_t client_link_acked(uint16_t srv_port) {
struct ETCP_LINK* l = find_link_by_remote_port(get_conn(client_instance), srv_port);
return l ? l->acked_packets : 0;
}
static void dump_state(void) {
printf("--- STATE DUMP ---\n");
if (server_instance) etcp_dump_all_conns(server_instance);
if (client_instance) etcp_dump_all_conns(client_instance);
printf("--- END DUMP ---\n");
fflush(stdout);
}
#define CHECK(cond, ...) do { if (!(cond)) { \
printf("FAIL %s:%d: ", __func__, __LINE__); \
printf(__VA_ARGS__); \
printf("\n"); \
dump_state(); \
goto fail; } } while (0)
// ===== poll conditions =====
static int c_conn_up_both(void) { return conn_up(server_instance) && conn_up(client_instance); }
static int c_link_up(void) { return g_link && g_link->initialized && g_link->link_status == 1; }
static int c_link_down(void) { return g_link && g_link->link_status == 0; }
static int c_socket_link_up(void) { struct ETCP_LINK* l = first_link_on_socket(g_sock); return l && l->initialized && l->link_status == 1; }
static int c_link_on_port_up(void) { struct ETCP_LINK* l = find_link_by_remote_port(g_conn, g_port); return l && l->initialized && l->link_status == 1; }
static int c_client_all_down(void) { struct ETCP_CONN* c = get_conn(client_instance); return !c || c->links_up == 0; }
static int c_server_all_down(void) { struct ETCP_CONN* c = get_conn(server_instance); return !c || c->links_up == 0; }
static int poll_until(int (*cond)(void), uint64_t timeout_ms, const char* what) {
uint64_t deadline = get_time_tb() + timeout_ms * 10;
while (get_time_tb() < deadline) {
if (cond()) return 0;
uasync_poll(ua, 5);
}
printf("TIMEOUT waiting for: %s (%llums)\n", what, (unsigned long long)timeout_ms);
return -1;
}
// ===== traffic =====
static void receive_test_packet(struct ETCP_CONN* conn, struct ll_entry* pkt) {
(void)conn;
uint8_t expected[PACKET_SIZE]; expected[0] = 0x7f;
uint32_t seq = packets_received++;
for (int i = 0; i < 4; i++) expected[1 + i] = (uint8_t)(seq >> (i * 8));
for (int i = 5; i < PACKET_SIZE; i++) expected[i] = (uint8_t)(seq + i);
if (pkt->len != PACKET_SIZE || memcmp(pkt->dgram, expected, PACKET_SIZE) != 0) {
fprintf(stderr, "FAIL: multilink payload/order mismatch at packet %u len=%u\n", seq, pkt->len);
traffic_corrupt = 1;
}
queue_dgram_free(pkt); queue_entry_free(pkt);
}
static int run_traffic(int n, uint64_t timeout_ms) {
struct ETCP_CONN* cc = get_conn(client_instance);
if (!cc || !cc->input_queue) return -1;
uint32_t target = packets_sent + (uint32_t)n;
uint64_t deadline = get_time_tb() + timeout_ms * 10;
while (1) {
while (packets_sent < target && queue_entry_count(cc->send_input_q) == 0) {
uint8_t buf[PACKET_SIZE];
uint32_t s = packets_sent;
buf[0] = 0x7f;
for (int i = 0; i < 4; i++) buf[1 + i] = (uint8_t)(s >> (i * 8));
for (int i = 5; i < PACKET_SIZE; i++) buf[i] = (uint8_t)(s + i);
struct ll_entry* pkt = ll_alloc_lldgram(PACKET_SIZE);
if (!pkt) return -1;
pkt->len = PACKET_SIZE;
memcpy(pkt->dgram, buf, PACKET_SIZE);
if (etcp_send(cc, pkt) != 0) { queue_dgram_free(pkt); queue_entry_free(pkt); return -1; }
packets_sent++;
}
if (traffic_corrupt || packets_received > target) return -1;
if (packets_received == target && !cc->input_wait_ack->count && !cc->input_send_q->count) return 0;
if (get_time_tb() > deadline) {
printf("traffic timeout: sent=%u recv=%u target=%u\n", packets_sent, packets_received, target);
return -1;
}
uasync_poll(ua, 5);
}
}
// Проверяет, что конкретный линк (по серверному порту) реально перенёс данные.
// Линк может пропустить короткий burst из-за keepalive-стабилизации link_status,
// поэтому повторяем до нескольких keepalive-тиков. Возвращает 0 если линк нёс трафик.
static int check_link_traffic(uint16_t srv_port, const char* label) {
for (int attempt = 0; attempt < 6; attempt++) {
uint64_t before = client_link_acked(srv_port);
if (run_traffic(TRAFFIC_PACKETS, TRAFFIC_TIMEOUT_MS) != 0) return -1;
if (client_link_acked(srv_port) > before) return 0;
uasync_poll(ua, 50);
}
printf(" [%s] link never carried traffic\n", label);
return -1;
}
// ===== instance lifecycle =====
static int start_instances(void) {
packets_sent = packets_received = 0; traffic_corrupt = 0;
server_instance = utun_instance_create(ua, server_config_path);
if (!server_instance || utun_instance_init(server_instance) < 0) {
printf("failed to create server\n"); return -1;
}
client_instance = utun_instance_create(ua, client_config_path);
if (!client_instance || utun_instance_init(client_instance) < 0) {
printf("failed to create client\n"); return -1;
}
etcp_bind(server_instance, 0x7f, receive_test_packet);
if (poll_until(c_conn_up_both, LINK_UP_TIMEOUT_MS, "initial connection") != 0) {
printf("initial connection timeout\n"); dump_state(); return -1;
}
printf("connection established: server links_up=%d/%d client links_up=%d/%d\n",
count_links_up(get_conn(server_instance)), count_links(get_conn(server_instance)),
count_links_up(get_conn(client_instance)), count_links(get_conn(client_instance)));
return 0;
}
static void stop_instances(void) {
if (server_instance) { server_instance->running = 0; utun_instance_destroy(server_instance); server_instance = NULL; }
if (client_instance) { client_instance->running = 0; utun_instance_destroy(client_instance); client_instance = NULL; }
}
// Убить серверный сокет (линк со стороны сервера закрывается сразу), ускорить детект на клиенте.
static void kill_server_socket(struct ETCP_SOCKET* sock, uint16_t srv_port) {
struct ETCP_LINK* cl = find_link_by_remote_port(get_conn(client_instance), srv_port);
if (cl) cl->last_recv_local_time = 0;
etcp_socket_remove(sock);
}
// ===== Session A: one-way adds + kill failover =====
static int session_a(void) {
printf("\n=== Session A: one-way adds (V1/V2) + link kill failover ===\n");
if (start_instances() != 0) return -1;
struct ETCP_CONN* sc = get_conn(server_instance);
struct ETCP_CONN* cc = get_conn(client_instance);
int held = 0;
CHECK(sc && cc, "no conn after start");
CHECK(etcp_conn_ref_take(sc) == 0 && etcp_conn_ref_take(cc) == 0, "test conn references");
held = 1;
uint32_t srv_reinit0 = sc->reinit_count;
uint32_t cli_reinit0 = cc->reinit_count;
struct ETCP_SOCKET* s0 = server_instance->etcp_sockets; // начальный серверный сокет udp0
struct ETCP_SOCKET* c0 = client_instance->etcp_sockets; // клиентский сокет udp0 (source для V1)
// -- per-link трафик на исходном линке --
printf("[A] traffic on initial link...\n");
CHECK(check_link_traffic(server_port, "initial") == 0, "initial link carried no traffic");
CHECK(sc->reinit_count == srv_reinit0 && cc->reinit_count == cli_reinit0, "reinit during initial traffic");
// -- V1: клиент дозванивается до нового серверного сокета S1 --
int sp1 = alloc_port();
struct ETCP_SOCKET* s1 = add_udp_socket(server_instance, sp1, "dyn_s1");
CHECK(s1, "V1: failed to add server socket S1");
struct sockaddr_storage s1_addr; mk_addr(&s1_addr, sp1);
struct ETCP_LINK* lc1 = etcp_link_new(cc, c0, &s1_addr, 0);
CHECK(lc1, "V1: failed to create client link");
printf("[A] V1: client link %d -> server port %d\n", lc1->local_link_id, sp1);
g_link = lc1;
CHECK(poll_until(c_link_up, LINK_UP_TIMEOUT_MS, "V1 link up (client)") == 0, "V1 link did not come up");
g_sock = s1;
CHECK(poll_until(c_socket_link_up, LINK_UP_TIMEOUT_MS, "V1 link up (server)") == 0, "V1 server link did not come up");
CHECK(sc->reinit_count == srv_reinit0 && cc->reinit_count == cli_reinit0, "reinit during V1");
CHECK(check_link_traffic(sp1, "V1") == 0, "V1 link carried no traffic");
// -- V2: сервер дозванивается до клиента --
int sp2 = alloc_port();
struct ETCP_SOCKET* s2 = add_udp_socket(server_instance, sp2, "dyn_s2");
CHECK(s2, "V2: failed to add server socket S2");
struct sockaddr_storage c0_addr; mk_addr(&c0_addr, client_port);
struct ETCP_LINK* ls2 = etcp_link_new(sc, s2, &c0_addr, 0);
CHECK(ls2, "V2: failed to create server outbound link");
printf("[A] V2: server link %d -> client port %d\n", ls2->local_link_id, client_port);
g_link = ls2;
CHECK(poll_until(c_link_up, LINK_UP_TIMEOUT_MS, "V2 link up (server)") == 0, "V2 link did not come up");
g_conn = cc; g_port = sp2;
CHECK(poll_until(c_link_on_port_up, LINK_UP_TIMEOUT_MS, "V2 link up (client)") == 0, "V2 client link did not come up");
CHECK(sc->reinit_count == srv_reinit0 && cc->reinit_count == cli_reinit0, "reinit during V2");
CHECK(check_link_traffic(sp2, "V2") == 0, "V2 link carried no traffic");
printf("[A] after adds: server links_up=%d/%d client links_up=%d/%d\n",
count_links_up(sc), count_links(sc), count_links_up(cc), count_links(cc));
// -- kill V2 link: failover, без реинита --
printf("[A] killing V2 link (server socket %d)...\n", sp2);
kill_server_socket(s2, sp2);
g_link = find_link_by_remote_port(cc, sp2);
CHECK(poll_until(c_link_down, LINK_DOWN_TIMEOUT_MS, "V2 link down") == 0, "V2 link did not go down");
CHECK(cc->links_up == 1, "client links_up != 1 after V2 kill (got %d)", cc->links_up);
CHECK(sc->links_up == 1, "server links_up != 1 after V2 kill (got %d)", sc->links_up);
CHECK(cc->initialized == 1, "client initialized lost after V2 kill");
CHECK(sc->reinit_count == srv_reinit0 && cc->reinit_count == cli_reinit0, "reinit after V2 kill");
CHECK(run_traffic(TRAFFIC_PACKETS, TRAFFIC_TIMEOUT_MS) == 0, "failover traffic after V2 kill failed");
// -- kill V1 link --
printf("[A] killing V1 link (server socket %d)...\n", sp1);
kill_server_socket(s1, sp1);
g_link = find_link_by_remote_port(cc, sp1);
CHECK(poll_until(c_link_down, LINK_DOWN_TIMEOUT_MS, "V1 link down") == 0, "V1 link did not go down");
CHECK(cc->links_up == 1, "client links_up != 1 after V1 kill (got %d)", cc->links_up);
CHECK(cc->initialized == 1, "client initialized lost after V1 kill");
CHECK(sc->reinit_count == srv_reinit0 && cc->reinit_count == cli_reinit0, "reinit after V1 kill");
CHECK(run_traffic(TRAFFIC_PACKETS, TRAFFIC_TIMEOUT_MS) == 0, "failover traffic after V1 kill failed");
// -- kill initial (последний) линк: всё вниз, реинита нет --
printf("[A] killing initial link (server socket %d)...\n", server_port);
kill_server_socket(s0, server_port);
g_link = find_link_by_remote_port(cc, server_port);
CHECK(poll_until(c_link_down, LINK_DOWN_TIMEOUT_MS, "initial link down") == 0, "initial link did not go down");
CHECK(poll_until(c_client_all_down, LINK_DOWN_TIMEOUT_MS, "client all links down") == 0, "client links not all down");
CHECK(poll_until(c_server_all_down, LINK_DOWN_TIMEOUT_MS, "server all links down") == 0, "server links not all down");
CHECK(cc->reinit_count == cli_reinit0, "reinit when last link died (client %u->%u)", cli_reinit0, cc->reinit_count);
CHECK(sc->reinit_count == srv_reinit0, "reinit when last link died (server %u->%u)", srv_reinit0, sc->reinit_count);
printf("[A] Session A PASSED\n");
etcp_conn_ref_free(sc); etcp_conn_ref_free(cc);
stop_instances();
return 0;
fail:
if (held) { etcp_conn_ref_free(sc); etcp_conn_ref_free(cc); }
stop_instances();
return -1;
}
// ===== Session: коллизии =====
static int session_collision(int which) {
const char* names[] = {
"V3a: both correct addr (true collision)",
"V3b: client wrong addr (server-only establish)",
"V3c: server wrong addr (client-only establish)",
};
printf("\n=== Session %s ===\n", names[which]);
if (start_instances() != 0) return -1;
struct ETCP_CONN* sc = get_conn(server_instance);
struct ETCP_CONN* cc = get_conn(client_instance);
CHECK(sc && cc, "no conn");
uint32_t srv_reinit0 = sc->reinit_count;
uint32_t cli_reinit0 = cc->reinit_count;
uint64_t srv_epoch = sc->reset_id, cli_epoch = cc->reset_id;
CHECK(run_traffic(TRAFFIC_PACKETS, TRAFFIC_TIMEOUT_MS) == 0, "traffic before link add failed");
int sp3 = alloc_port();
int cp1 = alloc_port();
int dead = alloc_port();
struct ETCP_SOCKET* s3 = add_udp_socket(server_instance, sp3, "dyn_s3");
CHECK(s3, "failed to add server socket S3");
struct ETCP_SOCKET* c1 = add_udp_socket(client_instance, cp1, "dyn_c1");
CHECK(c1, "failed to add client socket C1");
struct sockaddr_storage s3_addr; mk_addr(&s3_addr, sp3);
struct sockaddr_storage c1_addr; mk_addr(&c1_addr, cp1);
struct sockaddr_storage dead_addr; mk_addr(&dead_addr, dead);
struct ETCP_LINK* lc3 = NULL;
struct ETCP_LINK* ls3 = NULL;
if (which == COL_A) {
lc3 = etcp_link_new(cc, c1, &s3_addr, 0); // клиент -> S3 (верно)
ls3 = etcp_link_new(sc, s3, &c1_addr, 0); // сервер -> C1 (верно)
} else if (which == COL_B) {
lc3 = etcp_link_new(cc, c1, &dead_addr, 0); // клиент -> мёртвый порт
ls3 = etcp_link_new(sc, s3, &c1_addr, 0); // сервер -> C1 (верно)
} else {
lc3 = etcp_link_new(cc, c1, &s3_addr, 0); // клиент -> S3 (верно)
ls3 = etcp_link_new(sc, s3, &dead_addr, 0); // сервер -> мёртвый порт
}
CHECK(lc3 && ls3, "link creation failed (lc3=%p ls3=%p)", (void*)lc3, (void*)ls3);
if (which == COL_A) {
CHECK(run_traffic(TRAFFIC_PACKETS, TRAFFIC_TIMEOUT_MS) == 0, "traffic during collision failed");
g_conn = cc; g_port = sp3;
CHECK(poll_until(c_link_on_port_up, COLLISION_TIMEOUT_MS, "collision client link up") == 0, "client link down");
g_conn = sc; g_port = cp1;
CHECK(poll_until(c_link_on_port_up, COLLISION_TIMEOUT_MS, "collision server link up") == 0, "server link down");
struct ETCP_LINK* cl = find_link_by_remote_port(cc, sp3);
struct ETCP_LINK* sl = find_link_by_remote_port(sc, cp1);
CHECK(cl->is_server != sl->is_server, "collision left both ends in the same role");
CHECK(count_links(sc) == 2 && count_links(cc) == 2, "collision created duplicate links");
CHECK(check_link_traffic(sp3, "collision") == 0, "new link carried no traffic");
} else {
struct ETCP_LINK* good = (which == COL_B) ? ls3 : lc3;
g_link = good;
CHECK(poll_until(c_link_up, LINK_UP_TIMEOUT_MS, "good link up") == 0, "good link did not come up");
uint16_t peer_port = (which == COL_B) ? sp3 : cp1;
g_conn = (which == COL_B) ? cc : sc;
g_port = peer_port;
CHECK(poll_until(c_link_on_port_up, LINK_UP_TIMEOUT_MS, "peer incoming link up") == 0,
"peer incoming link did not come up");
CHECK(sc->reinit_count == srv_reinit0 && cc->reinit_count == cli_reinit0,
"unexpected reinit (srv %u->%u cli %u->%u)", srv_reinit0, sc->reinit_count, cli_reinit0, cc->reinit_count);
CHECK(run_traffic(TRAFFIC_PACKETS, TRAFFIC_TIMEOUT_MS) == 0, "traffic failed");
}
CHECK(sc->reinit_count == srv_reinit0 && cc->reinit_count == cli_reinit0, "link add reset the stream");
CHECK(sc->reset_id == srv_epoch && cc->reset_id == cli_epoch, "link add changed session epochs");
printf("[%s] PASSED\n", names[which]);
stop_instances();
return 0;
fail:
stop_instances();
return -1;
}
int main(void) {
if (create_temp_configs() != 0) return 1;
debug_config_init();
debug_set_level(DEBUG_LEVEL_ERROR);
if (getenv("UTUN_TEST_DEBUG")) {
debug_set_level(DEBUG_LEVEL_DEBUG);
debug_set_category_level(DEBUG_CATEGORY_ETCP, DEBUG_LEVEL_DEBUG);
debug_set_category_level(DEBUG_CATEGORY_CONNECTION, DEBUG_LEVEL_DEBUG);
debug_set_category_level(DEBUG_CATEGORY_KEEPALIVE, DEBUG_LEVEL_DEBUG);
debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_DEBUG);
}
printf("=== ETCP Multilink Test ===\n");
printf("server_port=%d client_port=%d dyn_ports=%d..\n", server_port, client_port, next_port);
utun_instance_set_tun_init_enabled(0);
ua = uasync_create();
int rc = 0;
rc |= session_a() != 0;
rc |= session_collision(COL_A) != 0;
rc |= session_collision(COL_B) != 0;
rc |= session_collision(COL_C) != 0;
if (ua) { uasync_poll(ua, 0); uasync_destroy(ua, 0); ua = NULL; }
cleanup_temp_configs();
if (rc == 0) { printf("\n=== ALL TESTS PASSED ===\n"); return 0; }
printf("\n=== TESTS FAILED ===\n");
return 1;
}