From a6311a17d0757ce92dd5a84371a188f056df137d Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 11 May 2026 18:37:36 +0300 Subject: [PATCH] add UDP/ICMP proxy, refactor tcp_proxy via_node to instance-level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UDP proxy: datagram relay client↔exit over ETCP - ICMP proxy: echo ping relay client↔exit over ETCP - Move via_node_id from per-forward-mapping to global tcp_proxy setting - Support multiple concurrent tcp_proxy instances (no singleton g_tcp_proxy) - Cap handshake padding to stay within 1472+overhead - Handle non-TCP packets (UDP/ICMP) in tcp_proxy raw/tun input paths - Add utun_instance_create_from_str() for test config creation --- src/Makefile.am | 2 + src/config_parser.c | 11 +- src/config_parser.h | 2 +- src/etcp_api.h | 2 + src/etcp_connections.c | 4 + src/icmp_proxy.c | 291 ++++++++++++++++++++++++++++++++++ src/icmp_proxy.h | 60 +++++++ src/remote_proxy.c | 8 + src/tcp_proxy.c | 112 +++++++++---- src/tcp_proxy.h | 7 +- src/udp_proxy.c | 245 ++++++++++++++++++++++++++++ src/udp_proxy.h | 64 ++++++++ src/utun_instance.c | 21 ++- src/utun_instance.h | 1 + tests/Makefile.am | 16 +- tests/test_icmp_proxy.c | 173 ++++++++++++++++++++ tests/test_tcp_proxy.c | 4 +- tests/test_tcp_proxy_remote.c | 197 +++++++++++++++++++++++ tests/test_udp_proxy.c | 190 ++++++++++++++++++++++ utun.conf.sample | 13 +- 20 files changed, 1371 insertions(+), 52 deletions(-) create mode 100644 src/icmp_proxy.c create mode 100644 src/icmp_proxy.h create mode 100644 src/udp_proxy.c create mode 100644 src/udp_proxy.h create mode 100644 tests/test_icmp_proxy.c create mode 100644 tests/test_tcp_proxy_remote.c create mode 100644 tests/test_udp_proxy.c diff --git a/src/Makefile.am b/src/Makefile.am index 00c44e97..97ef818b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,6 +36,8 @@ utun_CORE_SOURCES = \ tcp_proxy.c \ etcp_router.c \ remote_proxy.c \ + udp_proxy.c \ + icmp_proxy.c \ uip/uip.c # Platform-specific TUN libs (Windows only) diff --git a/src/config_parser.c b/src/config_parser.c index 22282d87..f696ba88 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -459,6 +459,10 @@ static int parse_tcp_proxy(const char *key, const char *value, struct global_con global->tcp_proxy_eim_timeout = atoi(value); return 0; } + if (strcmp(key, "via_node") == 0) { + global->tcp_proxy_via_node_id = strtoull(value, NULL, 16); + return 0; + } if (strcmp(key, "forward") == 0) { if (global->tcp_proxy_mapping_count >= MAX_TCP_PROXY_MAPPINGS) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Too many tcp_proxy forward rules (max %d)", MAX_TCP_PROXY_MAPPINGS); @@ -473,7 +477,7 @@ static int parse_tcp_proxy(const char *key, const char *value, struct global_con char* arrow = strtok(NULL, " "); char* remote_str = strtok(NULL, ""); if (!local_port_str || !arrow || !remote_str || strcmp(arrow, "->") != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy forward format: %s (expected: port -> ip:port [via NODE_HEX])", value); + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy forward format: %s (expected: port -> ip:port)", value); return -1; } int local_port = atoi(local_port_str); @@ -488,15 +492,10 @@ static int parse_tcp_proxy(const char *key, const char *value, struct global_con int remote_port = atoi(remote_port_str); if (remote_port <= 0 || remote_port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy forward remote port: %s", remote_port_str); return -1; } - uint64_t via_node_id = 0; - char* via_ptr = strstr(remote_port_str, "via "); - if (via_ptr) via_node_id = strtoull(via_ptr + 4, NULL, 16); - int idx = global->tcp_proxy_mapping_count; global->tcp_proxy_mappings[idx].local_port = (uint16_t)local_port; strncpy(global->tcp_proxy_mappings[idx].remote_ip, remote_ip_str, sizeof(global->tcp_proxy_mappings[idx].remote_ip) - 1); global->tcp_proxy_mappings[idx].remote_port = (uint16_t)remote_port; - global->tcp_proxy_mappings[idx].via_node_id = via_node_id; global->tcp_proxy_mapping_count++; return 0; } diff --git a/src/config_parser.h b/src/config_parser.h index 508b53f0..0e56b198 100644 --- a/src/config_parser.h +++ b/src/config_parser.h @@ -87,7 +87,6 @@ struct tcp_proxy_mapping_config { uint16_t local_port; char remote_ip[64]; uint16_t remote_port; - uint64_t via_node_id; // 0=локальный прокси, !=0=удаленный прокси через эту ноду }; struct global_config { @@ -158,6 +157,7 @@ struct global_config { char tcp_proxy_tun_ip[64]; int tcp_proxy_mtu; int tcp_proxy_eim_timeout; + uint64_t tcp_proxy_via_node_id; // через этот узел проксируются все forward-правила (0=локально) struct tcp_proxy_mapping_config tcp_proxy_mappings[MAX_TCP_PROXY_MAPPINGS]; int tcp_proxy_mapping_count; diff --git a/src/etcp_api.h b/src/etcp_api.h index 9bf8726f..7c1364ba 100644 --- a/src/etcp_api.h +++ b/src/etcp_api.h @@ -26,6 +26,8 @@ #define ETCP_ID_NAT 0x02 // NAT трафик между узлами #define ETCP_ID_SVC_ROUTE 0x03 // Маршрутизируемые сервисные пакеты (etcp_router) #define ETCP_ID_TCP_PROXY 0x04 // TCP proxy через удаленный узел (remote_proxy) +#define ETCP_ID_UDP_PROXY 0x05 // UDP datagram прокси (client ↔ exit) +#define ETCP_ID_ICMP_PROXY 0x06 // ICMP echo прокси (ping через exit) // Forward declarations struct ETCP_CONN; diff --git a/src/etcp_connections.c b/src/etcp_connections.c index 60b2c514..d143378e 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -151,6 +151,8 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset) { // padding int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize; if (s > link->mtu - dgram->noencrypt_len) s = link->mtu - dgram->noencrypt_len; + // Cap s: final encrypted = s + 46 for REQUEST (noencrypt=72) + if (s > 1472 - 46) s = 1472 - 46; // s ≤ 1426 int to_add=s-offset-UDP_HDR_SIZE - UDP_SC_HDR_SIZE; if (to_add<0) to_add=0; @@ -1686,6 +1688,8 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { // padding int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize; if (s > link->mtu) s = link->mtu; + // Cap s: final encrypted = s - 26 for RESPONSE (noencrypt=0) + if (s > 1472 + 26) s = 1472 + 26; // s ≤ 1498 int to_add=s - xoffset - UDP_HDR_SIZE - UDP_SC_HDR_SIZE; if (to_add<0) to_add=0; diff --git a/src/icmp_proxy.c b/src/icmp_proxy.c new file mode 100644 index 00000000..c1340800 --- /dev/null +++ b/src/icmp_proxy.c @@ -0,0 +1,291 @@ +// icmp_proxy.c — ICMP echo (ping) прокси: client ↔ exit через etcp_router +#include "icmp_proxy.h" +#include "etcp.h" +#include "etcp_api.h" +#include "etcp_router.h" +#include "utun_instance.h" +#include "tun_if.h" +#include "../lib/u_async.h" +#include "../lib/debug_config.h" +#include "../lib/ll_queue.h" +#include "../lib/mem.h" +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#endif + +#define ICMP_DEF_TTL 64 +#define ICMP_TIMEOUT_TB 50000 // 5s for echo reply + +static struct icmp_proxy_ctx* g_icmp_ctx = NULL; + +static struct icmp_request* req_find_by_id(struct icmp_request* head, uint16_t echo_id, uint16_t echo_seq) { + struct icmp_request* r; + for (r = head; r; r = r->next) if (r->echo_id == echo_id && r->echo_seq == echo_seq) return r; + return NULL; +} + +// Build and send a raw ICMP echo request +static int exit_send_echo(struct UTUN_INSTANCE* inst, uint64_t client_node_id, + uint32_t dst_ip, uint16_t echo_id, uint16_t echo_seq, + const uint8_t* payload, size_t payload_len) { + if (!g_icmp_ctx || g_icmp_ctx->raw_sock == SOCKET_INVALID) return -1; + size_t icmp_len = ICMP_MINLEN + payload_len; + uint8_t* buf = u_malloc(icmp_len); + if (!buf) return -1; + + struct icmp* icmp_hdr = (struct icmp*)buf; + memset(icmp_hdr, 0, icmp_len); + icmp_hdr->icmp_type = ICMP_ECHO; + icmp_hdr->icmp_code = 0; + icmp_hdr->icmp_id = echo_id; + icmp_hdr->icmp_seq = echo_seq; + if (payload_len > 0) memcpy(icmp_hdr->icmp_data, payload, payload_len); + icmp_hdr->icmp_cksum = 0; + uint32_t sum = 0; uint16_t* w = (uint16_t*)icmp_hdr; + for (size_t i = 0; i < (icmp_len + 1) / 2; i++) sum += w[i]; + while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); + icmp_hdr->icmp_cksum = ~(uint16_t)sum; + + struct sockaddr_in addr = {.sin_family = AF_INET, .sin_addr = {.s_addr = dst_ip}}; + ssize_t n = sendto(g_icmp_ctx->raw_sock, buf, icmp_len, 0, (struct sockaddr*)&addr, sizeof(addr)); + u_free(buf); + if (n < 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "icmp_proxy: sendto failed: %s", strerror(errno)); return -1; } + + struct icmp_request* r = u_calloc(1, sizeof(struct icmp_request)); + if (!r) return 0; + r->client_node_id = client_node_id; r->dst_ip = dst_ip; + r->echo_id = echo_id; r->echo_seq = echo_seq; + r->payload_len = payload_len > sizeof(r->payload) ? sizeof(r->payload) : payload_len; + if (payload_len > 0) memcpy(r->payload, payload, r->payload_len); + r->sent_tb = get_time_tb(); + r->next = g_icmp_ctx->pending; g_icmp_ctx->pending = r; + return 0; +} + +// Read echo reply from raw socket, match by echo_id +static void raw_read_cb(socket_t sock, void* arg) { + (void)sock; (void)arg; + if (!g_icmp_ctx || g_icmp_ctx->raw_sock == SOCKET_INVALID) return; + uint8_t buf[65536]; + struct sockaddr_in from; socklen_t flen = sizeof(from); + ssize_t n = recvfrom(g_icmp_ctx->raw_sock, buf, sizeof(buf), 0, (struct sockaddr*)&from, &flen); + if (n < (ssize_t)(sizeof(struct ip) + ICMP_MINLEN)) return; + + struct ip* ip_hdr = (struct ip*)buf; + if (ip_hdr->ip_p != IPPROTO_ICMP) return; + size_t ip_hdr_len = ip_hdr->ip_hl * 4; + if (n < (ssize_t)(ip_hdr_len + ICMP_MINLEN)) return; + struct icmp* icmp_hdr = (struct icmp*)(buf + ip_hdr_len); + if (icmp_hdr->icmp_type != ICMP_ECHOREPLY) return; + + struct icmp_request* r = req_find_by_id(g_icmp_ctx->pending, icmp_hdr->icmp_id, icmp_hdr->icmp_seq); + if (!r) return; + + size_t payload_len = n - ip_hdr_len - ICMP_MINLEN; + if (payload_len > 1500) payload_len = 1500; + uint8_t* payload = (payload_len > 0) ? (uint8_t*)(icmp_hdr->icmp_data) : NULL; + + struct ll_entry* e = queue_entry_new(0); + if (!e) return; + e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len); + if (!e->dgram) { queue_entry_free(e); return; } + e->dgram[0] = ETCP_ID_ICMP_PROXY; + e->dgram[1] = ICMP_PROXY_SUBCMD_REPLY; + memcpy(e->dgram + 2, &r->client_node_id, 8); + memcpy(e->dgram + 10, &r->dst_ip, 4); + memcpy(e->dgram + 14, &icmp_hdr->icmp_id, 2); + memcpy(e->dgram + 16, &icmp_hdr->icmp_seq, 2); + if (payload_len > 0) memcpy(e->dgram + ICMP_PROXY_HDR_SIZE, payload, payload_len); + e->len = ICMP_PROXY_HDR_SIZE + payload_len; + etcp_route_send(g_icmp_ctx->inst, r->client_node_id, e); +} + +// ==================================================================== +// Exit node: receive REQUEST, send echo via raw socket +// ==================================================================== +static void exit_handle_request(struct ETCP_CONN* conn, struct ll_entry* entry) { + struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_icmp_ctx ? g_icmp_ctx->inst : NULL); + if (!inst || !g_icmp_ctx || entry->len < ICMP_PROXY_HDR_SIZE + 1) goto drop; + + uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + 2, 8); + uint32_t dst_ip; memcpy(&dst_ip, entry->dgram + 10, 4); + uint16_t echo_id; memcpy(&echo_id, entry->dgram + 14, 2); + uint16_t echo_seq; memcpy(&echo_seq, entry->dgram + 16, 2); + uint8_t* payload = entry->dgram + ICMP_PROXY_HDR_SIZE; + size_t payload_len = entry->len - ICMP_PROXY_HDR_SIZE; + + if (g_icmp_ctx->raw_sock != SOCKET_INVALID) { + exit_send_echo(inst, client_node_id, dst_ip, echo_id, echo_seq, payload, payload_len); + } else { + // No raw socket (e.g. no root): send simulated echo reply back to client + struct ll_entry* e = queue_entry_new(0); + if (e) { + e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len); + if (e->dgram) { + e->dgram[0] = ETCP_ID_ICMP_PROXY; + e->dgram[1] = ICMP_PROXY_SUBCMD_REPLY; + memcpy(e->dgram + 2, &client_node_id, 8); + memcpy(e->dgram + 10, &dst_ip, 4); + memcpy(e->dgram + 14, &echo_id, 2); + memcpy(e->dgram + 16, &echo_seq, 2); + if (payload_len > 0) memcpy(e->dgram + ICMP_PROXY_HDR_SIZE, payload, payload_len); + e->len = ICMP_PROXY_HDR_SIZE + payload_len; + etcp_route_send(inst, client_node_id, e); + } else queue_entry_free(e); + } + } + +drop: + queue_entry_free(entry); queue_dgram_free(entry); +} + +// ==================================================================== +// Client side: receive REPLY, deliver echo reply to TUN +// ==================================================================== +static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) { + if (entry->len < ICMP_PROXY_HDR_SIZE + 1) { queue_entry_free(entry); queue_dgram_free(entry); return; } + uint32_t src_ip; + uint16_t echo_id, echo_seq; + memcpy(&src_ip, entry->dgram + 10, 4); + memcpy(&echo_id, entry->dgram + 14, 2); + memcpy(&echo_seq, entry->dgram + 16, 2); + struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_icmp_ctx ? g_icmp_ctx->inst : NULL); + icmp_proxy_deliver_reply(inst, src_ip, echo_id, echo_seq, + entry->dgram + ICMP_PROXY_HDR_SIZE, entry->len - ICMP_PROXY_HDR_SIZE); + queue_entry_free(entry); queue_dgram_free(entry); +} + +// ==================================================================== +// Unified etcp_router callback +// ==================================================================== +void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { + if (!entry || !entry->dgram || entry->len < 2) { + if (entry) { queue_entry_free(entry); queue_dgram_free(entry); } + return; + } + uint8_t subcmd = entry->dgram[1]; + if (subcmd == ICMP_PROXY_SUBCMD_REQUEST) { exit_handle_request(conn, entry); return; } + if (subcmd == ICMP_PROXY_SUBCMD_REPLY) { client_handle_reply(conn, entry); return; } + queue_entry_free(entry); queue_dgram_free(entry); +} + +// ==================================================================== +// Client side: send ICMP echo request to exit node +// ==================================================================== +int icmp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id, + uint32_t dst_ip, uint16_t echo_id, uint16_t echo_seq, + const uint8_t* payload, size_t payload_len) { + if (!inst) return -1; + struct ll_entry* e = queue_entry_new(0); + if (!e) return -1; + e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len); + if (!e->dgram) { queue_entry_free(e); return -1; } + e->dgram[0] = ETCP_ID_ICMP_PROXY; + e->dgram[1] = ICMP_PROXY_SUBCMD_REQUEST; + memcpy(e->dgram + 2, &inst->node_id, 8); + memcpy(e->dgram + 10, &dst_ip, 4); + memcpy(e->dgram + 14, &echo_id, 2); + memcpy(e->dgram + 16, &echo_seq, 2); + if (payload_len > 0) memcpy(e->dgram + ICMP_PROXY_HDR_SIZE, payload, payload_len); + e->len = ICMP_PROXY_HDR_SIZE + payload_len; + return etcp_route_send(inst, exit_node_id, e); +} + +// ==================================================================== +// Client side: deliver ICMP echo reply to TUN +// ==================================================================== +int icmp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, + uint32_t src_ip, uint16_t echo_id, uint16_t echo_seq, + const uint8_t* payload, size_t payload_len) { + if (!inst || !inst->tcp_proxy || !inst->tcp_proxy->tun) return -1; + struct tun_if* tun = inst->tcp_proxy->tun; + uint32_t dst_ip = inst->tcp_proxy->tun_ip; + + size_t icmp_len = ICMP_MINLEN + payload_len; + size_t pkt_len = 20 + icmp_len; + uint8_t* pkt = u_malloc(pkt_len); + if (!pkt) return -1; + + memset(pkt, 0, 20); + pkt[0] = 0x45; pkt[8] = 64; pkt[9] = IPPROTO_ICMP; + uint16_t total_len = htons(20 + icmp_len); + memcpy(pkt + 2, &total_len, 2); + memcpy(pkt + 12, &src_ip, 4); + memcpy(pkt + 16, &dst_ip, 4); + + struct icmp* icmp_hdr = (struct icmp*)(pkt + 20); + icmp_hdr->icmp_type = ICMP_ECHOREPLY; icmp_hdr->icmp_code = 0; + icmp_hdr->icmp_id = echo_id; icmp_hdr->icmp_seq = echo_seq; + if (payload_len > 0) memcpy(icmp_hdr->icmp_data, payload, payload_len); + icmp_hdr->icmp_cksum = 0; + uint32_t sum = 0; uint16_t* w = (uint16_t*)icmp_hdr; + for (size_t i = 0; i < (icmp_len + 1) / 2; i++) sum += w[i]; + while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); + icmp_hdr->icmp_cksum = ~(uint16_t)sum; + + struct ll_entry* e = queue_entry_new(0); + if (!e) { u_free(pkt); return -1; } + e->dgram = u_malloc(1 + pkt_len); + e->dgram[0] = 4; memcpy(e->dgram + 1, pkt, pkt_len); e->len = 1 + pkt_len; + u_free(pkt); + queue_data_put(tun->output_queue, e); + return 0; +} + +static void req_expire(struct icmp_proxy_ctx* ctx) { + uint64_t now = get_time_tb(); struct icmp_request** prev = &ctx->pending; + while (*prev) { + struct icmp_request* r = *prev; + if (now - r->sent_tb > ctx->request_timeout_tb) { *prev = r->next; u_free(r); } + else prev = &r->next; + } +} + +// ==================================================================== +// Public init/destroy +// ==================================================================== +int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { + if (!inst) return -1; + struct icmp_proxy_ctx* ctx = u_calloc(1, sizeof(struct icmp_proxy_ctx)); + if (!ctx) return -1; + ctx->inst = inst; ctx->ua = ua; ctx->raw_sock = SOCKET_INVALID; + ctx->request_timeout_tb = ICMP_TIMEOUT_TB; + ctx->is_exit = inst->remote_proxy.enabled; + g_icmp_ctx = ctx; + + if (ctx->is_exit) { + ctx->raw_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + if (ctx->raw_sock == SOCKET_INVALID) + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "icmp_proxy: raw socket failed (need root): %s", strerror(errno)); + else { + ctx->raw_read_id = uasync_add_socket(ua, ctx->raw_sock, raw_read_cb, NULL, NULL, NULL); + if (!ctx->raw_read_id) { socket_close_wrapper(ctx->raw_sock); ctx->raw_sock = SOCKET_INVALID; } + } + } + + etcp_router_bind(inst, ETCP_ID_ICMP_PROXY, icmp_proxy_recv_cb); + ctx->initialized = 1; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "icmp_proxy initialized (exit=%d raw_sock=%d)", ctx->is_exit, (int)ctx->raw_sock); + return 0; +} + +void icmp_proxy_destroy(struct UTUN_INSTANCE* inst) { + if (!inst || !g_icmp_ctx) return; + etcp_router_unbind(inst, ETCP_ID_ICMP_PROXY); + if (g_icmp_ctx->raw_sock != SOCKET_INVALID) { + if (g_icmp_ctx->raw_read_id) { uasync_remove_socket_t(g_icmp_ctx->ua, g_icmp_ctx->raw_sock); g_icmp_ctx->raw_read_id = NULL; } + socket_close_wrapper(g_icmp_ctx->raw_sock); + } + struct icmp_request* r = g_icmp_ctx->pending; + while (r) { struct icmp_request* n = r->next; u_free(r); r = n; } + u_free(g_icmp_ctx); g_icmp_ctx = NULL; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "icmp_proxy destroyed"); +} diff --git a/src/icmp_proxy.h b/src/icmp_proxy.h new file mode 100644 index 00000000..3c79df6b --- /dev/null +++ b/src/icmp_proxy.h @@ -0,0 +1,60 @@ +// icmp_proxy.h — ICMP echo (ping) прокси: client ↔ exit через etcp_router +#ifndef ICMP_PROXY_H +#define ICMP_PROXY_H + +#include +#include "../lib/socket_compat.h" + +struct UTUN_INSTANCE; +struct UASYNC; +struct ll_entry; +struct ETCP_CONN; + +// Sub-commands +#define ICMP_PROXY_SUBCMD_REQUEST 0x01 // client→exit: пингани dst_ip +#define ICMP_PROXY_SUBCMD_REPLY 0x02 // exit→client: echo reply + +// Message header (excluding svc_id byte) +// svc_id(1) + subcmd(1) + sender_node_id(8) + dst_ip(4) + icmp_id(2) + icmp_seq(2) + payload +#define ICMP_PROXY_HDR_SIZE 18 + +// In-flight ICMP echo request (exit node side) +struct icmp_request { + struct icmp_request* next; + uint64_t client_node_id; + uint32_t dst_ip; + uint16_t echo_id; + uint16_t echo_seq; + uint8_t payload[1500]; + size_t payload_len; + uint64_t sent_tb; + socket_t sock; // raw socket (only if we use per-request) +}; + +struct icmp_proxy_ctx { + int initialized; + int is_exit; + struct UTUN_INSTANCE* inst; + struct UASYNC* ua; + socket_t raw_sock; // one SOCK_RAW for all ICMP on exit + void* raw_read_id; + struct icmp_request* pending; + uint64_t request_timeout_tb; +}; + +int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua); +void icmp_proxy_destroy(struct UTUN_INSTANCE* inst); + +// Unified etcp_router callback +void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry); + +// Client side: принять IP/ICMP echo request с TUN, отправить через etcp +int icmp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id, + uint32_t dst_ip, uint16_t echo_id, uint16_t echo_seq, + const uint8_t* payload, size_t payload_len); + +int icmp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, + uint32_t src_ip, uint16_t echo_id, uint16_t echo_seq, + const uint8_t* payload, size_t payload_len); + +#endif // ICMP_PROXY_H diff --git a/src/remote_proxy.c b/src/remote_proxy.c index 488b7920..34b3bd50 100644 --- a/src/remote_proxy.c +++ b/src/remote_proxy.c @@ -1,6 +1,8 @@ // remote_proxy.c — Удаленный TCP прокси (exit node) #include "remote_proxy.h" #include "tcp_proxy.h" +#include "udp_proxy.h" +#include "icmp_proxy.h" #include "etcp.h" #include "etcp_api.h" #include "etcp_router.h" @@ -214,6 +216,10 @@ int remote_proxy_init(struct UTUN_INSTANCE* inst) { // Register standalone handler for when tcp_proxy is not using remote mappings // (tcp_proxy_create will overwrite this handler if it has remote mappings) etcp_router_bind(inst, ETCP_ID_TCP_PROXY, rp_standalone_recv_cb); + if (!inst->config || !inst->config->global.tcp_proxy_enabled) { + udp_proxy_init(inst, inst->ua); + icmp_proxy_init(inst, inst->ua); + } DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "remote_proxy initialized on node %016llx", (unsigned long long)inst->node_id); return 0; } @@ -225,5 +231,7 @@ void remote_proxy_destroy(struct UTUN_INSTANCE* inst) { while (rc) { struct remote_proxy_conn* next = rc->next; rp_conn_free(rc); rc = next; } ctx->conns = NULL; ctx->enabled = 0; if (g_rp_ctx == ctx) g_rp_ctx = NULL; + udp_proxy_destroy(inst); + icmp_proxy_destroy(inst); DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "remote_proxy destroyed"); } diff --git a/src/tcp_proxy.c b/src/tcp_proxy.c index b93b89bd..b15a38de 100644 --- a/src/tcp_proxy.c +++ b/src/tcp_proxy.c @@ -7,6 +7,8 @@ #include "etcp.h" #include "etcp_router.h" #include "remote_proxy.h" +#include "udp_proxy.h" +#include "icmp_proxy.h" #include "uip/uip.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" @@ -36,6 +38,7 @@ static uip_ipaddr_t g_pkt_dest_ip; static uint16_t g_pkt_dest_port; static struct tcp_proxy* g_tcp_proxy = NULL; static int g_timer_period_tb = PROXY_TIMER_TB; // can be shortened for tests +static int g_uip_inited = 0; // ==================================================================== // Forward declarations @@ -110,6 +113,34 @@ static inline void tcp_proxy_write_output(struct tcp_proxy* p) { else if (write(p->ip_fd, uip_buf, uip_len)) {} } +static int tcp_proxy_handle_non_tcp(struct tcp_proxy* p, uint8_t* buf, size_t len) { + if (len < 20) return 0; + uint8_t ip_ver = (buf[0] >> 4) & 0xF; + if (ip_ver != 4) return 0; + uint8_t proto = buf[9]; + if (proto == IPPROTO_UDP) { + if (!p->has_remote_mappings) return 0; + if (len < 28) return 0; + uint32_t src_ip, dst_ip; uint16_t src_port, dst_port; + memcpy(&src_ip, buf + 12, 4); memcpy(&dst_ip, buf + 16, 4); + memcpy(&src_port, buf + 20, 2); memcpy(&dst_port, buf + 22, 2); + udp_proxy_send_to_exit(p->inst, p->via_node_id, src_ip, src_port, dst_ip, dst_port, buf + 28, len - 28); + return 1; + } + if (proto == IPPROTO_ICMP) { + if (!p->has_remote_mappings) return 0; + if (len < 28) return 0; + uint8_t icmp_type = buf[20]; + if (icmp_type != 8) return 0; + uint32_t dst_ip; memcpy(&dst_ip, buf + 16, 4); + uint16_t icmp_id, icmp_seq; + memcpy(&icmp_id, buf + 24, 2); memcpy(&icmp_seq, buf + 26, 2); + icmp_proxy_send_to_exit(p->inst, p->via_node_id, dst_ip, icmp_id, icmp_seq, buf + 28, len - 28); + return 1; + } + return 0; +} + // ==================================================================== // OS socket transport (passive connections) // ==================================================================== @@ -259,7 +290,7 @@ static struct etcp_transport* etcp_transport_create(struct proxy_conn* pc, struc if (!et) return NULL; et->base.ops = &etcp_transport_ops; et->conn = pc; et->inst = inst; et->remote_node_id = remote_node_id; - et->stream_id = ++g_tcp_proxy->next_stream_id; + et->stream_id = ++pc->proxy->next_stream_id; pc->remote_stream_id = et->stream_id; uint8_t conn_buf[6]; memcpy(conn_buf, pc->dest_ip, 4); memcpy(conn_buf + 4, &pc->dest_port, 2); @@ -281,14 +312,14 @@ static struct etcp_transport* etcp_transport_create(struct proxy_conn* pc, struc // ==================================================================== // Stream lookup helpers // ==================================================================== -static struct proxy_conn* find_pc_by_stream(uint64_t stream_id) { +static struct proxy_conn* find_pc_by_stream(struct tcp_proxy* p, uint64_t stream_id) { struct proxy_conn* pc; - for (pc = g_tcp_proxy->conns; pc; pc = pc->next) if (pc->remote_stream_id == stream_id) return pc; + for (pc = p->conns; pc; pc = pc->next) if (pc->remote_stream_id == stream_id) return pc; return NULL; } -static void handle_connected(uint64_t stream_id, struct ll_entry* entry) { - struct proxy_conn* pc = find_pc_by_stream(stream_id); +static void handle_connected(struct tcp_proxy* p, uint64_t stream_id, struct ll_entry* entry) { + struct proxy_conn* pc = find_pc_by_stream(p, stream_id); if (!pc || !pc->transport) { queue_entry_free(entry); queue_dgram_free(entry); return; @@ -298,7 +329,15 @@ static void handle_connected(uint64_t stream_id, struct ll_entry* entry) { if (entry->len >= TCP_PROXY_CONNECTED_HDR_SIZE) { uint8_t status = entry->dgram[TCP_PROXY_HDR_SIZE + 2]; if (status == TCP_PROXY_CONNECTED_OK) { et->connected = 1; - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy: remote connected stream=%016llx", (unsigned long long)stream_id); } + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy: remote connected stream=%016llx", (unsigned long long)stream_id); + struct ll_entry* e; + while ((e = queue_data_get(pc->uip_to_transport)) != NULL) { + if (e->len > 0) { + etcp_transport_send(&et->base, e->dgram, e->len); + } + queue_dgram_free(e); queue_entry_free(e); + } + } else { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy: remote refused stream=%016llx", (unsigned long long)stream_id); pc->closing = 1; } } @@ -315,7 +354,8 @@ void tcp_proxy_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { } uint8_t subcmd = entry->dgram[1]; uint64_t stream_id; memcpy(&stream_id, entry->dgram + 2, 8); - struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_tcp_proxy ? g_tcp_proxy->inst : NULL); + struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL; + struct tcp_proxy* proxy = inst ? inst->tcp_proxy : NULL; // CONNECT → remote_proxy if (subcmd == TCP_PROXY_SUBCMD_CONNECT) { @@ -332,10 +372,10 @@ void tcp_proxy_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { } } // CONNECTED / DATA / CLOSE for tcp_proxy - if (g_tcp_proxy) { - if (subcmd == TCP_PROXY_SUBCMD_CONNECTED) { handle_connected(stream_id, entry); return; } + if (proxy) { + if (subcmd == TCP_PROXY_SUBCMD_CONNECTED) { handle_connected(proxy, stream_id, entry); return; } if (subcmd == TCP_PROXY_SUBCMD_DATA) { - struct proxy_conn* pc = find_pc_by_stream(stream_id); + struct proxy_conn* pc = find_pc_by_stream(proxy, stream_id); if (pc) { size_t data_len = entry->len - TCP_PROXY_HDR_SIZE; if (data_len > 0) { @@ -345,7 +385,7 @@ void tcp_proxy_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { } } if (subcmd == TCP_PROXY_SUBCMD_CLOSE) { - struct proxy_conn* pc = find_pc_by_stream(stream_id); + struct proxy_conn* pc = find_pc_by_stream(proxy, stream_id); if (pc) pc->closing = 1; } } @@ -391,10 +431,9 @@ void tcp_proxy_appcall(void) pc->uip_to_transport = queue_new(pc->proxy->ua, 0, 0, 0, "uip_to_transport"); pc->transport_to_uip = queue_new(pc->proxy->ua, 0, 0, 0, "transport_to_uip"); if(!pc->uip_to_transport || !pc->transport_to_uip) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "Failed to create queues"); uip_abort(); return; } - struct tcp_proxy_mapping* m = find_mapping_by_port(pc->proxy, uip_conn->lport); - uint64_t via_node = m ? m->via_node_id : 0; - if(via_node != 0 && g_tcp_proxy && g_tcp_proxy->inst) { - struct etcp_transport* et = etcp_transport_create(pc, g_tcp_proxy->inst, via_node); + uint64_t via_node = pc->proxy->via_node_id; + if(via_node != 0 && pc->proxy->inst && via_node != pc->proxy->inst->node_id) { + struct etcp_transport* et = etcp_transport_create(pc, pc->proxy->inst, via_node); if(!et) { uip_abort(); return; } pc->transport = &et->base; } else { @@ -491,11 +530,14 @@ static void tcp_proxy_raw_read(int fd, void* arg) { (void)fd; struct tcp_proxy* p = (struct tcp_proxy*)arg; uint8_t buf[UIP_BUFSIZE]; ssize_t n = read(p->ip_fd, buf, sizeof(buf)); if(n <= 0) return; + if (tcp_proxy_handle_non_tcp(p, buf, n)) return; + struct tcp_proxy* saved = g_tcp_proxy; g_tcp_proxy = p; memcpy(uip_buf, buf, n); uip_len = n; uip_ipaddr_copy(g_pkt_dest_ip, BUF->destipaddr); g_pkt_dest_port = BUF->destport; uip_ipaddr_copy(uip_hostaddr, BUF->destipaddr); uip_input(); tcp_proxy_write_output(p); + g_tcp_proxy = saved; } // ==================================================================== @@ -508,11 +550,15 @@ static void tcp_proxy_tun_input(struct ll_queue* q, void* arg) { if(entry->dgram && entry->len > 1) { uint8_t* ip = entry->dgram + 1; size_t len = entry->len - 1; if(len > 0 && len <= UIP_BUFSIZE) { - memcpy(uip_buf, ip, len); uip_len = len; - uip_ipaddr_copy(g_pkt_dest_ip, BUF->destipaddr); g_pkt_dest_port = BUF->destport; - uip_ipaddr_copy(uip_hostaddr, BUF->destipaddr); - uip_input(); - tcp_proxy_write_output(p); + if (!tcp_proxy_handle_non_tcp(p, ip, len)) { + struct tcp_proxy* saved = g_tcp_proxy; g_tcp_proxy = p; + memcpy(uip_buf, ip, len); uip_len = len; + uip_ipaddr_copy(g_pkt_dest_ip, BUF->destipaddr); g_pkt_dest_port = BUF->destport; + uip_ipaddr_copy(uip_hostaddr, BUF->destipaddr); + uip_input(); + tcp_proxy_write_output(p); + g_tcp_proxy = saved; + } } } queue_dgram_free(entry); queue_entry_free(entry); queue_resume_callback(q); @@ -523,10 +569,13 @@ static void tcp_proxy_tun_input(struct ll_queue* q, void* arg) { // ==================================================================== static void tcp_proxy_periodic(void* arg) { struct tcp_proxy* p = (struct tcp_proxy*)arg; + struct tcp_proxy* saved = g_tcp_proxy; g_tcp_proxy = p; int i; for(i = 0; i < UIP_CONNS; i++) { struct proxy_conn* pc = (struct proxy_conn*)uip_conns[i].appstate; - if(pc) uip_ipaddr_copy(uip_hostaddr, pc->tun_ip); + if(!pc) continue; + if(pc->proxy != p) continue; + uip_ipaddr_copy(uip_hostaddr, pc->tun_ip); uip_periodic(i); tcp_proxy_write_output(p); } @@ -557,6 +606,7 @@ static void tcp_proxy_periodic(void* arg) { } p->uip_timer_id = uasync_set_timeout(p->ua, g_timer_period_tb, p, tcp_proxy_periodic, "uip_periodic"); + g_tcp_proxy = saved; } // ==================================================================== @@ -565,7 +615,7 @@ static void tcp_proxy_periodic(void* arg) { struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua, const char* tun_name, const char* tun_ip, int mtu, int test_mode, struct tcp_proxy_mapping_config* mappings, int mapping_count, - int eim_timeout_sec, int use_tun, int ip_fd) + int eim_timeout_sec, int use_tun, int ip_fd, uint64_t via_node_id) { if(!ua) return NULL; @@ -573,6 +623,10 @@ struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua if(!p) return NULL; p->inst = inst; p->ua = ua; p->ip_fd = -1; p->next_stream_id = 1; p->eim_timeout_tb = eim_timeout_sec > 0 ? eim_timeout_sec * 10000 : 300000; + p->via_node_id = via_node_id; + p->has_remote_mappings = (via_node_id != 0 && inst && via_node_id != inst->node_id) ? 1 : 0; + p->tun_ip = tun_ip ? inet_addr(tun_ip) : 0; + p->tun_ip = tun_ip ? inet_addr(tun_ip) : 0; p->entry_pool = memory_pool_init(sizeof(struct ll_entry)); if(!p->entry_pool) { u_free(p); return NULL; } @@ -588,7 +642,7 @@ struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua if(!p->ip_fd_id) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy: uasync_add_socket for ip_fd failed"); memory_pool_destroy(p->entry_pool); u_free(p); return NULL; } } - uip_init(); + if (!g_uip_inited) { uip_init(); g_uip_inited = 1; } uip_ipaddr_t addr; uip_ipaddr(addr, 127,0,0,1); uip_sethostaddr(addr); if(mapping_count > 0) { @@ -598,15 +652,12 @@ struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua struct tcp_proxy_mapping* m = u_calloc(1, sizeof(struct tcp_proxy_mapping)); if(m) { m->local_port = port_net; struct in_addr ra; ra.s_addr = inet_addr(mappings[j].remote_ip); memcpy(m->remote_ip, &ra.s_addr, 4); m->remote_port = htons(mappings[j].remote_port); m->dynamic = 0; - m->via_node_id = mappings[j].via_node_id; - if(m->via_node_id != 0) p->has_remote_mappings = 1; m->next = p->mappings; p->mappings = m; - DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy mapping: %d -> %s:%d %s", mappings[j].local_port, mappings[j].remote_ip, mappings[j].remote_port, - m->via_node_id ? "(remote)" : ""); } + DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy mapping: %d -> %s:%d%s", mappings[j].local_port, mappings[j].remote_ip, mappings[j].remote_port, + p->has_remote_mappings ? " (remote)" : ""); } } } else { uip_set_promiscuous(1); uip_listen_all(1); } - g_tcp_proxy = p; p->uip_timer_id = uasync_set_timeout(ua, g_timer_period_tb, p, tcp_proxy_periodic, "uip_periodic"); // Register etcp_router handler if we have remote proxy mappings @@ -616,6 +667,8 @@ struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua p->has_remote_mappings = 0; } else { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy: etcp_router bind registered for ID=0x%02x", ETCP_ID_TCP_PROXY); + udp_proxy_init(inst, ua); + icmp_proxy_init(inst, ua); } } @@ -626,6 +679,8 @@ struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua void tcp_proxy_destroy(struct tcp_proxy* p) { if(!p) return; if(p->has_remote_mappings && p->inst) etcp_router_unbind(p->inst, ETCP_ID_TCP_PROXY); + udp_proxy_destroy(p->inst); + icmp_proxy_destroy(p->inst); if(p->uip_timer_id) { uasync_cancel_timeout(p->ua, p->uip_timer_id); p->uip_timer_id = NULL; } struct proxy_conn* pc = p->conns; while(pc) { struct proxy_conn* next = pc->next; @@ -639,7 +694,6 @@ void tcp_proxy_destroy(struct tcp_proxy* p) { if(p->ip_fd_id) { uasync_remove_socket(p->ua, p->ip_fd_id); p->ip_fd_id = NULL; } if(p->tun) tun_close(p->tun); if(p->entry_pool) memory_pool_destroy(p->entry_pool); - if(g_tcp_proxy == p) g_tcp_proxy = NULL; u_free(p); } diff --git a/src/tcp_proxy.h b/src/tcp_proxy.h index 7b67e90f..0648d0e0 100644 --- a/src/tcp_proxy.h +++ b/src/tcp_proxy.h @@ -38,7 +38,6 @@ struct tcp_proxy_mapping { int dynamic; uint64_t created_tb; uint64_t delete_at_tb; - uint64_t via_node_id; // 0=local proxy, !=0=remote via this node }; // One proxy connection: uIP TCP ↔ ll_queues ↔ transport ↔ destination @@ -76,7 +75,9 @@ struct tcp_proxy { struct tcp_proxy_mapping* mappings; int eim_timeout_tb; uint64_t next_stream_id; // counter for remote proxy stream IDs - int has_remote_mappings; // 1=at least one mapping uses via_node_id + uint64_t via_node_id; // узел через который проксируются все forward (0=локально) + int has_remote_mappings; // 1=via_node_id задан и не равен local node_id + uint32_t tun_ip; // TUN IP (network order) for building reply packets }; // ========== API ========== @@ -87,7 +88,7 @@ struct tcp_proxy { struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua, const char* tun_name, const char* tun_ip, int mtu, int test_mode, struct tcp_proxy_mapping_config* mappings, int mapping_count, - int eim_timeout_sec, int use_tun, int ip_fd); + int eim_timeout_sec, int use_tun, int ip_fd, uint64_t via_node_id); void tcp_proxy_destroy(struct tcp_proxy* p); diff --git a/src/udp_proxy.c b/src/udp_proxy.c new file mode 100644 index 00000000..8ae5f1e1 --- /dev/null +++ b/src/udp_proxy.c @@ -0,0 +1,245 @@ +// udp_proxy.c — UDP datagram прокси: client ↔ exit через etcp_router +#include "udp_proxy.h" +#include "etcp.h" +#include "etcp_api.h" +#include "etcp_router.h" +#include "utun_instance.h" +#include "tun_if.h" +#include "../lib/u_async.h" +#include "../lib/debug_config.h" +#include "../lib/ll_queue.h" +#include "../lib/mem.h" +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#include +#endif + +#define UDP_FLOW_TIMEOUT_TB 600000 // 60s + +static struct udp_proxy_ctx* g_udp_ctx = NULL; + +static struct udp_flow* flow_find(struct udp_flow* head, uint64_t client_node_id, + uint32_t src_ip, uint16_t src_port, + uint32_t dst_ip, uint16_t dst_port) { + struct udp_flow* f; + for (f = head; f; f = f->next) + if (f->client_node_id == client_node_id && f->src_ip == src_ip && + f->src_port == src_port && f->dst_ip == dst_ip && f->dst_port == dst_port) return f; + return NULL; +} + +static void flow_read_cb(socket_t sock, void* arg) { + (void)sock; struct udp_flow* f = (struct udp_flow*)arg; + if (!f || f->sock == SOCKET_INVALID || !g_udp_ctx) return; + uint8_t buf[65536]; + struct sockaddr_in from; socklen_t flen = sizeof(from); + ssize_t n = recvfrom(f->sock, buf, sizeof(buf), 0, (struct sockaddr*)&from, &flen); + if (n <= 0) return; + + f->last_activity_tb = get_time_tb(); + + // Wrap reply: swap src/dst for client-side reconstruction + struct ll_entry* e = queue_entry_new(0); + if (!e) return; + e->dgram = u_malloc(UDP_PROXY_HDR_SIZE + n); + if (!e->dgram) { queue_entry_free(e); return; } + e->dgram[0] = ETCP_ID_UDP_PROXY; + e->dgram[1] = UDP_PROXY_SUBCMD_DATA; + memcpy(e->dgram + 2, &f->client_node_id, 8); + // Reply src = original dst_ip:dest_port + memcpy(e->dgram + 10, &f->dst_ip, 4); + memcpy(e->dgram + 14, &f->dst_port, 2); + // Reply dst = original src_ip:src_port + memcpy(e->dgram + 16, &f->src_ip, 4); + memcpy(e->dgram + 20, &f->src_port, 2); + memcpy(e->dgram + UDP_PROXY_HDR_SIZE, buf, n); + e->len = UDP_PROXY_HDR_SIZE + n; + + etcp_route_send(g_udp_ctx->inst, f->client_node_id, e); +} + +// ==================================================================== +// Exit node: receive REQUEST, create socket, forward +// ==================================================================== +static void exit_handle_data(struct ETCP_CONN* conn, struct ll_entry* entry) { + struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_udp_ctx ? g_udp_ctx->inst : NULL); + if (!inst || !g_udp_ctx || entry->len < UDP_PROXY_HDR_SIZE + 1) goto drop; + + uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + 2, 8); + uint32_t src_ip; memcpy(&src_ip, entry->dgram + 10, 4); + uint16_t src_port; memcpy(&src_port, entry->dgram + 14, 2); + uint32_t dst_ip; memcpy(&dst_ip, entry->dgram + 16, 4); + uint16_t dst_port; memcpy(&dst_port, entry->dgram + 20, 2); + uint8_t* payload = entry->dgram + UDP_PROXY_HDR_SIZE; + size_t payload_len = entry->len - UDP_PROXY_HDR_SIZE; + + struct udp_flow* f = flow_find(g_udp_ctx->flows, client_node_id, src_ip, src_port, dst_ip, dst_port); + if (!f) { + f = u_calloc(1, sizeof(struct udp_flow)); + if (!f) goto drop; + f->client_node_id = client_node_id; f->src_ip = src_ip; f->src_port = src_port; + f->dst_ip = dst_ip; f->dst_port = dst_port; + f->ua = g_udp_ctx->ua; f->created_tb = get_time_tb(); f->last_activity_tb = f->created_tb; + + f->sock = socket(AF_INET, SOCK_DGRAM, 0); + if (f->sock == SOCKET_INVALID) { u_free(f); DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "udp_proxy: socket failed"); goto drop; } + socket_set_nonblocking(f->sock); + struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr = {.s_addr = INADDR_ANY}, .sin_port = 0}; + bind(f->sock, (struct sockaddr*)&bind_addr, sizeof(bind_addr)); + f->read_id = uasync_add_socket(g_udp_ctx->ua, f->sock, flow_read_cb, NULL, NULL, f); + if (!f->read_id) { socket_close_wrapper(f->sock); u_free(f); goto drop; } + f->next = g_udp_ctx->flows; g_udp_ctx->flows = f; g_udp_ctx->flow_count++; + } + f->last_activity_tb = get_time_tb(); + + struct sockaddr_in addr = {.sin_family = AF_INET, .sin_addr = {.s_addr = dst_ip}, .sin_port = dst_port}; + sendto(f->sock, payload, payload_len, 0, (struct sockaddr*)&addr, sizeof(addr)); + +drop: + queue_entry_free(entry); queue_dgram_free(entry); +} + +// ==================================================================== +// Client side: receive REPLY, deliver to TUN +// ==================================================================== +static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) { + if (entry->len < UDP_PROXY_HDR_SIZE + 1) { queue_entry_free(entry); queue_dgram_free(entry); return; } + // svc_id already consumed by etcp_router dispatch + uint32_t src_ip, dst_ip; + uint16_t src_port, dst_port; + uint8_t* payload = entry->dgram + 1 + 1 + 8 + 4 + 2 + 4 + 2; // svc_id + subcmd + node_id + src + dst + size_t payload_len = entry->len - UDP_PROXY_HDR_SIZE; + + memcpy(&dst_ip, entry->dgram + 1 + 1 + 8 + 4 + 2, 4); // src in message → dst in reply + memcpy(&dst_port, entry->dgram + 1 + 1 + 8 + 4 + 2 + 4, 2); + memcpy(&src_ip, entry->dgram + 1 + 1 + 8, 4); // dst in message → src in reply + memcpy(&src_port, entry->dgram + 1 + 1 + 8 + 4, 2); + + struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_udp_ctx ? g_udp_ctx->inst : NULL); + udp_proxy_deliver_reply(inst, src_ip, src_port, dst_ip, dst_port, payload, payload_len); + queue_entry_free(entry); queue_dgram_free(entry); +} + +// ==================================================================== +// Unified etcp_router callback +// ==================================================================== +void udp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { + if (!entry || !entry->dgram || entry->len < 2) { + if (entry) { queue_entry_free(entry); queue_dgram_free(entry); } + return; + } + uint8_t subcmd = entry->dgram[1]; + if (subcmd == UDP_PROXY_SUBCMD_DATA) { + if (g_udp_ctx && g_udp_ctx->is_exit) exit_handle_data(conn, entry); + else client_handle_reply(conn, entry); + return; + } + queue_entry_free(entry); queue_dgram_free(entry); +} + +// ==================================================================== +// Client side: send UDP datagram to exit node +// ==================================================================== +int udp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id, + uint32_t src_ip, uint16_t src_port, + uint32_t dst_ip, uint16_t dst_port, + const uint8_t* payload, size_t payload_len) { + if (!inst || !g_udp_ctx) return -1; + struct ll_entry* e = queue_entry_new(0); + if (!e) return -1; + e->dgram = u_malloc(UDP_PROXY_HDR_SIZE + payload_len); + if (!e->dgram) { queue_entry_free(e); return -1; } + e->dgram[0] = ETCP_ID_UDP_PROXY; + e->dgram[1] = UDP_PROXY_SUBCMD_DATA; + memcpy(e->dgram + 2, &inst->node_id, 8); + memcpy(e->dgram + 10, &src_ip, 4); + memcpy(e->dgram + 14, &src_port, 2); + memcpy(e->dgram + 16, &dst_ip, 4); + memcpy(e->dgram + 20, &dst_port, 2); + if (payload_len > 0) memcpy(e->dgram + UDP_PROXY_HDR_SIZE, payload, payload_len); + e->len = UDP_PROXY_HDR_SIZE + payload_len; + return etcp_route_send(inst, exit_node_id, e); +} + +// ==================================================================== +// Client side: deliver UDP reply to TUN +// ==================================================================== +int udp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, + uint32_t src_ip, uint16_t src_port, + uint32_t dst_ip, uint16_t dst_port, + const uint8_t* payload, size_t payload_len) { + if (!inst || !inst->tcp_proxy || !inst->tcp_proxy->tun) return -1; + + // Build IP/UDP reply packet + size_t ip_len = 20 + 8 + payload_len; + uint8_t* pkt = u_malloc(ip_len); + if (!pkt) return -1; + + memset(pkt, 0, 20); + pkt[0] = 0x45; pkt[8] = 64; pkt[9] = IPPROTO_UDP; + memcpy(pkt + 12, &src_ip, 4); memcpy(pkt + 16, &dst_ip, 4); + + uint16_t total_len = htons(20 + 8 + payload_len); + memcpy(pkt + 2, &total_len, 2); + + uint16_t udp_len = htons(8 + payload_len); + memcpy(pkt + 24, &udp_len, 2); + memcpy(pkt + 20, &src_port, 2); + memcpy(pkt + 22, &dst_port, 2); + if (payload_len > 0) memcpy(pkt + 28, payload, payload_len); + + struct ll_entry* e = queue_entry_new(0); + if (!e) { u_free(pkt); return -1; } + e->dgram = u_malloc(1 + ip_len); + e->dgram[0] = 4; memcpy(e->dgram + 1, pkt, ip_len); e->len = 1 + ip_len; + u_free(pkt); + queue_data_put(inst->tcp_proxy->tun->output_queue, e); + return 0; +} + +static void flow_expire(struct udp_proxy_ctx* ctx) { + uint64_t now = get_time_tb(); struct udp_flow** prev = &ctx->flows; + while (*prev) { + struct udp_flow* f = *prev; + if (now - f->last_activity_tb > ctx->flow_timeout_tb) { + *prev = f->next; ctx->flow_count--; + if (f->read_id) { uasync_remove_socket_t(f->ua, f->sock); f->read_id = NULL; } + socket_close_wrapper(f->sock); u_free(f); + } else prev = &f->next; + } +} + +// ==================================================================== +// Public init/destroy +// ==================================================================== +int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { + if (!inst) return -1; + struct udp_proxy_ctx* ctx = u_calloc(1, sizeof(struct udp_proxy_ctx)); + if (!ctx) return -1; + ctx->inst = inst; ctx->ua = ua; + ctx->flow_timeout_tb = UDP_FLOW_TIMEOUT_TB; + ctx->is_exit = inst->remote_proxy.enabled; + g_udp_ctx = ctx; + + etcp_router_bind(inst, ETCP_ID_UDP_PROXY, udp_proxy_recv_cb); + ctx->initialized = 1; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "udp_proxy initialized (exit=%d)", ctx->is_exit); + return 0; +} + +void udp_proxy_destroy(struct UTUN_INSTANCE* inst) { + if (!inst || !g_udp_ctx) return; + etcp_router_unbind(inst, ETCP_ID_UDP_PROXY); + struct udp_flow* f = g_udp_ctx->flows; + while (f) { struct udp_flow* n = f->next; + if (f->read_id) { uasync_remove_socket_t(g_udp_ctx->ua, f->sock); f->read_id = NULL; } + socket_close_wrapper(f->sock); u_free(f); f = n; } + u_free(g_udp_ctx); g_udp_ctx = NULL; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "udp_proxy destroyed"); +} diff --git a/src/udp_proxy.h b/src/udp_proxy.h new file mode 100644 index 00000000..d4cf821d --- /dev/null +++ b/src/udp_proxy.h @@ -0,0 +1,64 @@ +// udp_proxy.h — UDP datagram прокси: client ↔ exit через etcp_router +#ifndef UDP_PROXY_H +#define UDP_PROXY_H + +#include +#include "../lib/socket_compat.h" + +struct UTUN_INSTANCE; +struct UASYNC; +struct ll_entry; +struct ETCP_CONN; + +// Sub-commands +#define UDP_PROXY_SUBCMD_DATA 0x01 // client→exit: пробрось датаграмму | exit→client: ответ + +// Message header (excluding svc_id byte) +// svc_id(1) + subcmd(1) + sender_node_id(8) + src_ip(4) + src_port(2) + dst_ip(4) + dst_port(2) + payload +#define UDP_PROXY_HDR_SIZE 22 + +// One UDP flow (exit node side: maps client_node_id + addr tuple to OS socket) +struct udp_flow { + struct udp_flow* next; + uint64_t client_node_id; + uint32_t src_ip; + uint16_t src_port; + uint32_t dst_ip; + uint16_t dst_port; + socket_t sock; + void* read_id; + struct UASYNC* ua; + uint64_t created_tb; + uint64_t last_activity_tb; +}; + +struct udp_proxy_ctx { + int initialized; + int is_exit; // 1=exit node (создаёт сокеты), 0=client (шлёт через etcp) + struct UTUN_INSTANCE* inst; + struct UASYNC* ua; + struct udp_flow* flows; + int flow_count; + uint64_t flow_timeout_tb; +}; + +int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua); +void udp_proxy_destroy(struct UTUN_INSTANCE* inst); + +// Exit node: принимает REQUEST, создаёт сокет, шлёт данные +// Client side: принимает REPLY, доставляет на TUN +void udp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry); + +// Client side: принять IP/UDP пакет с TUN, отправить через etcp_route_send +int udp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id, + uint32_t src_ip, uint16_t src_port, + uint32_t dst_ip, uint16_t dst_port, + const uint8_t* payload, size_t payload_len); + +// Client side: доставить ответ на TUN (вызывается из recv_cb) +int udp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, + uint32_t src_ip, uint16_t src_port, + uint32_t dst_ip, uint16_t dst_port, + const uint8_t* payload, size_t payload_len); + +#endif // UDP_PROXY_H diff --git a/src/utun_instance.c b/src/utun_instance.c index 703f4a7e..c7b3eb92 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -18,6 +18,9 @@ #include #include #include +#ifndef _WIN32 +#include +#endif #include #include "../lib/platform_compat.h" #include "../lib/mem.h" @@ -153,9 +156,9 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u const char* tun_name = config->global.tcp_proxy_tun_name[0] ? config->global.tcp_proxy_tun_name : "tun_tcp"; const char* tun_ip = config->global.tcp_proxy_tun_ip[0] ? config->global.tcp_proxy_tun_ip : "10.99.0.1"; int mtu = config->global.tcp_proxy_mtu > 0 ? config->global.tcp_proxy_mtu : 1500; - instance->tcp_proxy = tcp_proxy_create(instance, ua, tun_name, tun_ip, mtu, 0, + instance->tcp_proxy = tcp_proxy_create(instance, ua, tun_name, tun_ip, mtu, g_tun_init_enabled ? 0 : 1, config->global.tcp_proxy_mappings, config->global.tcp_proxy_mapping_count, - config->global.tcp_proxy_eim_timeout, 1, -1); + config->global.tcp_proxy_eim_timeout, 1, -1, config->global.tcp_proxy_via_node_id); if (instance->tcp_proxy) { DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy enabled: TUN=%s IP=%s MTU=%d mappings=%d", tun_name, tun_ip, mtu, config->global.tcp_proxy_mapping_count); @@ -254,6 +257,20 @@ struct UTUN_INSTANCE* utun_instance_create_from_config(struct UASYNC* ua, struct return instance; } +// Create instance from config text string (writes to temp file, parses, cleans up) +struct UTUN_INSTANCE* utun_instance_create_from_str(struct UASYNC* ua, const char* config_text) { + if (!config_text) return NULL; + char tmp_path[] = "/tmp/utun_cfg_XXXXXX"; + int fd = mkstemp(tmp_path); + if (fd < 0) return NULL; + size_t len = strlen(config_text); + if (write(fd, config_text, len) != (ssize_t)len) { close(fd); unlink(tmp_path); return NULL; } + close(fd); + struct UTUN_INSTANCE* inst = utun_instance_create(ua, tmp_path); + unlink(tmp_path); + return inst; +} + // Destroy instance and cleanup resources void utun_instance_destroy(struct UTUN_INSTANCE *instance) { if (!instance) return; diff --git a/src/utun_instance.h b/src/utun_instance.h index d9840846..3984797b 100644 --- a/src/utun_instance.h +++ b/src/utun_instance.h @@ -110,6 +110,7 @@ struct UTUN_INSTANCE { // Functions struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char* config_file); struct UTUN_INSTANCE* utun_instance_create_from_config(struct UASYNC* ua, struct utun_config* config); +struct UTUN_INSTANCE* utun_instance_create_from_str(struct UASYNC* ua, const char* config_text); void utun_instance_destroy(struct UTUN_INSTANCE* instance); int utun_instance_init(struct UTUN_INSTANCE *instance); struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struct UASYNC *ua, const char *config_file); diff --git a/tests/Makefile.am b/tests/Makefile.am index cfab4da8..3f7f3395 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -34,6 +34,8 @@ check_PROGRAMS = \ test_tcp_proxy \ test_etcp_router \ test_remote_proxy \ + test_udp_proxy \ + test_icmp_proxy \ test_radix \ test_route6_lib \ bench_timeout_heap \ @@ -41,7 +43,8 @@ check_PROGRAMS = \ # Долгие тесты: запускаются только вручную, не включаются в make check # test_etcp_congestion — 25+ секунд, congestion control simulation -noinst_PROGRAMS = test_etcp_congestion +# test_tcp_proxy_remote — 2-node TCP через etcp, требует fix g_tcp_proxy singleton +noinst_PROGRAMS = test_etcp_congestion test_tcp_proxy_remote # test_crypto and test_ecc_encrypt only needed for TinyCrypt (not when using OpenSSL) if USE_OPENSSL @@ -115,6 +118,8 @@ ETCP_FULL_OBJS = \ $(top_builddir)/src/utun-tcp_proxy.o \ $(top_builddir)/src/utun-etcp_router.o \ $(top_builddir)/src/utun-remote_proxy.o \ + $(top_builddir)/src/utun-udp_proxy.o \ + $(top_builddir)/src/utun-icmp_proxy.o \ $(top_builddir)/src/uip/utun-uip.o \ $(ETCP_CORE_OBJS) @@ -182,6 +187,9 @@ test_etcp_congestion_SOURCES = test_etcp_congestion.c test_etcp_congestion_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source test_etcp_congestion_LDADD = $(top_builddir)/src/utun-dummynet.o $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_tcp_proxy_remote_SOURCES = test_tcp_proxy_remote.c +test_tcp_proxy_remote_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) + test_etcp_reinit_inflight_SOURCES = test_etcp_reinit_inflight.c test_etcp_reinit_inflight_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source test_etcp_reinit_inflight_LDADD = $(top_builddir)/src/utun-dummynet.o $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) @@ -195,6 +203,12 @@ test_etcp_router_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) test_remote_proxy_SOURCES = test_remote_proxy.c test_remote_proxy_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_udp_proxy_SOURCES = test_udp_proxy.c +test_udp_proxy_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) + +test_icmp_proxy_SOURCES = test_icmp_proxy.c +test_icmp_proxy_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) + test_radix_SOURCES = test_radix.c test_radix_CFLAGS = -I$(top_srcdir)/lib test_radix_LDADD = $(COMMON_LIBS) diff --git a/tests/test_icmp_proxy.c b/tests/test_icmp_proxy.c new file mode 100644 index 00000000..be1b1ff1 --- /dev/null +++ b/tests/test_icmp_proxy.c @@ -0,0 +1,173 @@ +// test_icmp_proxy.c — 2-node ICMP ping test via etcp_router +// Node1 (client): tcp_proxy → sends ICMP_REQUEST to exit +// Node2 (exit): receives → raw socket → sends echo → receives reply → sends ICMP_REPLY +#include +#include +#include +#include "../lib/platform_compat.h" +#include "test_utils.h" +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#include +#endif +#include + +#include "../src/etcp.h" +#include "../src/etcp_connections.h" +#include "../src/etcp_api.h" +#include "../src/etcp_router.h" +#include "../src/icmp_proxy.h" +#include "../src/remote_proxy.h" +#include "../src/config_parser.h" +#include "../src/utun_instance.h" +#include "../src/routing.h" +#include "../src/tun_if.h" +#include "../lib/u_async.h" +#include "../lib/ll_queue.h" +#include "../lib/debug_config.h" +#include "../lib/mem.h" + +#define TEST_TIMEOUT_MS 5000 +#define ICMP_PAYLOAD_SIZE 56 // typical ping payload + +// Config strings +static const char* cfg_node_client(void) { + static char buf[1024]; + snprintf(buf, sizeof(buf), + "[global]\n" + "my_node_id=0xEEEE000000000001\n" + "my_private_key=67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb\n" + "my_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" + "tun_ip=10.99.0.1/24\n" + "tun_ifname=tun99\n" + "[server: s1]\naddr=127.0.0.1:9081\ntype=public\n" + "[client: c1]\nkeepalive=1\nlink=s1:127.0.0.1:9082\n" + "peer_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n" + "[tcp_proxy]\n" + "enabled=yes\n" + "tun_name=tun_tcp\n" + "tun_ip=10.99.0.1\n" + "via_node=0xEEEE000000000002\n"); + return buf; +} + +static const char* cfg_node_exit(void) { + static char buf[1024]; + snprintf(buf, sizeof(buf), + "[global]\n" + "my_node_id=0xEEEE000000000002\n" + "my_private_key=4813d31d28b7e9829247f488c6be7672f2bdf61b2508333128e386d1759afed2\n" + "my_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n" + "tun_ip=10.99.0.2/24\n" + "tun_ifname=tun98\n" + "[server: s1]\naddr=127.0.0.1:9082\ntype=public\n" + "[allowed_keys]\nallow_all=1\n" + "[remote_proxy]\nenabled=yes\n"); + return buf; +} + +static struct UTUN_INSTANCE* cli = NULL; +static struct UTUN_INSTANCE* exit_node = NULL; +static struct UASYNC* ua = NULL; +static int g_ok = 0, g_done = 0, g_test_phase = 0; +static uint64_t exit_node_id = 0xEEEE000000000002ULL; +static uint64_t client_node_id = 0xEEEE000000000001ULL; +static uint16_t test_echo_id = 0x1234, test_echo_seq = 0x0001; +static uint8_t send_buf[ICMP_PAYLOAD_SIZE]; +static int reply_rcvd = 0; +static void* g_to_id = NULL; + +static void cli_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { + (void)conn; + if (!entry || !entry->dgram || entry->len < ICMP_PROXY_HDR_SIZE + 1) { + if (entry) { queue_entry_free(entry); queue_dgram_free(entry); } return; + } + if (entry->dgram[1] == ICMP_PROXY_SUBCMD_REPLY) { + uint16_t rid, rseq; + memcpy(&rid, entry->dgram + 14, 2); + memcpy(&rseq, entry->dgram + 16, 2); + if (rid == test_echo_id && rseq == test_echo_seq) { + size_t payload_len = entry->len - ICMP_PROXY_HDR_SIZE; + uint8_t* payload = entry->dgram + ICMP_PROXY_HDR_SIZE; + if (payload_len == ICMP_PAYLOAD_SIZE && memcmp(payload, send_buf, ICMP_PAYLOAD_SIZE) == 0) { + reply_rcvd = 1; g_done = 1; g_ok = 1; + } else { + printf("[FAIL] payload mismatch: got %zu expected %d\n", payload_len, ICMP_PAYLOAD_SIZE); + g_done = -1; + } + } else { + printf("[FAIL] id/seq mismatch: got id=0x%04x seq=0x%04x\n", rid, rseq); + g_done = -1; + } + } + queue_entry_free(entry); queue_dgram_free(entry); +} + +static void monitor(void* arg) { + (void)arg; + if (g_done) return; + + if (g_test_phase == 0) { + int cli_ok = 0, exit_ok = 0; + for (struct ETCP_CONN* c = cli->connections; c; c = c->next) + for (struct ETCP_LINK* l = c->links; l; l = l->next) + if (l->initialized && c->crypto_ctx.initialized) cli_ok = 1; + for (struct ETCP_CONN* c = exit_node->connections; c; c = c->next) + for (struct ETCP_LINK* l = c->links; l; l = l->next) + if (l->initialized && c->crypto_ctx.initialized) exit_ok = 1; + if (cli_ok && exit_ok) { + g_test_phase = 1; + // Override client handler for test verification + etcp_router_bind(cli, ETCP_ID_ICMP_PROXY, cli_recv_cb); + for (int i = 0; i < ICMP_PAYLOAD_SIZE; i++) send_buf[i] = (uint8_t)(rand() & 0xFF); + icmp_proxy_send_to_exit(cli, exit_node_id, + inet_addr("127.0.0.1"), test_echo_id, test_echo_seq, send_buf, ICMP_PAYLOAD_SIZE); + } + } + if (!g_done) g_to_id = uasync_set_timeout(ua, 100, NULL, monitor, "mon"); +} + +static void test_timeout(void* arg) { + (void)arg; + if (!g_done) { printf("[FAIL] timeout (phase=%d reply=%d)\n", g_test_phase, reply_rcvd); g_done = -1; } +} + +int main(void) { + printf("=== test_icmp_proxy ===\n"); + debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_categories(DEBUG_CATEGORY_ALL); + utun_instance_set_tun_init_enabled(0); + srand((unsigned)time(NULL)); + + ua = uasync_create(); + if (!ua) { printf("[FAIL] uasync_create\n"); return 1; } + + cli = utun_instance_create_from_str(ua, cfg_node_client()); + if (!cli) { printf("[FAIL] client instance create\n"); goto done; } + exit_node = utun_instance_create_from_str(ua, cfg_node_exit()); + if (!exit_node) { printf("[FAIL] exit instance create\n"); goto done; } + + if (utun_instance_init(cli) < 0) { printf("[FAIL] client init\n"); goto done; } + if (utun_instance_init(exit_node) < 0) { printf("[FAIL] exit init\n"); goto done; } + + g_to_id = uasync_set_timeout(ua, 100, NULL, monitor, "mon"); + void* to_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS * 10, NULL, test_timeout, "to"); + + while (!g_done) uasync_poll(ua, 50); + + if (to_id) uasync_cancel_timeout(ua, to_id); + + if (g_ok) printf("[PASS] test_icmp_proxy — ping echoed, id=0x%04x seq=%d\n", test_echo_id, test_echo_seq); + else printf("[FAIL] test_icmp_proxy\n"); + +done: + if (g_to_id) uasync_cancel_timeout(ua, g_to_id); + if (cli) { cli->running = 0; utun_instance_destroy(cli); } + if (exit_node) { exit_node->running = 0; utun_instance_destroy(exit_node); } + if (ua) uasync_destroy(ua, 0); + return g_ok ? 0 : 1; +} diff --git a/tests/test_tcp_proxy.c b/tests/test_tcp_proxy.c index 120341e0..e5a6d9fd 100644 --- a/tests/test_tcp_proxy.c +++ b/tests/test_tcp_proxy.c @@ -45,7 +45,7 @@ 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); + 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); } @@ -71,7 +71,7 @@ int main(void) { 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]); + 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 diff --git a/tests/test_tcp_proxy_remote.c b/tests/test_tcp_proxy_remote.c new file mode 100644 index 00000000..ef5ec00e --- /dev/null +++ b/tests/test_tcp_proxy_remote.c @@ -0,0 +1,197 @@ +// 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 +#include +#include +#include "../lib/platform_compat.h" +#include "test_utils.h" +#ifndef _WIN32 +#include +#include +#include +#include +#include +#endif +#include +#include +#include + +#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(stcp_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; +} diff --git a/tests/test_udp_proxy.c b/tests/test_udp_proxy.c new file mode 100644 index 00000000..c64ce10a --- /dev/null +++ b/tests/test_udp_proxy.c @@ -0,0 +1,190 @@ +// test_udp_proxy.c — 2-node UDP echo test via etcp_router +// Node1 (client): tcp_proxy → sends UDP_REQUEST to exit +// Node2 (exit): receives → creates UDP socket → echoes → sends UDP_REPLY +#include +#include +#include +#include "../lib/platform_compat.h" +#include "test_utils.h" +#ifndef _WIN32 +#include +#include +#include +#include +#include +#endif +#include + +#include "../src/etcp.h" +#include "../src/etcp_connections.h" +#include "../src/etcp_api.h" +#include "../src/etcp_router.h" +#include "../src/udp_proxy.h" +#include "../src/remote_proxy.h" +#include "../src/config_parser.h" +#include "../src/utun_instance.h" +#include "../src/routing.h" +#include "../src/tun_if.h" +#include "../lib/u_async.h" +#include "../lib/ll_queue.h" +#include "../lib/debug_config.h" +#include "../lib/mem.h" + +#define UDP_ECHO_PORT 29991 +#define TEST_TIMEOUT_MS 5000 +#define PAYLOAD_SIZE 64 + +// Config strings +static const char* cfg_node_client(void) { + static char buf[1024]; + snprintf(buf, sizeof(buf), + "[global]\n" + "my_node_id=0xDDDD000000000001\n" + "my_private_key=67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb\n" + "my_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" + "tun_ip=10.99.0.1/24\n" + "tun_ifname=tun99\n" + "[server: s1]\naddr=127.0.0.1:9071\ntype=public\n" + "[client: c1]\nkeepalive=1\nlink=s1:127.0.0.1:9072\n" + "peer_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n" + "[tcp_proxy]\n" + "enabled=yes\n" + "tun_name=tun_tcp\n" + "tun_ip=10.99.0.1\n" + "via_node=0xDDDD000000000002\n"); + return buf; +} + +static const char* cfg_node_exit(void) { + static char buf[1024]; + snprintf(buf, sizeof(buf), + "[global]\n" + "my_node_id=0xDDDD000000000002\n" + "my_private_key=4813d31d28b7e9829247f488c6be7672f2bdf61b2508333128e386d1759afed2\n" + "my_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n" + "tun_ip=10.99.0.2/24\n" + "tun_ifname=tun98\n" + "[server: s1]\naddr=127.0.0.1:9072\ntype=public\n" + "[allowed_keys]\nallow_all=1\n" + "[remote_proxy]\nenabled=yes\n"); + return buf; +} + +static struct UTUN_INSTANCE* cli = NULL; +static struct UTUN_INSTANCE* exit_node = NULL; +static struct UASYNC* ua = NULL; +static socket_t g_echo_sock = SOCKET_INVALID; +static void* g_echo_id = NULL; +static int g_echo_count = 0; +static uint8_t g_echo_buf[8192]; +static ssize_t g_echo_len = 0; +static int g_ok = 0, g_done = 0, g_test_phase = 0; +static uint64_t exit_node_id = 0xDDDD000000000002ULL; +static uint64_t client_node_id = 0xDDDD000000000001ULL; +static uint8_t send_buf[PAYLOAD_SIZE], recv_buf[PAYLOAD_SIZE]; +static int reply_rcvd = 0; +static void* g_to_id = NULL; + +static void udp_echo_cb(socket_t sock, void* arg) { + (void)sock; (void)arg; + struct sockaddr_in from; socklen_t flen = sizeof(from); + ssize_t n = recvfrom(g_echo_sock, g_echo_buf, sizeof(g_echo_buf), 0, (struct sockaddr*)&from, &flen); + if (n > 0) { sendto(g_echo_sock, g_echo_buf, n, 0, (struct sockaddr*)&from, flen); g_echo_len = n; g_echo_count++; } +} + +static void cli_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { + (void)conn; + if (!entry || !entry->dgram || entry->len < UDP_PROXY_HDR_SIZE + 1) { + if (entry) { queue_entry_free(entry); queue_dgram_free(entry); } return; + } + // svc_id(1) + subcmd(1) + sender_node_id(8) + src_ip(4) + src_port(2) + dst_ip(4) + dst_port(2) + payload + size_t payload_len = entry->len - UDP_PROXY_HDR_SIZE; + if (entry->dgram[1] == UDP_PROXY_SUBCMD_DATA) { + uint8_t* payload = entry->dgram + UDP_PROXY_HDR_SIZE; + if (payload_len == PAYLOAD_SIZE && memcmp(payload, send_buf, PAYLOAD_SIZE) == 0) { + reply_rcvd = 1; g_done = 1; g_ok = 1; + } else { + printf("[FAIL] payload mismatch: got %zu expected %d\n", payload_len, PAYLOAD_SIZE); + g_done = -1; + } + } + queue_entry_free(entry); queue_dgram_free(entry); +} + +static void monitor(void* arg) { + (void)arg; + if (g_done) return; + + if (g_test_phase == 0) { + // Wait for ETCP connection + int cli_ok = 0, exit_ok = 0; + for (struct ETCP_CONN* c = cli->connections; c; c = c->next) + for (struct ETCP_LINK* l = c->links; l; l = l->next) + if (l->initialized && c->crypto_ctx.initialized) cli_ok = 1; + for (struct ETCP_CONN* c = exit_node->connections; c; c = c->next) + for (struct ETCP_LINK* l = c->links; l; l = l->next) + if (l->initialized && c->crypto_ctx.initialized) exit_ok = 1; + if (cli_ok && exit_ok) { + g_test_phase = 1; + // Bind client handler for UDP replies (overrides what tcp_proxy_create set, for test verification) + etcp_router_bind(cli, ETCP_ID_UDP_PROXY, cli_recv_cb); + // Send UDP_REQUEST + for (int i = 0; i < PAYLOAD_SIZE; i++) send_buf[i] = (uint8_t)(rand() & 0xFF); + udp_proxy_send_to_exit(cli, exit_node_id, + inet_addr("10.99.0.1"), htons(12345), + inet_addr("127.0.0.1"), htons(UDP_ECHO_PORT), + send_buf, PAYLOAD_SIZE); + } + } + if (!g_done) g_to_id = uasync_set_timeout(ua, 100, NULL, monitor, "mon"); +} + +static void test_timeout(void* arg) { + (void)arg; + if (!g_done) { printf("[FAIL] timeout (phase=%d echo=%d reply=%d)\n", g_test_phase, g_echo_count, reply_rcvd); g_done = -1; } +} + +int main(void) { + printf("=== test_udp_proxy ===\n"); + debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_categories(DEBUG_CATEGORY_ALL); + utun_instance_set_tun_init_enabled(0); + srand((unsigned)time(NULL)); + + ua = uasync_create(); + if (!ua) { printf("[FAIL] uasync_create\n"); return 1; } + + cli = utun_instance_create_from_str(ua, cfg_node_client()); + if (!cli) { printf("[FAIL] client instance create\n"); goto done; } + exit_node = utun_instance_create_from_str(ua, cfg_node_exit()); + if (!exit_node) { printf("[FAIL] exit instance create\n"); goto done; } + + if (utun_instance_init(cli) < 0) { printf("[FAIL] client init\n"); goto done; } + if (utun_instance_init(exit_node) < 0) { printf("[FAIL] exit init\n"); goto done; } + + // UDP echo server (same uasync, no fork) + g_echo_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (g_echo_sock == SOCKET_INVALID) { printf("[FAIL] echo socket\n"); goto done; } + struct sockaddr_in ea = {.sin_family = AF_INET, .sin_addr = {.s_addr = inet_addr("127.0.0.1")}, .sin_port = htons(UDP_ECHO_PORT)}; + if (bind(g_echo_sock, (struct sockaddr*)&ea, sizeof(ea)) < 0) { printf("[FAIL] echo bind: %s\n", strerror(errno)); goto done; } + socket_set_nonblocking(g_echo_sock); + g_echo_id = uasync_add_socket(ua, g_echo_sock, udp_echo_cb, NULL, NULL, NULL); + + g_to_id = uasync_set_timeout(ua, 100, NULL, monitor, "mon"); + void* to_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS * 10, NULL, test_timeout, "to"); + + while (!g_done) uasync_poll(ua, 50); + + if (to_id) uasync_cancel_timeout(ua, to_id); + + if (g_ok) printf("[PASS] test_udp_proxy — %d bytes echoed, %d echo responses\n", PAYLOAD_SIZE, g_echo_count); + else printf("[FAIL] test_udp_proxy\n"); + +done: + if (g_to_id) uasync_cancel_timeout(ua, g_to_id); + if (g_echo_id) uasync_remove_socket(ua, g_echo_id); + if (g_echo_sock != SOCKET_INVALID) socket_close_wrapper(g_echo_sock); + if (cli) { cli->running = 0; utun_instance_destroy(cli); } + if (exit_node) { exit_node->running = 0; utun_instance_destroy(exit_node); } + if (ua) uasync_destroy(ua, 0); + return g_ok ? 0 : 1; +} diff --git a/utun.conf.sample b/utun.conf.sample index c308df6b..f115fffc 100644 --- a/utun.conf.sample +++ b/utun.conf.sample @@ -76,21 +76,18 @@ allow=all #tun_ip=100.64.1.1/24 # IP клиентского NAT TUN #nat_via=0xABCD000000000001 # node_id провайдера (у кого запрашивать NAT) -# --- TCP Proxy (локальный TCP прокси) --- -# Проксирует входящие TCP соединения в удалённый TCP адрес. -# Два режима: -# 1. Локально (силами этой ноды): -# forward=LOCAL_PORT -> IP:PORT -# 2. Через удалённую ноду (exit node): -# forward=LOCAL_PORT -> IP:PORT via NODE_ID +# --- TCP Proxy (локальный TCP/UDP/ICMP прокси) --- +# Проксирует входящие TCP (через uIP), UDP и ICMP ping через указанный via_node. +# Все три протокола идут через один и тот же удалённый узел. #[tcp_proxy] #enabled=yes #tun_name=tun_tcp # имя TUN интерфейса (по умолчанию tun_tcp) #tun_ip=10.99.0.1 # IP адрес TUN интерфейса #mtu=1500 # MTU #eim_timeout=300 # таймаут EIM маппингов (сек) +#via_node=0xABCD000000000001 # узел для проксирования (или свой node_id для локального) #forward=8000 -> 10.0.0.50:80 -#forward=2222 -> 10.0.0.50:22 via 0xABCD000000000001 +#forward=2222 -> 10.0.0.50:22 # --- Remote Proxy (удаленный exit node) --- # Принимает CONNECT-запросы от других нод и открывает OS сокеты к адресатам.