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.
 
 
 
 
 
 

197 lines
8.6 KiB

// test_tcp_proxy_remote.c — 2-node TCP 1MB forward via etcp_router
// Architecture: Client_A (active uIP) ↔ socketpair ↔ B (passive, etcp) ↔ Exit (remote_proxy) ↔ echo
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../lib/platform_compat.h"
#include "test_utils.h"
#ifndef _WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#endif
#include <signal.h>
#include <errno.h>
#include <time.h>
#include "../src/tcp_proxy.h"
#include "../src/etcp.h"
#include "../src/etcp_router.h"
#include "../src/remote_proxy.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* 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(s<n){ssize_t r=send(cli,buf+s,n-s,0); if(r<0)goto done; s+=r;}}
done: close(cli); close(srv); _exit(0);
}
static void start_test(void) {
struct tcp_proxy_mapping_config m = {.local_port=9090,.remote_ip="127.0.0.1",.remote_port=g_echo_port};
g_proxy_b = tcp_proxy_create(g_b, g_ua, NULL, NULL, 0, 0, &m, 1, 0, 0, g_pair[1], 0xCCCC000000000001ULL);
if (!g_proxy_b) { printf("[FAIL] proxy_b create\n"); g_done=-1; return; }
g_b->tcp_proxy = g_proxy_b;
g_cli = tcp_proxy_create(NULL, g_ua, NULL, NULL, 0, 0, NULL, 0, 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[remote_proxy]\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"
"[remote_proxy]\nenabled=yes\n", srv_port, cli_port); return b; }
int main(void) {
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_destroy(g_cli);
if (g_proxy_b) tcp_proxy_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;
}