Skip to content

Commit

Permalink
Ensure SO_REUSEPORT is applied if enabled
Browse files Browse the repository at this point in the history
  • Loading branch information
semigodking committed Nov 26, 2023
1 parent f5b1b82 commit 7d75713
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 88 deletions.
4 changes: 2 additions & 2 deletions redsocks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1019,8 +1019,8 @@ static int redsocks_init_instance(redsocks_instance *instance)
if (make_socket_transparent(fd))
log_error(LOG_WARNING, "Continue without TPROXY support");

// if (apply_reuseport(fd))
// log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled");
if (apply_reuseport(fd))
log_error(LOG_WARNING, "Continue without SO_REUSEPORT enabled");

#if defined(__APPLE__) || defined(__FreeBSD__)
bindaddr_len = instance->config.bindaddr.ss_len > 0 ? instance->config.bindaddr.ss_len : sizeof(instance->config.bindaddr);
Expand Down
93 changes: 56 additions & 37 deletions redudp.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,21 @@ static char shared_buff[MAX_UDP_PACKET_SIZE];// max size of UDP packet is less t
static void redudp_fini_instance(redudp_instance *instance);
static int redudp_fini();

struct bound_udp4_key {
struct in_addr sin_addr;
uint16_t sin_port;
struct bound_udp_key {
int sa_family;
union {
uint16_t sin_port;
uint16_t sin6_port;
};
// Do not change position
union {
struct in_addr sin_addr;
struct in6_addr sin6_addr;
};
};

struct bound_udp4 {
struct bound_udp4_key key;
struct bound_udp {
struct bound_udp_key key;
int ref;
int fd;
time_t t_last_rx;
Expand All @@ -79,29 +87,36 @@ static udprelay_subsys *relay_subsystems[] =
* Helpers
*/
// TODO: separate binding to privileged process (this operation requires uid-0)
static void* root_bound_udp4 = NULL; // to avoid two binds to same IP:port
static void* root_bound_udp = NULL; // to avoid two binds to same IP:port

static int bound_udp4_cmp(const void *a, const void *b)
static int bound_udp_cmp(const void *a, const void *b)
{
return memcmp(a, b, sizeof(struct bound_udp4_key));
return memcmp(a, b, sizeof(struct bound_udp_key));
}

static void bound_udp4_mkkey(struct bound_udp4_key *key, const struct sockaddr_in *addr)
static void bound_udp_mkkey(struct bound_udp_key *key, const struct sockaddr *addr)
{
memset(key, 0, sizeof(*key));
key->sin_addr = addr->sin_addr;
key->sin_port = addr->sin_port;
key->sa_family = addr->sa_family;
if (addr->sa_family == AF_INET) {
key->sin_addr = ((const struct sockaddr_in*)addr)->sin_addr;
key->sin_port = ((const struct sockaddr_in*)addr)->sin_port;
}
else if (addr->sa_family == AF_INET6) {
key->sin6_addr = ((const struct sockaddr_in6*)addr)->sin6_addr;
key->sin6_port = ((const struct sockaddr_in6*)addr)->sin6_port;
}
}

static int bound_udp4_get(const struct sockaddr_in *addr)
static int bound_udp_get(const struct sockaddr *addr)
{
struct bound_udp4_key key;
struct bound_udp4 *node, **pnode;
struct bound_udp_key key;
struct bound_udp *node, **pnode;

bound_udp4_mkkey(&key, addr);
bound_udp_mkkey(&key, addr);
// I assume, that memory allocation for lookup is awful, so I use
// tfind/tsearch pair instead of tsearch/check-result.
pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp);
pnode = tfind(&key, &root_bound_udp, bound_udp_cmp);
if (pnode) {
assert((*pnode)->ref > 0);
(*pnode)->ref++;
Expand All @@ -117,7 +132,7 @@ static int bound_udp4_get(const struct sockaddr_in *addr)

node->key = key;
node->ref = 1;
node->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
node->fd = socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
node->t_last_rx = redsocks_time(NULL);
if (node->fd == -1) {
log_errno(LOG_ERR, "socket");
Expand All @@ -142,7 +157,7 @@ static int bound_udp4_get(const struct sockaddr_in *addr)
goto fail;
}

pnode = tsearch(node, &root_bound_udp4, bound_udp4_cmp);
pnode = tsearch(node, &root_bound_udp, bound_udp_cmp);
if (!pnode) {
log_errno(LOG_ERR, "tsearch(%p) == %p", node, pnode);
goto fail;
Expand All @@ -160,14 +175,14 @@ static int bound_udp4_get(const struct sockaddr_in *addr)
return -1;
}

static void bound_udp4_put(const struct sockaddr_in *addr)
static void bound_udp_put(const struct sockaddr *addr)
{
struct bound_udp4_key key;
struct bound_udp4 **pnode, *node;
struct bound_udp_key key;
struct bound_udp **pnode, *node;
void *parent;

bound_udp4_mkkey(&key, addr);
pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp);
bound_udp_mkkey(&key, addr);
pnode = tfind(&key, &root_bound_udp, bound_udp_cmp);
assert(pnode && (*pnode)->ref > 0);

node = *pnode;
Expand All @@ -176,7 +191,7 @@ static void bound_udp4_put(const struct sockaddr_in *addr)
if (node->ref)
return;

parent = tdelete(node, &root_bound_udp4, bound_udp4_cmp);
parent = tdelete(node, &root_bound_udp, bound_udp_cmp);
assert(parent);

close(node->fd); // expanding `pnode` to avoid use after free
Expand All @@ -188,10 +203,10 @@ static void bound_udp4_put(const struct sockaddr_in *addr)
* For each destination address, if no packet received from it for a certain period,
* it is removed and the corresponding FD is closed.
*/
static void bound_udp4_action(const void *nodep, const VISIT which, const int depth)
static void bound_udp_action(const void *nodep, const VISIT which, const int depth)
{
time_t now;
struct bound_udp4 *datap;
struct bound_udp *datap;
void *parent;
char buf[RED_INET_ADDRSTRLEN];

Expand All @@ -202,13 +217,13 @@ static void bound_udp4_action(const void *nodep, const VISIT which, const int de
case endorder:
case leaf:
now = redsocks_time(NULL);
datap = *(struct bound_udp4 **) nodep;
datap = *(struct bound_udp **) nodep;
// TODO: find a proper way to make timeout configurable for each instance.
if (datap->t_last_rx + 20 < now) {
parent = tdelete(datap, &root_bound_udp4, bound_udp4_cmp);
parent = tdelete(datap, &root_bound_udp, bound_udp_cmp);
assert(parent);

inet_ntop(AF_INET, &datap->key.sin_addr, &buf[0], sizeof(buf));
inet_ntop(datap->key.sa_family, &datap->key.sin_addr, &buf[0], sizeof(buf));
log_error(LOG_DEBUG, "Close UDP socket %d to %s:%u", datap->fd,
&buf[0], datap->key.sin_port);
close(datap->fd);
Expand Down Expand Up @@ -277,11 +292,10 @@ void redudp_fwd_pkt_to_sender(redudp_client *client, void *buf, size_t len,

// When working with TPROXY, we have to get sender FD from tree on
// receipt of each packet from relay.
// FIXME: Support IPv6
fd = (do_tproxy(client->instance) && srcaddr->ss_family == AF_INET)
? bound_udp4_get((struct sockaddr_in*)srcaddr) : event_get_fd(client->instance->listener);
fd = do_tproxy(client->instance)
? bound_udp_get((struct sockaddr*)srcaddr) : event_get_fd(client->instance->listener);
if (fd == -1) {
redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure");
redudp_log_error(client, LOG_WARNING, "bound_udp_get failure");
return;
}
// TODO: record remote address in client
Expand Down Expand Up @@ -579,6 +593,8 @@ static int redudp_onexit(parser_section *section)
return -1;
}

if (err)
parser_error(section->context, "%s", err);
return err?-1:0;
}

Expand All @@ -591,7 +607,9 @@ static int redudp_init_instance(redudp_instance *instance)
int error;
int fd = -1;
int bindaddr_len = 0;
char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN];
char buf1[RED_INET_ADDRSTRLEN],
buf2[RED_INET_ADDRSTRLEN],
buf3[RED_INET_ADDRSTRLEN];

instance->shared_buff = &shared_buff[0];
if (instance->relay_ss->instance_init
Expand Down Expand Up @@ -633,9 +651,10 @@ static int redudp_init_instance(redudp_instance *instance)
log_error(LOG_INFO, "redudp @ %s: TPROXY", red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)));
}
else {
log_error(LOG_INFO, "redudp @ %s: destaddr=%s",
log_error(LOG_INFO, "redudp @ %s: relay=%s destaddr=%s",
red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)),
red_inet_ntop(&instance->config.destaddr, buf2, sizeof(buf2)));
red_inet_ntop(&instance->config.relayaddr, buf2, sizeof(buf2)),
red_inet_ntop(&instance->config.destaddr, buf3, sizeof(buf3)));
}

if (apply_reuseport(fd))
Expand Down Expand Up @@ -718,7 +737,7 @@ static struct event * audit_event = NULL;

static void redudp_audit(int sig, short what, void *_arg)
{
twalk(root_bound_udp4, bound_udp4_action);
twalk(root_bound_udp, bound_udp_action);
}

static int redudp_init()
Expand Down
2 changes: 1 addition & 1 deletion shadowsocks-udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ static void ss_connect_relay(redudp_client *client)
int fd = -1;
int error;

fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
fd = socket(addr->ss_family, SOCK_DGRAM, IPPROTO_UDP);
if (fd == -1) {
redudp_log_errno(client, LOG_ERR, "socket");
goto fail;
Expand Down
Loading

0 comments on commit 7d75713

Please sign in to comment.