shadowsocks-rust icon indicating copy to clipboard operation
shadowsocks-rust copied to clipboard

[Feature Request] Support port in ACL outbound_block_list/outbound_allow_list

Open kery opened this issue 8 months ago β€’ 28 comments

Support for port configuration in ACL files is a useful and powerful feature. Is it possible to implement this feature?

For example,

[outbound_allow_list]
1.1.1.1/32:123
2.2.2.0/24:234,456
3.3.3.3:456-500

kery avatar Jun 20 '25 07:06 kery

Your proposed syntax does not play well with IPv6:

# No way to parse the following line?
2001:da8/32:123

# Still does not look right.
[2001:da8/32]:123

The loosely-structured ACL file format is probably not a good fit for powerful features like this one.

Shameless plug

In my own project shadowsocks-go, the router supports route actions based on source and destination port ranges, with inversion support. The main JSON configuration file covers everything.

database64128 avatar Jun 20 '25 08:06 database64128

Yes, you're right. But it's just an example. Maybe introduce a keyword port is a good idea?

[outbound_allow_list]
1.1.1.1/32 port 123
2.2.2.0/24 port 234,456
3.3.3.3 port 456-500
2001:da8/32 port 123

kery avatar Jun 20 '25 08:06 kery

I am very curious about what senario you are trying to support.

Allowing shadowsocks clients to access the targeted IP, but only specified ports?

Denying shadowsocks clients to access the targeted IP with only specified ports?

zonyitoo avatar Jun 20 '25 11:06 zonyitoo

Can't speak for @kery, but I never actually used this feature myself, except when testing it of course.

I implemented it mostly for fun, and I ended up with 2 implementations: one based on a bit set for many scattered ports and port ranges, and one using binary search for when there's only a few ranges.

Yes, I know, it's over-engineered! But I just can't help myself! πŸ˜…

database64128 avatar Jun 20 '25 14:06 database64128

I am very curious about what senario you are trying to support.

Allowing shadowsocks clients to access the targeted IP, but only specified ports?

Denying shadowsocks clients to access the targeted IP with only specified ports?

Yes. The scenario is that there are two services in my network, let's say service1 and service 2, both provide video content. The video content is not sensitive data, so I prefer to use none/plain method to avoid unnecessary encryption/decryption. Additionally, per my test the video cannot be played smoothly when encryption is enabled. So, the none/plain method is a must for my use case. But if I use non/plain method, that means everyone can access my network (once they know the shadowsocks server's IP and port), even other services. So, I need ACL to restrict the access to only these two services.

Here is an example configuration of ssserver.

{
    "servers": [
        {
            "server": "0.0.0.0",
            "server_port": 1234,
            "method": "none",
            "mode": "tcp_only",
            "acl": "1234.acl" <<< access limited to 1.1.1.1 and TCP port 5555
        },
        {
            "server": "0.0.0.0",
            "server_port": 4567,
            "method": "none",
            "mode": "tcp_only",
            "outbound_bind_interface": "eth1.3",
            "acl": "4567.acl" <<< access limited to 0.0.0.0/0 and TCP port 6666
        }
    ]
}

kery avatar Jun 20 '25 15:06 kery

The feature is something like following configuration of Dante (a socks5 server).

socks pass {
    from: 0.0.0.0/0 to: 1.1.1.1/32 port = 5555
    protocol: tcp
    log: error
}

socks pass {
    from: 0.0.0.0/0 to: 0.0.0.0/0 port = 6666
    protocol: tcp
    log: error
}

kery avatar Jun 20 '25 15:06 kery

The video content is not sensitive data, so I prefer to use none/plain method to avoid unnecessary encryption/decryption. Additionally, per my test the video cannot be played smoothly when encryption is enabled. So, the none/plain method is a must for my use case.

I'm curious which encryption methods have you tried? And how much bandwidth does your video content require? On modern CPUs with dedicated AES instructions, the AES-GCM based ones should provide the highest throughput.

On the other hand, the none/plain method implementation of shadowsocks-rust still has plenty of room for optimizations. On Linux you can use splice(2) with pipes to achieve zero-copy transfers between stream sockets, which the Go standard library just provides for you, whereas with tokio you'd have to implement the whole thing yourself.

database64128 avatar Jun 20 '25 17:06 database64128

I really doubt that the performance of the current implementation couldn’t fulfill your bandwidth requirements.

I am running shadowsocks on R4S, which is an aarch64 CPU. It can easily achieve 1000Mbps with any AES-* method.

If you enables encryption method, then the feature requested by this issue doesn't exist.

zonyitoo avatar Jun 21 '25 03:06 zonyitoo

Further more, there are only IP networks in your syntax design, like:

[outbound_allow_list]
1.1.1.1/32 port 123
2.2.2.0/24 port 234,456

but how about domain names, they are regular expressions! Of course there should have no (blank space) in domain name, but I really don't know what users have already set in their host rules, may be they have rules that contains port.

But to be honest, I couldn't find a more appropriate syntax design right now.

zonyitoo avatar Jun 21 '25 18:06 zonyitoo

I really doubt that the performance of the current implementation couldn’t fulfill your bandwidth requirements.

I am running shadowsocks on R4S, which is an aarch64 CPU. It can easily achieve 1000Mbps with any AES-* method.

If you enables encryption method, then the feature requested by this issue doesn't exist.

I think the bottleneck is not the encryption/descryption. I did following test:

  1. method none: The throughput is about 9.25Mbps, video can be played smoothly;
  2. method aes-128-gcm: The throughput is about 3.35Mbps, video can NOT be played smoothly;

I also did a iperf3 test, both aes-128-gcm and none are about 145 Mbps, which is much bigger than the required throughput for smooth video play. Be aware that all the tests were done in iPhone 15 Pro.

Not sure why the throughput drops when encryption is enabled.

kery avatar Jun 23 '25 08:06 kery

@kery What protocol (e.g. rtmp, rtsp, srt) do you use to stream the video? Do you have "no_delay" set to true in shadowsocks-rust's config?

database64128 avatar Jun 23 '25 08:06 database64128

HTTP protocol (actually is udpxy). RTSP has the same issue.

Just tried with no_delay set to true, the symptom is same as before.

kery avatar Jun 23 '25 08:06 kery

You mentioned that all tests were done on an iPhone. What shadowsocks client do you use on your iPhone? Can you test video playback on a computer?

database64128 avatar Jun 23 '25 08:06 database64128

The shadowsocks client is Shadowrocket. Sure, I'll have a try on my laptop.

kery avatar Jun 23 '25 08:06 kery

What video player do you use on your iPhone to stream the video?

database64128 avatar Jun 23 '25 08:06 database64128

The video player on my iPhone is APTV.

Just now I tested again on my laptop (Windows 11 with latest sslocal), the video player is VLC. The result is same as iPhone, i.e. using method none the video can be played smoothly; using method aes-128-gcm the video cannot be played smoothly.

kery avatar Jun 23 '25 13:06 kery

In my experience, VLC tends to have issues when streaming videos. Can you try mpv? On your iPhone, there are also video players like Outplayer that are based on mpv.

database64128 avatar Jun 23 '25 14:06 database64128

In my opinion, it's not related to the video player (mpv doesn't support proxy setting, so the following tests are still based on VLC).

I tested and confirmed that the throughput of direct access to the video is about 9Mbps. And then I accessed the video via shadowsocks proxy with following combination. The result shows that the issue only presents when access it from internet and the method aes-128-gam (or other methods) is enabled.

lan wan
none 9Mbps 9Mbps
aes-128-gcm 9Mbps 3Mbps

kery avatar Jun 23 '25 15:06 kery

In my opinion, it's not related to the video player (mpv doesn't support proxy setting, so the following tests are still based on VLC).

In my experience, it is very much related to how the video player configures and reads from the socket. Even more so considering the fact that you can only observe reduced bandwidth from the internet (higher RTT, lower MTU). You can use tcpdump to capture packets and compare the flow patterns of packets.

I also just tested and can confirm that mpv supports HTTP proxies. Besides, no matter what protocol you use, you can always just run a tunnel local server with sslocal and ask mpv to connect to it.

If you believe it is caused by something specific to shadowsocks-rust, you can test with other implementations like shadowsocks-go or sing-box.

database64128 avatar Jun 23 '25 19:06 database64128

9Mbps is quite low. How to reproduce?

zonyitoo avatar Jun 24 '25 09:06 zonyitoo

9Mbps is quite low. How to reproduce?

9Mbps is the bitrate of the video stream, not the maximum throughput of shadowsocks-rust, so the test result is normal.

Actually, I've found the root cause. The remote server is the same host where ssserver running, so sserver will use loopback interface (lo) to connect the "remote" server (udpxy), and the negotiated MSS is 65496 (lo's MTU is 65535).

When method none is used, I don't see huge packets (e.g. size > 1500), even though the MSS is huge. I think it's because all the video packets (each video packet is about 1382 bytes) are forwarded very quickly; when encryption is enabled, delay is introduced which causing some of the video packets not forwarded in time, and at some point the video producer (udpxy) may send a huge packet (e.g. 65549 bytes) because it merges a dozens of the normal packets (1382 bytes).

I tried to change the lo's MTU to 1500, it seems that the system doesn't follow the change, but I think it's another problem.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         client host         β”‚          β”‚         server host       β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚          β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚video playerβ”œβ”€β”€β”€ sslocal β”œβ”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€ ssserver β”œβ”€β”€β”€β”€β”€ udpxy β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚          β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

kery avatar Jun 24 '25 10:06 kery

This sounds like insufficient buffering on the video player's side, which VLC tends to have problems with. I have seen it many times myself, where VLC struggles but mpv can playback the video stream smoothly.

database64128 avatar Jun 24 '25 10:06 database64128

I tried to change the lo's MTU to 1500, it seems that the system doesn't follow the change, but I think it's another problem.

Why are you trying to lower lo's MTU? Doing so would only reduce efficiency. It's not a fix for anything.

TCP connections are byte streams. Writing more than the number of bytes a single segment can carry is perfectly fine, and does not mean the TCP stack will send out huge IP packets.

database64128 avatar Jun 24 '25 11:06 database64128

@database64128 I just tried with mpv player on Windows 11, with sslocal's tunnel protocol. the symptom is same as before. So, it's not the video player's problem.

Why are you trying to lower lo's MTU? Doing so would only reduce efficiency. It's not a fix for anything.

TCP connections are byte streams. Writing more than the number of bytes a single segment can carry is perfectly fine, and does not mean the TCP stack will send out huge IP packets. Thanks for reminding! I just want to verify my suspicions. I'll change it back to the default MTU.

I'm also curious about why the throughput is just ~= 3Mbps when there are huge packets on loopback interface.

kery avatar Jun 24 '25 11:06 kery

Earlier you said you tried with "no_delay": true, did you set this on both ssserver and sslocal?

database64128 avatar Jun 24 '25 11:06 database64128

In case chunk size makes a difference, do you mind try with "method": "2022-blake3-aes-128-gcm" and see if it changes the bandwidth?

database64128 avatar Jun 24 '25 11:06 database64128

@database64128

Earlier you said you tried with "no_delay": true, did you set this on both ssserver and sslocal?

Yes, it's enabled on both side.

In case chunk size makes a difference, do you mind try with "method": "2022-blake3-aes-128-gcm" and see if it changes the bandwidth?

Just tried with 2022-blake3-aes-128-gcm, still the same.

If you agree I can share the server to you for troubleshooting^^.

kery avatar Jun 24 '25 11:06 kery

@kery Sure. If you are on Telegram, DM me. Same username as my GitHub account. Otherwise, use the email addresses in our GitHub profiles.

database64128 avatar Jun 24 '25 12:06 database64128