From 63c7777baf995e66e460e7a1bb0a1941131579ed Mon Sep 17 00:00:00 2001 From: Radkesvat <134321679+radkesvat@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:22:27 +0000 Subject: [PATCH] separate freebind and support fwmark option on linux --- tunnels/adapters/connector/tcp/CMakeLists.txt | 1 + tunnels/adapters/connector/tcp/freebind.c | 59 ++++++++++++++++ tunnels/adapters/connector/tcp/freebind.h | 9 +++ .../adapters/connector/tcp/tcp_connector.c | 67 +++++++------------ tunnels/adapters/connector/tcp/types.h | 6 ++ 5 files changed, 98 insertions(+), 44 deletions(-) create mode 100644 tunnels/adapters/connector/tcp/freebind.c create mode 100644 tunnels/adapters/connector/tcp/freebind.h diff --git a/tunnels/adapters/connector/tcp/CMakeLists.txt b/tunnels/adapters/connector/tcp/CMakeLists.txt index 02790da0..fa8bb6c6 100644 --- a/tunnels/adapters/connector/tcp/CMakeLists.txt +++ b/tunnels/adapters/connector/tcp/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(TcpConnector STATIC tcp_connector.c + freebind.c ) diff --git a/tunnels/adapters/connector/tcp/freebind.c b/tunnels/adapters/connector/tcp/freebind.c new file mode 100644 index 00000000..098c7138 --- /dev/null +++ b/tunnels/adapters/connector/tcp/freebind.c @@ -0,0 +1,59 @@ +// Remember to define _CRT_RAND_S before you include +// stdlib.h. +#define _CRT_RAND_S +#include "freebind.h" +#include "frand.h" +#include "loggers/network_logger.h" +#include "tunnel.h" + +bool applyFreeBindRandomDestIp(tunnel_t* self,socket_context_t *dest_ctx) +{ + + tcp_connector_state_t* state = TSTATE(self); + + unsigned int seed = fastRand(); + + switch (dest_ctx->address.sa.sa_family) + { + case AF_INET: + // no probelm if overflows + { +#ifdef OS_UNIX + const uint32_t large_random = (((uint32_t) rand_r(&seed)) % state->outbound_ip_range); +#else + const uint32_t large_random = (((uint32_t) rand_s(&seed)) % state->outbound_ip_range); +#endif + uint32_t calc = ntohl((uint32_t) dest_ctx->address.sin.sin_addr.s_addr); + calc = calc & ~(state->outbound_ip_range - 1ULL); + calc = htonl(calc + large_random); + + memcpy(&(dest_ctx->address.sin.sin_addr), &calc, sizeof(struct in_addr)); + } + break; + case AF_INET6: + // no probelm if overflows + { +#ifdef OS_UNIX + const uint64_t large_random = (((uint64_t) rand_r(&seed)) % state->outbound_ip_range); +#else + const uint64_t large_random = (((uint64_t) rand_s(&seed)) % state->outbound_ip_range); +#endif + uint64_t *addr_ptr = (uint64_t *) &dest_ctx->address.sin6.sin6_addr; + addr_ptr += 1; + + uint64_t calc = ntohll(*addr_ptr); + calc = calc & ~(state->outbound_ip_range - 1ULL); + calc = htonll(calc + large_random); + + memcpy(8 + ((char *) &(dest_ctx->address.sin6.sin6_addr)), &calc, sizeof(calc)); + } + break; + + default: + LOGE("TcpConnector: invalid destination address family"); + return false; + } + return true; + +} + diff --git a/tunnels/adapters/connector/tcp/freebind.h b/tunnels/adapters/connector/tcp/freebind.h new file mode 100644 index 00000000..adfcba70 --- /dev/null +++ b/tunnels/adapters/connector/tcp/freebind.h @@ -0,0 +1,9 @@ +#pragma once +#include "api.h" +#include "types.h" + + +bool applyFreeBindRandomDestIp(tunnel_t* self,socket_context_t *dest_ctx); + + + diff --git a/tunnels/adapters/connector/tcp/tcp_connector.c b/tunnels/adapters/connector/tcp/tcp_connector.c index 63472fcb..38846c2d 100644 --- a/tunnels/adapters/connector/tcp/tcp_connector.c +++ b/tunnels/adapters/connector/tcp/tcp_connector.c @@ -1,10 +1,8 @@ -// Remember to define _CRT_RAND_S before you include -// stdlib.h. -#define _CRT_RAND_S #include "tcp_connector.h" #include "basic_types.h" #include "frand.h" +#include "freebind.h" #include "hsocket.h" #include "loggers/network_logger.h" #include "sync_dns.h" @@ -14,7 +12,7 @@ #include "utils/mathutils.h" #include "utils/sockutils.h" -static void cleanup(tcp_connector_con_state_t *cstate, bool write_queue) +static void cleanup(tcp_connector_con_state_t *cstate, bool flush_queue) { if (cstate->io) { @@ -24,7 +22,7 @@ static void cleanup(tcp_connector_con_state_t *cstate, bool write_queue) // all data must be written before sending fin, event loop will hold them for us context_t *cw = contextQueuePop(cstate->data_queue); - if (write_queue) + if (flush_queue) { hio_write(cstate->io, cw->payload); dropContexPayload(cw); @@ -250,6 +248,7 @@ static void upStream(tunnel_t *self, context_t *c) case kCdvsFromDest: break; } + if (dest_ctx->address_type == kSatDomainName) { if (! dest_ctx->domain_resolved) @@ -262,48 +261,11 @@ static void upStream(tunnel_t *self, context_t *c) } } } + if (state->outbound_ip_range > 0) { - unsigned int seed = fastRand(); - - switch (dest_ctx->address.sa.sa_family) + if (! applyFreeBindRandomDestIp(self, dest_ctx)) { - case AF_INET: - // no probelm if overflows - { -#ifdef OS_UNIX - const uint32_t large_random = (((uint32_t) rand_r(&seed)) % state->outbound_ip_range); -#else - const uint32_t large_random = (((uint32_t) rand_s(&seed)) % state->outbound_ip_range); -#endif - uint32_t calc = ntohl((uint32_t) dest_ctx->address.sin.sin_addr.s_addr); - calc = calc & ~(state->outbound_ip_range - 1ULL); - calc = htonl(calc + large_random); - - memcpy(&(dest_ctx->address.sin.sin_addr), &calc, sizeof(struct in_addr)); - } - break; - case AF_INET6: - // no probelm if overflows - { -#ifdef OS_UNIX - const uint64_t large_random = (((uint64_t) rand_r(&seed)) % state->outbound_ip_range); -#else - const uint64_t large_random = (((uint64_t) rand_s(&seed)) % state->outbound_ip_range); -#endif - uint64_t *addr_ptr = (uint64_t *) &dest_ctx->address.sin6.sin6_addr; - addr_ptr += 1; - - uint64_t calc = ntohll(*addr_ptr); - calc = calc & ~(state->outbound_ip_range - 1ULL); - calc = htonll(calc + large_random); - - memcpy(8 + ((char *) &(dest_ctx->address.sin6.sin6_addr)), &calc, sizeof(calc)); - } - break; - - default: - LOGE("TcpConnector: invalid destination address family"); CSTATE_DROP(c); cleanup(cstate, false); goto fail; @@ -314,6 +276,7 @@ static void upStream(tunnel_t *self, context_t *c) hloop_t *loop = getWorkerLoop(c->line->tid); int sockfd = socket(dest_ctx->address.sa.sa_family, SOCK_STREAM, 0); + if (sockfd < 0) { LOGE("TcpConnector: socket fd < 0"); @@ -333,6 +296,19 @@ static void upStream(tunnel_t *self, context_t *c) setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN, (const char *) &yes, sizeof(yes)); } +#ifdef OS_LINUX + if (state->fwmark != kFwMarkInvalid) + { + if (setsockopt(sockfd, SOL_SOCKET, SO_MARK, &state->fwmark, sizeof(state->fwmark)) < 0) + { + LOGE("TcpConnector: setsockopt SO_MARK error"); + CSTATE_DROP(c); + cleanup(cstate, false); + goto fail; + } + } +#endif + hio_t *upstream_io = hio_get(loop, sockfd); assert(upstream_io != NULL); @@ -524,11 +500,14 @@ tunnel_t *newTcpConnector(node_instance_context_t *instance_info) LOGF("JSON Error: TcpConnector->settings->port (number field) : The vaule was empty or invalid"); return NULL; } + if (state->dest_port_selected.status == kDvsConstant) { socketContextPortSet(&(state->constant_dest_addr), state->dest_port_selected.value); } + getIntFromJsonObjectOrDefault(&(state->fwmark), settings, "fwmark", kFwMarkInvalid); + tunnel_t *t = newTunnel(); t->state = state; t->upStream = &upStream; diff --git a/tunnels/adapters/connector/tcp/types.h b/tunnels/adapters/connector/tcp/types.h index 15a41a0c..0aa9bf4d 100644 --- a/tunnels/adapters/connector/tcp/types.h +++ b/tunnels/adapters/connector/tcp/types.h @@ -12,6 +12,11 @@ enum tcp_connector_dynamic_value_status kCdvsFromDest, }; +enum +{ + kFwMarkInvalid = -1 +}; + typedef struct tcp_connector_state_s { // settings @@ -23,6 +28,7 @@ typedef struct tcp_connector_state_s dynamic_value_t dest_port_selected; socket_context_t constant_dest_addr; uint64_t outbound_ip_range; + int fwmark; } tcp_connector_state_t;