Browse Source

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.
pull/2909/head
zonyitoo 2 years ago
committed by Max Lv
parent
commit
01dd7df1da
2 changed files with 37 additions and 8 deletions
  1. 1
      .gitignore
  2. 44
      src/udprelay.c

1
.gitignore

@ -97,3 +97,4 @@ doc/*.html
# Visual Studio Code
.vscode/*
.devcontainer/*

44
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);

Loading…
Cancel
Save