Shadowsocks-libev: Support multiple server addresses correctly in ss-local

Created on 12 Nov 2016  ·  18Comments  ·  Source: shadowsocks/shadowsocks-libev

According to local.c#1032, ss-local will attempt to use a random remote address when multiple server addresses are supplied and use the first one as UDP relay.

When a hostname is supplied, according to netutils.c#L170, ss-local will try to resolve it and PREFER IPv4 addresses.

This could bring issues when it's used under an IPv6-only environment when both IPv4/IPv6 server address is supplied or a hostname with both A and AAAA record.

According to here, here's how Chrome does it:

When a hostname has both IPv6 and IPv4 addresses, and the IPv6 address is listed first, we start a timer (300ms) (deliberately chosen to be different from the backup connect job). If the timer fires, that means the IPv6 connect() hasn't completed yet, and we start a second socket connect() where we give it the same AddressList, except we move all IPv6 addresses that are in front of the first IPv4 address to the end. That way, we will use the first IPv4 address. We will race these two connect()s and pass the first one to complete to ConnectJob::set_socket().

Maybe we could use the same/similar mechanism here.

  1. Hostnames are expanded to all A/AAAA records in it, remaining its original order;
  2. Try to connect to all the servers in order with 300ms timeout (perhaps configurable?). After timeout the next address is tried in parallel with another 300ms timeout. The fastest one will be picked. However if the next address is an IPv6 address while the current address is IPv4, the next one is tried in parallel immediately without timeout. (preference for IPv6)
  3. After the connection is established, the address used will be marked as fastest. On the next connection, this fastest address will be tried first, then all the other addresses in order after a 300ms timeout.
enhancement

Most helpful comment

I have to say dual stack is really a disaster. Let's keep this issue open, maybe oneday I can figure out a easy way to implement this.

All 18 comments

I prefer to adding a -6 option to resolve IPv6 address first. Then you can run multiple ss-local instances simultaneously with port reuse.

I don't really get that.

Running multiple ss-local instances like this:

ss-local -c config.json -l 1080 -s s1.test.com
ss-local -c config.json -l 1080 -s s1.test.com -6

It means to connect IPv4 and IPv6 addresses separately and let OS perform the load balance [1].

[1] https://lwn.net/Articles/542629/

I didn't know that was possible. I tried this: (background running another ss-local connecting to ipv6 address)

$ ss-local -c config-ipv4.json -l 1080
 2016-11-12 11:01:34 INFO: onetime authentication enabled
 2016-11-12 11:01:34 INFO: initializing ciphers... chacha20
 2016-11-12 11:01:34 INFO: tcp port reuse enabled
 2016-11-12 11:01:34 ERROR: listen() error
$

As that article points out:

SO_REUSEPORT distributes datagrams evenly across all of the receiving threads.

I'm not actually going for load balancing. It's about making it work in both IPv4-only and IPv6-only environments (or in different subnets) without reconfiguration.

If it's for load balancing, the random pick would be fine.

I think it would be hard for shadowsocks-libev to do this. My suggestion is to involve HAProxy here to do the failover.

How about mptcp?

Doesn't sound too hard. You just need to make some parallel connections.

Maybe you can help to implement this? BTW, I still recommend to use haproxy instead.

Actually I wish to use this feature on shadowsocks-android (currently my WLAN is IPv6-only and mobile data is IPv4-only) so haproxy might not be as desirable.

I'm not sure what's the best way to implement this in shadowsocks-libev. It'll probably take me longer to implement this. :stuck_out_tongue_winking_eye:

I have to say dual stack is really a disaster. Let's keep this issue open, maybe oneday I can figure out a easy way to implement this.

I think the more generic (but slower) way is to keep all IPv4 & IPv6 addresses of the domain name and iterate over all the addresses one by one until you find the correct one that can be connected, because there's no guarantee that all addresses are working.

I am not sure if its the same subject, but i was wondering is it possible to add multipliable ss servers
in the config and do some kind of loadbalancing or best performance for the reason of anti detection
by the gfw.

(I don't know if it is appropriate to ask in this thread.)

I want to know why the --reuse-port option is switched off by default in some newer version of shadowsocks? After all there is no bad effect for normal use.

FYI, I'm using shadowsocks-libev 3.0.2-1 on archlinux.

Please support ipv6 fallback to ipv4.
Test url: http://fallback-v6.ipv6-test.com/json/proto.php

  • Visit directly with curl on server I got
    ({"address":"63.223.1.1","protocol":"4"})
  • client browser using a SOCKS5 proxy created by ssh -D, got
    ({"address":"63.223.1.1","protocol":"4"})
  • client browser using a SOCKS5 proxy created by ss-local, got
    ERR_EMPTY_RESPONSE

ss version 3.1.0 on linux

@tenwx That should be implemented by browsers and I can't reproduce the behavior you reported either.

@Mygod No, it depends on ss-server behavior. It can be reproduced.
Your situation I guess is due to your server does not support ipv6.
dig fallback-v6.ipv6-test.com any

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.62.rc1.el6_9.4 <<>> fallback-v6.ipv6-test.com any
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12238
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;fallback-v6.ipv6-test.com.     IN      ANY

;; ANSWER SECTION:
fallback-v6.ipv6-test.com. 119  IN      A       5.135.165.173
fallback-v6.ipv6-test.com. 119  IN      AAAA    2001:41d0:8:e8ad::fa11:bac

;; Query time: 103 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Feb 23 15:00:21 2018
;; MSG SIZE  rcvd: 87

Actually , this feature is very useful when you need distribute the traffic to different ss-server.

I would like this

server_address 1 = 1.2.3.4 local_port 1 =1234
server_address 2 = 5.6.7.8 local_port 2 =5678

for ss-redir , then iptables will redirect local traffic into different local port.

Was this page helpful?
0 / 5 - 0 ratings