From a8d9efb9d1bc0618484d958abd5352eb65126afa Mon Sep 17 00:00:00 2001 From: Zhuofei Wang Date: Sun, 26 Nov 2023 22:04:29 +0800 Subject: [PATCH] Redudp supports IPv6 --- redudp.c | 91 ++++++++++++++++++++++++++++------------------- shadowsocks-udp.c | 2 +- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/redudp.c b/redudp.c index 346126b2..bee36e7f 100644 --- a/redudp.c +++ b/redudp.c @@ -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; @@ -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++; @@ -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"); @@ -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; @@ -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; @@ -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 @@ -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]; @@ -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); @@ -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 @@ -593,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 @@ -635,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)) @@ -720,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() diff --git a/shadowsocks-udp.c b/shadowsocks-udp.c index 52b941a0..82bb5885 100644 --- a/shadowsocks-udp.c +++ b/shadowsocks-udp.c @@ -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;