Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TCP fast open support #161

Closed
xqdoo00o opened this issue Jan 6, 2020 · 8 comments
Closed

TCP fast open support #161

xqdoo00o opened this issue Jan 6, 2020 · 8 comments

Comments

@xqdoo00o
Copy link

xqdoo00o commented Jan 6, 2020

How about add TCP Fastopen like v2ray did(v2ray)?

@riobard
Copy link

riobard commented Feb 1, 2020

It seems TFO brings more problems than benefits. See https://squeeze.isobar.com/2019/04/11/the-sad-story-of-tcp-fast-open/

Over the years, Google and Mozilla did more testing and eventually found that TFO wasn’t beneficial. Clients that initiate TFO connections encounter failures requiring them to re-try without TFO so often that, on average, TFO costs more time than it saves. In some networks, TFO never works – for example, China Mobile’s firewall consistently fails to accept TFO requiring every connection to be retried without it, leading to TFO actually increasing roundtrips.

The tracking and privacy concern is also real, especially in SS use case.

@fortuna
Copy link

fortuna commented May 27, 2020

An alternative to TFO is to keep on the client a pool of established connections to the server (or even just one), so you have one already established when you need it.
The trade-off is that aggressive clients may overload the server with unused connections.

@riobard
Copy link

riobard commented May 27, 2020

@fortuna True. Actually an even better idea is to slightly extend the Shadowsocks protocol so it can reuse connections. The popular proxy app Surge has a custom protocol called Snell, which adopted my idea, resulting in significant reduction in average connection establishing time (basically all benefits of TFO without any of its drawbacks).

I should probably write a SIP draft :)

@fortuna
Copy link

fortuna commented May 27, 2020

Reusing connections sounds like a good idea. Besides performance it may add resistance against censorship. But I don't think we can do it in a backward-compatible way. How would you indicate EOF? Currently we rely on the TCP FIN. It probably requires a protocol change.

@riobard
Copy link

riobard commented May 27, 2020

It's actually a pretty easy fix but further testing must be done to see if it can be backward-compatible.

Recall that using AEAD ciphers we split a stream of data into small chunks with a 2-byte chunk length prefix. We can define a zero-length chunk to signal EOF of an existing stream. Upon receiving zero-length chunk, implementations that understanding connection reuse can put the TCP connection into idle pool for later reuse. The connection pool should be a LIFO queue to prioritize reusing recently used connections, and close cold/aging connections to keep the pool under a certain size (say 10 idle connections at max).

Currently zero-length chunk is undefined in the spec and actually we are vulnerable to truncation attack. Fortunately sensitive data should be protected by inner TLS with necessary mitigation so we can be a bit loose in the outer Shadowsocks layer.

Could you please check if Outline client/server properly handles this edge case? I guess existing implementations should probably error out and close the TCP connection when it does not expect zero-length chunks.

Of course at this point stream ciphers must be abandoned altogether.

(I have some even crazier ideas to further enhance this…😄)

@fortuna
Copy link

fortuna commented May 27, 2020

My interpretation of the Shadowsocks spec is that a zero-length chunk is perfectly valid. It's just overhead. I believe outline-ss-server will simply ignore that chunk. It will read the payload size (0), validate the tag, then read 0 bytes of payload, and validate the tag for the empty payload.

I actually considered about using empty chunks for padding in a backward-compatible way to deceive censors, but it's not very flexible.

So I don't think we can use an empty chunk. But we could use a marker that fails AEAD validation with the ss-subkey info in the HKDF, but succeeds with a different one (e.g. ss-eof). Similar to shadowsocks/shadowsocks-org#144 (comment).

My expectation is that older servers will close the connection on an invalid chunk, but new servers can keep the connection going. Though I think the behavior of closing the connection on invalid chunk is not really in the spec.

@riobard
Copy link

riobard commented May 27, 2020

Your interpretation is also perfectly valid and actually TLS has similar definition to allow zero-length data record. From RFC 8446 Section 5.1

Application Data messages contain data that is opaque to TLS.
Application Data messages are always protected. Zero-length
fragments of Application Data MAY be sent, as they are potentially
useful as a traffic analysis countermeasure. Application Data
fragments MAY be split across multiple records or coalesced into a
single record.

But I hate to further complicate the key derivation part. We should not stuff protocol layer concerns into crypto layer…

If you want to do it super cleanly, it's probably better to use the reserved two higher bits in the 2-byte chunk length. The spec currently strictly requires the two bits to be cleared. We could use one bit to signal that the following chunk is an enhanced chunk, such that it comes with a fixed-size header to signal various aspects (e.g. EOF, padding used, actual data size, etc).

@riobard
Copy link

riobard commented Jul 8, 2020

We won't plan to support TFO any more given the tracking and performance concerns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants