aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMicah Elizabeth Scott <beth@torproject.org>2024-01-10 18:22:22 -0800
committerMicah Elizabeth Scott <beth@torproject.org>2024-01-25 08:56:48 -0800
commit25feebf9d0a0de3e36fcf3331e41e0f9e9d16afa (patch)
treea18a10e08a147fc5baefdba237b28a050ea573a8
parent7a7177dee689a87c0b3506f323c586511d206351 (diff)
downloadtorspec-25feebf9d0a0de3e36fcf3331e41e0f9e9d16afa.tar.gz
torspec-25feebf9d0a0de3e36fcf3331e41e0f9e9d16afa.zip
Continued work on structure, formatting, TODOs
-rw-r--r--proposals/XXX-udp-app-support.md350
1 files changed, 238 insertions, 112 deletions
diff --git a/proposals/XXX-udp-app-support.md b/proposals/XXX-udp-app-support.md
index b9bda00..1acc2ef 100644
--- a/proposals/XXX-udp-app-support.md
+++ b/proposals/XXX-udp-app-support.md
@@ -17,24 +17,34 @@ This leads into an analysis that references appropriate standards and proposes s
## History
-There have been multiple attempts already within Tor to define some type of UDP support.
+There have already been multiple attempts over Tor's history to define some type of UDP extension.
-[Proposal 100](https://spec.torproject.org/proposals/100-tor-spec-udp.html) by Marc Liberatore in 2006 suggested a way to "add support for tunneling unreliable datagrams through tor with as few modifications to the protocol as possible."
+### 2006
+
+[Proposal 100](https://spec.torproject.org/proposals/100-tor-spec-udp.html) by Marc Liberatore in 2006 suggested a way to "add support for tunneling unreliable datagrams through tor with as few modifications to the protocol as possible."
This proposal suggested extending the existing TLS+TCP protocol with a new DTLS+UDP link mode.
The focus of this work was on a potential way to support unreliable traffic, not necessarily on UDP itself or on UDP applications.
-In proposal 100, a Tor *stream* is used for one pairing of local and remote address and port, copying the technique used by Tor for TCP. This works for some types of UDP applications, as we detail below, but for many peer-to-peer use cases it's unhelpful to allocate local ports that can only be used with a single remote peer.
+In proposal 100, a Tor *stream* is used for one pairing of local and remote address and port, copying the technique used by Tor for TCP.
+This works for some types of UDP applications, but it's broken by common behaviors like ICE connectivity checks, NAT traversal attempts, or using multiple servers via the same socket.
+We go into more detail about application behavior below.
-Furthermore, no additional large-message fragmentation protocol is defined, so the MTU in proposal 100 is limited to what fits in a single Tor cell. This value we will see is unusably small for most applications.
+No additional large-message fragmentation protocol is defined, so the MTU in proposal 100 is limited to what fits in a single Tor cell.
+This value we will see is much too small for most applications.
It's possible these UDP protocol details would have been elaborated during design, but the proposal hit a snag elsewhere:
there was no agreement on a way to avoid facilitating new attacks against anonymity.
-In 2018, Nick Mathewson and Mike Perry wrote a
+### 2018
+
+In 2018, Nick Mathewson and Mike Perry wrote a
[summary of the side-channel issues with unreliable transports for Tor](https://research.torproject.org/techreports/side-channel-analysis-2018-11-27.pdf).
-The focus of this document is on the link between Tor relays, but there is considerable overlap between the attack space explored here and the potential risks of any application-level UDP support.
+
+The focus of this document is on the communication between Tor relays, but there is considerable overlap between the attack space explored here and the potential risks of any application-level UDP support.
Attacks that are described here, such as drops and injections, may be applied by malicious exits or some types of third parties even in an implementation using only present-day reliable Tor transports.
+### 2020
+
[Proposal 339](https://spec.torproject.org/proposals/339-udp-over-tor.html) by Nick Mathewson in 2020 introduced a simpler UDP encapsulation design which had similar stream mapping properties as in proposal 100, but with the unreliable transport omitted. Datagrams are tunneled over a new type of Tor stream using a new type of Tor message.
As a prerequisite, it depends on [proposal 319](https://spec.torproject.org/proposals/319-wide-everything.html) to support messages that may be larger than a cell, extending the MTU to support arbitrarily large UDP datagrams.
@@ -93,7 +103,7 @@ BSD-style sockets support UDP via `SOCK_DGRAM`.
UDP is a stateless protocol, but sockets do have state.
Each socket is bound, either explicitly with `bind()` or automatically, to a source IP and port.
-At the API level, a socket is said to be *connected* to a remote address:port if that address is the default destination.
+At the API level, a socket is said to be *connected* to a remote `(address, port)` if that address is the default destination.
A *connected* socket will also filter out incoming packets with source addresses different from this default destination.
A socket is considered *unconnected* if `connect()` has not been called.
These sockets have no default destination, and they accept datagrams from any source.
@@ -148,15 +158,15 @@ RFC4787 defines three types with different properties, and does not make one sin
We can gain some additional insight by looking at requirements that come from outside RFC4787.
- **Endpoint-Independent Filtering** allows incoming datagrams from any peer once a mapping has been established.
-
+
RFC4787 recommends this approach, with the concession that it may not be ideal for all security requirements.
-
+
In the context of Tor, we can likely rule out this technique entirely.
It makes traffic injection attacks possible from any source address, provided you can guess the UDP port number used at an exit.
It also makes possible clearnet hosting of UDP servers using an exit node's IP, which may have undesirable abuse properties.
-
+
It precludes "Port overlapping" behavior as defined in RFC7857 section 3, which may be necessary in order to achieve sufficient utilization of local port numbers on exit nodes.
-
+
It is still common for present-day applications to *prefer* endpoint-independent filtering, as it allows incoming connections from peers which cannot use STUN or a similar address fixing protocol.
Choosing endpoint-independent filtering would have *some* compatibility benefit, but among modern protocols which use ICE and STUN there would be no improvement.
The cost, on the other hand, would be an uncomfortably low-cost traffic injection attack and additional risks toward exit nodes.
@@ -166,7 +176,7 @@ We can gain some additional insight by looking at requirements that come from ou
This is a permitted alternative according to RFC4787, in which incoming datagrams are allowed from only IP addresses we have previously sent to, but any port on that IP may be the sender.
The intended benefits of this approach versus the port-dependent filtering below are unclear, and may no longer be relevant. In theory they would be:
-
+
- To support a class of applications that rely on, for a single local port, multiple remote ports achieving filter acceptance status when only one of those ports has been sent a datagram.
We are currently lacking examples of applications in this category.
Any application using ICE should be outside this category, as each port would have its own connectivity check datagrams exchanged in each direction.
@@ -257,179 +267,301 @@ In alphabetical order:
| WiFi Calling | Telecom | IPsec tunnel | Out of scope | Still out of scope |
| Zoom | Telecom | client/server or P2P, UDP/TCP | Works | Slight latency improvement |
-# Design approaches
+# High level approaches
-Now that we've defined some categories of UDP traffic we are interested in handling, this section starts to examine different implementation techniques.
+Now that we've defined some categories of UDP traffic we are interested in handling, this section starts to examine different high-level implementation techniques we could adopt.
We can broadly split these into *datagram routing* and *tunneling*.
+Ideally we would be choosing a design that solves problems we have in the near-term while also providing a solid foundation for future enhancements to Tor, including changes which may add full support for unreliable delivery of datagrams. If we proceed down that path with insufficient understanding of the long-term goal, there's a risk that we will choose to adopt complexity in service of future goals while failing to serve them adequately when the time comes.
+
## Datagram routing
-These approaches seek to use an anonymity network that can directly route datagrams from place to place.
-These approaches are the most obviously suitable for implementing UDP, but they also form the widest departure from classic Tor.
+These approaches seek to use a network that can directly route datagrams from place to place. These approaches are the most obviously suitable for implementing UDP, but they also form the widest departure from classic Tor.
+
+### Intentional UDP leak
+
+The simplest approach would be to allow UDP traffic to bypass the anonymity layer. This is an unacceptable loss of anonymity in many cases, given that the client's real IP address is made visible to web application providers.
+
+In other cases, this is an acceptable or even preferable approach. For example, VPN users may be more concerned with achieving censorship-resistant network connectivity than hiding personal identifiers from application vendors.
+
+In threat models where application vendors are more trustworthy than the least trustworthy Tor exits, it may be more appropriate to allow direct peer-to-peer connections than to trust Tor exits with unencrypted connection establishment traffic.
### 3rd party implementations
-TODO: What examples do we have of 3rd party mixnets that already have these desirable datagram properties? i2p would be a good reference point here, but only for the mixnet portion itself not for any kind of exit bridge.
+Another option would be to use an unrelated anonymizer system for datagram traffic. It's not clear that a suitable system already exists. I2P provides a technical solution for routing anonymized datagrams, but not a Tor-style infrastructure of exit node operators.
-### Hybrid approaches
+This points to the key weakness of relying on a separate network for UDP: Tor has an especially well-developed community of volunteers running relays. Any UDP solution that is inconvenient for relay operators has little chance of adoption.
-TODO: If we do have a good story for some potential datagram mixnet above, examine the possibility of splitting traffic between Tor and that network.
+### Future proofing
-TODO: Also discuss splitting traffic between exit and rend for onions? This is technically more likely to be useful for tunneling below, so maybe we don't go into it here.
+This is likely where we would seek to expand Tor's design in order to add end-to-end support for unreliable delivery in the future.
+A specific design is out of the scope of this document.
-### Making room for a future Tor implementation
+It is worth thinking early about how we can facilitate combinations of approaches.
+We may find a need for an abstraction similar to a network routing table, allowing multiple UDP providers to coexist.
-TODO: Consider whether datagram message types could realistically carry over to a future Tor protocol that supports unrealiable delivery. Would we be building a foundation for real or is it not useful yet?
+Even without bringing any new network configurations to Tor, achieving interoperable support for both exit nodes and onion services in a Tor UDP implementation requires some attention to how multiple UDP providers can coexist.
## Tunneling
-### Using TURN (RFC8656) encapsulated in a Tor stream
+The approaches in this section add a new construct which does not exist in UDP itself: a point to point tunnel between clients and some other location at which they establish the capability to send and receive UDP datagrams.
+
+Any tunneling approach requires some way to discover tunnel endpoints.
+We would like this to come as an extension of Tor's existing process for distributing consensus and representing exit policy.
+
+We expect exit policies for UDP to have limited practical amounts of diversity.
+VPN implementations will need to know ahead of time which tunnel circuits to build, or they will suffer a significant spike in latency for the first outgoing datagram to a new peer.
+Additionally, it's common for UDP port numbers to be randomly assigned.
+This makes highly specific exit policies even less useful and even higher overhead than they are with TCP.
+
+### Using TURN encapsulated in a Tor stream
+
+The scope of this tunnel is quite similar to the existing TURN relays, used commonly by WebRTC applications to implement fallbacks for clients who cannot find a more direct connection path.
+
+TURN is defined by [RFC8656](https://www.rfc-editor.org/rfc/rfc8656) as a set of extensions built on the framework from STUN in [RFC8489](https://www.rfc-editor.org/rfc/rfc8489.html). The capabilities are a good match for our needs, offering clients the ability to encapsulate UDP datagrams within a TCP stream, and to allocate local port mappings on the server.
-TODO: Comparison vs. an entirely out-of-protocol and potentially out-of-process TURN server. Is this complexity warranted?
+TURN was designed to be a set of modular and extensible pieces, which may be too far opposed to Tor's design philosophy of providing single canonical representations. Any adoption of TURN will need to consider the potential for malicious implementations to mark traffic, facilitating deanonymization attacks.
+
+TURN has a popular embeddable C-language implementation, [coturn](https://github.com/coturn/coturn), which may be suitable for including alongside or inside C tor.
### Using Tor streams to an exit
-TODO: Bulk of the design goes here, this is what we likely start with
+Most of the discussion on UDP implementation in Tor so far has assumed this approach. Essentially it's the same strategy as TCP exits, but for UDP. When the OP initializes support for UDP, it pre-builds circuits to exits that support required UDP exit policies. These pre-built circuits can then be used as tunnels for UDP datagrams.
+
+Within this overall approach, there are various ways we could choose to assign Tor *streams* for the UDP traffic. This will be considered below.
### Using Tor streams to a rendezvous point
-TODO: possible generalization of above to support onion UDP. big caveat that apps need extensions to support onion addressing.
+To implement onion services which advertise UDP, we may consider using multiple simultaneous tunnels.
+In addition to exit nodes, clients could establish the ability to allocate virtual UDP ports on a rendezvous node of some kind.
+
+The most immediate challenge in UDP rendezvous would then become application support. Protocols like STUN and ICE deal directly with IPv4 and IPv6 formats in order to advertise a routable address to their peer. Supporting onion services in WebRTC would require protocol extensions and software modifications for STUN, TURN, ICE, and SDP at minimum.
+
+UDP-like rendezvous extensions would have limited meaning unless they form part of a long-term strategy to forward datagrams in some new way for enhanced performance or compatibility. Otherwise, application authors might as well stick with Tor's existing TCP-like rendezvous functionality.
+
+# Specific designs using Tor streams
+
+Let's look more closely at Tor *streams*, the multiplexing layer right below circuits.
+
+Streams have a 16-bit identifier, allocated arbitrarily by clients. Stream lifetimes are subject to some ambiguity still in the Tor spec. They are allocated by clients, but may be destroyed by either peer.
+
+## Stream per tunnel
+
+The fewest new streams would be a single stream for all of UDP. This is what we get if we choose an off-the-shelf protocol like TURN as our UDP proxy.
+
+This approach would require only a single new Tor message type:
+
+- `CONNECT_TURN`
+
+ - Establish a stream as a connection to the exit relay's built-in (or configured) TURN server.
+
+Note that RFC8656 requires authentication before data can be relayed, which is a good default best practice for the internet perhaps but is the opposite of what Tor is trying to do. We would either deviate from the specification to relax this auth requirement, or we would provide a way for clients to discover credentials: perhaps by fixing them ahead of time or by including them in the relay descriptor.
+
+## Stream per socket
+
+One stream **per socket** was the approach suggested in [Proposal 339](https://spec.torproject.org/proposals/339-udp-over-tor.html) by Nick Mathewson in 2020.
+
+In proposal 339, there would be one new type of stream and three new message types: `CONNECT_UDP`, `CONNECTED_UDP`, and `DATAGRAM`.
+
+Each stream's lifetime would match the lifetime of a source port allocation.
+There would be a single peer `(remote address, remote port)` allowed per `local port` allocation.
+This matches the usage of BSD-style sockets on which `connect()` has completed.
+It's incompatible with many of the applications analyzed.
+Multiple peers are typically needed for a variety of reasons, like connectivity checks or multi-region servers.
-#### Stream usage
+This approach would be simplest to implement and specify, especially in the existing C tor implementation.
-An early design juncture in this project is the particular choice of scope for one *stream* in the existing Tor protocol.
+It also unfortunately has very limited compatibility, and no clear path toward incremental upgrades if we wish to improve compatibility later.
-- One stream **per socket** was the approach suggested in Proposal 339.
+## Stream per flow
- Each stream would match the lifetime of a source port allocation.
- There would be a single peer address/port allowed per allocation.
- This matches the usage of BSD-style sockets on which `connect()` has completed.
- It's incompatible with many of the applications analyzed.
- Multiple peers are typically needed for a variety of reasons, like connectivity checks or multi-region servers.
+One stream **per flow** has also been suggested.
+In particular, Mike Perry brought up this approach during our conversations about UDP earlier and we spent some time analyzing it.
- This approach would be simplest to implement and specify.
- It also unfortunately has very limited compatibility, and no clear path toward incremental upgrades if we wish to improve compatibility later.
-
-- One stream **per flow** has also been suggested. This would assign a stream ID to the combination of an allocated source port and a remote peer address/port. The flows may contain additional flags, like transmit and receive filtering, IPv4/v6, and *Don't Fragment*.
+This would assign a stream ID to the tuple consisting of at least `(local port, remote address, remote port)`. Additional flags may be included for features like transmit and receive filtering, IPv4/v6 choice, and IP *Don't Fragment*.
- This has advantages in keeping the datagram cells simple, with no additional IDs beyond the existing circuit ID.
- It may also have advantages in DoS-prevention and in privacy analysis.
-
- By decoupling protocol features from the lifetime of sockets on the exit side, we facilitate implementing the desirable "Port overlapping" NAT behavior as mentioned above.
+This has advantages in keeping the datagram cells simple, with no additional IDs beyond the existing circuit ID.
+It may also have advantages in DoS-prevention and in privacy analysis.
- Stream lifetimes, in this case, would not have any specific meaning other than the lifetime of the ID itself.
- The bundle of flows associated with one source port would still all be limited to the lifetime of a Tor circuit, by scoping the source port identifier to be contained within the lifetime of its circuit.
+By decoupling protocol features from the lifetime of sockets on the exit side, we facilitate implementing the desirable "Port overlapping" NAT behavior mentioned above.
- It would be necessary to allocate a new stream ID any time a new set of parameters (source port, remote address, remote port) is seen.
- This would most commonly happen as a result of a first datagram sent to a new peer, coinciding with the establishment of a NAT-style mapping and the possible allocation of a socket on the exit.
- A less common case needs to be considered too: what if the parameter tuple first occurs on the exit side?
- We don't yet have a way to allocate stream IDs from either end of a circuit.
- This would need to be considered, and the simplest solution might just be to partition the stream ID space into a half that can be allocated by each side.
- This leaves a quite unfair split in the common case where streams are almost always allocated by the OP side.
+Stream lifetimes, in this case, would not have any specific meaning other than the lifetime of the ID itself.
+The bundle of flows associated with one source port would still all be limited to the lifetime of a Tor circuit, by scoping the source port identifier to be contained within the lifetime of its circuit.
- When is this exit-originated circuit ID allocation potentially needed?
- It is clearly needed when using **address-dependent filtering**.
- An incoming datagram from a previously-unseen peer port is expected to be deliverable, and the exit would need to allocate an ID for it.
+It would be necessary to allocate a new stream ID any time a new `(local port, remote address, remote port)` tuple is seen.
+This would most commonly happen as a result of a first datagram sent to a new peer, coinciding with the establishment of a NAT-style mapping and the possible allocation of a socket on the exit.
- Even with the stricter **address and port-dependent filtering** we may still be exposed to exit-originated circuit IDs if there are mismatches in the lifetime of the filter and the stream.
+A less common case needs to be considered too: what if the parameter tuple first occurs on the exit side?
+We don't yet have a way to allocate stream IDs from either end of a circuit.
+This would need to be considered.
+One simple solution would be to statically partition the stream ID space into a portion that can be independently allocated by each side.
- This approach thus requires some attention to either correctly allocating stream IDs on both sides of the circuit, or choosing a filtering strategy and filter/mapping lifetime that does not ever leave stream IDs undefined when expecting incoming datagrams.
+When is this exit-originated circuit ID allocation potentially needed?
+It is clearly needed when using **address-dependent filtering**.
+An incoming datagram from a previously-unseen peer port is expected to be deliverable, and the exit would need to allocate an ID for it.
-- One stream **per mapping** is an alternative which attemps to reduce the number of edge cases by merging the lifetimes of one stream and one **endpoint-independent mapping**.
+Even with the stricter **address and port-dependent filtering** we may still be exposed to exit-originated circuit IDs if there are mismatches in the lifetime of the filter and the stream.
- A mapping would always be allocated from the OP side.
- It could explicitly specify a filtering style, if we wish to allow applications to request non-port-dependent filtering for compatibility.
- Each datagram within the stream would still need to be tagged with a peer address/port in some way.
+This approach thus requires some attention to either correctly allocating stream IDs on both sides of the circuit, or choosing a filtering strategy and filter/mapping lifetime that does not ever leave stream IDs undefined when expecting incoming datagrams.
- This puts us in a very similar design space to TURN, RFC8656.
- In TURN, "allocations" are made explicitly on request, and assigned a random relayed port.
- TURN also uses its "allocations" as an opportunity to support a *Don't Fragment* flag.
-
- Within the scope of one allocation, datagrams each still may have an arbitrary peer address. This takes up additional space in every datagram.
+## Stream per mapping
-- Hybrid approach, one stream **per mapping** and **per flow** at the same time
+One stream **per mapping** is an alternative which attemps to reduce the number of edge cases by merging the lifetimes of one stream and one **endpoint-independent mapping**.
- We could try to combine the strengths of both approaches, using the lifetime of one stream to define a mapping and to carry otherwise-unbundled traffic while also allowing additional streams to bundle datagrams that would otherwise have repetetive headers.
+A mapping would always be allocated from the OP side.
+It could explicitly specify a filtering style, if we wish to allow applications to request non-port-dependent filtering for compatibility.
+Each datagram within the stream would still need to be tagged with a peer address/port in some way.
- This avoids the space overhead of a purely **per mapping** approach and avoids the ID allocation and lifetime complexity introduced with **per flow**.
-
- This takes more inspiration from TURN, where commonly used peers will be defined as a "channel" with an especially short header.
- In TURN, a "channel" is only ever allocated from the originating side of the connection.
- Incoming datagrams with no channel can always be represented in the long form, so TURN never has to allocate channels unexpectedly.
+This approach would involve a single new type of stream, two new messages that pertain to these *flow* streams:
-# Protocol Designs
+- `NEW_UDP_MAPPING`
+
+ - Always client-to-exit
+ - Creates a new mapping, with a specified stream ID
+ - Succeeds instantly; no reply is expected, early data is ok.
+ - Externally-visible local port number is arbitrary, and must be determined through interaction with other endpoints
+ - Might contain an IP "don't fragment" flag
+ - Might contain a requested filtering mode
+ - Lifetime is until circuit teardown or `END` message
+
+- `UDP_MAPPING_DATAGRAM`
+
+ - conveys one datagram on a stream defined by `NEW_UDP_MAPPING`.
+ - Includes peer address (IPv4/IPv6) as well as datagram content
+
+This puts us in a very similar design space to TURN, RFC8656.
+In TURN, "allocations" are made explicitly on request, and assigned a random relayed port.
+TURN also uses its "allocations" as an opportunity to support a *Don't Fragment* flag.
+
+The principal disadvantage of this approach is in space overhead, especially the proportional overhead on small datagrams which must still carry a full-size address.
+
+## Hybrid stream appoach
+
+We can extend the approach above with an optimization that addresses the undesiarable space overhead from redundant address headers.
+
+This uses two new types of stream, in order to have streams **per mapping** and **per flow** at the same time.
-Currently we have two design candidates, and both are described here.
+The per-mapping stream remains the sole interface for managing the lifetime of a mapped UDP port. Mappings are created explicitly by the client. As an optimization, within the lifetime of a mapping there may exist some number of *flows*, each assigned their own ID.
-TODO: Exit selection is likely common to both of these approaches. Talk about how we specify UDP exit policy, how we provide a smart tradeoff between ability to filter harmful traffic while avoiding an explosion in exit policy complexity.
+This tries to combine the strengths of both approaches, using the lifetime of one stream to define a mapping and to carry otherwise-unbundled traffic while also allowing additional streams to bundle datagrams that would otherwise have repetetive headers.
+It avoids the space overhead of a purely **per mapping** approach and avoids the ID allocation and lifetime complexity introduced with **per flow**.
-## Tor UDP Mapping Protocol
+This approach takes some inspiration from TURN, where commonly used peers will be defined as a "channel" with an especially short header.
+Incoming datagrams with no channel can always be represented in the long form, so TURN never has to allocate channels unexpectedly.
-This is a *tunneled* approach, using Tor's existing protocol objects where possible. Exits advertise support for specific UDP policies. Clients choose an exit where they would like to allocate a UDP local port. They create one *stream* with a lifetime matching the lifetime of that mapping. Optional additional *streams* may be used as fast paths for common peers. This is the *hybrid approach* to stream usage, from above.
+The implementation here could be a strict superset of the **per mapping** implementation, adding new commands for flows while retaining existing behavior for mappings. There would be a total of four new message types:
- `NEW_UDP_MAPPING`
- - always client-to-exit
- - stream ID for mapping
- - no address given
- - "don't fragment" flag
- - port-specific filtering flag
- - no reply? early data always ok.
- - ended on circuit teardown or by `END`
+ - Same as above
- `UDP_MAPPING_DATAGRAM`
- - conveys one datagram on a stream defined by ALLOCATE_UDP_MAPPING.
- - Includes peer address (ipv4/ipv6) as well as datagram content
+ - Same as above
- `NEW_UDP_FLOW`
- Lifetime is <= lifetime of UDP mapping.
- stream ID for parent mapping, and new flow within that mapping
- ended on mapping end or by explicit `END`
- - Includes peer address (ipv4/ipv6)
+ - Includes peer address (IPv4/IPv6)
- `UDP_FLOW_DATAGRAM`
- Datagram contents without address, for flow streams.
- - If a flow stream exists, the exit must use `UDP_FLOW_DATAGRAM` instead of `UDP_MAPPING_DATAGRAM`.
- - Mappings that do not request port-specific filtering may always get unexpected UDP_MAPPING_DATAGRAMs. Mappings that do use port-specific filtering could make a flow for their only expected peers, then expect to never see `UDP_MAPPING_DATAGRAM`.
-## TURN over Tor
+We must consider the traffic marking opportunities we open when allowing an exit to represent one incoming datagram as either a *flow* or *mapping* datagram.
-In this approach, a single Tor stream is used for all UDP traffic.
+It's possible this traffic injection potential is not worse than the baseline amount of injection potential than every UDP protocol presents. See more on risks below. For this hybrid stream approach specifically, there's a limited mitigation we can use to allow exits only a bounded amount of leaked information per UDP peer:
-- `CONNECT_TURN`
+We would like to state that exits may not choose to send a `UDP_MAPPING_DATAGRAM` when they could have sent a `UDP_FLOW_DATAGRAM`.
+Sometimes it is genuinely unclear though: an exit may have received this datagram in-between processing `NEW_UDP_MAPPING` and `NEW_UDP_FLOW`.
+We could choose to terminate circuits which send a `UDP_MAPPING_DATAGRAM` for a peer that has already been referenced in a `UDP_FLOW_DATAGRAM`, giving exits a one-way gate to let them switch a peer from *mapping* datagram to *flow* datagram but not the reverse.
- - Establish a stream as a connection to the exit relay's built-in (or configured) TURN server.
- - RFC8656 requires authentication before data can be relayed, which is a good default best practice for the internet perhaps but is the opposite of what Tor is trying to do. We would either modify the specification to relax this auth requirement, or we would provide a way for clients to discover credentials: either by fixing them ahead of time or by including them in the relay descriptor.
+Mappings that do not request port-specific filtering may always get unexpected `UDP_MAPPING_DATAGRAM`s. Mappings that do use port-specific filtering could make a flow for their only expected peers, then expect to never see `UDP_MAPPING_DATAGRAM`.
+
+We may wish for `NEW_UDP_MAPPING` to have an option requiring that only `UDP_FLOW_DATAGRAM` is to be used, never `UDP_MAPPING_DATAGRAM`.
+This would remove the potential for ambiguity, but costs in compatibility as it's no longer possible to implement non-port-specific filtering.
# Risks
-## Performance and compatibility regressions
+Any proposed UDP support involves significant risks to user privacy and software maintainability.
+We will try to elaborate some of these risks here, so they can be compared against the expected benefits.
+
+## Behavior regressions
+
+In some applications it is possible that Tor's implementation of a UDP compatibility layer will cause a regression in the ultimate level of performance or security.
+
+Performance regressions can occur accidentally due to bugs or compatibility glitches.
+They may also occur for more fundamental reasons of protocol layering.
+For example, the redundant error correction layers when tunneling QUIC over TCP.
+These performance degradations are expected to be minor, but there's some unavoidable risk.
+
+We may mitigate the risk of severe performance or compatibility regressions by giving users a way to toggle UDP support per-application.
+
+Privacy and security regressions have more severe consequences and they can be much harder to detect.
+There are straightforward downgrades, like WebRTC apps that give up TURN-over-TLS for plaintext TURN-over-UDP.
+More subtly, the act of centralizing connection establishment traffic in Tor exit nodes can make users an easier target for other attacks.
+
+## Bandwidth usage
-TODO: By implementing UDP at all, we open ourselves up to bugs and performance regressions vs any existing TCP compatibility support present in applications.
+We expect an increase in overall exit bandwidth requirements due to peer-to-peer file sharing applications.
-## Additional malicious traffic
+Current users attempting to use BitTorrent over Tor are hampered by the lack of UDP compatibility. Interoperability with common file-sharing peers would make Tor more appealing to users with a large and sustained appetite for anonymized bandwidth.
-TODO: Various kinds of traffic we want to avoid
+## Malicious traffic
+
+We expect UDP compatibility in Tor will give malicious actors additional opportunities to transmit unwanted traffic.
+
+- Amplification attacks against arbitrary targets
+
+ These are possible only in limited circumstances where the protocol allows an arbitrary reply address, like SIP.
+ The peer is often at fault for having an overly permissive configuration.
+ Nevertheless, any of these *easy* amplification targets can be exploited from Tor with little consequence, creating a nuisance for the ultimate target and for exit operators.
+
+- Amplification attacks against exit relay
+
+ An amplification peer which doesn't allow arbitrary destinations can still be used to attack the exit relay itself or other users of that relay.
+ This is essentially the same attack that is possible against any NAT the attacker is behind.
-- Amplification attacks against arbitrary target (protocol contains reply addr, like SIP)
-- Amplification attacks against exit relay (similar to attacks against NAT)
- Malicious fragmented traffic
-- Excessive sends to a host that has never replied (DoS)
-- Excessive number of peers (makes port scanning too much easier)
-See also RFC 7675, on the concept of "Send consent".
+ If we allow sending large UDP datagrams over IPv4 without the *Don't Fragment* flag set, we allow attackers to generate fragmented IP datagrams.
+ This is not itself a problem, but it has historically been a common source of inconsistencies in firewall behavior.
+
+- Excessive sends to an uninterested peer
+
+ Whereas TCP mandates a successful handshake, UDP will happily send unlimited amounts of traffic to a peer that has never responded.
+ To prevent denial of service attacks we have an opportunity and perhaps a responsibility to define our supported subset of UDP to include true bidirectional traffic but exclude continued sends to peers who do not respond.
+
+ See also [RFC7675](https://www.rfc-editor.org/rfc/rfc7675.html) and STUN's concept of "Send consent".
+
+- Excessive number of peers
+
+ We may want to place conservative limits on the maximum number of peers per mapping or per circuit, in order to make bulk scanning of UDP port space less convenient.
-## Additional high-bandwidth application traffic
+ The limit does need to be on peers, not stream IDs as we presently do for TCP.
-TODO: Analyze bittorrent and friends
+ In this proposal stream IDs are not necessarily meaningful except as a representational choice made by clients.
+ Strategies like the *per-mapping* stream assignment like we have for TCP.
-## Additional exposure to malicious exit nodes
+## Local port usage
-TODO: There may be new cases where an exit node could modify or inspect UDP-tunneled traffic that would otherwise have been unavailable, e.g. by bypassing tor or by using a secure TURN-over-TLS tunnel.
+Exit routers will have a limited number of local UDP ports. In the most constrained scenario, an exit may have a single IP with 16384 or fewer ephemeral ports available. These ports could each be allocated by one client for an unbounded amount of exclusive use.
+
+In order to enforce high levels of isolation between different subsequent users of the same local UDP port, we may wish to enforce a delay between allocations during which nobody may own the port. Effective isolation requires this timer to be greater than any timer we expect to encounter on a peer or a NAT. In RFC4787's recommendations a NAT's mapping timer must be longer than 2 minutes. Our timer should ideally be *much* longer than 2 minutes.
+
+An attacker who allocates ports for only this minimum duration of 2 minutes would need to send 136.5 requests per second to achieve sustained use of all available ports. With multiple simultaneous clients this could easily be done while bypassing per-circuit rate limiting.
+
+The expanded definition of "Port overlapping" from [RFC7857 section 3](https://datatracker.ietf.org/doc/html/rfc7857#section-3), may form at least a partial mitigation:
+
+ This document clarifies that this port overlapping behavior may be extended to connections originating from different internal source IP addresses and ports as long as their destinations are different.
+
+This gives us an opportunity for a vast reduction in the number of required ports and file descriptors. Practically, though, it does require us to make a guess about which potential peers one source port may communicate with.
+
+Our UDP implementation will need to choose a port assignment based on knowledge of only the first peer the app is sending to.
+Heuristically, we can make this work. The first peer in practice will be less unique than subsequent peers. Applications will contact centralized services before contacting peers. This ordering is necessary in the general case of ICE-like connection establishment.
## Additional risks to anonymity
@@ -460,9 +592,3 @@ This goal may be at odds with our privacy requirements. At minimum, a pool of ma
TODO: Seems likely applications do often leak enough information through the plaintext portions of their UDP traffic in order to facilitate fingerprinting, I should look closer at this and confirm or deny.
Even if the application traffic itself is fingerprint-resistant, this is easily combined with the above traffic injection attacks in order to mark specific communicating peers.
-
-## Port allocation risks
-
-TODO: Without a way to limit port allocations, exits can quickly run out.
-
-TODO: Incorporate a mitigation in the form of Port Overlapping