Draft: draft-ietf-masque-connect-udp

Commentary: This PR is purely editorial and was made by cherry - picking most of great work in .
This document describes how to proxy UDP over HTTP.  Similar to how
the CONNECT method allows proxying TCP over HTTP, this document
defines a new mechanism to proxy UDP.  When using HTTP/2 or HTTP/3,
it uses Extended CONNECT; when using HTTP/1.1, it uses Upgrade.
This document describes how to proxy UDP over HTTP.  Similar to how
the CONNECT method (see HTTP) allows proxying TCP TCP over HTTP, this
document defines a new mechanism to proxy UDP UDP.
UDP Proxying supports all versions of HTTP and uses HTTP Datagrams
HTTP-DGRAM.  When using HTTP/2 H2 or HTTP/3 H3, UDP proxying uses
HTTP Extended CONNECT as described in EXT-CONNECT2 and EXT-CONNECT3.
When using HTTP/1.x H1, UDP proxying uses HTTP Upgrade as defined in
HTTP.
This document describes how to proxy UDP in HTTP, similar to how the
HTTP CONNECT method allows proxying TCP in HTTP.  More specifically,
this document defines a protocol that allows HTTP clients to create a
tunnel for UDP communications through an HTTP server that acts as a
proxy.
While HTTP provides the CONNECT method (see HTTP) for creating a TCP
TCP tunnel to a proxy, it lacks a method for doing so for UDP UDP
traffic.
This document describes a protocol for tunnelling UDP to a server
acting as a UDP-specific proxy over HTTP.  UDP tunnels are commonly
used to create an end-to-end virtual connection, which can then be
secured using QUIC QUIC or another protocol running over UDP.  Unlike
CONNECT, the UDP proxy itself is identified with an absolute URL
containing the traffic's destination.  Clients generate those URLs
using a URI Template TEMPLATE, as described in client-config.
This protocol supports all versions of HTTP by using HTTP Datagrams
HTTP-DGRAM.  When using HTTP/2 H2 or HTTP/3 H3, it uses HTTP Extended
CONNECT as described in EXT-CONNECT2 and EXT-CONNECT3.  When using
HTTP/1.x H1, it uses HTTP Upgrade as defined in HTTP.
In this document, we use the term "proxy" to refer to the HTTP server
that acts upon the client's UDP proxying request to open a UDP socket
to a target server, and generates the response to this request.  If
there are HTTP intermediaries (as defined in HTTP) between the client
and the proxy, those are referred to as "intermediaries" in this
document.
In this document, we use the term "UDP proxy" to refer to the HTTP
server that acts upon the client's UDP tunnelling request to open a
UDP socket to a target server, and generates the response to this
request.  If there are HTTP intermediaries (as defined in HTTP)
between the client and the UDP proxy, those are referred to as
"intermediaries" in this document.
If any of the requirements above are not met by a URI Template, the
client MUST reject its configuration and fail the request without
sending it to the proxy.
Since the original HTTP CONNECT method allowed conveying the target
host and port but not the scheme, proxy authority, path, nor query,
there exist proxy configuration interfaces that only allow the user
to configure the proxy host and the proxy port.  Client
implementations of this specification that are constrained by such
limitations MAY attempt to access UDP Proxying capabilities using the
default template, which is defined as:
"https://$PROXY_HOST:$PROXY_PORT/.well-known/masque/
udp/{target_host}/{target_port}/" where $PROXY_HOST and $PROXY_PORT
are the configured host and port of the proxy respectively.  Proxy
deployments SHOULD offer service at this location if they need to
interoperate with such clients.
If any of the requirements above are not met by a URI Template, the
client MUST reject its configuration and fail the request without
sending it to the UDP proxy.
Since the original HTTP CONNECT method allowed conveying the target
host and port but not the scheme, proxy authority, path, nor query,
there exist proxy configuration interfaces that only allow the user
to configure the proxy host and the proxy port.  Client
implementations of this specification that are constrained by such
limitations MAY attempt to access UDP proxying capabilities using the
default template, which is defined as:
"https://$PROXY_HOST:$PROXY_PORT/.well-known/masque/
udp/{target_host}/{target_port}/" where $PROXY_HOST and $PROXY_PORT
are the configured host and port of the UDP proxy respectively.  UDP
proxy deployments SHOULD offer service at this location if they need
to interoperate with such clients.
This document defines the "connect-udp" HTTP Upgrade Token. "connect-
udp" uses the Capsule Protocol as defined in HTTP-DGRAM.  The format
of HTTP Datagrams is defined in format.
Clients issue requests containing a "connect-udp" upgrade token to
initiate a UDP tunnel associated with a single HTTP stream.  Tunnels
are commonly used to create an end-to-end virtual connection, which
can then be secured using QUIC QUIC or another protocol running over
UDP.  The target of the tunnel is indicated by the client to the
proxy via the "target_host" and "target_port" variables of the URI
Template (see client-config).  If the request is successful, the
proxy commits to converting received HTTP Datagrams into UDP packets
and vice versa until the tunnel is closed.
To allow negotiation of a tunnel for UDP over HTTP, this document
defines the "connect-udp" HTTP Upgrade Token.  The resulting UDP
tunnels use the Capsule Protocol (see HTTP-DGRAM) with HTTP Datagram
in the format defined in format.
To initiate a UDP tunnel associated with a single HTTP stream,
clients issue a request containing the "connect-udp" upgrade token.
The target of the tunnel is indicated by the client to the UDP proxy
via the "target_host" and "target_port" variables of the URI
Template, see client-config.  If the request is successful, the UDP
proxy commits to converting received HTTP Datagrams into UDP packets
and vice versa until the tunnel is closed.
Upon receiving a UDP proxying request, the recipient proxy extracts
the "target_host" and "target_port" variables from the URI it has
reconstructed from the request headers, and establishes a tunnel by
directly opening a UDP socket to the requested target.
Unlike TCP, UDP is connection-less.  The proxy that opens the UDP
socket has no way of knowing whether the destination is reachable.
Therefore it needs to respond to the request without waiting for a
packet from the target.  However, if the target_host is a DNS name,
the proxy MUST perform DNS resolution before replying to the HTTP
request.  If errors occur during this process (for example, a DNS
resolution failure), the proxy MUST fail the request and SHOULD send
details using the Proxy-Status header field PROXY-STATUS.
Proxies can use connected UDP sockets if their operating system
supports them, as that allows the proxy to rely on the kernel to only
send it UDP packets that match the correct 5-tuple.  If the proxy
uses a non-connected socket, it MUST validate the IP source address
and UDP source port on received packets to ensure they match the
client's request.  Packets that do not match MUST be discarded by the
proxy.
The lifetime of the socket is tied to the request stream.  The proxy
MUST keep the socket open while the request stream is open.  If a
proxy is notified by its operating system that its socket is no
longer usable (for example, this can happen when an ICMP "Destination
Unreachable" message is received, see ICMP6), it MUST close the
request stream.  Proxies MAY choose to close sockets due to a period
of inactivity, but they MUST close the request stream when closing
the socket.  Proxies that close sockets after a period of inactivity
SHOULD NOT use a period lower than two minutes, see BEHAVE.
A successful response (as defined in resp1 and resp23) indicates that
the proxy has opened a socket to the requested target and is willing
to proxy UDP payloads.  Any response other than a successful response
indicates that the request has failed, and the client MUST therefore
abort the request.
Proxies MUST NOT introduce fragmentation at the IP layer when
forwarding HTTP Datagrams onto a UDP socket.  In IPv4, the Don't
Fragment (DF) bit MUST be set if possible, to prevent fragmentation
on the path.  Future extensions MAY remove these requirements.
Upon receiving a UDP proxying request, the recipient UDP proxy
extracts the "target_host" and "target_port" variables from the URI
it has reconstructed from the request headers, and establishes a
tunnel by directly opening a UDP socket to the requested target.
Unlike TCP, UDP is connection-less.  The UDP proxy that opens the UDP
socket has no way of knowing whether the destination is reachable.
Therefore it needs to respond to the request without waiting for a
packet from the target.  However, if the target_host is a DNS name,
the UDP proxy MUST perform DNS resolution before replying to the HTTP
request.  If errors occur during this process (for example, a DNS
resolution failure), the UDP proxy MUST fail the request and SHOULD
send details using an appropriate Proxy-Status header field PROXY-
STATUS.
UDP proxies can use connected UDP sockets if their operating system
supports them, as that allows the UDP proxy to rely on the kernel to
only send it UDP packets that match the correct 5-tuple.  If the UDP
proxy uses a non-connected socket, it MUST validate the IP source
address and UDP source port on received packets to ensure they match
the client's request.  Packets that do not match MUST be discarded by
the UDP proxy.
The lifetime of the socket is tied to the request stream.  The UDP
proxy MUST keep the socket open while the request stream is open.  If
a UDP proxy is notified by its operating system that its socket is no
longer usable (for example, this can happen when an ICMP "Destination
Unreachable" message is received, see ICMP6), it MUST close the
request stream.  UDP proxies MAY choose to close sockets due to a
period of inactivity, but they MUST close the request stream when
closing the socket.  UDP proxies that close sockets after a period of
inactivity SHOULD NOT use a period lower than two minutes, see
BEHAVE.
A successful response (as defined in resp1 and resp23) indicates that
the UDP proxy has opened a socket to the requested target and is
willing to proxy UDP payloads.  Any response other than a successful
response indicates that the request has failed, and the client MUST
therefore abort the request.
UDP proxies MUST NOT introduce fragmentation at the IP layer when
forwarding HTTP Datagrams onto a UDP socket.  In IPv4, the Don't
Fragment (DF) bit MUST be set if possible, to prevent fragmentation
on the path.  Future extensions MAY remove these requirements.
the request SHALL include a single Host header field containing
the origin of the proxy.
the request SHALL include a single Host header field containing
the origin of the UDP proxy.
The ":authority" pseudo-header field SHALL contain the authority
of the proxy.
The ":authority" pseudo-header field SHALL contain the authority
of the UDP proxy.
The proxy SHALL indicate a successful response by replying with any
2xx (Successful) HTTP status code, without any Transfer-Encoding or
Content-Length header fields.
The UDP proxy SHALL indicate a successful response by replying with
any 2xx (Successful) HTTP status code, without any Transfer-Encoding
or Content-Length header fields.
In order to allow implementations to support multiple draft versions
of this specification during its development, we introduce the
"connect-udp-version" header field.  When sent by the client, it
contains a list of draft numbers supported by the client (e.g.,
"connect-udp-version: 0, 2").  When sent by the proxy, it contains a
single draft number selected by the proxy from the list provided by
the client (e.g., "connect-udp-version: 2").  Sending this header
field is RECOMMENDED but not required.  The "connect-udp-version"
header field is a List Structured Field, see STRUCT-FIELD.  Each list
member MUST be an Integer.
In order to allow implementations to support multiple draft versions
of this specification during its development, we introduce the
"connect-udp-version" header field.  When sent by the client, it
contains a list of draft numbers supported by the client (e.g.,
"connect-udp-version: 0, 2").  When sent by the UDP proxy, it
contains a single draft number selected by the UDP proxy from the
list provided by the client (e.g., "connect-udp-version: 2").
Sending this header field is RECOMMENDED but not required.  The
"connect-udp-version" header field is a List Structured Field, see
STRUCT-FIELD.  Each list member MUST be an Integer.
context ID: it can be used by any client or proxy, independent of
which endpoint initially allocated it.
context ID: it can be used by any client or UDP proxy, independent of
which endpoint initially allocated it.
Clients MAY optimistically start sending UDP packets in HTTP
Datagrams before receiving the response to its UDP proxying request.
However, implementors should note that such proxied packets may not
be processed by the proxy if it responds to the request with a
failure, or if the proxied packets are received by the proxy before
the request.
By virtue of the definition of the UDP header UDP, it is not possible
to encode UDP payloads longer than 65527 bytes.  Therefore, endpoints
MUST NOT send HTTP Datagrams with a Payload field longer than 65527
using Context ID zero.  An endpoint that receives a DATAGRAM capsule
using Context ID zero whose Payload field is longer than 65527 MUST
abort the stream.  If a proxy knows it can only send out UDP packets
of a certain length due to its underlying link MTU, it SHOULD discard
incoming DATAGRAM capsules using Context ID zero whose Payload field
is longer than that limit without buffering the capsule contents.
Proxies SHOULD strive to avoid increasing burstiness of UDP traffic:
they SHOULD NOT queue packets in order to increase batching.
Clients MAY optimistically start sending UDP packets in HTTP
Datagrams before receiving the response to its UDP proxying request.
However, implementors should note that such proxied packets may not
be processed by the UDP proxy if it responds to the request with a
failure, or if the proxied packets are received by the UDP proxy
before the request.
By virtue of the definition of the UDP header UDP, it is not possible
to encode UDP payloads longer than 65527 bytes.  Therefore, endpoints
MUST NOT send HTTP Datagrams with a Payload field longer than 65527
using Context ID zero.  An endpoint that receives a DATAGRAM capsule
using Context ID zero whose Payload field is longer than 65527 MUST
abort the stream.  If a UDP proxy knows it can only send out UDP
packets of a certain length due to its underlying link MTU, it SHOULD
discard incoming DATAGRAM capsules using Context ID zero whose
Payload field is longer than that limit without buffering the capsule
contents.
UDP proxies SHOULD strive to avoid increasing burstiness of UDP
traffic: they SHOULD NOT queue packets in order to increase batching.
If a client or proxy with a connection containing a UDP proxying
request stream disables congestion control, it MUST NOT signal ECN
support on that connection.  That is, it MUST mark all IP headers
with the Not-ECT codepoint.  It MAY continue to report ECN feedback
via ACK_ECN frames, as the peer may not have disabled congestion
control.
If a client or UDP proxy with a connection containing a UDP proxying
request stream disables congestion control, it MUST NOT signal ECN
support on that connection.  That is, it MUST mark all IP headers
with the Not-ECT codepoint.  It MAY continue to report ECN feedback
via ACK_ECN frames, as the peer may not have disabled congestion
control.
When using HTTP/3 with the QUIC Datagram extension DGRAM, UDP
payloads are transmitted in QUIC DATAGRAM frames.  Since those cannot
be fragmented, they can only carry payloads up to a given length
determined by the QUIC connection configuration and the path MTU.  If
a proxy is using QUIC DATAGRAM frames and it receives a UDP payload
from the target that will not fit inside a QUIC DATAGRAM frame, the
proxy SHOULD NOT send the UDP payload in a DATAGRAM capsule, as that
defeats the end-to-end unreliability characteristic that methods such
as Datagram Packetization Layer Path MTU Discovery (DPLPMTUD) depend
on DPLPMTUD.  In this scenario, the proxy SHOULD drop the UDP payload
and send an ICMP "Packet Too Big" message to the target, see ICMP6.
When using HTTP/3 with the QUIC Datagram extension DGRAM, UDP
payloads are transmitted in QUIC DATAGRAM frames.  Since those cannot
be fragmented, they can only carry payloads up to a given length
determined by the QUIC connection configuration and the path MTU.  If
a UDP proxy is using QUIC DATAGRAM frames and it receives a UDP
payload from the target that will not fit inside a QUIC DATAGRAM
frame, the UDP proxy SHOULD NOT send the UDP payload in a DATAGRAM
capsule, as that defeats the end-to-end unreliability characteristic
that methods such as Datagram Packetization Layer Path MTU Discovery
(DPLPMTUD) depend on DPLPMTUD.  In this scenario, the UDP proxy
SHOULD drop the UDP payload and send an ICMP "Packet Too Big" message
to the target, see ICMP6.
Note that UDP proxying clients do not have the ability in this
specification to control the ECN codepoints on UDP packets the proxy
sends to the target, nor can proxies communicate the markings of each
UDP packet from target to proxy.
A UDP proxy MUST ignore ECN bits in the IP header of UDP packets
received from the target, and MUST set the ECN bits to Not-ECT on UDP
packets it sends to the target.  These do not relate to the ECN
markings of packets sent between client and proxy in any way.
There are significant risks in allowing arbitrary clients to
establish a tunnel to arbitrary targets, as that could allow bad
actors to send traffic and have it attributed to the proxy.  Proxies
that support UDP proxying ought to restrict its use to authenticated
users.
Because the CONNECT method creates a TCP connection to the target,
the target has to indicate its willingness to accept TCP connections
by responding with a TCP SYN-ACK before the proxy can send it
application data.  UDP doesn't have this property, so a UDP proxy
could send more data to an unwilling target than a CONNECT proxy.
However, in practice denial of service attacks target open TCP ports
so the TCP SYN-ACK does not offer much protection in real scenarios.
While a proxy could potentially limit the number of UDP packets it is
willing to forward until it has observed a response from the target,
that is unlikely to provide any protection against denial of service
attacks because such attacks target open UDP ports where the protocol
running over UDP would respond, and that would be interpreted as
willingness to accept UDP by the proxy.
Note that UDP proxying clients do not have the ability in this
specification to control the ECN codepoints on UDP packets the UDP
proxy sends to the target, nor can UDP proxies communicate the
markings of each UDP packet from target to UDP proxy.
A UDP proxy MUST ignore ECN bits in the IP header of UDP packets
received from the target, and MUST set the ECN bits to Not-ECT on UDP
packets it sends to the target.  These do not relate to the ECN
markings of packets sent between client and UDP proxy in any way.
There are significant risks in allowing arbitrary clients to
establish a tunnel to arbitrary targets, as that could allow bad
actors to send traffic and have it attributed to the UDP proxy.  HTTP
servers that support UDP proxying ought to restrict its use to
authenticated users.
Because the CONNECT method creates a TCP connection to the target,
the target has to indicate its willingness to accept TCP connections
by responding with a TCP SYN-ACK before the CONNECT proxy can send it
application data.  UDP doesn't have this property, so a UDP proxy
could send more data to an unwilling target than a CONNECT proxy.
However, in practice denial of service attacks target open TCP ports
so the TCP SYN-ACK does not offer much protection in real scenarios.
While a UDP proxy could potentially limit the number of UDP packets
it is willing to forward until it has observed a response from the
target, that is unlikely to provide any protection against denial of
service attacks because such attacks target open UDP ports where the
protocol running over UDP would respond, and that would be
interpreted as willingness to accept UDP by the UDP proxy.