You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Feature request: “disorder” mode for fragment (send selected TLS ClientHello segment with low TTL)
#7260
Some DPI systems (e.g. TSPU in Russia) now reassemble the TCP stream before inspecting the TLS ClientHello. Because of this, the existing fragment options are becoming ineffective: freedom fragment (packets / length / interval / maxSplit) and finalmask fragment. Every segment is still sent in order with a normal TTL, so the DPI simply reassembles the ClientHello and reads the SNI.
Proposed technique ("disorder")
Break the order in which the DPI sees the segments, while keeping the stream valid for the server:
The first segment, containing the beginning of the ClientHello up to (or into) the SNI, is sent with a low TTL (e.g. 1), so it expires at the first hop and never reaches the DPI.
The rest of the ClientHello, still fragmented, is sent immediately with the normal TTL.
The server receives the later segments out of order and replies with SACK, so the kernel retransmits the missing first segment automatically, now with the normal TTL.
The server reassembles a valid ClientHello by sequence numbers. The DPI only sees the middle of the stream without its beginning and fails to detect the SNI.
This is exactly what the ByeDPI--disorder option does: setsockopt(IP_TTL, 1) before send() of the chosen part, then the TTL is restored. Today this can be reproduced with Xray only by chaining it through an external SOCKS proxy (sockopt.dialerProxy → ByeDPI). That adds a process and an extra hop, and requires deploying additional software.
Proposed config
An optional disorder object for both freedom fragment and finalmask fragment:
parts: which fragment(s) to send with the low TTL, as a range or list (e.g. "1", "1-2").
ttl: TTL / hop limit for those fragments, default 1.
Supporting it in finalmask fragment would be especially useful, because it would apply directly to proxy outbounds (VLESS / Trojan over TLS / REALITY) without a dialerProxy to freedom.
Implementation notes
Use syscall.RawConn.Control to call setsockopt with IP_TTL (IPv4) or IPV6_UNICAST_HOPS (IPv6) before writing the selected fragment, and restore the original value before writing the next one.
TCP_NODELAY must be enabled, and the fragment must fit within the initial congestion window so it leaves immediately with the low TTL, before the TTL is restored. This is the case for a fresh connection sending a ClientHello.
Linux retransmits only the lost segment after SACK, so disorder on the first part is enough.
Windows retransmits from the highest ACKed byte, so everything after the lost part is resent. ByeDPI recommends combining it with a split before the disordered part (--split 1+s --disorder 3+s). It may be worth documenting this or handling it automatically.
The cost is about one extra RTT per new TCP connection (fast retransmit after SACK; RTO ~200 ms in the worst case). This is acceptable, especially with mux / XMUX.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Problem
Some DPI systems (e.g. TSPU in Russia) now reassemble the TCP stream before inspecting the TLS ClientHello. Because of this, the existing
fragmentoptions are becoming ineffective: freedomfragment(packets/length/interval/maxSplit) and finalmaskfragment. Every segment is still sent in order with a normal TTL, so the DPI simply reassembles the ClientHello and reads the SNI.Proposed technique ("disorder")
Break the order in which the DPI sees the segments, while keeping the stream valid for the server:
1), so it expires at the first hop and never reaches the DPI.This is exactly what the ByeDPI
--disorderoption does:setsockopt(IP_TTL, 1)beforesend()of the chosen part, then the TTL is restored. Today this can be reproduced with Xray only by chaining it through an external SOCKS proxy (sockopt.dialerProxy→ ByeDPI). That adds a process and an extra hop, and requires deploying additional software.Proposed config
An optional
disorderobject for both freedomfragmentand finalmaskfragment:parts: which fragment(s) to send with the low TTL, as a range or list (e.g."1","1-2").ttl: TTL / hop limit for those fragments, default1.Supporting it in finalmask
fragmentwould be especially useful, because it would apply directly to proxy outbounds (VLESS / Trojan over TLS / REALITY) without adialerProxyto freedom.Implementation notes
syscall.RawConn.Controlto callsetsockoptwithIP_TTL(IPv4) orIPV6_UNICAST_HOPS(IPv6) before writing the selected fragment, and restore the original value before writing the next one.disorderon the first part is enough.--split 1+s --disorder 3+s). It may be worth documenting this or handling it automatically.References
--disorderdescription and implementation: https://github.com/hufrea/byedpi (README section "--disorder",desync.c)All reactions