// test_tcp_proxy_remote.c — 2-node TCP 1MB forward via etcp_router // Architecture: Client_A (active uIP) ↔ socketpair ↔ B (passive, etcp) ↔ Exit (tcp_proxy_server) ↔ echo #include #include #include #include "../lib/platform_compat.h" #include "test_utils.h" #ifndef _WIN32 #include #include #include #include #include #endif #include #include #include #include "../src/proxy/tcp_proxy_client.h" #include "../src/etcp.h" #include "../src/etcp_router.h" #include "../src/proxy/tcp_proxy_server.h" #include "../src/config_parser.h" #include "../src/utun_instance.h" #include "../src/uip/uip.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" #define TEST_SIZE (1 * 1024 * 1024) #define CHUNK_SIZE 1460 #define TIMEOUT_MS 45000 static void* g_to_id; static volatile int g_done, g_phase; static pid_t g_echo_pid; static struct UTUN_INSTANCE* g_exit, *g_b; static struct UASYNC* g_ua; static int g_pair[2]; static struct tcp_proxy_client* g_proxy_b, *g_cli; static int g_conn_idx, g_conn_up; static struct uip_conn* g_uc; static uint8_t *g_send_buf, *g_recv_buf; static size_t g_sent, g_rcvd; static int g_ok; static struct timespec g_t_start, g_t_end; static double g_elapsed; static int g_echo_port, g_srv_a, g_srv_b; static void on_signal(int sig) { (void)sig; g_done = -1; } static int alloc_port(void) { int s = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in a = {.sin_family=AF_INET, .sin_addr={.s_addr=htonl(INADDR_LOOPBACK)}}; bind(s, (struct sockaddr*)&a, sizeof(a)); struct sockaddr_in b; socklen_t l = sizeof(b); getsockname(s, (struct sockaddr*)&b, &l); int p = ntohs(b.sin_port); close(s); return p; } static void echo_server(uint16_t port) { signal(SIGALRM, on_signal); alarm(10); int srv = socket(AF_INET, SOCK_STREAM, 0); if (srv < 0) _exit(1); int opt = 1; setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in a = {.sin_family=AF_INET, .sin_port=htons(port), .sin_addr={.s_addr=htonl(INADDR_LOOPBACK)}}; if (bind(srv,(struct sockaddr*)&a,sizeof(a))<0||listen(srv,1)<0){close(srv);_exit(1);} int cli = accept(srv,NULL,NULL); if (cli<0){close(srv);_exit(1);} uint8_t buf[65536]; ssize_t n; while((n=recv(cli,buf,sizeof(buf),0))>0){ssize_t s=0; while(stcp_proxy = g_proxy_b; g_cli = tcp_proxy_client_create(NULL, g_ua, NULL, NULL, 0, 0, NULL, 0, 0, g_pair[0], 0); if (!g_cli) { printf("[FAIL] cli create\n"); g_done=-1; return; } g_conn_idx = tcp_proxy_active_open(g_cli, "10.99.0.100", 9090); if (g_conn_idx < 0) { printf("[FAIL] active_open\n"); g_done=-1; return; } g_uc = &uip_conns[g_conn_idx]; g_phase = 1; } static void poll_test(void) { if (g_phase == 1) { if (g_uc->tcpstateflags == UIP_ESTABLISHED) { clock_gettime(CLOCK_MONOTONIC,&g_t_start); g_phase=2; } else if (g_uc->tcpstateflags == UIP_CLOSED) { printf("[FAIL] handshake\n"); g_done=-1; } return; } if (g_phase == 2) { while (g_sent < TEST_SIZE) { size_t c = TEST_SIZE - g_sent; if (c > CHUNK_SIZE) c = CHUNK_SIZE; if (tcp_proxy_active_send(g_cli,g_conn_idx,g_send_buf+g_sent,c)!=0) break; g_sent += c; } g_phase = 3; return; } if (g_phase == 3) { if (tcp_proxy_active_send_done(g_cli,g_conn_idx)) g_phase=4; return; } if (g_phase == 4) { ssize_t n = tcp_proxy_active_recv(g_cli,g_conn_idx,g_recv_buf+g_rcvd,TEST_SIZE-g_rcvd); if (n > 0) g_rcvd += n; if (g_rcvd >= TEST_SIZE) { clock_gettime(CLOCK_MONOTONIC,&g_t_end); g_elapsed = (g_t_end.tv_sec-g_t_start.tv_sec)+(g_t_end.tv_nsec-g_t_start.tv_nsec)/1e9; g_ok = (memcmp(g_send_buf,g_recv_buf,TEST_SIZE)==0); g_done = 1; } } } static void monitor(void* arg) { (void)arg; if (g_done) return; if (g_phase == 0 && !g_conn_up) { int bup=0, eup=0; if (g_b) for (struct ETCP_CONN*c=g_b->connections;c;c=c->next) for (struct ETCP_LINK*l=c->links;l;l=l->next) if (l->initialized&&c->crypto_ctx.initialized) bup=1; if (g_exit) for (struct ETCP_CONN*c=g_exit->connections;c;c=c->next) for (struct ETCP_LINK*l=c->links;l;l=l->next) if (l->initialized&&c->crypto_ctx.initialized) eup=1; if (bup && eup) { g_conn_up=1; start_test(); } } if (g_conn_up && !g_done) poll_test(); if (!g_done) g_to_id = uasync_set_timeout(g_ua, 5, NULL, monitor, "mon"); } static void test_timeout(void* arg) { (void)arg; if (!g_done) { printf("[FAIL] timeout phase=%d sent=%zu rcvd=%zu\n",g_phase,g_sent,g_rcvd); g_done=-1; } } static const char* cfg_exit(int srv_port) { static char b[1024]; snprintf(b,sizeof(b), "[global]\nmy_node_id=0xCCCC000000000001\n" "my_private_key=67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb\n" "my_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" "tun_ip=10.99.0.1/24\ntun_ifname=tun99\n" "[server:s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n[tcp_proxy_server]\nenabled=yes\n", srv_port); return b; } static const char* cfg_b(int srv_port, int cli_port) { static char b[1024]; snprintf(b,sizeof(b), "[global]\nmy_node_id=0xCCCC000000000002\n" "my_private_key=4813d31d28b7e9829247f488c6be7672f2bdf61b2508333128e386d1759afed2\n" "my_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n" "tun_ip=10.99.0.2/24\ntun_ifname=tun98\n" "[server:s1]\naddr=127.0.0.1:%d\ntype=public\n[client:c1]\nkeepalive=1\nlink=s1:127.0.0.1:%d\n" "peer_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" "[tcp_proxy_server]\nenabled=yes\n", srv_port, cli_port); return b; } int main(void) { printf("[SKIP] Active connections API removed, test skipped\n"); return 0; #if 0 printf("=== test_tcp_proxy_remote ===\n"); debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_categories(DEBUG_CATEGORY_ALL); utun_instance_set_tun_init_enabled(0); signal(SIGTERM, on_signal); signal(SIGINT, on_signal); srand((unsigned)time(NULL)); g_send_buf = malloc(TEST_SIZE); g_recv_buf = malloc(TEST_SIZE); if (!g_send_buf || !g_recv_buf) { printf("[FAIL] malloc\n"); return 1; } for (size_t i = 0; i < TEST_SIZE; i++) g_send_buf[i] = (uint8_t)(rand() & 0xFF); g_echo_port = alloc_port(); g_srv_a = alloc_port(); g_srv_b = alloc_port(); if (!g_echo_port || !g_srv_a || !g_srv_b) { printf("[FAIL] alloc_port\n"); return 1; } g_echo_pid = fork(); if (g_echo_pid == 0) echo_server(g_echo_port); usleep(50000); g_ua = uasync_create(); if (!g_ua) { printf("[FAIL] uasync\n"); goto done; } g_exit = utun_instance_create_from_str(g_ua, cfg_exit(g_srv_a)); g_b = utun_instance_create_from_str(g_ua, cfg_b(g_srv_b, g_srv_a)); if (!g_exit || !g_b) { printf("[FAIL] create\n"); goto done; } if (utun_instance_init(g_exit) < 0 || utun_instance_init(g_b) < 0) { printf("[FAIL] init\n"); goto done; } for (int i = 0; i < 50; i++) uasync_poll(g_ua, 10); if (socketpair(AF_UNIX, SOCK_STREAM, 0, g_pair) < 0) { perror("pair"); goto done; } g_to_id = uasync_set_timeout(g_ua, 50, NULL, monitor, "mon"); void* to_id = uasync_set_timeout(g_ua, TIMEOUT_MS*10, NULL, test_timeout, "to"); while (!g_done) uasync_poll(g_ua, 10); if (to_id) uasync_cancel_timeout(g_ua, to_id); if (g_ok) printf("[PASS] test_tcp_proxy_remote — 1MB in %.2fs (%.2f MB/s)\n", g_elapsed, (TEST_SIZE/1e6)/g_elapsed); else if (g_done == -1) printf("[FAIL] test_tcp_proxy_remote\n"); done: if (g_to_id) uasync_cancel_timeout(g_ua, g_to_id); if (g_cli) tcp_proxy_client_destroy(g_cli); if (g_proxy_b) tcp_proxy_client_destroy(g_proxy_b); if (g_exit) { g_exit->running=0; utun_instance_destroy(g_exit); } if (g_b) { g_b->running=0; utun_instance_destroy(g_b); } if (g_ua) uasync_destroy(g_ua, 0); if (g_echo_pid) { kill(g_echo_pid, SIGTERM); waitpid(g_echo_pid, NULL, 0); } free(g_send_buf); free(g_recv_buf); return g_ok ? 0 : 1; #endif }