RFC 9484

Proxying IP in HTTP

๐Ÿ“… published October 2023, from ietf

Proxying IP in HTTP ๐ŸŽ€

okay bestie, this is RFC 9484, called Proxying IP in HTTP ๐Ÿ’– It came out in October 2023 and it's a Standards Track document, which means it's an actual Internet standard and not just a vibe. โœจ It was written by T. Pauly, Ed. (Apple Inc.), D. Schinazi (Google LLC), A. Chernyakhovsky (Google LLC), M. Kรผhlewind (Ericsson), and M. Westerlund (Ericsson) ๐Ÿ‘‘ The ISSN is 2070-1721. It updates RFC 9298, so keep that in mind because it's a whole plot point ๐Ÿฆ‹

Abstract ๐ŸŒธ

this doc is about proxying IP packets over HTTP ๐Ÿซถ It's basically like UDP proxying in HTTP, except it lets you send arbitrary IP packets, which is so much more range ๐Ÿ˜ญ The main thing it defines is how an HTTP client can make an IP tunnel through an HTTP server that acts as an IP proxy. And yes, it updates RFC 9298 ๐Ÿ’…

Status of This Memo โœจ

this is an Internet Standards Track document ๐ŸŽ€

it's a product of the Internet Engineering Task Force (IETF) ๐ŸŒˆ It represents the consensus of the IETF community, it got public review, and the Internet Engineering Steering Group (IESG) approved it for publication ๐Ÿ‘‘ If you want more info on what Internet Standards are, check Section 2 of RFC 7841.

info about the current status of this doc, any errata, and how to give feedback lives at https://www.rfc-editor.org/info/rfc9484 ๐Ÿฆ‹

Copyright (c) 2023 IETF Trust and the persons identified as the document authors. All rights reserved. ๐Ÿ’…

this document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stand on the date it was published ๐Ÿ“œ Please actually read those, bestie, because they explain your rights and restrictions here. Also, any Code Components pulled out of this document must include Revised BSD License text, as described in Section 4.e of the Trust Legal Provisions. They come without warranty, as described in the Revised BSD License ๐Ÿซถ

Table of Contents ๐Ÿงš

1.  Introduction
2.  Conventions and Definitions
3.  Configuration of Clients
4.  Tunnelling IP over HTTP
  4.1.  IP Proxy Handling
  4.2.  HTTP/1.1 Request
  4.3.  HTTP/1.1 Response
  4.4.  HTTP/2 and HTTP/3 Requests
  4.5.  HTTP/2 and HTTP/3 Responses
  4.6.  Limiting Request Scope
  4.7.  Capsules
    4.7.1.  ADDRESS_ASSIGN Capsule
    4.7.2.  ADDRESS_REQUEST Capsule
    4.7.3.  ROUTE_ADVERTISEMENT Capsule
  4.8.  IPv6 Extension Headers
5.  Context Identifiers
6.  HTTP Datagram Payload Format
7.  IP Packet Handling
  7.1.  Link Operation
  7.2.  Routing Operation
    7.2.1.  Error Signalling
8.  Examples
  8.1.  Remote Access VPN
  8.2.  Site-to-Site VPN
  8.3.  IP Flow Forwarding
  8.4.  Proxied Connection Racing
9.  Extensibility Considerations
10. Performance Considerations
  10.1.  MTU Considerations
  10.2.  ECN Considerations
  10.3.  Differentiated Services Considerations
11. Security Considerations
12. IANA Considerations
  12.1.  HTTP Upgrade Token Registration
  12.2.  MASQUE URI Suffixes Registry Creation
  12.3.  Updates to masque Well-Known URI Registration
  12.4.  HTTP Capsule Types Registrations
13. References
  13.1.  Normative References
  13.2.  Informative References
Acknowledgments
Authors' Addresses

1. Introduction ๐ŸŒท

okay so HTTP already has the CONNECT method (see Section 9.3.6 of [HTTP]) for making a TCP [TCP] tunnel to a destination, and there's a similar mechanism for UDP [CONNECT-UDP] ๐Ÿฆ‹ The tea is that neither of them can tunnel other IP protocols [IANA-PN], and they also can't carry fields from the IP header. Main character problem ๐Ÿ˜ญ

so this document describes a protocol for tunneling IP through an HTTP server that acts as an IP-specific proxy over HTTP ๐Ÿš€ It's giving versatile, because it works for remote access VPNs, site-to-site VPNs, secure point-to-point communication, or general-purpose packet tunneling ๐Ÿ’œ

IP proxying works a lot like UDP proxying [CONNECT-UDP]. The proxy gets identified with an absolute URL, which can optionally include the traffic's destination. Clients make these URLs with a URI Template [TEMPLATE], which Section 3 covers ๐ŸŽ€

this protocol works with every existing version of HTTP by using HTTP Datagrams [HTTP-DGRAM] โœจ With HTTP/2 [HTTP/2] or HTTP/3 [HTTP/3], it uses HTTP Extended CONNECT, as described in [EXT-CONNECT2] and [EXT-CONNECT3]. With HTTP/1.x [HTTP/1.1], it uses HTTP Upgrade, as defined in Section 7.8 of [HTTP] ๐Ÿซถ

also, this document updates [CONNECT-UDP] by changing the "masque" well-known URI. See Section 12.3 for the deets ๐ŸŒˆ

2. Conventions and Definitions ๐Ÿ“

the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. ๐Ÿ’… So those capital words are the real rules, and the lowercase ones are just regular English.

in this document, the "IP proxy" is the HTTP server that answers the IP proxying request ๐Ÿงš The "client" is meant in the HTTP sense, so the client is the one who builds the IP proxying request. If there are HTTP intermediaries (as defined in Section 3.7 of [HTTP]) between the client and the IP proxy, the doc calls those "intermediaries" ๐Ÿฆ‹ And "IP proxying endpoints" means both the client and the IP proxy together.

this doc uses terminology from [QUIC]. When it defines protocol types, the definition format uses the notation from Section 1.3 of [QUIC]. It also uses the variable-length integer encoding from Section 16 of [QUIC]. Variable-length integer values don't have to be encoded in the minimum number of bytes needed, which is a very chill rule ๐ŸŒธ

one more thing: when the HTTP version in use doesn't support multiplexing streams (like HTTP/1.1), any mention of "stream" in this document means the entire connection ๐Ÿซถ

3. Configuration of Clients ๐ŸŽ€

clients get set up to use IP proxying over HTTP through a URI Template [TEMPLATE] ๐Ÿ’– The URI Template MAY contain two variables: "target" and "ipproto". See Section 4.6 for those. When you're designing the template, you need to think about whether each variable is optional, so that the variable either identifies itself or can be left out of the syntax. That's the kind of thing that'll save you a headache later ๐Ÿ˜ญ

here are some examples:

https://example.org/.well-known/masque/ip/{target}/{ipproto}/
https://proxy.example.org:4443/masque/ip?t={target}&i={ipproto}
https://proxy.example.org:4443/masque/ip{?target,ipproto}
https://masque.example.org/?user=bob

Figure 1: URI Template Examples

ok now the rules for the URI Template, which are the important part ๐Ÿ’…

  • the URI Template MUST be a level 3 template or lower ๐ŸŒˆ
  • the URI Template MUST be in absolute form, and it MUST include non-empty scheme, authority, and path components ๐Ÿ’œ
  • the path component of the URI Template MUST start with a slash / ๐Ÿฆ‹
  • all template variables MUST be within the path or query components of the URI ๐Ÿงš
  • the URI Template MAY contain the two variables "target" and "ipproto", and it MAY contain other variables too. If the "target" or "ipproto" variables are included, their values MUST NOT be empty. Clients can instead use * to indicate a wildcard or no-preference value. See Section 4.6 ๐ŸŒธ
  • the URI Template MUST NOT contain any non-ASCII Unicode characters, and it MUST only contain ASCII characters in the range 0x21-0x7E inclusive. Percent-encoding is allowed here, see Section 2.1 of [URI] ๐Ÿ“
  • the URI Template MUST NOT use Reserved Expansion (the + operator), Fragment Expansion (the # operator), Label Expansion with Dot-Prefix, Path Segment Expansion with Slash-Prefix, nor Path-Style Parameter Expansion with Semicolon-Prefix ๐Ÿซถ

clients SHOULD validate the requirements above, but they MAY use a general-purpose URI Template implementation that doesn't do this specific validation. If a client notices that any of the requirements above aren't met by a URI Template, the client MUST reject its configuration and abort the request without sending it to the IP proxy ๐Ÿ’€ no sending bad templates into the void, periodt.

as with UDP proxying, some client configurations only let the user set the proxy host and port. Clients with those limits MAY try to reach IP proxying through the default template, which is https://$PROXY_HOST:$PROXY_PORT/.well-known/masque/ip/{target}/{ipproto}/. Here $PROXY_HOST and $PROXY_PORT are the configured host and port of the IP proxy ๐ŸŽ€ IP proxy deployments SHOULD offer service at that location if they need to interoperate with those clients ๐ŸŒˆ

4. Tunnelling IP over HTTP โœจ

to set up negotiation for an IP tunnel over HTTP, this document defines the connect-ip HTTP upgrade token ๐Ÿฆ‹ The resulting IP tunnels use the Capsule Protocol (see Section 3.2 of [HTTP-DGRAM]) with HTTP Datagrams in the format defined in Section 6. It's giving layers, and I'm obsessed ๐Ÿ˜ญ

to start an IP tunnel tied to a single HTTP stream, a client sends a request that contains the connect-ip upgrade token ๐Ÿ’–

when the client sends its IP proxying request, it SHALL perform URI Template expansion to figure out the path and query of that request. See Section 3 ๐Ÿ’…

because of how the Capsule Protocol is defined (see Section 3.2 of [HTTP-DGRAM]), IP proxying requests don't carry any message content. Successful IP proxying responses don't carry message content either ๐Ÿซถ

IP proxying over HTTP MUST be operated over TLS or QUIC encryption, or another equivalent encryption protocol, so you get confidentiality, integrity, and authentication. This one is non-negotiable, bestie ๐Ÿ”ฅ

4.1. IP Proxy Handling โœจ

okay so when an IP proxying request lands, the recipient has two possible moves ๐Ÿ’…

  • If the recipient is configured to use another HTTP server, it will act as an intermediary by forwarding the request to the other HTTP server. Note that such intermediaries may need to re-encode the request if they forward it using a version of HTTP that is different from the one used to receive it, as the request encoding differs by version (see below).
  • Otherwise, the recipient will act as an IP proxy. The IP proxy can choose to reject the IP proxying request. Otherwise, it extracts the optional "target" and "ipproto" variables from the URI it has reconstructed from the request headers, decodes their percent-encoding, and establishes an IP tunnel.

IP proxies MUST validate whether the decoded "target" and "ipproto" variables meet the requirements in Section 4.6. If they do not, the IP proxy MUST treat the request as malformed; see Section 8.1.1 of [HTTP/2] and Section 4.1.2 of [HTTP/3]. If the "target" variable is a DNS name, the IP proxy MUST perform DNS resolution (to obtain the corresponding IPv4 and/or IPv6 addresses via A and/or AAAA records) before replying to the HTTP request. If errors occur during this process, the IP proxy MUST reject the request and SHOULD send details using an appropriate Proxy-Status header field [PROXY-STATUS]. For example, if DNS resolution returns an error, the proxy can use the dns_error proxy error type from Section 2.3.2 of [PROXY-STATUS]. ๐Ÿ™ˆ

The lifetime of the IP forwarding tunnel is tied to the IP proxying request stream. The IP proxy MUST maintain all IP address and route assignments associated with the IP forwarding tunnel while the request stream is open. IP proxies MAY choose to tear down the tunnel due to a period of inactivity, but they MUST close the request stream when doing so.

A successful IP proxying response (as defined in Sections 4.3 and 4.5) indicates that the IP proxy has established an IP tunnel and is willing to proxy IP payloads. Any response other than a successful IP proxying response indicates that the request has failed; thus, the client MUST abort the request.

Along with a successful IP proxying response, the IP proxy can send capsules to assign addresses and advertise routes to the client (Section 4.7). The client can also assign addresses and advertise routes to the IP proxy for network-to-network routing. network-to-network routing is giving main character energy ๐Ÿฆ‹


4.2. HTTP/1.1 Request ๐ŸŽ€

When using HTTP/1.1 [HTTP/1.1], an IP proxying request will meet the following requirements:

  • The method SHALL be "GET".
  • The request SHALL include a single Host header field containing the host and optional port of the IP proxy.
  • The request SHALL include a Connection header field with value "Upgrade" (note that this requirement is case-insensitive, as per Section 7.6.1 of [HTTP]).
  • The request SHALL include an Upgrade header field with value "connect-ip".

An IP proxying request that does not conform to these restrictions is malformed. The recipient of such a malformed request MUST respond with an error and SHOULD use the 400 (Bad Request) status code. ๐Ÿ’€

For example, if the client is configured with URI Template "https://example.org/.well-known/masque/ip/{target}/{ipproto}/" and wishes to open an IP forwarding tunnel with no target or protocol limitations, it could send the following request:

GET https://example.org/.well-known/masque/ip/*/*/ HTTP/1.1
Host: example.org
Connection: Upgrade
Upgrade: connect-ip
Capsule-Protocol: ?1

Figure 2: Example HTTP/1.1 Request


4.3. HTTP/1.1 Response ๐ŸŒธ

The server indicates a successful IP proxying response by replying with the following requirements:

  • The HTTP status code on the response SHALL be 101 (Switching Protocols).
  • The response SHALL include a Connection header field with value "Upgrade" (note that this requirement is case-insensitive, as per Section 7.6.1 of [HTTP]).
  • The response SHALL include a single Upgrade header field with value "connect-ip".
  • The response SHALL meet the requirements of HTTP responses that start the Capsule Protocol; see Section 3.2 of [HTTP-DGRAM].

If any of these requirements are not met, the client MUST treat this proxying attempt as failed and close the connection.

For example, the server could respond with:

HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: connect-ip
Capsule-Protocol: ?1

Figure 3: Example HTTP/1.1 Response


4.4. HTTP/2 and HTTP/3 Requests ๐Ÿงš

When using HTTP/2 [HTTP/2] or HTTP/3 [HTTP/3], IP proxying requests use HTTP Extended CONNECT. This requires that servers send an HTTP Setting, as specified in [EXT-CONNECT2] and [EXT-CONNECT3], and that requests use HTTP pseudo-header fields with the following requirements:

  • The :method pseudo-header field SHALL be "CONNECT".
  • The :protocol pseudo-header field SHALL be "connect-ip".
  • The :authority pseudo-header field SHALL contain the authority of the IP proxy.
  • The :path and :scheme pseudo-header fields SHALL NOT be empty. Their values SHALL contain the scheme and path from the URI Template after the URI Template expansion process has been completed; see Section 3. Variables in the URI Template can determine the scope of the request, such as requesting full-tunnel IP packet forwarding, or a specific proxied flow; see Section 4.6.

An IP proxying request that does not conform to these restrictions is malformed; see Section 8.1.1 of [HTTP/2] and Section 4.1.2 of [HTTP/3].

For example, if the client is configured with URI Template "https://example.org/.well-known/masque/ip/{target}/{ipproto}/" and wishes to open an IP forwarding tunnel with no target or protocol limitations, it could send the following request:

HEADERS
:method = CONNECT
:protocol = connect-ip
:scheme = https
:path = /.well-known/masque/ip/*/*/
:authority = example.org
capsule-protocol = ?1

Figure 4: Example HTTP/2 or HTTP/3 Request


4.5. HTTP/2 and HTTP/3 Responses ๐Ÿ’–

The server indicates a successful IP proxying response by replying with the following requirements:

  • The HTTP status code on the response SHALL be in the 2xx (Successful) range.
  • The response SHALL meet the requirements of HTTP responses that start the Capsule Protocol; see Section 3.2 of [HTTP-DGRAM].

If any of these requirements are not met, the client MUST treat this proxying attempt as failed and abort the request. As an example, any status code in the 3xx range will be treated as a failure and cause the client to abort the request. ๐Ÿ™…โ€โ™€๏ธ

For example, the server could respond with:

HEADERS
:status = 200
capsule-protocol = ?1

Figure 5: Example HTTP/2 or HTTP/3 Response


4.6. Limiting Request Scope ๐Ÿ”ฅ

Unlike UDP proxying requests, which require specifying a target host, IP proxying requests can allow endpoints to send arbitrary IP packets to any host. The client can choose to restrict a given request to a specific IP prefix or IP protocol by adding parameters to its request. When the IP proxy knows that a request is scoped to a target prefix or protocol, it can leverage this information to optimize its resource allocation; for example, the IP proxy can assign the same public IP address to two IP proxying requests that are scoped to different prefixes and/or different protocols.

The scope of the request is indicated by the client to the IP proxy via the "target" and "ipproto" variables of the URI Template; see Section 3. Both the "target" and "ipproto" variables are optional; if they are not included, they are considered to carry the wildcard value "*".

target: The variable "target" contains a hostname or IP prefix of a specific host to which the client wants to proxy packets. If the "target" variable is not specified or its value is "*", the client is requesting to communicate with any allowable host. "target" supports using DNS names, IPv6 prefixes, and IPv4 prefixes. Note that IPv6 scoped addressing zone identifiers [IPv6-ZONE-ID] are not supported. If the target is an IP prefix (IP address optionally followed by a percent-encoded slash followed by the prefix length in bits), the request will only support a single IP version. If the target is a hostname, the IP proxy is expected to perform DNS resolution to determine which route(s) to advertise to the client. The IP proxy SHOULD send a ROUTE_ADVERTISEMENT capsule that includes routes for all addresses that were resolved for the requested hostname, that are accessible to the IP proxy, and belong to an address family for which the IP proxy also sends an Assigned Address. โœจ

ipproto: The variable "ipproto" contains an Internet Protocol Number; see the defined list in the "Assigned Internet Protocol Numbers" IANA registry [IANA-PN]. If present, it specifies that a client only wants to proxy a specific IP protocol for this request. If the value is "*", or the variable is not included, the client is requesting to use any IP protocol. The IP protocol indicated in the "ipproto" variable represents an allowable next header value carried in IP headers that are directly sent in HTTP Datagrams (the outermost IP headers). ICMP traffic is always allowed, regardless of the value of this field.

Using the terms IPv6address, IPv4address, and reg-name from [URI], the "target" and "ipproto" variables MUST adhere to the format in Figure 6, using notation from [ABNF]. Additionally:

  • If "target" contains an IPv6 literal or prefix, the colons (":") MUST be percent-encoded. For example, if the target host is "2001:db8::42", it will be encoded in the URI as "2001%3Adb8%3A%3A42".
  • If present, the IP prefix length in "target" SHALL be preceded by a percent-encoded slash ("/"): "%2F". The IP prefix length MUST represent a decimal integer between 0 and the length of the IP address in bits, inclusive.
  • If "target" contains an IP prefix and the prefix length is strictly less than the length of the IP address in bits, the lower bits of the IP address that are not covered by the prefix length MUST all be set to 0.
  • "ipproto" MUST represent a decimal integer between 0 and 255 inclusive or the wildcard value "*".
target = IPv6prefix / IPv4prefix / reg-name / "*"
IPv6prefix = IPv6address ["%2F" 1*3DIGIT]
IPv4prefix = IPv4address ["%2F" 1*2DIGIT]
ipproto = 1*3DIGIT / "*"

Figure 6: URI Template Variable Format

IP proxies MAY perform access control using the scoping information provided by the client, i.e., if the client is not authorized to access any of the destinations included in the scope, then the IP proxy can immediately reject the request. this is basically the bouncer at the club, and honestly iconic ๐Ÿ‘‘


4.7. Capsules ๐Ÿฆ‹

This document defines multiple new capsule types that allow endpoints to exchange IP configuration information. Both endpoints MAY send any number of these new capsules. ๐ŸŒˆ

4.7.1. ADDRESS_ASSIGN Capsule

okay so the ADDRESS_ASSIGN capsule (capsule type 0x01) is how an endpoint hands its peer a list of IP addresses or prefixes ๐ŸŽ€ every capsule carries the FULL list of prefixes currently assigned to the receiver, not just the new ones. and any of those addresses can be used as the source address on IP packets the receiver sends ๐Ÿ’…

ADDRESS_ASSIGN Capsule {
  Type (i) = 0x01,
  Length (i),
  Assigned Address (..) ...,
}

Figure 7: ADDRESS_ASSIGN Capsule Format

the capsule holds a sequence of zero or more Assigned Addresses โœจ

Assigned Address {
  Request ID (i),
  IP Version (8),
  IP Address (32..128),
  IP Prefix Length (8),
}

Figure 8: Assigned Address Format

each Assigned Address has these fields ๐Ÿฆ‹

Request ID: Request identifier, encoded as a variable-length integer. if this address assignment is responding to an Address Request (see Section 4.7.2), this field SHALL contain the value from the matching field in that request. otherwise, this field SHALL be zero.

IP Version: IP Version of this address assignment, encoded as an unsigned 8-bit integer. it MUST be either 4 or 6 ๐ŸŒธ

IP Address: Assigned IP address. if the IP Version field is 4, the IP Address field SHALL be 32 bits long. if the IP Version field is 6, the IP Address field SHALL be 128 bits long ๐Ÿ”ฅ

IP Prefix Length: the number of bits in the IP address that define the prefix being assigned, encoded as an unsigned 8-bit integer. this MUST be less than or equal to the length of the IP Address field in bits. if the prefix length equals the length of the IP address, the receiver is allowed to send packets from a single source address. if the prefix length is less than the length of the IP address, the receiver is allowed to send packets from any source address inside the prefix. if the prefix length is strictly less than the length of the IP address in bits, the lower bits of the IP Address field that the prefix length doesn't cover MUST all be set to 0 ๐Ÿงš

if any of the capsule fields are malformed when received, the receiver of the capsule MUST follow the error-handling procedure in Section 3.3 of [HTTP-DGRAM]. ๐Ÿ˜ญ

if an ADDRESS_ASSIGN capsule doesn't include an address that was sent in a previous ADDRESS_ASSIGN capsule, that means the address was removed. an ADDRESS_ASSIGN capsule can also be empty, which means ALL addresses were removed ๐Ÿ’€

in some IP proxying-over-HTTP deployments, an endpoint needs its peer to assign it an address before it knows what source address to put on its own packets. for example, in the remote access VPN case (Section 8.1), the client can't send IP packets until it knows what address to use. in these deployments, the endpoint waiting for an address assignment MUST send an ADDRESS_REQUEST capsule. this isn't needed if the endpoint doesn't need any address assignment, like when it's configured out-of-band with static addresses ๐Ÿซถ

ADDRESS_ASSIGN capsules are usually sent in response to ADDRESS_REQUEST capsules, but endpoints MAY send ADDRESS_ASSIGN capsules unprompted. the main character move is still to wait for the request, but unprompted is allowed ๐Ÿ‘‘

4.7.2. ADDRESS_REQUEST Capsule

the ADDRESS_REQUEST capsule (capsule type 0x02) lets an endpoint ask its peer to assign it IP addresses ๐Ÿ“ it can optionally say which address it would prefer to get.

ADDRESS_REQUEST Capsule {
  Type (i) = 0x02,
  Length (i),
  Requested Address (..) ...,
}

Figure 9: ADDRESS_REQUEST Capsule Format

the ADDRESS_REQUEST capsule holds a sequence of one or more Requested Addresses ๐Ÿ’œ

Requested Address {
  Request ID (i),
  IP Version (8),
  IP Address (32..128),
  IP Prefix Length (8),
}

Figure 10: Requested Address Format

each Requested Address has these fields ๐ŸŒˆ

Request ID: Request identifier, encoded as a variable-length integer. this is the ID of this specific address request. every request from a given endpoint has a different ID. Request IDs MUST NOT be reused by an endpoint and MUST NOT be zero ๐Ÿ™ˆ

IP Version: IP Version of this address request, encoded as an unsigned 8-bit integer. it MUST be either 4 or 6.

IP Address: Requested IP address. if the IP Version field is 4, the IP Address field SHALL be 32 bits long. if the IP Version field is 6, the IP Address field SHALL be 128 bits long.

IP Prefix Length: Length of the requested IP prefix in bits, encoded as an unsigned 8-bit integer. this MUST be less than or equal to the length of the IP Address field in bits. if the prefix length is strictly less than the length of the IP address in bits, the lower bits of the IP Address field that the prefix length doesn't cover MUST all be set to 0 โœจ

if the IP address is all-zero (0.0.0.0 or ::), that means the sender wants an address from that family but has no preference for a specific one ๐Ÿฆ‹ in that case, the prefix length still shows the prefix length the sender prefers.

if any of the capsule fields are malformed when received, the receiver of the capsule MUST follow the error-handling procedure in Section 3.3 of [HTTP-DGRAM]. ๐Ÿ˜ญ

when the ADDRESS_REQUEST capsule comes in, an endpoint SHOULD assign one or more IP addresses to its peer and then reply with an ADDRESS_ASSIGN capsule to let the peer know about the assignment ๐Ÿ”ฅ for each Requested Address, the receiver of the ADDRESS_REQUEST capsule SHALL respond with an Assigned Address that has a matching Request ID. if the requested address was assigned, the IP Address and IP Prefix Length fields in that Assigned Address response SHALL be set to the assigned values. if the requested address was NOT assigned, the IP address SHALL be all-zero and the IP Prefix Length SHALL be the maximum length (0.0.0.0/32 or ::/128) to show that no address was assigned ๐Ÿฅฒ these rejections SHOULD NOT be included in later ADDRESS_ASSIGN capsules. also note that other Assigned Address entries that don't match any Request ID can be in that same ADDRESS_ASSIGN response ๐Ÿงš

if an endpoint gets an ADDRESS_REQUEST capsule with zero Requested Addresses, it MUST abort the IP proxying request stream. no requests, no stream, periodt ๐Ÿ’…

the order of Requested Addresses doesn't mean anything, and the Request ID is just a unique identifier. it doesn't carry any priority or importance ๐ŸŒธ

4.7.3. ROUTE_ADVERTISEMENT Capsule

the ROUTE_ADVERTISEMENT capsule (capsule type 0x03) lets an endpoint tell its peer that it's willing to route traffic to a set of IP address ranges ๐Ÿฆ‹ it basically says "I already have a route to each of these ranges, and if you send me IP packets for them in HTTP Datagrams, I'll forward them along my existing route." any address inside one of those ranges can be used as the destination address on IP packets the receiver of this capsule sends ๐ŸŒˆ

ROUTE_ADVERTISEMENT Capsule {
  Type (i) = 0x03,
  Length (i),
  IP Address Range (..) ...,
}

Figure 11: ROUTE_ADVERTISEMENT Capsule Format

the ROUTE_ADVERTISEMENT capsule holds a sequence of zero or more IP Address Ranges ๐Ÿ’–

IP Address Range {
  IP Version (8),
  Start IP Address (32..128),
  End IP Address (32..128),
  IP Protocol (8),
}

Figure 12: IP Address Range Format

each IP Address Range has these fields ๐ŸŽ€

IP Version: IP Version of this range, encoded as an unsigned 8-bit integer. it MUST be either 4 or 6.

Start IP Address and End IP Address: Inclusive start and end IP addresses of the advertised range. if the IP Version field is 4, these fields SHALL be 32 bits long. if the IP Version field is 6, these fields SHALL be 128 bits long. the Start IP Address MUST be less than or equal to the End IP Address โœจ

IP Protocol: the Internet Protocol Number for traffic that can be sent to this range, encoded as an unsigned 8-bit integer. if the value is 0, ALL protocols are allowed. if the value isn't 0, it stands for an allowable next header value in the IP headers sent directly in HTTP Datagrams (the outermost IP headers). ICMP traffic is always allowed, no matter what this field says ๐Ÿงš

if any of the capsule fields are malformed when received, the receiver of the capsule MUST follow the error-handling procedure in Section 3.3 of [HTTP-DGRAM]. ๐Ÿ˜ญ

when the ROUTE_ADVERTISEMENT capsule comes in, an endpoint MAY update its local state about what its peer is willing to route (subject to local policy), for example by installing entries in a routing table ๐Ÿซถ

each ROUTE_ADVERTISEMENT has the FULL list of address ranges. if multiple ROUTE_ADVERTISEMENT capsules get sent in one direction, each new one supersedes the older ones. so if a range was in a prior capsule but the latest ROUTE_ADVERTISEMENT capsule doesn't include it, the receiver treats that range as withdrawn ๐Ÿ™ˆ

if multiple ranges using the same IP protocol overlap, some routing table implementations might reject them. to prevent overlap, the ranges are ordered. this puts the work on the sender and makes checking things way easier for the receiver ๐Ÿ’… if an IP Address Range A comes before an IP Address Range B in the same ROUTE_ADVERTISEMENT capsule, they MUST follow these requirements:

  • The IP Version of A MUST be less than or equal to the IP Version of B.
  • If the IP Version of A and B are equal, the IP Protocol of A MUST be less than or equal to the IP Protocol of B.
  • If the IP Version and IP Protocol of A and B are both equal, the End IP Address of A MUST be strictly less than the Start IP Address of B.

if an endpoint gets a ROUTE_ADVERTISEMENT capsule that doesn't meet these requirements, it MUST abort the IP proxying request stream ๐Ÿ”ฅ

since setting the IP protocol to zero means ALL protocols are allowed, the requirements above still let two routes overlap when one has its IP protocol set to zero and the other has a non-zero value. endpoints MUST NOT send a ROUTE_ADVERTISEMENT capsule with routes that overlap like that. checking for this is OPTIONAL, but if an endpoint notices the violation, it MUST abort the IP proxying request stream ๐Ÿ˜ญ๐Ÿ‘‘

4.8. IPv6 Extension Headers โœจ

okay so both request scoping (check Section 4.6) and the ROUTE_ADVERTISEMENT capsule (Section 4.7.3) use Internet Protocol Numbers ๐Ÿ”ข. These numbers cover upper layers (Section 2 of [IPv6], with TCP and UDP as examples) AND IPv6 extension headers (Section 4 of [IPv6], with Fragment and Options headers as examples) ๐Ÿ’…. IP proxies MAY reject requests that try to scope to protocol numbers used for extension headers, which is a totally valid choice ๐Ÿ™ˆ.

When implementations that support scoping or routing by Internet Protocol Number receive packets, they MUST walk the chain of extensions to find the outermost non-extension Internet Protocol Number to match against the scoping rule ๐Ÿซถ. Also, the ROUTE_ADVERTISEMENT capsule uses Internet Protocol Number 0 to mean "all protocols are allowed" ๐ŸŒˆ. It does NOT restrict the route to the IPv6 Hop-by-Hop Options header (Section 4.3 of [IPv6]), so don't mix those up, bestie ๐Ÿ’€.


5. Context Identifiers ๐ŸŽ€

the mechanism for proxying IP in HTTP that this document defines lets future extensions swap HTTP Datagrams that carry different semantics from IP payloads ๐Ÿฆ‹. Some of these extensions augment IP payloads with extra data or compress IP header fields, while others exchange data that is completely separate from IP payloads ๐Ÿงš. To make this work, every HTTP Datagram tied to an IP proxying request stream starts with a Context ID field, see Section 6 โœจ.

Context IDs are 62-bit integers (0 to 2^62-1) ๐Ÿ”ฅ. They're encoded as variable-length integers, see Section 16 of [QUIC]. The Context ID value of 0 is reserved for IP payloads, and non-zero values are dynamically allocated ๐Ÿ’œ. Non-zero even-numbered Context IDs are client-allocated, and odd-numbered Context IDs are proxy-allocated ๐Ÿ‘‘.

The Context ID namespace is tied to a given HTTP request, so it's possible for the same numeric Context ID to be allocated at the same time in different requests, potentially with different semantics ๐Ÿ˜ญ. Context IDs MUST NOT be re-allocated within a given HTTP request, but they MAY be allocated in any order ๐Ÿ“. The even/odd allocation rules exist so endpoints don't need to synchronize with each other ๐Ÿซถ. Once a Context ID is allocated, though, those rules stop applying to how it's used: either the client or the IP proxy can use it, no matter which endpoint allocated it โœจ.

Registration is how an endpoint tells its peer what the semantics and format of a given Context ID are ๐Ÿ’…. This document doesn't define how registration happens. Future extensions MAY use HTTP header fields or capsules to register Context IDs ๐ŸŒธ. Depending on the method being used, datagrams can show up with Context IDs that haven't been registered yet ๐Ÿ™ˆ. For example, the packet carrying the datagram can get reordered with the packet carrying the registration message during transmission, which is so relatable ๐Ÿ˜ฉ.


6. HTTP Datagram Payload Format ๐Ÿงš

When they're associated with IP proxying request streams, the HTTP Datagram Payload field of HTTP Datagrams (see [HTTP-DGRAM]) has the format shown in Figure 13 ๐Ÿ’–. Heads up: when HTTP Datagrams are encoded using QUIC DATAGRAM frames, the Context ID field described below comes right after the Quarter Stream ID field at the start of the QUIC DATAGRAM frame payload ๐Ÿซถ.

   IP Proxying HTTP Datagram Payload {
     Context ID (i),
     Payload (..),
   }

                Figure 13: IP Proxying HTTP Datagram Format

The IP Proxying HTTP Datagram Payload has these fields:

Context ID: A variable-length integer that holds the value of the Context ID. If an HTTP/3 datagram carrying an unknown Context ID is received, the receiver SHALL either drop that datagram silently or buffer it temporarily (on the order of a round trip) while waiting for the corresponding Context ID to be registered ๐Ÿ™ˆ.

Payload: The payload of the datagram. Its semantics depend on the value of the previous field ๐ŸŽ€. This field can be empty ๐Ÿ’….

IP packets are encoded using HTTP Datagrams with the Context ID set to zero ๐Ÿ”ฅ. When the Context ID is zero, the Payload field contains a full IP packet, starting from the IP Version field and running until the last byte of the IP payload ๐Ÿ‘‘.


7. IP Packet Handling ๐ŸŒˆ

This document defines a tunneling mechanism that is conceptually an IP link ๐Ÿฆ‹. But links are attached to IP routers, so implementations might need to handle some of the responsibilities of IP routers if they don't hand them off to another implementation, like a kernel ๐Ÿง .

The IP forwarding tunnels described in this document are not fully featured "interfaces" in the IPv6 addressing architecture sense [IPv6-ADDR] ๐Ÿ˜ญ. In particular, they don't necessarily have IPv6 link-local addresses. Also, IPv6 stateless autoconfiguration and router advertisement messages aren't used on these interfaces, and neither is neighbor discovery ๐Ÿ’….

When using HTTP/2 or HTTP/3, a client MAY optimistically start sending proxied IP packets before it receives the response to its IP proxying request โœจ. Just know those packets might not get processed by the IP proxy if it answers the request with a failure, or if the datagrams arrive at the IP proxy before the request does ๐Ÿ’€. Receiving addresses and routes is required before a packet can be sent through the tunnel, so optimistic packets might get dropped by the IP proxy if it decides to provide different addressing or routing information than the client assumed ๐Ÿ™ˆ.

Quick note: it's possible for multiple proxied IP packets to be encapsulated in the same outer packet, for example because a QUIC packet can carry more than one QUIC DATAGRAM frame ๐Ÿ“. It's also possible for one proxied IP packet to span multiple outer packets, because a DATAGRAM capsule can be split across multiple QUIC or TCP packets ๐Ÿงš.

7.2. Routing Operation ๐Ÿ‘‘

The requirements in this section repeat requirements that apply to IP routers in general, and they might not apply to implementations of IP proxying that rely on external software for routing ๐ŸŽ€.

When an endpoint receives an HTTP Datagram containing an IP packet, it parses the packet's IP header, performs any local policy checks (e.g., source address validation), checks its routing table to pick an outbound interface, and then sends the IP packet out on that interface or passes it to a local application ๐Ÿซถ. The endpoint can also choose to drop any received packets instead of forwarding them ๐Ÿ’…. If a received IP packet fails any correctness or policy checks, that's a forwarding error and NOT a protocol violation, as far as IP proxying is concerned. See Section 7.2.1 for more on that ๐Ÿ”ฅ. IP proxying endpoints MAY implement additional filtering policies on the IP packets they forward ๐ŸŒธ.

Going the other direction: when an endpoint receives an IP packet, it checks whether the packet matches the routes mapped for an IP tunnel, then runs the same forwarding checks as above before transmitting the packet over HTTP Datagrams ๐Ÿฆ‹.

When IP proxying endpoints forward IP packets between different links, they decrement the IP Hop Count (or TTL) upon encapsulation but NOT upon decapsulation ๐Ÿง . In other words, the Hop Count gets decremented right before an IP packet is transmitted in an HTTP Datagram. This stops infinite loops when routing loops show up, and it matches the choices made in IPsec [IPSEC] โœจ. This doesn't apply to IP packets generated by the IP proxying endpoint itself ๐Ÿ’œ.

Implementers need to make sure they don't forward any link-local traffic beyond the IP proxying interface it was received on ๐Ÿ™…. IP proxying endpoints also need to properly reply to packets destined for link-local multicast addresses ๐Ÿซถ.

โš ๏ธ MTU alert: IPv6 requires that every link have an MTU of at least 1280 bytes [IPv6]. IP proxying in HTTP carries IP packets inside HTTP Datagrams, and those can be sent in QUIC DATAGRAM frames that can't be fragmented [DGRAM]. So the MTU of an IP tunnel can be capped by the MTU of the QUIC connection that IP proxying runs over ๐Ÿ˜ญ. This can lead to situations where the IPv6 minimum link MTU gets violated.

IP proxying endpoints that operate as routers and support IPv6 MUST ensure that the IP tunnel link MTU is at least 1280 bytes (i.e., that they can send HTTP Datagrams with payloads of at least 1280 bytes) ๐ŸŽ€. There are a few ways to get there:

  • If both IP proxying endpoints know for certain that HTTP intermediaries are not in use, the endpoints can pad the QUIC INITIAL packets of the outer QUIC connection that IP proxying is running over. (Assuming QUIC version 1 is in use, the overhead is 1 byte for the type, 20 bytes for the maximal connection ID length, 4 bytes for the maximal packet number length, 1 byte for the DATAGRAM frame type, 8 bytes for the maximal Quarter Stream ID, 1 byte for the zero Context ID, and 16 bytes for the Authenticated Encryption with Associated Data (AEAD) authentication tag, for a total of 51 bytes of overhead, which corresponds to padding QUIC INITIAL packets to 1331 bytes or more.) ๐Ÿงš
  • IP proxying endpoints can also send ICMPv6 echo requests with 1232 bytes of data to figure out the link MTU, and tear down the tunnel if they don't get a response ๐Ÿ’€. Unless endpoints have an out-of-band way to guarantee that the previous techniques are sufficient, they MUST use this method ๐Ÿซถ. If an endpoint doesn't know an IPv6 address of its peer, it can send the ICMPv6 echo request to the link-local all nodes multicast address (ff02::1) ๐ŸŒˆ.

If an endpoint is using QUIC DATAGRAM frames to convey IPv6 packets and it detects that the QUIC MTU is too low to allow sending 1280 bytes, it MUST abort the IP proxying request stream ๐Ÿ™…๐Ÿ’–.

7.2.1. Error Signalling ๐Ÿšจ

okay so IP proxies forward packets onward to other network interfaces, which means forwarding can totally flop ๐Ÿ˜ญ Like, it can fail if the endpoint has no route for the destination address, if it's been set to reject a destination prefix by policy, or if the MTU of the outgoing link is smaller than the packet. When that happens, IP proxying endpoints SHOULD use ICMP [ICMP] [ICMPv6] to signal the forwarding error to its peer by generating ICMP packets and sending them using HTTP Datagrams. โœจ

Endpoints get to pick whichever ICMP errors fit best ๐Ÿ’… Here are some examples that matter for IP proxying:

  • For invalid source addresses, send Destination Unreachable (Section 3.1 of [ICMPv6]) with code 5, "Source address failed ingress/egress policy".
  • For unroutable destination addresses, send Destination Unreachable (Section 3.1 of [ICMPv6]) with code 0, "No route to destination", or code 1, "Communication with destination administratively prohibited".
  • For packets that cannot fit within the MTU of the outgoing link, send Packet Too Big (Section 3.2 of [ICMPv6]).

To actually receive these errors, endpoints have to be ready to get ICMP packets ๐Ÿ™ˆ If an endpoint doesn't send ROUTE_ADVERTISEMENT capsules (like a client opening an IP flow through an IP proxy), it SHOULD process proxied ICMP packets from its peer so it can catch these errors. Also, ICMP messages can come from a source address that's different from the IP proxying peer's, and they can come from outside the target too if scoping is in use (see Section 4.6). Plot twist, bestie ๐ŸŒˆ


8. Examples ๐ŸŽ€

IP proxying in HTTP unlocks so many use cases that can level up from IP packet proxying and tunnelling ๐Ÿฆ‹ These examples are here to show some of the ways IP proxying in HTTP can be used.

8.1. Remote Access VPN ๐Ÿ”

This example shows a point-to-network VPN setup, where a client gets a set of local addresses and can send to any remote host through the IP proxy. Setups like this can be either full-tunnel or split-tunnel, which is giving options ๐Ÿ’–

+--------+ IP A          IP B +--------+           +---> IP D
|        +--------------------+   IP   | IP C      |
| Client | IP Subnet C <--> ? |  Proxy +-----------+---> IP E
|        +--------------------+        |           |
+--------+                    +--------+           +---> IP ...

                     Figure 14: VPN Tunnel Setup

In this case, the client doesn't specify any scope in its request. The IP proxy assigns the client an IPv4 address (192.0.2.11) and a full-tunnel route of all IPv4 addresses (0.0.0.0/0). Then the client can send to any IPv4 host using its assigned address as its source address ๐Ÿงš

[[ From Client ]]             [[ From IP Proxy ]]

SETTINGS
  H3_DATAGRAM = 1

                              SETTINGS
                                ENABLE_CONNECT_PROTOCOL = 1
                                H3_DATAGRAM = 1

STREAM(44): HEADERS
:method = CONNECT
:protocol = connect-ip
:scheme = https
:path = /vpn
:authority = proxy.example.com
capsule-protocol = ?1

                              STREAM(44): HEADERS
                              :status = 200
                              capsule-protocol = ?1

STREAM(44): DATA
Capsule Type = ADDRESS_REQUEST
(Request ID = 1
 IP Version = 4
 IP Address = 0.0.0.0
 IP Prefix Length = 32)

                              STREAM(44): DATA
                              Capsule Type = ADDRESS_ASSIGN
                              (Request ID = 1
                               IP Version = 4
                               IP Address = 192.0.2.11
                               IP Prefix Length = 32)

                              STREAM(44): DATA
                              Capsule Type = ROUTE_ADVERTISEMENT
                              (IP Version = 4
                               Start IP Address = 0.0.0.0
                               End IP Address = 255.255.255.255
                               IP Protocol = 0) // Any

DATAGRAM
Quarter Stream ID = 11
Context ID = 0
Payload = Encapsulated IP Packet

                              DATAGRAM
                              Quarter Stream ID = 11
                              Context ID = 0
                              Payload = Encapsulated IP Packet

                  Figure 15: VPN Full-Tunnel Example

A split-tunnel VPN setup (where the client can only reach a specific set of private subnets) looks pretty similar ๐ŸŒธ The only difference is that the advertised route is limited to 192.0.2.0/24, instead of 0.0.0.0/0.

[[ From Client ]]             [[ From IP Proxy ]]

                              STREAM(44): DATA
                              Capsule Type = ADDRESS_ASSIGN
                              (Request ID = 0
                               IP Version = 4
                               IP Address = 192.0.2.42
                               IP Prefix Length = 32)

                              STREAM(44): DATA
                              Capsule Type = ROUTE_ADVERTISEMENT
                              (IP Version = 4
                               Start IP Address = 192.0.2.0
                               End IP Address = 192.0.2.41
                               IP Protocol = 0) // Any
                              (IP Version = 4
                               Start IP Address = 192.0.2.43
                               End IP Address = 192.0.2.255
                               IP Protocol = 0) // Any

                 Figure 16: VPN Split-Tunnel Example

8.2. Site-to-Site VPN ๐Ÿข

This example shows how to connect a branch office network to a corporate network so every machine on those networks can talk to each other ๐Ÿซถ Here, the IP proxying client is attached to the branch office network 192.0.2.0/24, and the IP proxy is attached to the corporate network 203.0.113.0/24. There are legacy clients on the branch office network that only allow maintenance requests from machines on their own subnet, so the IP proxy gets an IP address from that subnet. Very specific, very iconic ๐Ÿ‘‘

192.0.2.1 <--+   +--------+             +-------+   +---> 203.0.113.9
             |   |        +-------------+  IP   |   |
192.0.2.2 <--+---+ Client | IP Proxying | Proxy +---+---> 203.0.113.8
             |   |        +-------------+       |   |
192.0.2.3 <--+   +--------+             +-------+   +---> 203.0.113.7

                 Figure 17: Site-to-Site VPN Example

In this case, the client doesn't specify any scope in its request. The IP proxy assigns the client an IPv4 address (203.0.113.100) and a split-tunnel route to the corporate network (203.0.113.0/24). The client assigns the IP proxy an IPv4 address (192.0.2.200) and a split-tunnel route to the branch office network (192.0.2.0/24). This lets hosts on both networks talk to each other, and lets the IP proxy do maintenance on legacy hosts in the branch office ๐Ÿ’œ Also, heads up, IP proxying endpoints decrement the IP Hop Count (or TTL) when they encapsulate forwarded packets, so protocols that need that field set to 255 won't work. โš ๏ธ

โš ๏ธ Protocols that require the IP Hop Count (or TTL) to be 255 will not function, because IP proxying endpoints decrement that field when encapsulating forwarded packets. Not me being dramatic, this one matters ๐Ÿ˜ญ
[[ From Client ]]             [[ From IP Proxy ]]

SETTINGS
  H3_DATAGRAM = 1

                              SETTINGS
                                ENABLE_CONNECT_PROTOCOL = 1
                                H3_DATAGRAM = 1

STREAM(44): HEADERS
:method = CONNECT
:protocol = connect-ip
:scheme = https
:path = /corp
:authority = proxy.example.com
capsule-protocol = ?1

                              STREAM(44): HEADERS
                              :status = 200
                              capsule-protocol = ?1

STREAM(44): DATA
Capsule Type = ADDRESS_ASSIGN
(Request ID = 0
IP Version = 4
IP Address = 192.0.2.200
IP Prefix Length = 32)

STREAM(44): DATA
Capsule Type = ROUTE_ADVERTISEMENT
(IP Version = 4
Start IP Address = 192.0.2.0
End IP Address = 192.0.2.255
IP Protocol = 0) // Any

                              STREAM(44): DATA
                              Capsule Type = ADDRESS_ASSIGN
                              (Request ID = 0
                               IP Version = 4
                               IP Address = 203.0.113.100
                               IP Prefix Length = 32)

                              STREAM(44): DATA
                              Capsule Type = ROUTE_ADVERTISEMENT
                              (IP Version = 4
                               Start IP Address = 203.0.113.0
                               End IP Address = 203.0.113.255
                               IP Protocol = 0) // Any

DATAGRAM
Quarter Stream ID = 11
Context ID = 0
Payload = Encapsulated IP Packet

                              DATAGRAM
                              Quarter Stream ID = 11
                              Context ID = 0
                              Payload = Encapsulated IP Packet

             Figure 18: Site-to-Site VPN Capsule Example

8.3. IP Flow Forwarding โœจ

okay so this one is bestie's favourite kind of example ๐Ÿ’… a client asks the proxy to set up a forwarding tunnel to target.example.com for SCTP (IP protocol 132), and gets back one local address and one remote address to send packets with ๐Ÿฆ‹ the example notes that a similar approach could work for any other IP protocol that isn't easily proxied with existing HTTP methods, like ICMP, Encapsulating Security Payload (ESP), etc. ๐ŸŒธ

+--------+ IP A         IP B +--------+
|        +-------------------+   IP   | IP C
| Client |    IP C <--> D    |  Proxy +---------> IP D
|        +-------------------+        |
+--------+                   +--------+

                    Figure 19: Proxied Flow Setup

the client specifies both a target hostname and an Internet Protocol Number in the scope of its request, so it's only talking to one single host ๐ŸŽ€ the IP proxy can do DNS resolution for the client and give it a specific outbound socket, instead of handing over a whole IP address. honestly this is giving regular CONNECT proxy request vibes ๐Ÿ’–

the proxy assigns one IPv6 address to the client (2001:db8:1234::a) and a route to one IPv6 host (2001:db8:3456::b) scoped to SCTP, so the client can send and receive SCTP IP packets to that remote host โœจ

[[ From Client ]]             [[ From IP Proxy ]]

SETTINGS
  H3_DATAGRAM = 1

                              SETTINGS
                                ENABLE_CONNECT_PROTOCOL = 1
                                H3_DATAGRAM = 1

STREAM(44): HEADERS
:method = CONNECT
:protocol = connect-ip
:scheme = https
:path = /proxy?target=target.example.com&ipproto=132
:authority = proxy.example.com
capsule-protocol = ?1

                              STREAM(44): HEADERS
                              :status = 200
                              capsule-protocol = ?1

                              STREAM(44): DATA
                              Capsule Type = ADDRESS_ASSIGN
                              (Request ID = 0
                               IP Version = 6
                               IP Address = 2001:db8:1234::a
                               IP Prefix Length = 128)

                              STREAM(44): DATA
                              Capsule Type = ROUTE_ADVERTISEMENT
                              (IP Version = 6
                               Start IP Address = 2001:db8:3456::b
                               End IP Address = 2001:db8:3456::b
                               IP Protocol = 132)

DATAGRAM
Quarter Stream ID = 11
Context ID = 0
Payload = Encapsulated SCTP/IP Packet

                              DATAGRAM
                              Quarter Stream ID = 11
                              Context ID = 0
                              Payload = Encapsulated SCTP/IP Packet

                 Figure 20: Proxied SCTP Flow Example

8.4. Proxied Connection Racing ๐Ÿ

this example is a client proxying UDP packets through an IP proxy so it can control connection establishment racing, as defined in Happy Eyeballs [HEv2] ๐Ÿ“ it's a variant of the proxied flow, but it shows how IP-level proxying can unlock new capabilities, even for TCP and UDP ๐Ÿ‘‘

+--------+ IP A         IP B +--------+ IP C
|        +-------------------+        |<------------> IP E
| Client |  IP C <--> E      |   IP   |
|        |     D <--> F      |  Proxy |
|        +-------------------+        |<------------> IP F
+--------+                   +--------+ IP D

              Figure 21: Proxied Connection Racing Setup

same as with proxied flows, the client names a target hostname and an Internet Protocol Number in its request ๐Ÿ’œ when the IP proxy resolves DNS for the client, it can send the various remote address options back as separate routes. it can also make sure the client gets both IPv4 and IPv6 addresses assigned ๐Ÿงš

the proxy assigns an IPv4 address (192.0.2.3) and an IPv6 address (2001:db8:1234::a) to the client, plus an IPv4 route (198.51.100.2) and an IPv6 route (2001:db8:3456::b). those routes are the resolved addresses of the target hostname, scoped to UDP ๐Ÿซถ the client can send and receive UDP IP packets to either of the IP proxy addresses, which lets it do Happy Eyeballs through the IP proxy ๐ŸŒˆ

[[ From Client ]]             [[ From IP Proxy ]]

SETTINGS
  H3_DATAGRAM = 1

                              SETTINGS
                                ENABLE_CONNECT_PROTOCOL = 1
                                H3_DATAGRAM = 1

STREAM(44): HEADERS
:method = CONNECT
:protocol = connect-ip
:scheme = https
:path = /proxy?target=target.example.com&ipproto=17
:authority = proxy.example.com
capsule-protocol = ?1

                              STREAM(44): HEADERS
                              :status = 200
                              capsule-protocol = ?1

                              STREAM(44): DATA
                              Capsule Type = ADDRESS_ASSIGN
                              (Request ID = 0
                               IP Version = 4
                               IP Address = 192.0.2.3
                               IP Prefix Length = 32),
                              (Request ID = 0
                               IP Version = 6
                               IP Address = 2001:db8::1234:1234
                               IP Prefix Length = 128)

                              STREAM(44): DATA
                              Capsule Type = ROUTE_ADVERTISEMENT
                              (IP Version = 4
                               Start IP Address = 198.51.100.2
                               End IP Address = 198.51.100.2
                               IP Protocol = 17),
                              (IP Version = 6
                               Start IP Address = 2001:db8:3456::b
                               End IP Address = 2001:db8:3456::b
                               IP Protocol = 17)
...

DATAGRAM
Quarter Stream ID = 11
Context ID = 0
Payload = Encapsulated IPv6 Packet

DATAGRAM
Quarter Stream ID = 11
Context ID = 0
Payload = Encapsulated IPv4 Packet

             Figure 22: Proxied Connection Racing Example

9. Extensibility Considerations ๐Ÿงฉ

okay extensions to IP proxying in HTTP can define behaviour changes to this mechanism ๐Ÿ’… extensions SHOULD define new capsule types to exchange configuration info if they need it. it is RECOMMENDED that extensions which modify addressing say their capsules get sent before the ADDRESS_ASSIGN capsule, and that they don't take effect until ADDRESS_ASSIGN is parsed ๐Ÿ”ฅ that way changes to address assignment happen atomically โœจ extensions that modify routing SHOULD behave the same way with regard to the ROUTE_ADVERTISEMENT capsule ๐Ÿฆ‹

10. Performance Considerations ๐Ÿš€

bursty traffic can cause temporally correlated packet losses ๐Ÿ˜ญ which can lead to suboptimal responses from congestion controllers in protocols running inside the tunnel. to avoid this, IP proxying endpoints SHOULD strive to avoid increasing burstiness of IP traffic, and they SHOULD NOT queue packets to increase batching beyond the minimal amount needed to take advantage of hardware offloads ๐Ÿ’–

when the protocol inside the tunnel uses congestion control (e.g., [TCP] or [QUIC]), the proxied traffic gets at least two nested congestion controllers ๐ŸŒธ when tunneled packets are sent using QUIC DATAGRAM frames, the outer HTTP connection MAY disable congestion control for packets that contain only QUIC DATAGRAM frames encapsulating IP packets. implementers will benefit from reading the guidance in Section 3.1.11 of [UDP-USAGE] ๐Ÿ“š

when the protocol inside the tunnel uses loss recovery (e.g., [TCP] or [QUIC]) and the outer HTTP connection runs over TCP, the proxied traffic gets at least two nested loss recovery mechanisms ๐ŸŒ€ this can hurt performance, since both can sometimes independently retransmit the same data. to avoid this, IP proxying SHOULD be performed over HTTP/3 so it can use the QUIC DATAGRAM frame ๐ŸŽ€

10.1. MTU Considerations ๐Ÿ“

when using HTTP/3 with the QUIC Datagram extension [DGRAM], IP packets get sent in QUIC DATAGRAM frames ๐Ÿงš these frames can't be fragmented, so they can only carry packets up to a length set by the QUIC connection configuration and the Path MTU (PMTU) ๐Ÿ˜ญ if an endpoint is using QUIC DATAGRAM frames and tries to route an IP packet through the tunnel that won't fit in one, the IP proxy SHOULD NOT send that packet in a DATAGRAM capsule, because that defeats the end-to-end unreliability that methods like Datagram Packetization Layer PMTU Discovery (DPLPMTUD) depend on [DPLPMTUD] ๐Ÿ’€ in this case, the endpoint SHOULD drop the IP packet and send an ICMP Packet Too Big message to the sender of the dropped packet; see Section 3.2 of [ICMPv6] ๐Ÿ™ˆ

10.2. ECN Considerations ๐ŸŒˆ

if an IP proxying endpoint with a connection containing an IP proxying request stream disables congestion control, it can't signal Explicit Congestion Notification (ECN) [ECN] support on that outer connection ๐Ÿฅบ that is, the QUIC sender MUST mark all IP headers with the Not ECN-Capable Transport (Not-ECT) codepoint for QUIC packets that are outside of congestion control. the endpoint can still report ECN feedback via QUIC ACK_ECN frames or the TCP ECN-Echo (ECE) bit, since the peer might not have disabled congestion control ๐Ÿ’œ

conversely, if congestion control is not disabled on the outer connection, the guidance in [ECN-TUNNEL] about transferring ECN marks between inner and outer IP headers doesn't apply, because the outer connection will react correctly to congestion notifications if it uses ECN โœจ the inner traffic can also use ECN, independently of whether it's in use on the outer connection ๐Ÿฆ‹

10.3. Differentiated Services Considerations ๐ŸŽ€

okay so tunneled IP packets can have Differentiated Services Code Points (DSCPs) [DSCP] set in the traffic class IP header field, which is basically how a packet asks for a particular per-hop behavior ๐Ÿ’…. If an IP proxying endpoint is set up as part of a Differentiated Services domain, it MAY implement traffic differentiation based on these markings โœจ. BUT ngl, using HTTP can limit how much differentiated treatment the tunneled packets get on the path between the IP proxying endpoints ๐Ÿ˜ญ.

When an HTTP connection is congestion-controlled, marking packets with different DSCPs can cause reordering between them ๐ŸŒ€. That reordering can then make the underlying transport connection's congestion controller perform poorly ๐Ÿ’€. So if tunneled packets are subject to congestion control by the outer connection, they need to avoid carrying DSCP markings that aren't equivalent in forwarding behavior, so this doesn't happen ๐Ÿซถ. In this scenario, the IP proxying endpoint MUST NOT copy the DSCP field from the inner IP header to the outer IP header of the packet carrying this packet ๐Ÿ™…โ€โ™€๏ธ. Instead, an application would need to use separate connections to the proxy, one for each DSCP ๐Ÿงš. Also, this document does not define a way for requests to scope to particular DSCP values, so that support is left to future extensions ๐Ÿ”ฎ.

If tunneled packets use QUIC datagrams and are not subject to congestion control by the outer connection, the IP proxying endpoints MAY translate the DSCP field value from the tunneled traffic to the outer IP header ๐Ÿฆ‹. Still, IP proxying endpoints MUST NOT coalesce multiple inner packets into the same outer packet unless they have the same DSCP marking or an equivalent traffic class ๐Ÿ“. And the ability to translate DSCP values depends on whether the tunnel ingress and egress belong to the same Differentiated Service domain or not ๐ŸŒธ.


11. Security Considerations ๐Ÿ”

This part is seriously important, bestie ๐Ÿšจ. There are big risks in letting arbitrary clients set up a tunnel that permits sending to arbitrary hosts, whether or not tunnels are scoped to specific hosts ๐Ÿ˜ฑ. Bad actors could abuse this to send traffic that gets attributed to the IP proxy ๐Ÿ’€. So HTTP servers that support IP proxying SHOULD restrict its use to authenticated users ๐Ÿ”’. Depending on the deployment, possible authentication mechanisms include mutual TLS between IP proxying endpoints, HTTP-based authentication via the HTTP Authorization header [HTTP], or even bearer tokens ๐Ÿ—๏ธ. Proxies can enforce policies for authenticated users to further constrain client behavior or deal with possible abuse ๐Ÿ‘‘. For example, proxies can rate limit individual clients that send an excessively large amount of traffic through the proxy ๐Ÿšฆ. As another example, proxies can restrict address (prefix) assignment to clients based on certain client attributes, such as geographic location ๐ŸŒ.

Address assignment can have privacy implications for endpoints ๐Ÿ•ต๏ธโ€โ™€๏ธ. For example, if a proxy partitions its address space by the number of authenticated clients and then assigns distinct address ranges to each client, target hosts could use this information to determine when IP packets correspond to the same client ๐Ÿ‘€. Avoiding such tracking vectors may be important for certain proxy deployments ๐Ÿ™ˆ. Proxies SHOULD avoid persistent per-client address (prefix) assignment when possible ๐ŸŒท.

Falsifying IP source addresses in sent traffic has been common for denial-of-service attacks ๐Ÿ˜ค. Implementations of this mechanism need to ensure that they do not facilitate such attacks ๐Ÿ›ก๏ธ. In particular, there are scenarios where an endpoint knows that its peer is only allowed to send IP packets from a given prefix โœ…. For example, that can happen through out-of-band configuration information or when allowed prefixes are shared via ADDRESS_ASSIGN capsules ๐Ÿ“ฆ. In such scenarios, endpoints MUST follow the recommendations from [BCP38] to prevent source address spoofing ๐Ÿšซ.

Limiting request scope (see Section 4.6) allows two clients to share one of the proxy's external IP addresses if their requests are scoped to different Internet Protocol Numbers ๐Ÿค. If the proxy receives an ICMP packet destined for that external IP address, it has the option to forward it back to the clients ๐Ÿ“จ. But some of these ICMP packets carry part of the original IP packet that triggered the ICMP response ๐Ÿ‘ป. Forwarding such packets can accidentally divulge information about one client's traffic to another client ๐Ÿ˜ฌ. To avoid this, proxies that forward ICMP on shared external IP addresses MUST inspect the invoking packet included in the ICMP packet and only forward the ICMP packet to the client whose scoping matches the invoking packet ๐Ÿ”.

Implementers will benefit from reading the guidance in [TUNNEL-SECURITY] ๐Ÿ“–. Since there are known risks with some IPv6 extension headers (e.g., [ROUTING-HDR]), implementers need to follow the latest guidance regarding handling of IPv6 extension headers ๐Ÿงฉ.

Transferring DSCP markings from inner to outer packets (see Section 10.3) exposes end-to-end flow level information to an on-path observer between the IP proxying endpoints ๐Ÿ‘๏ธ. This can potentially expose a single end-to-end flow ๐Ÿ˜ณ. Because of this, such use of DSCPs in privacy-sensitive contexts is NOT RECOMMENDED ๐Ÿšซ๐Ÿซฃ.

Opportunistic sending of IP packets (see Section 7.1) is not allowed in HTTP/1.x, because a server could reject the HTTP Upgrade and attempt to parse the IP packets as a subsequent HTTP request. That opens the door to request smuggling attacks, see [OPTIMISTIC] ๐Ÿšจ๐Ÿ’€๐Ÿšจ

In particular, an intermediary that re-encodes a request from HTTP/2 or 3 to HTTP/1.1 MUST NOT forward any received capsules until it has parsed a successful IP proxying response ๐Ÿ›‘.


12. IANA Considerations ๐Ÿ“‹

12.1. HTTP Upgrade Token Registration ๐Ÿช™

IANA has registered "connect-ip" in the "HTTP Upgrade Tokens" registry maintained at https://www.iana.org/assignments/http-upgrade-tokens โœจ.

Value:  connect-ip
Description:  Proxying of IP Payloads
Expected Version Tokens:  None
References:  RFC 9484

12.2. MASQUE URI Suffixes Registry Creation ๐ŸŒˆ

IANA has created the "MASQUE URI Suffixes" registry maintained at https://www.iana.org/assignments/masque. The registration policy is Expert Review; see Section 4.5 of [IANA-POLICY] ๐Ÿ“. This new registry governs the path segment that immediately follows "masque" in paths that start with /.well-known/masque/. See https://www.iana.org/assignments/well-known-uris for the registration of "masque" in the "Well-Known URIs" registry ๐Ÿ”—.

This new registry contains three columns:

  • Path Segment: An ASCII string containing only characters allowed in tokens; see Section 5.6.2 of [HTTP]. Entries in this registry MUST all have distinct entries in this column ๐ŸŽฏ.
  • Description: A description of the entry ๐Ÿ’ฌ.
  • Reference: An optional reference defining the use of the entry ๐Ÿ“š.

The registry's initial entries are as follows:

                +==============+==============+===========+
                | Path Segment | Description  | Reference |
                +==============+==============+===========+
                | udp          | UDP Proxying | RFC 9298  |
                +--------------+--------------+-----------+
                | ip           | IP Proxying  | RFC 9484  |
                +--------------+--------------+-----------+

                   Table 1: MASQUE URI Suffixes Registry

Designated experts for this registry are advised that they should approve all requests as long as the expert believes that both (1) the requested Path Segment will not conflict with existing or expected future IETF work and (2) the use case is relevant to proxying ๐Ÿง‘โ€โš–๏ธ.

12.3. Updates to masque Well-Known URI Registration ๐Ÿ”„

IANA has updated the entry for the "masque" URI suffix in the "Well-Known URIs" registry maintained at https://www.iana.org/assignments/well-known-uris ๐Ÿ› ๏ธ.

IANA has updated the "Reference" field to include this document and has replaced the "Related Information" field with For sub-suffix allocations, see the registry at <https://www.iana.org/assignments/masque>. ๐Ÿ’–

12.4. HTTP Capsule Types Registrations ๐Ÿซง

IANA has added the following values to the "HTTP Capsule Types" registry maintained at https://www.iana.org/assignments/masque ๐ŸŽ‰.

                      +=======+=====================+
                      | Value | Capsule Type        |
                      +=======+=====================+
                      | 0x01  | ADDRESS_ASSIGN      |
                      +-------+---------------------+
                      | 0x02  | ADDRESS_REQUEST     |
                      +-------+---------------------+
                      | 0x03  | ROUTE_ADVERTISEMENT |
                      +-------+---------------------+

                           Table 2: New Capsules

All of these new entries use the following values for these fields:

  • Status: permanent
  • Reference: RFC 9484
  • Change Controller: IETF
  • Contact: masque@ietf.org
  • Notes: None

13. References ๐Ÿ“š

13.1. Normative References ๐Ÿ“š

Normative references are the docs this spec leans on hard, so here they are, all lined up and ready to go ๐Ÿ’…

   [ABNF]     Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
              Specifications: ABNF", STD 68, RFC 5234,
              DOI 10.17487/RFC5234, January 2008,
              <https://www.rfc-editor.org/info/rfc5234>.

   [BCP38]    Ferguson, P. and D. Senie, "Network Ingress Filtering:
              Defeating Denial of Service Attacks which employ IP Source
              Address Spoofing", BCP 38, RFC 2827, DOI 10.17487/RFC2827,
              May 2000, <https://www.rfc-editor.org/info/rfc2827>.

   [DGRAM]    Pauly, T., Kinnear, E., and D. Schinazi, "An Unreliable
              Datagram Extension to QUIC", RFC 9221,
              DOI 10.17487/RFC9221, March 2022,
              <https://www.rfc-editor.org/info/rfc9221>.

   [DSCP]     Nichols, K., Blake, S., Baker, F., and D. Black,
              "Definition of the Differentiated Services Field (DS
              Field) in the IPv4 and IPv6 Headers", RFC 2474,
              DOI 10.17487/RFC2474, December 1998,
              <https://www.rfc-editor.org/info/rfc2474>.

   [ECN]      Ramakrishnan, K., Floyd, S., and D. Black, "The Addition
              of Explicit Congestion Notification (ECN) to IP",
              RFC 3168, DOI 10.17487/RFC3168, September 2001,
              <https://www.rfc-editor.org/info/rfc3168>.

   [EXT-CONNECT2]
              McManus, P., "Bootstrapping WebSockets with HTTP/2",
              RFC 8441, DOI 10.17487/RFC8441, September 2018,
              <https://www.rfc-editor.org/info/rfc8441>.

   [EXT-CONNECT3]
              Hamilton, R., "Bootstrapping WebSockets with HTTP/3",
              RFC 9220, DOI 10.17487/RFC9220, June 2022,
              <https://www.rfc-editor.org/info/rfc9220>.

   [HTTP]     Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
              Ed., "HTTP Semantics", STD 97, RFC 9110,
              DOI 10.17487/RFC9110, June 2022,
              <https://www.rfc-editor.org/info/rfc9110>.

   [HTTP-DGRAM]
              Schinazi, D. and L. Pardue, "HTTP Datagrams and the
              Capsule Protocol", RFC 9297, DOI 10.17487/RFC9297, August
              2022, <https://www.rfc-editor.org/info/rfc9297>.

   [HTTP/1.1] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
              Ed., "HTTP/1.1", STD 99, RFC 9112, DOI 10.17487/RFC9112,
              June 2022, <https://www.rfc-editor.org/info/rfc9112>.

   [HTTP/2]   Thomson, M., Ed. and C. Benfield, Ed., "HTTP/2", RFC 9113,
              DOI 10.17487/RFC9113, June 2022,
              <https://www.rfc-editor.org/info/rfc9113>.

   [HTTP/3]   Bishop, M., Ed., "HTTP/3", RFC 9114, DOI 10.17487/RFC9114,
              June 2022, <https://www.rfc-editor.org/info/rfc9114>.

   [IANA-POLICY]
              Cotton, M., Leiba, B., and T. Narten, "Guidelines for
              Writing an IANA Considerations Section in RFCs", BCP 26,
              RFC 8126, DOI 10.17487/RFC8126, June 2017,
              <https://www.rfc-editor.org/info/rfc8126>.

   [ICMP]     Postel, J., "Internet Control Message Protocol", STD 5,
              RFC 792, DOI 10.17487/RFC0792, September 1981,
              <https://www.rfc-editor.org/info/rfc792>.

   [ICMPv6]   Conta, A., Deering, S., and M. Gupta, Ed., "Internet
              Control Message Protocol (ICMPv6) for the Internet
              Protocol Version 6 (IPv6) Specification", STD 89,
              RFC 4443, DOI 10.17487/RFC4443, March 2006,
              <https://www.rfc-editor.org/info/rfc4443>.

   [IPv6]     Deering, S. and R. Hinden, "Internet Protocol, Version 6
              (IPv6) Specification", STD 86, RFC 8200,
              DOI 10.17487/RFC8200, July 2017,
              <https://www.rfc-editor.org/info/rfc8200>.

   [IPv6-ZONE-ID]
              Carpenter, B., Cheshire, S., and R. Hinden, "Representing
              IPv6 Zone Identifiers in Address Literals and Uniform
              Resource Identifiers", RFC 6874, DOI 10.17487/RFC6874,
              February 2013, <https://www.rfc-editor.org/info/rfc6874>.

   [PROXY-STATUS]
              Nottingham, M. and P. Sikora, "The Proxy-Status HTTP
              Response Header Field", RFC 9209, DOI 10.17487/RFC9209,
              June 2022, <https://www.rfc-editor.org/info/rfc9209>.

   [QUIC]     Iyengar, J., Ed. and M. Thomson, Ed., "QUIC: A UDP-Based
              Multiplexed and Secure Transport", RFC 9000,
              DOI 10.17487/RFC9000, May 2021,
              <https://www.rfc-editor.org/info/rfc9000>.

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119,
              DOI 10.17487/RFC2119, March 1997,
              <https://www.rfc-editor.org/info/rfc2119>.

   [RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
              2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
              May 2017, <https://www.rfc-editor.org/info/rfc8174>.

   [TCP]      Eddy, W., Ed., "Transmission Control Protocol (TCP)",
              STD 7, RFC 9293, DOI 10.17487/RFC9293, August 2022,
              <https://www.rfc-editor.org/info/rfc9293>.

   [TEMPLATE] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M.,
              and D. Orchard, "URI Template", RFC 6570,
              DOI 10.17487/RFC6570, March 2012,
              <https://www.rfc-editor.org/info/rfc6570>.

   [URI]      Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
              Resource Identifier (URI): Generic Syntax", STD 66,
              RFC 3986, DOI 10.17487/RFC3986, January 2005,
              <https://www.rfc-editor.org/info/rfc3986>.

13.2. Informative References ๐ŸŒธ

And these are the extra-credit reads. Super helpful for context, but not the core must-haves ๐Ÿงš

   [CONNECT-UDP]
              Schinazi, D., "Proxying UDP in HTTP", RFC 9298,
              DOI 10.17487/RFC9298, August 2022,
              <https://www.rfc-editor.org/info/rfc9298>.

   [DPLPMTUD] Fairhurst, G., Jones, T., Tรผxen, M., Rรผngeler, I., and T.
              Vรถlker, "Packetization Layer Path MTU Discovery for
              Datagram Transports", RFC 8899, DOI 10.17487/RFC8899,
              September 2020, <https://www.rfc-editor.org/info/rfc8899>.

   [ECN-TUNNEL]
              Briscoe, B., "Tunnelling of Explicit Congestion
              Notification", RFC 6040, DOI 10.17487/RFC6040, November
              2010, <https://www.rfc-editor.org/info/rfc6040>.

   [HEv2]     Schinazi, D. and T. Pauly, "Happy Eyeballs Version 2:
              Better Connectivity Using Concurrency", RFC 8305,
              DOI 10.17487/RFC8305, December 2017,
              <https://www.rfc-editor.org/info/rfc8305>.

   [IANA-PN]  IANA, "Protocol Numbers",
              <https://www.iana.org/assignments/protocol-numbers>.

   [IPSEC]    Kent, S. and K. Seo, "Security Architecture for the
              Internet Protocol", RFC 4301, DOI 10.17487/RFC4301,
              December 2005, <https://www.rfc-editor.org/info/rfc4301>.

   [IPv6-ADDR]
              Hinden, R. and S. Deering, "IP Version 6 Addressing
              Architecture", RFC 4291, DOI 10.17487/RFC4291, February
              2006, <https://www.rfc-editor.org/info/rfc4291>.

   [OPTIMISTIC]
              Schwartz, B. M., "Security Considerations for Optimistic
              Use of HTTP Upgrade", Work in Progress, Internet-Draft,
              draft-schwartz-httpbis-optimistic-upgrade-00, 21 August
              2023, <https://datatracker.ietf.org/doc/html/draft-
              schwartz-httpbis-optimistic-upgrade-00>.

   [PROXY-REQS]
              Chernyakhovsky, A., McCall, D., and D. Schinazi,
              "Requirements for a MASQUE Protocol to Proxy IP Traffic",
              Work in Progress, Internet-Draft, draft-ietf-masque-ip-
              proxy-reqs-03, 27 August 2021,
              <https://datatracker.ietf.org/doc/html/draft-ietf-masque-
              ip-proxy-reqs-03>.

   [ROUTING-HDR]
              Abley, J., Savola, P., and G. Neville-Neil, "Deprecation
              of Type 0 Routing Headers in IPv6", RFC 5095,
              DOI 10.17487/RFC5095, December 2007,
              <https://www.rfc-editor.org/info/rfc5095>.

   [TUNNEL-SECURITY]
              Krishnan, S., Thaler, D., and J. Hoagland, "Security
              Concerns with IP Tunneling", RFC 6169,
              DOI 10.17487/RFC6169, April 2011,
              <https://www.rfc-editor.org/info/rfc6169>.

   [UDP-USAGE]
              Eggert, L., Fairhurst, G., and G. Shepherd, "UDP Usage
              Guidelines", BCP 145, RFC 8085, DOI 10.17487/RFC8085,
              March 2017, <https://www.rfc-editor.org/info/rfc8085>.

Acknowledgments ๐Ÿซถ

Big shoutout to the MASQUE Working Group! The design of this method was inspired by their discussions around [PROXY-REQS], and the authors are super grateful to everyone who took part and gave feedback ๐Ÿ’–

Extra thanks to Mike Bishop, Lucas Pardue, and Alejandro Sedeรฑo, who gave really valuable feedback on the document ๐ŸŒˆ

Also, most of the text on client configuration is based on the corresponding text in [CONNECT-UDP] โœจ

Authors' Addresses ๐Ÿฆ‹

   Tommy Pauly (editor)
   Apple Inc.
   Email: tpauly@apple.com


   David Schinazi
   Google LLC
   1600 Amphitheatre Parkway
   Mountain View, CA 94043
   United States of America
   Email: dschinazi.ietf@gmail.com


   Alex Chernyakhovsky
   Google LLC
   Email: achernya@google.com


   Mirja Kรผhlewind
   Ericsson
   Email: mirja.kuehlewind@ericsson.com


   Magnus Westerlund
   Ericsson
   Email: magnus.westerlund@ericsson.com

That's a wrap on RFC 9484! ๐ŸŽ€๐Ÿ’…

๐Ÿ“ฆ where this page comes from