Skip to content

Commit

Permalink
separate freebind and support fwmark option on linux
Browse files Browse the repository at this point in the history
  • Loading branch information
radkesvat committed Aug 7, 2024
1 parent 8d79c40 commit 63c7777
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 44 deletions.
1 change: 1 addition & 0 deletions tunnels/adapters/connector/tcp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

add_library(TcpConnector STATIC
tcp_connector.c
freebind.c

)

Expand Down
59 changes: 59 additions & 0 deletions tunnels/adapters/connector/tcp/freebind.c
Original file line number Diff line number Diff line change
@@ -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;

}

9 changes: 9 additions & 0 deletions tunnels/adapters/connector/tcp/freebind.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once
#include "api.h"
#include "types.h"


bool applyFreeBindRandomDestIp(tunnel_t* self,socket_context_t *dest_ctx);



67 changes: 23 additions & 44 deletions tunnels/adapters/connector/tcp/tcp_connector.c
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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)
{
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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");
Expand All @@ -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);

Expand Down Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions tunnels/adapters/connector/tcp/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ enum tcp_connector_dynamic_value_status
kCdvsFromDest,
};

enum
{
kFwMarkInvalid = -1
};

typedef struct tcp_connector_state_s
{
// settings
Expand All @@ -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;

Expand Down

0 comments on commit 63c7777

Please sign in to comment.