Repository navigation
feat: add Snell version 6 support (outbound + inbound) - #3410
Conversation
Port the Snell v6 client/server implementation from SagerNet/sing-snell into transport/singsnell (GPL-3.0-or-later), with imports rewritten to github.com/metacubex/sing and a small compat layer for interfaces that the pinned sing release does not provide. Builtins newer than the Go version declared in go.mod are replaced with equivalents. The snell outbound accepts `version: 6` and a `mode` option (default / unshaped / unsafe-raw) that must match the server. TCP, UDP over TCP and connection reuse are supported; http/tls simple-obfs is rejected for v6 since the protocol replaced it with PSK-derived traffic shaping, while shadow-tls/restls/jls wrappers still apply. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Accept `version: 6` on the snell listener, with a `mode` option (default / unshaped / unsafe-raw) that must match the client. The listener builds a snellv6 service from transport/singsnell and forwards its TCP and UDP connections into the mihomo tunnel. v6 rejects PSKs outside 12..255 bytes and the http/tls simple-obfs layer, while the shadow-tls/restls/jls wrappers keep working. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Since you are already using AI for programming, you shouldn't just port the |
Replace the ported sing-snell package with a native implementation that builds on the existing transport/snell infrastructure, the same way v4/v5 are structured. A v6Conn wraps the raw connection with the v6 record layer and the existing request/reply, UDP packet, pool and listener code is reused unchanged, since v6 kept the inner request format. v6 keeps the v4 AES-128-GCM record framing and adds a PSK-derived profile (transport/snell/v6_profile.go): the salt is scattered inside a padded block, each record carries an unencrypted prefix used as the header AAD, padding is interleaved with the payload ciphertext and record sizes follow a shaped schedule. The derivation follows the protocol notes published by SagerNet/sing-snell. The `unshaped` mode drops the profile layer and `unsafe-raw` drops the crypto as well. The sing-based listener bridge and the transport/singsnell package are removed; the inbound now uses ServerStreamConnV6 in its normal request loop. Before removal, the native layer was cross-tested against the ported package in all three modes (TCP and UDP, both directions). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Missed from the previous commit; wires the native v6 record layer into the shared Snell stream constructors. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Thanks for the review. I have reworked it as suggested: the ported package is gone and v6 is now implemented natively on top of Before removing the ported package I cross-tested the native layer against it in all three modes (TCP and UDP, both directions). The PR description is updated; I can squash the history if you prefer. |
|
Interop update: the native implementation is now verified against the official snell-server 6.0.0 binary in all three modes ( |
What
Adds Snell v6 to the snell outbound and inbound, implemented natively on top of the existing
transport/snellinfrastructure in the same way v4/v5 are structured.transport/snell/v6.go: av6Connthat wraps the raw connection with the v6 record layer (default/unshaped/unsafe-raw), exposed throughStreamConnV6/ServerStreamConnV6. The request/reply layer, UDP packet framing, the reuse pool and the listener request loop are reused unchanged, since v6 kept the inner request format.transport/snell/v6_profile.go: the PSK-derived profile of the default mode (salt block scatter/mask, per-record prefix used as header AAD, padding generation and padding/ciphertext interleave, shaped record sizes). The derivation follows the protocol notes published by SagerNet/sing-snell; v6 otherwise keeps the v4 AES-128-GCM framing and KDF.version: 6with amodeoption that must match the server. TCP, UDP over TCP andreusework through the existing code paths. http/tls simple-obfs is rejected on v6; the shadow-tls/restls/jls wrappers still apply. PSKs must be 12..255 bytes.version: 6andmodeand serves it through its normal request loop.docs/config.yamlupdated for both sides.Why
Snell v6 (snell-server 6.0.0) is now the default in Surge. Closes #3083.
Testing
transport/snell/v6_test.go: profile determinism, salt block and interleave round trips, loopback stream / zero chunk / packet frame tests for all three modes, wrong-PSK rejection.listener/inbound/snell_test.go: outbound ↔ inbound end-to-end for all three modes covering TCP, reuse and UDP, plus shadow-tls v3 on top of v6 and config validation.default,unshaped,unsafe-raw): HTTP over TCP (including an ~86 KB response spanning many records), three sequential requests over a reused session, and UDP DNS queries all succeed. Not yet tested with a Surge client as the peer of the inbound.History
The first two commits were a straight port of sing-snell; the later commits replace it with the native implementation as requested in review. Happy to squash if preferred.
🤖 Generated with Claude Code