Skip to content

Draft: Unreliable+unordered WebRTC data channel transport for Snowflake rev2

This merge request adds a non-production-ready version of snowflake that use udp like transport mode to reduce abstraction cost. It is based on the rev1 version at !219 (closed), which is based on https://gitlab.torproject.org/shelikhoo/snowflake/-/tree/dev-speedwithudp?ref_type=heads .

There are some script ready for testing: https://gist.github.com/xiaokangwang/3dc8f703879cbd3764f8ed6dfa2da047

To build the code, run build.sh with Dockerfile in the same directory. To start the network, run start_network.sh. See run a client for example for how to get a client working.

Addresses #40352.

Summary of Changes

The primary change is not changed and remain the same, copied from @dcf 's summary.

These changes aim to let the WebRTC link between client and proxy
use an unreliable, unordered (UDP-like) data channel,
in place of the reliable, ordered (TCP-like) data channels that are now used.
Packets from KCP, instead of being getting
[length-prefixed](https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/blob/1bde730b39de4f542d11af17f946ee2ea9427806/common/encapsulation/encapsulation.go)
and written into the data channel stream,
are [written 1:1 as discrete data channel messages](https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/merge_requests/219/diffs#606aacce09df75f5918f9366c0825b00b58468b4_190_164).
Any retransmission or reordering of message payloads
is the responsibility of KCP, not of the data channel.

The core change is
[here](https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/blob/2f757fd6297dd2b2e769092f266e8d70afd9ed72/client/lib/webrtc.go#L243-253),
where `Ordered = false` and `MaxRetransmits = 0`
are passed to `webrtc.DataChannelInit`.
(Compare [RFC 8831](https://datatracker.ietf.org/doc/html/rfc8831#name-sctp-protocol-consideration):
"Limiting the number of retransmissions to zero,
combined with unordered delivery,
provides a UDP-like service where each user message is sent exactly once and delivered in the order received.")

As for the transfer the ClientID, it is now transfer with the protocol field of the channel opening message. Here is a break down of different version of channel opening messages:

Current:

(empty)

Rev1:

u

Rev2:

u + [Space] + hex(ClientID)

The proxy will transfer the entire proxy field as a http query parameter and send it to server, without looking into it.

The server is the one that will parse the protocl field back to its components.

On the client side, the client id is now generated by caller and then propagated to different part of instances.

Merge request reports