Browse Source

Isolate datagram proxy state and replies between instances and peers

proxy
evgeny 3 days ago
parent
commit
268243feeb
  1. 119
      src/proxy/icmp_proxy.c
  2. 4
      src/proxy/icmp_proxy.h
  3. 3
      src/proxy/tcp_proxy_client.c
  4. 6
      src/proxy/tcp_proxy_server.c
  5. 69
      src/proxy/udp_proxy.c
  6. 5
      src/proxy/udp_proxy.h
  7. 2
      src/utun_instance.h
  8. 2
      tests/test_udp_proxy.c

119
src/proxy/icmp_proxy.c

@ -13,6 +13,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <openssl/rand.h>
#ifndef _WIN32 #ifndef _WIN32
#include <unistd.h> #include <unistd.h>
#include <netinet/in.h> #include <netinet/in.h>
@ -48,11 +49,11 @@ struct ip {
#define ICMP_DEF_TTL 64 #define ICMP_DEF_TTL 64
#define ICMP_TIMEOUT_TB 50000 // 5s for echo reply #define ICMP_TIMEOUT_TB 50000 // 5s for echo reply
struct icmp_proxy_ctx* g_icmp_ctx = NULL;
static void req_expire_timer_cb(void* arg); static void req_expire_timer_cb(void* arg);
static void req_expire(struct icmp_proxy_ctx* ctx); static void req_expire(struct icmp_proxy_ctx* ctx);
// Сумма по сетевому порядку; нечётный хвост дополняется нулём без чтения за границей.
static uint16_t icmp_checksum(const uint8_t* data, size_t len) { static uint16_t icmp_checksum(const uint8_t* data, size_t len) {
uint32_t sum = 0; uint32_t sum = 0;
while (len >= 2) { sum += ((uint16_t)data[0] << 8) | data[1]; data += 2; len -= 2; } while (len >= 2) { sum += ((uint16_t)data[0] << 8) | data[1]; data += 2; len -= 2; }
@ -61,9 +62,9 @@ static uint16_t icmp_checksum(const uint8_t* data, size_t len) {
return htons((uint16_t)~sum); return htons((uint16_t)~sum);
} }
static struct icmp_request* req_find_by_id(struct icmp_request* head, uint16_t echo_id, uint16_t echo_seq) { static struct icmp_request* req_find_by_id(struct icmp_request* head, uint16_t echo_id, uint16_t echo_seq, uint32_t src_ip) {
struct icmp_request* r; struct icmp_request* r;
for (r = head; r; r = r->next) if (r->echo_id == echo_id && r->echo_seq == echo_seq) return r; for (r = head; r; r = r->next) if (r->wire_id == echo_id && r->wire_seq == echo_seq && r->dst_ip == src_ip) return r;
return NULL; return NULL;
} }
@ -71,17 +72,25 @@ static struct icmp_request* req_find_by_id(struct icmp_request* head, uint16_t e
static int exit_send_echo(struct UTUN_INSTANCE* inst, uint64_t client_node_id, static int exit_send_echo(struct UTUN_INSTANCE* inst, uint64_t client_node_id,
uint32_t dst_ip, uint32_t orig_src_ip, uint16_t echo_id, uint16_t echo_seq, uint32_t dst_ip, uint32_t orig_src_ip, uint16_t echo_id, uint16_t echo_seq,
const uint8_t* payload, size_t payload_len) { const uint8_t* payload, size_t payload_len) {
if (!g_icmp_ctx || g_icmp_ctx->raw_sock == SOCKET_INVALID) return -1; struct icmp_proxy_ctx* ctx = inst ? inst->icmp_proxy : NULL;
if (!ctx || ctx->raw_sock == SOCKET_INVALID) return -1;
if (payload_len > 65507) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: payload too large len=%zu", payload_len); return -1; }
size_t icmp_len = ICMP_MINLEN + payload_len; size_t icmp_len = ICMP_MINLEN + payload_len;
uint8_t* buf = u_malloc(icmp_len); uint8_t* buf = u_malloc(icmp_len);
if (!buf) return -1; if (!buf) return -1;
struct icmp_request* r = u_calloc(1, sizeof(*r));
if (!r) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: request allocation failed"); u_free(buf); return -1; }
do {
uint32_t token = ++ctx->next_token;
r->wire_id = htons((uint16_t)(token >> 16)); r->wire_seq = htons((uint16_t)token);
} while (req_find_by_id(ctx->pending, r->wire_id, r->wire_seq, dst_ip));
struct icmp* icmp_hdr = (struct icmp*)buf; struct icmp* icmp_hdr = (struct icmp*)buf;
memset(icmp_hdr, 0, icmp_len); memset(icmp_hdr, 0, icmp_len);
icmp_hdr->icmp_type = ICMP_ECHO; icmp_hdr->icmp_type = ICMP_ECHO;
icmp_hdr->icmp_code = 0; icmp_hdr->icmp_code = 0;
icmp_hdr->icmp_id = echo_id; icmp_hdr->icmp_id = r->wire_id;
icmp_hdr->icmp_seq = echo_seq; icmp_hdr->icmp_seq = r->wire_seq;
if (payload_len > 0) memcpy(icmp_hdr->icmp_data, payload, payload_len); if (payload_len > 0) memcpy(icmp_hdr->icmp_data, payload, payload_len);
icmp_hdr->icmp_cksum = 0; icmp_hdr->icmp_cksum = 0;
icmp_hdr->icmp_cksum = icmp_checksum((const uint8_t*)icmp_hdr, icmp_len); icmp_hdr->icmp_cksum = icmp_checksum((const uint8_t*)icmp_hdr, icmp_len);
@ -89,66 +98,68 @@ static int exit_send_echo(struct UTUN_INSTANCE* inst, uint64_t client_node_id,
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto dst=0x%08x id=0x%04x seq=%u len=%zu", DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto dst=0x%08x id=0x%04x seq=%u len=%zu",
dst_ip, echo_id, echo_seq, icmp_len); dst_ip, echo_id, echo_seq, icmp_len);
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_addr = {.s_addr = dst_ip}}; 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)); ssize_t n = sendto(ctx->raw_sock, buf, icmp_len, 0, (struct sockaddr*)&addr, sizeof(addr));
u_free(buf); u_free(buf);
if (n < 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto failed: %s", strerror(errno)); return -1; } if (n < 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto failed: %s", strerror(errno)); u_free(r); return -1; }
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto sent %zd bytes", n); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: sendto sent %zd bytes", n);
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->orig_src_ip = orig_src_ip; r->client_node_id = client_node_id; r->dst_ip = dst_ip; r->orig_src_ip = orig_src_ip;
r->echo_id = echo_id; r->echo_seq = echo_seq; r->echo_id = echo_id; r->echo_seq = echo_seq;
r->payload_len = payload_len > sizeof(r->payload) ? sizeof(r->payload) : payload_len; r->payload_len = payload_len > sizeof(r->payload) ? sizeof(r->payload) : payload_len;
if (payload_len > 0) memcpy(r->payload, payload, r->payload_len); if (payload_len > 0) memcpy(r->payload, payload, r->payload_len);
r->sent_tb = get_time_tb(); r->sent_tb = get_time_tb();
r->next = g_icmp_ctx->pending; g_icmp_ctx->pending = r; r->next = ctx->pending; ctx->pending = r;
if (!g_icmp_ctx->expire_timer) if (!ctx->expire_timer)
g_icmp_ctx->expire_timer = uasync_set_timeout(g_icmp_ctx->ua, g_icmp_ctx->request_timeout_tb, g_icmp_ctx, req_expire_timer_cb, "icmp_expire"); ctx->expire_timer = uasync_set_timeout(ctx->ua, ctx->request_timeout_tb, ctx, req_expire_timer_cb, "icmp_expire");
return 0; return 0;
} }
// Чтение echo ответа из raw сокета, сопоставление по echo_id // Чтение echo ответа из raw сокета, сопоставление по echo_id
static void raw_read_cb(socket_t sock, void* arg) { static void raw_read_cb(socket_t sock, void* arg) {
(void)sock; (void)arg; (void)sock;
if (!g_icmp_ctx || g_icmp_ctx->raw_sock == SOCKET_INVALID) return; struct icmp_proxy_ctx* ctx = arg;
if (!ctx || ctx->raw_sock == SOCKET_INVALID) return;
uint8_t buf[65536]; uint8_t buf[65536];
struct sockaddr_in from; socklen_t flen = sizeof(from); 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); ssize_t n = recvfrom(ctx->raw_sock, buf, sizeof(buf), 0, (struct sockaddr*)&from, &flen);
if (n <= 0) { if (n < 0) DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: recvfrom error: %s", strerror(errno)); return; } if (n <= 0) { if (n < 0) DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: recvfrom error: %s", strerror(errno)); return; }
if (n < (ssize_t)(sizeof(struct ip) + ICMP_MINLEN)) return; if (n < (ssize_t)(sizeof(struct ip) + ICMP_MINLEN)) return;
struct ip* ip_hdr = (struct ip*)buf; struct ip* ip_hdr = (struct ip*)buf;
if (ip_hdr->ip_p != IPPROTO_ICMP) return; if (ip_hdr->ip_p != IPPROTO_ICMP) return;
size_t ip_hdr_len = ip_hdr->ip_hl * 4; size_t ip_hdr_len = ip_hdr->ip_hl * 4;
if (n < (ssize_t)(ip_hdr_len + ICMP_MINLEN)) return; if (ip_hdr_len < 20 || n < (ssize_t)(ip_hdr_len + ICMP_MINLEN)) return;
struct icmp* icmp_hdr = (struct icmp*)(buf + ip_hdr_len); struct icmp* icmp_hdr = (struct icmp*)(buf + ip_hdr_len);
if (icmp_hdr->icmp_type != ICMP_ECHOREPLY) return; if (icmp_hdr->icmp_type != ICMP_ECHOREPLY || icmp_hdr->icmp_code != 0) return;
if (icmp_checksum((const uint8_t*)icmp_hdr, n - ip_hdr_len) != 0) {
DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: invalid reply checksum"); return;
}
struct icmp_request* r = req_find_by_id(g_icmp_ctx->pending, icmp_hdr->icmp_id, icmp_hdr->icmp_seq); struct icmp_request* r = req_find_by_id(ctx->pending, icmp_hdr->icmp_id, icmp_hdr->icmp_seq, from.sin_addr.s_addr);
if (!r) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: unclaimed echo reply id=0x%04x seq=%u from=0x%08x", if (!r) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: unclaimed echo reply id=0x%04x seq=%u from=0x%08x",
icmp_hdr->icmp_id, icmp_hdr->icmp_seq, from.sin_addr.s_addr); return; } icmp_hdr->icmp_id, icmp_hdr->icmp_seq, from.sin_addr.s_addr); return; }
{ struct icmp_request** rp = &g_icmp_ctx->pending; { struct icmp_request** rp = &ctx->pending;
while (*rp) { if (*rp == r) { *rp = r->next; break; } rp = &(*rp)->next; } } while (*rp) { if (*rp == r) { *rp = r->next; break; } rp = &(*rp)->next; } }
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: echo reply id=0x%04x seq=%u from=0x%08x", DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: echo reply id=0x%04x seq=%u from=0x%08x",
icmp_hdr->icmp_id, icmp_hdr->icmp_seq, from.sin_addr.s_addr); icmp_hdr->icmp_id, icmp_hdr->icmp_seq, from.sin_addr.s_addr);
size_t payload_len = n - ip_hdr_len - ICMP_MINLEN; 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; uint8_t* payload = (payload_len > 0) ? (uint8_t*)(icmp_hdr->icmp_data) : NULL;
struct ll_entry* e = queue_entry_new(0); struct ll_entry* e = queue_entry_new(0);
if (!e) return; if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: reply entry allocation failed"); u_free(r); return; }
e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len); e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len);
if (!e->dgram) { queue_entry_free(e); return; } if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: reply allocation failed"); queue_entry_free(e); u_free(r); return; }
e->dgram[0] = ETCP_RT_ID_ICMP_PROXY; e->dgram[0] = ETCP_RT_ID_ICMP_PROXY;
e->dgram[1] = ICMP_PROXY_SUBCMD_REPLY; e->dgram[1] = ICMP_PROXY_SUBCMD_REPLY;
memcpy(e->dgram + 2, &r->dst_ip, 4); memcpy(e->dgram + 2, &r->dst_ip, 4);
memcpy(e->dgram + 6, &r->orig_src_ip, 4); memcpy(e->dgram + 6, &r->orig_src_ip, 4);
memcpy(e->dgram + 10, &icmp_hdr->icmp_id, 2); memcpy(e->dgram + 10, &r->echo_id, 2);
memcpy(e->dgram + 12, &icmp_hdr->icmp_seq, 2); memcpy(e->dgram + 12, &r->echo_seq, 2);
if (payload_len > 0) memcpy(e->dgram + ICMP_PROXY_HDR_SIZE, payload, payload_len); if (payload_len > 0) memcpy(e->dgram + ICMP_PROXY_HDR_SIZE, payload, payload_len);
e->len = ICMP_PROXY_HDR_SIZE + payload_len; e->len = ICMP_PROXY_HDR_SIZE + payload_len;
int ret = etcp_route_send(g_icmp_ctx->inst, TOPO_GROUP_UTUN, r->client_node_id, e, 0, 0); int ret = etcp_route_send(ctx->inst, TOPO_GROUP_UTUN, r->client_node_id, e, 0, 0);
if (ret != 0) DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: etcp_route_send reply failed: %d", ret); if (ret != 0) DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: etcp_route_send reply failed: %d", ret);
else DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: reply forwarded to client %016llx", (unsigned long long)r->client_node_id); else DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: reply forwarded to client %016llx", (unsigned long long)r->client_node_id);
u_free(r); u_free(r);
@ -158,8 +169,9 @@ static void raw_read_cb(socket_t sock, void* arg) {
// Exit узел: принять REQUEST, отправить echo через raw сокет // Exit узел: принять REQUEST, отправить echo через raw сокет
// ==================================================================== // ====================================================================
static void exit_handle_request(struct ETCP_CONN* conn, struct ll_entry* entry) { 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); struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL;
if (!inst || !g_icmp_ctx || entry->len < ICMP_PROXY_RECV_HDR_SIZE + 1) goto drop; struct icmp_proxy_ctx* ctx = inst ? inst->icmp_proxy : NULL;
if (!inst || !ctx || !inst->tcp_proxy_server.enabled || entry->len < ICMP_PROXY_RECV_HDR_SIZE) goto drop;
uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8); uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
uint32_t dst_ip; memcpy(&dst_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4); uint32_t dst_ip; memcpy(&dst_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4);
@ -169,9 +181,9 @@ static void exit_handle_request(struct ETCP_CONN* conn, struct ll_entry* entry)
uint8_t* payload = entry->dgram + ICMP_PROXY_RECV_HDR_SIZE; uint8_t* payload = entry->dgram + ICMP_PROXY_RECV_HDR_SIZE;
size_t payload_len = entry->len - ICMP_PROXY_RECV_HDR_SIZE; size_t payload_len = entry->len - ICMP_PROXY_RECV_HDR_SIZE;
if (g_icmp_ctx->raw_sock != SOCKET_INVALID) { if (ctx->raw_sock != SOCKET_INVALID) {
exit_send_echo(inst, client_node_id, dst_ip, orig_src_ip, echo_id, echo_seq, payload, payload_len); exit_send_echo(inst, client_node_id, dst_ip, orig_src_ip, echo_id, echo_seq, payload, payload_len);
} else if (g_icmp_ctx->test_loopback) { } else if (ctx->test_loopback) {
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: test loopback reply to 0x%08x", dst_ip); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: test loopback reply to 0x%08x", dst_ip);
struct ll_entry* e = queue_entry_new(0); struct ll_entry* e = queue_entry_new(0);
if (e) { if (e) {
@ -200,7 +212,7 @@ drop:
// Сторона клиента: принять REPLY, доставить echo ответ в TUN // Сторона клиента: принять REPLY, доставить echo ответ в TUN
// ==================================================================== // ====================================================================
static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) { static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) {
if (entry->len < ICMP_PROXY_RECV_HDR_SIZE + 1) { queue_dgram_free(entry); queue_entry_free(entry); return; } if (entry->len < ICMP_PROXY_RECV_HDR_SIZE) { queue_dgram_free(entry); queue_entry_free(entry); return; }
uint32_t src_ip; uint32_t src_ip;
uint32_t orig_src_ip; uint32_t orig_src_ip;
uint16_t echo_id, echo_seq; uint16_t echo_id, echo_seq;
@ -210,7 +222,7 @@ static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry)
memcpy(&echo_seq, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 11, 2); memcpy(&echo_seq, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 11, 2);
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: client got reply id=0x%04x seq=%u from=0x%08x dst=0x%08x", DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: client got reply id=0x%04x seq=%u from=0x%08x dst=0x%08x",
echo_id, echo_seq, src_ip, orig_src_ip); echo_id, echo_seq, src_ip, orig_src_ip);
struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_icmp_ctx ? g_icmp_ctx->inst : NULL); struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL;
icmp_proxy_deliver_reply(inst, orig_src_ip, src_ip, echo_id, echo_seq, icmp_proxy_deliver_reply(inst, orig_src_ip, src_ip, echo_id, echo_seq,
entry->dgram + ICMP_PROXY_RECV_HDR_SIZE, entry->len - ICMP_PROXY_RECV_HDR_SIZE); entry->dgram + ICMP_PROXY_RECV_HDR_SIZE, entry->len - ICMP_PROXY_RECV_HDR_SIZE);
queue_dgram_free(entry); queue_entry_free(entry); queue_dgram_free(entry); queue_entry_free(entry);
@ -220,13 +232,20 @@ static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry)
// Единый etcp_router коллбэк // Единый etcp_router коллбэк
// ==================================================================== // ====================================================================
void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
if (!entry || !entry->dgram || entry->len < ROUTER_SVC_HDR_SIZE) { if (!entry || !entry->dgram || entry->len <= ROUTER_SVC_HDR_SIZE) {
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
return; return;
} }
uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF]; uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF];
if (subcmd == ICMP_PROXY_SUBCMD_REQUEST) { exit_handle_request(conn, entry); return; } struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL;
if (subcmd == ICMP_PROXY_SUBCMD_REPLY) { client_handle_reply(conn, entry); return; } uint64_t peer; memcpy(&peer, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
if (subcmd == ICMP_PROXY_SUBCMD_REQUEST && inst && inst->tcp_proxy_server.enabled) {
exit_handle_request(conn, entry); return;
}
if (subcmd == ICMP_PROXY_SUBCMD_REPLY && inst && inst->tcp_proxy_client && peer == inst->tcp_proxy_client->via_node_id) {
client_handle_reply(conn, entry); return;
}
DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: rejected subcmd=%u peer=%016llx", subcmd, (unsigned long long)peer);
queue_dgram_free(entry); queue_entry_free(entry); queue_dgram_free(entry); queue_entry_free(entry);
} }
@ -236,7 +255,7 @@ void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
int icmp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id, int icmp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id,
uint32_t dst_ip, uint32_t orig_src_ip, uint16_t echo_id, uint16_t echo_seq, uint32_t dst_ip, uint32_t orig_src_ip, uint16_t echo_id, uint16_t echo_seq,
const uint8_t* payload, size_t payload_len) { const uint8_t* payload, size_t payload_len) {
if (!inst) return -1; if (!inst || payload_len > 65507) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "icmp_proxy: invalid send"); return -1; }
struct ll_entry* e = queue_entry_new(0); struct ll_entry* e = queue_entry_new(0);
if (!e) return -1; if (!e) return -1;
e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len); e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len);
@ -301,8 +320,8 @@ int icmp_proxy_deliver_reply(struct UTUN_INSTANCE* inst,
} }
void icmp_proxy_set_test_loopback(struct UTUN_INSTANCE* inst, int enabled) { void icmp_proxy_set_test_loopback(struct UTUN_INSTANCE* inst, int enabled) {
(void)inst; struct icmp_proxy_ctx* ctx = inst ? inst->icmp_proxy : NULL;
if (g_icmp_ctx) g_icmp_ctx->test_loopback = enabled; if (ctx) ctx->test_loopback = enabled;
} }
static void req_expire_timer_cb(void* arg) { static void req_expire_timer_cb(void* arg) {
@ -327,6 +346,7 @@ static void req_expire(struct icmp_proxy_ctx* ctx) {
// ==================================================================== // ====================================================================
int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) {
if (!inst) return -1; if (!inst) return -1;
if (inst->icmp_proxy) return 0;
struct icmp_proxy_ctx* ctx = u_calloc(1, sizeof(struct icmp_proxy_ctx)); struct icmp_proxy_ctx* ctx = u_calloc(1, sizeof(struct icmp_proxy_ctx));
if (!ctx) return -1; if (!ctx) return -1;
ctx->inst = inst; ctx->ua = ua; ctx->raw_sock = SOCKET_INVALID; ctx->inst = inst; ctx->ua = ua; ctx->raw_sock = SOCKET_INVALID;
@ -334,15 +354,19 @@ int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) {
ctx->is_exit = inst->tcp_proxy_server.enabled; ctx->is_exit = inst->tcp_proxy_server.enabled;
ctx->test_loopback = 0; ctx->test_loopback = 0;
ctx->expire_timer = NULL; ctx->expire_timer = NULL;
g_icmp_ctx = ctx; if (RAND_bytes((unsigned char*)&ctx->next_token, sizeof(ctx->next_token)) != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: token initialization failed"); u_free(ctx); return -1;
}
inst->icmp_proxy = ctx;
if (ctx->is_exit) { if (ctx->is_exit) {
ctx->raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); ctx->raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (ctx->raw_sock == SOCKET_INVALID) if (ctx->raw_sock == SOCKET_INVALID)
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: raw socket(SOCK_RAW) failed: %s", strerror(errno)); DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "icmp_proxy: raw socket(SOCK_RAW) failed: %s", strerror(errno));
else { else {
socket_set_nonblocking(ctx->raw_sock);
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: raw socket created fd=%d", ctx->raw_sock); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy: raw socket created fd=%d", ctx->raw_sock);
ctx->raw_read_id = uasync_add_socket_t(ua, ctx->raw_sock, raw_read_cb, NULL, NULL, "icmp_raw", NULL); ctx->raw_read_id = uasync_add_socket_t(ua, ctx->raw_sock, raw_read_cb, NULL, NULL, "icmp_raw", ctx);
if (!ctx->raw_read_id) { socket_close_wrapper(ctx->raw_sock); ctx->raw_sock = SOCKET_INVALID; } if (!ctx->raw_read_id) { socket_close_wrapper(ctx->raw_sock); ctx->raw_sock = SOCKET_INVALID; }
} }
} }
@ -354,15 +378,16 @@ int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) {
} }
void icmp_proxy_destroy(struct UTUN_INSTANCE* inst) { void icmp_proxy_destroy(struct UTUN_INSTANCE* inst) {
if (!inst || !g_icmp_ctx) return; struct icmp_proxy_ctx* ctx = inst ? inst->icmp_proxy : NULL;
if (!ctx) return;
etcp_router_unbind(inst, ETCP_RT_ID_ICMP_PROXY); etcp_router_unbind(inst, ETCP_RT_ID_ICMP_PROXY);
if (g_icmp_ctx->expire_timer) { uasync_cancel_timeout(g_icmp_ctx->ua, g_icmp_ctx->expire_timer); g_icmp_ctx->expire_timer = NULL; } if (ctx->expire_timer) { uasync_cancel_timeout(ctx->ua, ctx->expire_timer); ctx->expire_timer = NULL; }
if (g_icmp_ctx->raw_sock != SOCKET_INVALID) { if (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; } if (ctx->raw_read_id) { uasync_remove_socket_t(ctx->ua, ctx->raw_sock); ctx->raw_read_id = NULL; }
socket_close_wrapper(g_icmp_ctx->raw_sock); socket_close_wrapper(ctx->raw_sock);
} }
struct icmp_request* r = g_icmp_ctx->pending; struct icmp_request* r = ctx->pending;
while (r) { struct icmp_request* n = r->next; u_free(r); r = n; } while (r) { struct icmp_request* n = r->next; u_free(r); r = n; }
u_free(g_icmp_ctx); g_icmp_ctx = NULL; u_free(ctx); inst->icmp_proxy = NULL;
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy destroyed"); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "icmp_proxy destroyed");
} }

4
src/proxy/icmp_proxy.h

@ -35,6 +35,8 @@ struct icmp_request {
uint32_t orig_src_ip; // IP исходного отправителя (чтобы вернуть reply правильному адресату) uint32_t orig_src_ip; // IP исходного отправителя (чтобы вернуть reply правильному адресату)
uint16_t echo_id; uint16_t echo_id;
uint16_t echo_seq; uint16_t echo_seq;
uint16_t wire_id;
uint16_t wire_seq;
uint8_t payload[1500]; uint8_t payload[1500];
size_t payload_len; size_t payload_len;
uint64_t sent_tb; uint64_t sent_tb;
@ -49,12 +51,12 @@ struct icmp_proxy_ctx {
socket_t raw_sock; // один SOCK_RAW для всего ICMP на exit socket_t raw_sock; // один SOCK_RAW для всего ICMP на exit
void* raw_read_id; void* raw_read_id;
struct icmp_request* pending; struct icmp_request* pending;
uint32_t next_token;
uint64_t request_timeout_tb; uint64_t request_timeout_tb;
int test_loopback; // 1 = виртуальный loopback без raw сокета (только тесты) int test_loopback; // 1 = виртуальный loopback без raw сокета (только тесты)
void* expire_timer; // периодический таймер очистки истёкших запросов void* expire_timer; // периодический таймер очистки истёкших запросов
}; };
extern struct icmp_proxy_ctx* g_icmp_ctx;
int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua); int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua);
void icmp_proxy_destroy(struct UTUN_INSTANCE* inst); void icmp_proxy_destroy(struct UTUN_INSTANCE* inst);

3
src/proxy/tcp_proxy_client.c

@ -825,8 +825,7 @@ void tcp_proxy_client_destroy(struct tcp_proxy_client* p) {
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy client destroying: lwip=%d socks=%d http=%d", DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy client destroying: lwip=%d socks=%d http=%d",
p->conn_count, p->socks_conn_count, p->http_conn_count); p->conn_count, p->socks_conn_count, p->http_conn_count);
if (p->inst) etcp_router_unbind(p->inst, ETCP_RT_ID_TCP_PROXY_CLIENT); if (p->inst) etcp_router_unbind(p->inst, ETCP_RT_ID_TCP_PROXY_CLIENT);
udp_proxy_destroy(p->inst); if (p->inst && !p->inst->tcp_proxy_server.enabled) { udp_proxy_destroy(p->inst); icmp_proxy_destroy(p->inst); }
icmp_proxy_destroy(p->inst);
if (p->socks_listen) socks_proxy_close_listen(p->ua, p->socks_listen, NULL); if (p->socks_listen) socks_proxy_close_listen(p->ua, p->socks_listen, NULL);
socks_proxy_conn_free_all(&p->socks_conns, &p->socks_conn_count); socks_proxy_conn_free_all(&p->socks_conns, &p->socks_conn_count);

6
src/proxy/tcp_proxy_server.c

@ -26,7 +26,6 @@
#include <fcntl.h> #include <fcntl.h>
#endif #endif
static struct tcp_proxy_server* g_tcp_proxy_server_ctx = NULL;
static void on_fin_cb(struct tcp_conn* tc, void* arg); static void on_fin_cb(struct tcp_conn* tc, void* arg);
static void on_error_cb(struct tcp_conn* tc, int err, void* arg); static void on_error_cb(struct tcp_conn* tc, int err, void* arg);
@ -570,12 +569,9 @@ int tcp_proxy_server_init(struct UTUN_INSTANCE* inst) {
ctx->enabled = inst->config->global.tcp_proxy_server_enabled; ctx->enabled = inst->config->global.tcp_proxy_server_enabled;
ctx->inst = inst; ctx->inst = inst;
if (!ctx->enabled) return 0; if (!ctx->enabled) return 0;
g_tcp_proxy_server_ctx = ctx;
etcp_router_bind(inst, ETCP_RT_ID_TCP_PROXY_SERVER, tcp_proxy_server_recv_cb); etcp_router_bind(inst, ETCP_RT_ID_TCP_PROXY_SERVER, tcp_proxy_server_recv_cb);
if (!inst->config->global.tcp_proxy_client_enabled) {
udp_proxy_init(inst, inst->ua); udp_proxy_init(inst, inst->ua);
icmp_proxy_init(inst, inst->ua); icmp_proxy_init(inst, inst->ua);
}
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy server initialized node=%016llx", (unsigned long long)inst->node_id); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy server initialized node=%016llx", (unsigned long long)inst->node_id);
return 0; return 0;
} }
@ -587,7 +583,7 @@ void tcp_proxy_server_destroy(struct UTUN_INSTANCE* inst) {
struct tcp_proxy_server_conn* rc = ctx->conns; struct tcp_proxy_server_conn* rc = ctx->conns;
while (rc) { struct tcp_proxy_server_conn* next = rc->next; tcp_proxy_server_conn_free(rc); rc = next; } while (rc) { struct tcp_proxy_server_conn* next = rc->next; tcp_proxy_server_conn_free(rc); rc = next; }
ctx->conns = NULL; ctx->enabled = 0; ctx->conns = NULL; ctx->enabled = 0;
if (g_tcp_proxy_server_ctx == ctx) g_tcp_proxy_server_ctx = NULL; etcp_router_unbind(inst, ETCP_RT_ID_TCP_PROXY_SERVER);
udp_proxy_destroy(inst); udp_proxy_destroy(inst);
icmp_proxy_destroy(inst); icmp_proxy_destroy(inst);
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy server destroyed"); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "TCP proxy server destroyed");

69
src/proxy/udp_proxy.c

@ -23,7 +23,6 @@
#define UDP_FLOW_TIMEOUT_TB 600000 // 60с #define UDP_FLOW_TIMEOUT_TB 600000 // 60с
struct udp_proxy_ctx* g_udp_ctx = NULL;
static void flow_expire_timer_cb(void* arg); static void flow_expire_timer_cb(void* arg);
static void flow_expire(struct udp_proxy_ctx* ctx); static void flow_expire(struct udp_proxy_ctx* ctx);
@ -40,11 +39,15 @@ static struct udp_flow* flow_find(struct udp_flow* head, uint64_t client_node_id
static void flow_read_cb(socket_t sock, void* arg) { static void flow_read_cb(socket_t sock, void* arg) {
(void)sock; struct udp_flow* f = (struct udp_flow*)arg; (void)sock; struct udp_flow* f = (struct udp_flow*)arg;
if (!f || f->sock == SOCKET_INVALID || !g_udp_ctx) return; struct udp_proxy_ctx* ctx = f ? f->ctx : NULL;
uint8_t buf[1600]; if (!f || f->sock == SOCKET_INVALID || !ctx) return;
uint8_t buf[65507];
struct sockaddr_in from; socklen_t flen = sizeof(from); struct sockaddr_in from; socklen_t flen = sizeof(from);
ssize_t n = recvfrom(f->sock, buf, sizeof(buf), 0, (struct sockaddr*)&from, &flen); ssize_t n = recvfrom(f->sock, buf, sizeof(buf), 0, (struct sockaddr*)&from, &flen);
if (n <= 0) return; if (n < 0) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "udp_proxy: recv failed errno=%d", errno); return; }
if (from.sin_addr.s_addr != f->dst_ip || from.sin_port != f->dst_port) {
DEBUG_WARN(DEBUG_CATEGORY_PROXY, "udp_proxy: unexpected reply source"); return;
}
f->last_activity_tb = get_time_tb(); f->last_activity_tb = get_time_tb();
@ -54,7 +57,7 @@ static void flow_read_cb(socket_t sock, void* arg) {
e->dgram = u_malloc(UDP_PROXY_HDR_SIZE + n); e->dgram = u_malloc(UDP_PROXY_HDR_SIZE + n);
if (!e->dgram) { queue_entry_free(e); return; } if (!e->dgram) { queue_entry_free(e); return; }
e->dgram[0] = ETCP_RT_ID_UDP_PROXY; e->dgram[0] = ETCP_RT_ID_UDP_PROXY;
e->dgram[1] = UDP_PROXY_SUBCMD_DATA; e->dgram[1] = UDP_PROXY_SUBCMD_REPLY;
// Ответ src = оригинальный dst_ip:dest_port // Ответ src = оригинальный dst_ip:dest_port
memcpy(e->dgram + 2, &f->dst_ip, 4); memcpy(e->dgram + 2, &f->dst_ip, 4);
memcpy(e->dgram + 6, &f->dst_port, 2); memcpy(e->dgram + 6, &f->dst_port, 2);
@ -64,15 +67,16 @@ static void flow_read_cb(socket_t sock, void* arg) {
memcpy(e->dgram + UDP_PROXY_HDR_SIZE, buf, n); memcpy(e->dgram + UDP_PROXY_HDR_SIZE, buf, n);
e->len = UDP_PROXY_HDR_SIZE + n; e->len = UDP_PROXY_HDR_SIZE + n;
etcp_route_send(g_udp_ctx->inst, TOPO_GROUP_UTUN, f->client_node_id, e, 0, 0); etcp_route_send(ctx->inst, TOPO_GROUP_UTUN, f->client_node_id, e, 0, 0);
} }
// ==================================================================== // ====================================================================
// Exit узел: принять REQUEST, создать сокет, переслать // Exit узел: принять REQUEST, создать сокет, переслать
// ==================================================================== // ====================================================================
static void exit_handle_data(struct ETCP_CONN* conn, struct ll_entry* entry) { 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); struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL;
if (!inst || !g_udp_ctx || entry->len < UDP_PROXY_RECV_HDR_SIZE + 1) goto drop; struct udp_proxy_ctx* ctx = inst ? inst->udp_proxy : NULL;
if (!inst || !ctx || !inst->tcp_proxy_server.enabled || entry->len < UDP_PROXY_RECV_HDR_SIZE) goto drop;
uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8); uint64_t client_node_id; memcpy(&client_node_id, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
uint32_t src_ip; memcpy(&src_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4); uint32_t src_ip; memcpy(&src_ip, entry->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, 4);
@ -82,24 +86,25 @@ static void exit_handle_data(struct ETCP_CONN* conn, struct ll_entry* entry) {
uint8_t* payload = entry->dgram + UDP_PROXY_RECV_HDR_SIZE; uint8_t* payload = entry->dgram + UDP_PROXY_RECV_HDR_SIZE;
size_t payload_len = entry->len - UDP_PROXY_RECV_HDR_SIZE; size_t payload_len = entry->len - UDP_PROXY_RECV_HDR_SIZE;
struct udp_flow* f = flow_find(g_udp_ctx->flows, client_node_id, src_ip, src_port, dst_ip, dst_port); struct udp_flow* f = flow_find(ctx->flows, client_node_id, src_ip, src_port, dst_ip, dst_port);
if (!f) { if (!f) {
f = u_calloc(1, sizeof(struct udp_flow)); f = u_calloc(1, sizeof(struct udp_flow));
if (!f) goto drop; if (!f) goto drop;
f->ctx = ctx;
f->client_node_id = client_node_id; f->src_ip = src_ip; f->src_port = src_port; 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->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->ua = ctx->ua; f->created_tb = get_time_tb(); f->last_activity_tb = f->created_tb;
f->sock = socket(AF_INET, SOCK_DGRAM, 0); f->sock = socket(AF_INET, SOCK_DGRAM, 0);
if (f->sock == SOCKET_INVALID) { u_free(f); DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "udp_proxy: socket failed"); goto drop; } if (f->sock == SOCKET_INVALID) { u_free(f); DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "udp_proxy: socket failed"); goto drop; }
socket_set_nonblocking(f->sock); socket_set_nonblocking(f->sock);
struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr = {.s_addr = INADDR_ANY}, .sin_port = 0}; 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)); bind(f->sock, (struct sockaddr*)&bind_addr, sizeof(bind_addr));
f->read_id = uasync_add_socket_t(g_udp_ctx->ua, f->sock, flow_read_cb, NULL, NULL, "udp_flow", f); f->read_id = uasync_add_socket_t(ctx->ua, f->sock, flow_read_cb, NULL, NULL, "udp_flow", f);
if (!f->read_id) { socket_close_wrapper(f->sock); u_free(f); goto drop; } 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->next = ctx->flows; ctx->flows = f; ctx->flow_count++;
if (!g_udp_ctx->expire_timer) if (!ctx->expire_timer)
g_udp_ctx->expire_timer = uasync_set_timeout(g_udp_ctx->ua, g_udp_ctx->flow_timeout_tb, g_udp_ctx, flow_expire_timer_cb, "udp_expire"); ctx->expire_timer = uasync_set_timeout(ctx->ua, ctx->flow_timeout_tb, ctx, flow_expire_timer_cb, "udp_expire");
} }
f->last_activity_tb = get_time_tb(); f->last_activity_tb = get_time_tb();
@ -114,7 +119,7 @@ drop:
// Сторона клиента: принять REPLY, доставить в TUN // Сторона клиента: принять REPLY, доставить в TUN
// ==================================================================== // ====================================================================
static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) { static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) {
if (entry->len < UDP_PROXY_RECV_HDR_SIZE + 1) { queue_dgram_free(entry); queue_entry_free(entry); return; } if (entry->len < UDP_PROXY_RECV_HDR_SIZE) { queue_dgram_free(entry); queue_entry_free(entry); return; }
const uint8_t* d = entry->dgram; const uint8_t* d = entry->dgram;
uint32_t src_ip, dst_ip; uint32_t src_ip, dst_ip;
uint16_t src_port, dst_port; uint16_t src_port, dst_port;
@ -126,7 +131,7 @@ static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry)
uint8_t* payload = d + UDP_PROXY_RECV_HDR_SIZE; uint8_t* payload = d + UDP_PROXY_RECV_HDR_SIZE;
size_t payload_len = entry->len - UDP_PROXY_RECV_HDR_SIZE; size_t payload_len = entry->len - UDP_PROXY_RECV_HDR_SIZE;
struct UTUN_INSTANCE* inst = conn ? conn->instance : (g_udp_ctx ? g_udp_ctx->inst : NULL); struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL;
udp_proxy_deliver_reply(inst, src_ip, src_port, dst_ip, dst_port, payload, payload_len); udp_proxy_deliver_reply(inst, src_ip, src_port, dst_ip, dst_port, payload, payload_len);
queue_dgram_free(entry); queue_entry_free(entry); queue_dgram_free(entry); queue_entry_free(entry);
} }
@ -135,16 +140,20 @@ static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry)
// Единый etcp_router коллбэк // Единый etcp_router коллбэк
// ==================================================================== // ====================================================================
void udp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { void udp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
if (!entry || !entry->dgram || entry->len < ROUTER_SVC_HDR_SIZE) { if (!entry || !entry->dgram || entry->len <= ROUTER_SVC_HDR_SIZE) {
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
return; return;
} }
struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL;
uint64_t peer; memcpy(&peer, entry->dgram + ROUTER_SVC_SRC_OFF, 8);
uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF]; uint8_t subcmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF];
if (subcmd == UDP_PROXY_SUBCMD_DATA) { if (subcmd == UDP_PROXY_SUBCMD_REQUEST && inst && inst->tcp_proxy_server.enabled) {
if (g_udp_ctx && g_udp_ctx->is_exit) exit_handle_data(conn, entry); exit_handle_data(conn, entry); return;
else client_handle_reply(conn, entry); }
return; if (subcmd == UDP_PROXY_SUBCMD_REPLY && inst && inst->tcp_proxy_client && peer == inst->tcp_proxy_client->via_node_id) {
client_handle_reply(conn, entry); return;
} }
DEBUG_WARN(DEBUG_CATEGORY_PROXY, "udp_proxy: rejected subcmd=%u peer=%016llx", subcmd, (unsigned long long)peer);
queue_dgram_free(entry); queue_entry_free(entry); queue_dgram_free(entry); queue_entry_free(entry);
} }
@ -155,13 +164,13 @@ 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 src_ip, uint16_t src_port,
uint32_t dst_ip, uint16_t dst_port, uint32_t dst_ip, uint16_t dst_port,
const uint8_t* payload, size_t payload_len) { const uint8_t* payload, size_t payload_len) {
if (!inst || !g_udp_ctx) return -1; if (!inst || !inst->udp_proxy || payload_len > 65507) { DEBUG_WARN(DEBUG_CATEGORY_PROXY, "udp_proxy: invalid send"); return -1; }
struct ll_entry* e = queue_entry_new(0); struct ll_entry* e = queue_entry_new(0);
if (!e) return -1; if (!e) return -1;
e->dgram = u_malloc(UDP_PROXY_HDR_SIZE + payload_len); e->dgram = u_malloc(UDP_PROXY_HDR_SIZE + payload_len);
if (!e->dgram) { queue_entry_free(e); return -1; } if (!e->dgram) { queue_entry_free(e); return -1; }
e->dgram[0] = ETCP_RT_ID_UDP_PROXY; e->dgram[0] = ETCP_RT_ID_UDP_PROXY;
e->dgram[1] = UDP_PROXY_SUBCMD_DATA; e->dgram[1] = UDP_PROXY_SUBCMD_REQUEST;
memcpy(e->dgram + 2, &src_ip, 4); memcpy(e->dgram + 2, &src_ip, 4);
memcpy(e->dgram + 6, &src_port, 2); memcpy(e->dgram + 6, &src_port, 2);
memcpy(e->dgram + 8, &dst_ip, 4); memcpy(e->dgram + 8, &dst_ip, 4);
@ -237,13 +246,14 @@ static void flow_expire(struct udp_proxy_ctx* ctx) {
// ==================================================================== // ====================================================================
int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) {
if (!inst) return -1; if (!inst) return -1;
if (inst->udp_proxy) return 0;
struct udp_proxy_ctx* ctx = u_calloc(1, sizeof(struct udp_proxy_ctx)); struct udp_proxy_ctx* ctx = u_calloc(1, sizeof(struct udp_proxy_ctx));
if (!ctx) return -1; if (!ctx) return -1;
ctx->inst = inst; ctx->ua = ua; ctx->inst = inst; ctx->ua = ua;
ctx->flow_timeout_tb = UDP_FLOW_TIMEOUT_TB; ctx->flow_timeout_tb = UDP_FLOW_TIMEOUT_TB;
ctx->expire_timer = NULL; ctx->expire_timer = NULL;
ctx->is_exit = inst->tcp_proxy_server.enabled; ctx->is_exit = inst->tcp_proxy_server.enabled;
g_udp_ctx = ctx; inst->udp_proxy = ctx;
etcp_router_bind(inst, ETCP_RT_ID_UDP_PROXY, udp_proxy_recv_cb); etcp_router_bind(inst, ETCP_RT_ID_UDP_PROXY, udp_proxy_recv_cb);
ctx->initialized = 1; ctx->initialized = 1;
@ -252,13 +262,14 @@ int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) {
} }
void udp_proxy_destroy(struct UTUN_INSTANCE* inst) { void udp_proxy_destroy(struct UTUN_INSTANCE* inst) {
if (!inst || !g_udp_ctx) return; struct udp_proxy_ctx* ctx = inst ? inst->udp_proxy : NULL;
if (!ctx) return;
etcp_router_unbind(inst, ETCP_RT_ID_UDP_PROXY); etcp_router_unbind(inst, ETCP_RT_ID_UDP_PROXY);
if (g_udp_ctx->expire_timer) { uasync_cancel_timeout(g_udp_ctx->ua, g_udp_ctx->expire_timer); g_udp_ctx->expire_timer = NULL; } if (ctx->expire_timer) { uasync_cancel_timeout(ctx->ua, ctx->expire_timer); ctx->expire_timer = NULL; }
struct udp_flow* f = g_udp_ctx->flows; struct udp_flow* f = ctx->flows;
while (f) { struct udp_flow* n = f->next; 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; } if (f->read_id) { uasync_remove_socket_t(ctx->ua, f->sock); f->read_id = NULL; }
socket_close_wrapper(f->sock); u_free(f); f = n; } socket_close_wrapper(f->sock); u_free(f); f = n; }
u_free(g_udp_ctx); g_udp_ctx = NULL; u_free(ctx); inst->udp_proxy = NULL;
DEBUG_INFO(DEBUG_CATEGORY_PROXY, "udp_proxy destroyed"); DEBUG_INFO(DEBUG_CATEGORY_PROXY, "udp_proxy destroyed");
} }

5
src/proxy/udp_proxy.h

@ -17,7 +17,8 @@ struct ll_entry;
struct ETCP_CONN; struct ETCP_CONN;
// Подкоманды // Подкоманды
#define UDP_PROXY_SUBCMD_DATA 0x01 // client→exit: пробрось датаграмму | exit→client: ответ #define UDP_PROXY_SUBCMD_REQUEST 0x01
#define UDP_PROXY_SUBCMD_REPLY 0x02
// Заголовок сообщения на отправке (включая байт svc_id; src/dst node_id добавляет роутер): // Заголовок сообщения на отправке (включая байт svc_id; src/dst node_id добавляет роутер):
// svc_id(1) + subcmd(1) + src_ip(4) + src_port(2) + dst_ip(4) + dst_port(2) + payload // svc_id(1) + subcmd(1) + src_ip(4) + src_port(2) + dst_ip(4) + dst_port(2) + payload
@ -29,6 +30,7 @@ struct ETCP_CONN;
// Один UDP поток (сторона exit узла: сопоставляет client_node_id + кортеж адресов с OS сокетом) // Один UDP поток (сторона exit узла: сопоставляет client_node_id + кортеж адресов с OS сокетом)
struct udp_flow { struct udp_flow {
struct udp_flow* next; struct udp_flow* next;
struct udp_proxy_ctx* ctx;
uint64_t client_node_id; uint64_t client_node_id;
uint32_t src_ip; uint32_t src_ip;
uint16_t src_port; uint16_t src_port;
@ -52,7 +54,6 @@ struct udp_proxy_ctx {
void* expire_timer; // периодический таймер очистки истёкших потоков void* expire_timer; // периодический таймер очистки истёкших потоков
}; };
extern struct udp_proxy_ctx* g_udp_ctx;
int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua); int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua);
void udp_proxy_destroy(struct UTUN_INSTANCE* inst); void udp_proxy_destroy(struct UTUN_INSTANCE* inst);

2
src/utun_instance.h

@ -248,6 +248,8 @@ struct UTUN_INSTANCE {
// TCP proxy server (exit node) // TCP proxy server (exit node)
struct tcp_proxy_server tcp_proxy_server; struct tcp_proxy_server tcp_proxy_server;
struct udp_proxy_ctx* udp_proxy;
struct icmp_proxy_ctx* icmp_proxy;
// Networks (queue of NETWORK_ENTRY, indexed by 56-bit id) // Networks (queue of NETWORK_ENTRY, indexed by 56-bit id)
struct ll_queue* networks; struct ll_queue* networks;

2
tests/test_udp_proxy.c

@ -96,7 +96,7 @@ static void cli_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return;
} }
size_t payload_len = entry->len - UDP_PROXY_RECV_HDR_SIZE; size_t payload_len = entry->len - UDP_PROXY_RECV_HDR_SIZE;
if (entry->dgram[ROUTER_SVC_PAYLOAD_OFF] == UDP_PROXY_SUBCMD_DATA) { if (entry->dgram[ROUTER_SVC_PAYLOAD_OFF] == UDP_PROXY_SUBCMD_REPLY) {
uint8_t* payload = entry->dgram + UDP_PROXY_RECV_HDR_SIZE; uint8_t* payload = entry->dgram + UDP_PROXY_RECV_HDR_SIZE;
if (payload_len == PAYLOAD_SIZE && memcmp(payload, send_buf, PAYLOAD_SIZE) == 0) { if (payload_len == PAYLOAD_SIZE && memcmp(payload, send_buf, PAYLOAD_SIZE) == 0) {
reply_rcvd = 1; g_done = 1; g_ok = 1; reply_rcvd = 1; g_done = 1; g_ok = 1;

Loading…
Cancel
Save