Broken dual stack UDP forwarding
According to #113, I am not meant to use "server": ["::", "0.0.0.0"], and am meant to use either only :: or 0.0.0.0 (or leave it out completely). This works fine for TCP forwarding on both IPv4 and IPv6, but breaks dual stack UDP forwarding.
Consider the cases:
"server": ["::", "0.0.0.0"]: "bind: address already in use" error (same as #113)"server": ["::"]: shadowsocks UDP relay listens on::, but setsIPV6_V6ONLY, so it doesn't receive IPv4 packets (https://github.com/shadowsocks/shadowsocks-libev/issues/1568)"server": ["0.0.0.0"]: shadowsocks UDP relay listens on0.0.0.0, so it doesn't receive IPv6 packets- No
"server"config at all: shadowsocks UDP relay listens on0.0.0.0, so it doesn't receive IPv6 packets
If I don't use v2ray-plugin, I can use "server": ["::", "0.0.0.0"] and it works fine, so that feels like the intended way to enable dual stack UDP relay (https://github.com/shadowsocks/shadowsocks-libev/issues/1349). But since enabling v2ray-plugin causes the "bind: address already in use" error (#113), I'm reporting the bug here.
The root cause seems to be v2ray-plugin trying to bind to [::]:<port> twice (in my case, <port> is 443):
$ sudo strace -fe trace=%net ss-server -c /etc/shadowsocks-libev/config.json -s :: -s 0.0.0.0
...snip...
2019/09/01 17:25:17 V2Ray 4.19.1 (Po) Custom
2019/09/01 17:25:17 A unified platform for anti-censorship.
strace: Process 5987 attached
strace: Process 5989 attached
strace: Process 5990 attached
strace: Process 5991 attached
[pid 5991] socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_TCP) = 3
[pid 5991] socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_TCP) = 3
[pid 5991] setsockopt(3, SOL_IPV6, IPV6_V6ONLY, [1], 4) = 0
[pid 5991] bind(3, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = 0
[pid 5991] socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_TCP) = 6
[pid 5991] setsockopt(6, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0
[pid 5991] bind(6, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = 0
[pid 5991] socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 3
[pid 5991] setsockopt(3, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0
[pid 5991] setsockopt(3, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0
[pid 5991] setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
[pid 5991] bind(3, {sa_family=AF_INET6, sin6_port=htons(443), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = 0
[pid 5991] listen(3, 128) = 0
[pid 5991] getsockname(3, {sa_family=AF_INET6, sin6_port=htons(443), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [112->28]) = 0
[pid 5991] socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 6
[pid 5991] setsockopt(6, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0
[pid 5991] setsockopt(6, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0
[pid 5991] setsockopt(6, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
[pid 5991] bind(6, {sa_family=AF_INET6, sin6_port=htons(443), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = -1 EADDRINUSE (Address already in use)
2019/09/01 17:25:17 failed to start server: v2ray.com/core/app/proxyman/inbound: failed to listen TCP on 443 > v2ray.com/core/transport/internet: failed to listen on address: 0.0.0.0:443 > v2ray.com/core/transport/internet/websocket: failed to listen TCP on0.0.0.0:443 > listen tcp 0.0.0.0:443: bind: address already in use
Golang's socket implementation listens on dual stack address when listens on "::" or "0.0.0.0", and it cannot be hacked.
A workaround is that you could listen on your server's public ip address, e.g.
{
"server":["189.1.2.3","2001:x:x::xx:x"], <--- your public v4 and v6 address
"server_port":6666,
"local_port":1080,
"password":"password",
"timeout":60,
"method":"chacha20-ietf-poly1305",
"mode":"tcp_and_udp",
"fast_open": true,
"plugin":"/usr/local/bin/v2ray-plugin",
"plugin_opts":"server"
}
This could prevent go's runtime from listening both v4/v6 on same address.
@madeye Do you think it's necessary to check the -localAddr option and remove the duplicate :: or 0.0.0.0? Seems very dirty...
You can set "server": "::".
https://unix.stackexchange.com/a/152618/145080
Was this a recent change? Since I mentioned before that it doesn't work
"server": ["::"]: shadowsocks UDP relay listens on::, but setsIPV6_V6ONLY, so it doesn't receive IPv4 packets (shadowsocks/shadowsocks-libev#1568)
It seems like a bug on shadowsocks-libev.
In shadowsocks-libev, ::0 means binding to all IPv6 addresses.
The design choice here is that we want to allow the user bind to all IPv6 addresses and one IPv4 address. If we bind ::0 to all addresses including IPv4 and IPv6, it's impossible anymore.
However, golang application bind even 0.0.0.0 to both IPv4 and IPv6, which is causing the conflict here.
A workaround is starting another shadowsocks-libev process to work in UDP relay only mode.
@madeye Well there are "IPv4-mapped IPv6 addresses" so...