Skip to content

Commit

Permalink
Redir UDP send back sockets should always be IPv6
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
zonyitoo authored and Max Lv committed Jun 4, 2022
1 parent c2fc967 commit 01dd7df
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,4 @@ doc/*.html

# Visual Studio Code
.vscode/*
.devcontainer/*
44 changes: 36 additions & 8 deletions src/udprelay.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 01dd7df

Please sign in to comment.