From 01dd7df1da98710a619a4a853f6f357dfc46f60f Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Sat, 4 Jun 2022 00:02:03 +0800 Subject: [PATCH] Redir UDP send back sockets should always be IPv6 ref shadowsocks/shadowsocks-rust#855 When receiving UDP packets from remote servers, Redir will have to create transparent sockets to bind() on those target addresses and then relay data back to the local client. Target addresses may be IPv4 or IPv6 addresses, so we may have to create that transprent sockets based on the target addresses' family. The most simpliest and easiest way is to always create IPv6 sockets and converts IPv4 binding addresses and peer addresses to IPv4-mapped-IPv6. --- .gitignore | 1 + src/udprelay.c | 44 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index e1d5051a..8ab41791 100644 --- a/.gitignore +++ b/.gitignore @@ -97,3 +97,4 @@ doc/*.html # Visual Studio Code .vscode/* +.devcontainer/* diff --git a/src/udprelay.c b/src/udprelay.c index 580ad4bd..5de38830 100644 --- a/src/udprelay.c +++ b/src/udprelay.c @@ -743,6 +743,29 @@ resolv_cb(struct sockaddr *addr, void *data) #endif +void convert_ipv4_mapped_ipv6(struct sockaddr_storage* addr) { + if (addr->ss_family == AF_INET) { + struct sockaddr_in* ipv4_addr = (struct sockaddr_in*)addr; + + struct sockaddr_storage mapped_addr; + memset(&mapped_addr, 0, sizeof(mapped_addr)); + + struct sockaddr_in6* mapped_ipv6_addr = (struct sockaddr_in6*)&mapped_addr; + mapped_ipv6_addr->sin6_family = AF_INET6; + mapped_ipv6_addr->sin6_port = ipv4_addr->sin_port; + uint8_t* ipv6_raw_addr = mapped_ipv6_addr->sin6_addr.s6_addr; + ipv6_raw_addr[10] = 0xff; + ipv6_raw_addr[11] = 0xff; + in_addr_t ipv4_raw_addr = ntohl(ipv4_addr->sin_addr.s_addr); + ipv6_raw_addr[12] = (ipv4_raw_addr >> 24) & 0xff; + ipv6_raw_addr[13] = (ipv4_raw_addr >> 16) & 0xff; + ipv6_raw_addr[14] = (ipv4_raw_addr >> 8) & 0xff; + ipv6_raw_addr[15] = ipv4_raw_addr & 0xff; + + memcpy(addr, &mapped_addr, sizeof(mapped_addr)); + } +} + static void remote_recv_cb(EV_P_ ev_io *w, int revents) { @@ -858,21 +881,19 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) } } - size_t remote_src_addr_len = get_sockaddr_len((struct sockaddr *)&remote_ctx->src_addr); #ifdef MODULE_REDIR + convert_ipv4_mapped_ipv6(&dst_addr); size_t remote_dst_addr_len = get_sockaddr_len((struct sockaddr *)&dst_addr); - int src_fd = socket(remote_ctx->src_addr.ss_family, SOCK_DGRAM, 0); + int src_fd = socket(AF_INET6, SOCK_DGRAM, 0); if (src_fd < 0) { ERROR("[udp] remote_recv_socket"); goto CLEAN_UP; } - int opt = 1; - int sol = remote_ctx->src_addr.ss_family == AF_INET6 ? SOL_IPV6 : SOL_IP; - int flag = remote_ctx->src_addr.ss_family == AF_INET6 ? IPV6_TRANSPARENT : IP_TRANSPARENT; - if (setsockopt(src_fd, sol, flag, &opt, sizeof(opt))) { + int opt = 1; + if (setsockopt(src_fd, SOL_IPV6, IPV6_TRANSPARENT, &opt, sizeof(opt))) { ERROR("[udp] remote_recv_setsockopt"); close(src_fd); goto CLEAN_UP; @@ -889,7 +910,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) } #ifdef IP_TOS // Set QoS flag - int tos = 46 << 2; + int tos = 46 << 2; int rc = setsockopt(src_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); if (rc < 0 && errno != ENOPROTOOPT) { LOGE("setting ipv4 dscp failed: %d", errno); @@ -907,8 +928,14 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) goto CLEAN_UP; } + struct sockaddr_storage mapped_src_addr; + memcpy(&mapped_src_addr, &remote_ctx->src_addr, sizeof(remote_ctx->src_addr)); + convert_ipv4_mapped_ipv6(&mapped_src_addr); + + size_t remote_src_addr_len = get_sockaddr_len((struct sockaddr *)&mapped_src_addr); + int s = sendto(src_fd, buf->data, buf->len, 0, - (struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len); + (struct sockaddr *)&mapped_src_addr, remote_src_addr_len); if (s == -1 && !(errno == EAGAIN || errno == EWOULDBLOCK)) { ERROR("[udp] remote_recv_sendto"); close(src_fd); @@ -917,6 +944,7 @@ remote_recv_cb(EV_P_ ev_io *w, int revents) close(src_fd); #else + size_t remote_src_addr_len = get_sockaddr_len((struct sockaddr *)&remote_ctx->src_addr); int s = sendto(server_ctx->fd, buf->data, buf->len, 0, (struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len);