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.
 
 
 
 
 
 

137 lines
6.4 KiB

// test_tcp_proxy.c — TCP proxy test: 1MB echo through 2 uIP instances + socketpair
// No root required — uses raw-fd mode instead of TUN
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <errno.h>
#include <time.h>
#include "test_utils.h"
#include "../src/tcp_proxy.h"
#include "../src/config_parser.h"
#include "../src/uip/uip.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#define TEST_PORT 9090
#define ECHO_PORT 9999
#define TEST_SIZE (1024 * 1024)
#define POLL_TIMEOUT_MS 10000
#define DATA_TIMEOUT_MS 120000
static int g_test_ok = 0;
static pid_t echo_pid = 0;
static void echo_server(uint16_t port) {
int srv = socket(AF_INET, SOCK_STREAM, 0);
if(srv < 0) { perror("echo socket"); exit(1); }
int opt = 1; setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(port)}; addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(bind(srv, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("echo bind"); close(srv); exit(1); }
if(listen(srv, 1) < 0) { perror("echo listen"); close(srv); exit(1); }
int cli = accept(srv, NULL, NULL);
if(cli < 0) { perror("echo accept"); close(srv); exit(1); }
uint8_t buf[65536]; ssize_t n;
while((n = recv(cli, buf, sizeof(buf), 0)) > 0) { ssize_t sent = 0; while(sent < n) { ssize_t s = send(cli, buf + sent, n - sent, 0); if(s < 0) goto done; sent += s; } }
done: close(cli); close(srv);
}
static void run_instance_b(int ip_fd) {
struct UASYNC* ua = uasync_create();
if(!ua) { fprintf(stderr, "B: uasync_create failed\n"); _exit(1); }
struct tcp_proxy_mapping_config m = {.local_port = TEST_PORT, .remote_ip = "127.0.0.1", .remote_port = ECHO_PORT};
struct tcp_proxy* b = tcp_proxy_create(NULL, ua, NULL, NULL, 0, 0, &m, 1, 0, 0, ip_fd, 0);
if(!b) { fprintf(stderr, "B: tcp_proxy_create failed\n"); uasync_destroy(ua, 0); _exit(1); }
while(1) uasync_poll(ua, 100);
}
int main(void) {
// 1. Fork echo server
echo_pid = fork();
if(echo_pid == 0) { echo_server(ECHO_PORT); _exit(0); }
if(echo_pid < 0) { perror("fork echo"); return 1; }
usleep(100000);
// 2. Socketpair
int pair[2];
if(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) { perror("socketpair"); kill(echo_pid, SIGTERM); waitpid(echo_pid, NULL, 0); return 1; }
// 3. Fork child = Instance B
pid_t child = fork();
if(child == 0) { close(pair[0]); run_instance_b(pair[1]); _exit(0); }
if(child < 0) { perror("fork child"); close(pair[0]); close(pair[1]); kill(echo_pid, SIGTERM); waitpid(echo_pid, NULL, 0); return 1; }
close(pair[1]);
// 4. Parent = Instance A
struct UASYNC* ua = uasync_create();
if(!ua) { printf("[FAIL] uasync_create\n"); close(pair[0]); kill(child, SIGTERM); kill(echo_pid, SIGTERM); waitpid(child, NULL, 0); waitpid(echo_pid, NULL, 0); return 1; }
struct tcp_proxy* a = tcp_proxy_create(NULL, ua, NULL, NULL, 0, 0, NULL, 0, 0, 0, pair[0], 0);
if(!a) { printf("[FAIL] tcp_proxy_create\n"); uasync_destroy(ua, 0); close(pair[0]); kill(child, SIGTERM); kill(echo_pid, SIGTERM); waitpid(child, NULL, 0); waitpid(echo_pid, NULL, 0); return 1; }
// 5. Active open
int ci = tcp_proxy_active_open(a, "10.0.0.1", TEST_PORT);
if(ci < 0) { printf("[FAIL] active_open\n"); tcp_proxy_destroy(a); uasync_destroy(ua, 0); close(pair[0]); kill(child, SIGTERM); kill(echo_pid, SIGTERM); waitpid(child, NULL, 0); waitpid(echo_pid, NULL, 0); return 1; }
// 6. Wait for handshake
struct uip_conn* uc = &uip_conns[ci];
int timeout_ms = POLL_TIMEOUT_MS;
while(uc->tcpstateflags != UIP_ESTABLISHED && timeout_ms > 0) {
uasync_poll(ua, 100); timeout_ms -= 10;
if(uc->tcpstateflags == UIP_CLOSED) break;
}
if(uc->tcpstateflags != UIP_ESTABLISHED) {
printf("[FAIL] handshake (state=0x%x)\n", uc->tcpstateflags);
tcp_proxy_destroy(a); uasync_destroy(ua, 0); close(pair[0]); kill(child, SIGTERM); kill(echo_pid, SIGTERM); waitpid(child, NULL, 0); waitpid(echo_pid, NULL, 0); return 1;
}
// 7. Generate 1MB data
uint8_t* send_buf = malloc(TEST_SIZE); uint8_t* recv_buf = malloc(TEST_SIZE);
if(!send_buf || !recv_buf) { printf("[FAIL] malloc\n"); tcp_proxy_destroy(a); uasync_destroy(ua, 0); close(pair[0]); kill(child, SIGTERM); kill(echo_pid, SIGTERM); waitpid(child, NULL, 0); waitpid(echo_pid, NULL, 0); return 1; }
srand(time(NULL)); int k; for(k = 0; k < TEST_SIZE; k++) send_buf[k] = (uint8_t)(rand() & 0xFF);
// 8. Queue data for sending
size_t off;
for(off = 0; off < TEST_SIZE; ) { size_t chunk = TEST_SIZE - off; if(chunk > 1460) chunk = 1460;
if(tcp_proxy_active_send(a, ci, send_buf + off, chunk) != 0) { printf("[FAIL] active_send at %zu\n", off); goto fail; }
off += chunk; }
// 9. Wait until all queued data is sent
timeout_ms = DATA_TIMEOUT_MS;
while(!tcp_proxy_active_send_done(a, ci) && timeout_ms > 0) {
uasync_poll(ua, 100); timeout_ms -= 10;
}
if(!tcp_proxy_active_send_done(a, ci)) { printf("[FAIL] send timeout\n"); goto fail; }
// 10. Half-close: all data sent, now signal FIN to echo server
tcp_proxy_active_close(a, ci);
// 11. Receive echoed data back
size_t total_rcvd = 0; timeout_ms = DATA_TIMEOUT_MS;
while(total_rcvd < TEST_SIZE && timeout_ms > 0) {
uasync_poll(ua, 100); timeout_ms -= 10;
ssize_t n = tcp_proxy_active_recv(a, ci, recv_buf + total_rcvd, TEST_SIZE - total_rcvd);
if(n > 0) total_rcvd += n;
if(uc->tcpstateflags == UIP_CLOSED && total_rcvd < TEST_SIZE) break;
}
// 12. Verify
if(total_rcvd == TEST_SIZE && memcmp(send_buf, recv_buf, TEST_SIZE) == 0) {
printf("[PASS] test_tcp_proxy — 1MB echo verified\n"); g_test_ok = 1;
} else {
printf("[FAIL] test_tcp_proxy — %zu/%d bytes\n", total_rcvd, TEST_SIZE);
if(total_rcvd == TEST_SIZE) for(k = 0; k < TEST_SIZE; k++) if(send_buf[k] != recv_buf[k]) { printf(" diff at %d: %02x/%02x\n", k, send_buf[k], recv_buf[k]); break; }
}
fail:
free(send_buf); free(recv_buf);
tcp_proxy_destroy(a); uasync_destroy(ua, 0); close(pair[0]);
kill(child, SIGTERM); waitpid(child, NULL, 0);
kill(echo_pid, SIGTERM); waitpid(echo_pid, NULL, 0);
return g_test_ok ? 0 : 1;
}