├── .circleci
└── config.yml
├── .github
├── CODEOWNERS
└── workflows
│ ├── archive.yml
│ ├── ghpages.yml
│ ├── publish.yml
│ └── update.yml
├── .gitignore
├── .note.xml
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── draft-ietf-masque-quic-proxy.md
└── interop
├── LICENSE
├── asset
├── badge.png
├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
└── marked.min.js
├── index.html
├── lib
├── display.mjs
├── modal.mjs
└── summary.mjs
├── parse_interop_tags.py
├── results
├── index.mjs
├── nwfw.json
└── quiche.json
├── style.css
└── tests
├── draft-ietf-masque-connect-udp.mjs
├── draft-pauly-masque-quic-proxy.mjs
└── index.mjs
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: martinthomson/i-d-template:latest
6 | working_directory: ~/draft
7 |
8 | steps:
9 | - run:
10 | name: "Print Configuration"
11 | command: |
12 | xml2rfc --version
13 | gem list -q kramdown-rfc2629
14 | echo -n 'mmark '; mmark --version
15 |
16 | - restore_cache:
17 | name: "Restoring cache - Git"
18 | keys:
19 | - v2-cache-git-{{ .Branch }}-{{ .Revision }}
20 | - v2-cache-git-{{ .Branch }}
21 | - v2-cache-git-
22 |
23 | - restore_cache:
24 | name: "Restoring cache - References"
25 | keys:
26 | - v1-cache-references-{{ epoch }}
27 | - v1-cache-references-
28 |
29 | # Workaround for https://discuss.circleci.com/t/22437
30 | - run:
31 | name: Tag Checkout
32 | command: |
33 | if [ -n "$CIRCLE_TAG" ] && [ -d .git ]; then
34 | remote=$(echo "$CIRCLE_REPOSITORY_URL" | \
35 | sed -e 's,/^git.github.com:,https://github.com/,')
36 | git fetch -f "$remote" "refs/tags/$CIRCLE_TAG:refs/tags/$CIRCLE_TAG" || \
37 | (echo 'Removing .git cache for tag build'; rm -rf .git)
38 | fi
39 |
40 | - checkout
41 |
42 | # Build txt and html versions of drafts
43 | - run:
44 | name: "Build Drafts"
45 | command: "make 'CLONE_ARGS=--reference ~/git-reference'"
46 |
47 | # Update editor's copy on gh-pages
48 | - run:
49 | name: "Update GitHub Pages"
50 | command: |
51 | if [ "${CIRCLE_TAG#draft-}" == "$CIRCLE_TAG" ]; then
52 | make gh-pages
53 | fi
54 |
55 | # For tagged builds, upload to the datatracker.
56 | - deploy:
57 | name: "Upload to Datatracker"
58 | command: |
59 | if [ "${CIRCLE_TAG#draft-}" != "$CIRCLE_TAG" ]; then
60 | make upload
61 | fi
62 |
63 | # Archive GitHub Issues
64 | - run:
65 | name: "Archive GitHub Issues"
66 | command: "make archive || make archive DISABLE_ARCHIVE_FETCH=true && make gh-archive"
67 |
68 | # Create and store artifacts
69 | - run:
70 | name: "Create Artifacts"
71 | command: "make artifacts CI_ARTIFACTS=/tmp/artifacts"
72 |
73 | - store_artifacts:
74 | path: /tmp/artifacts
75 |
76 | - run:
77 | name: "Prepare for Caching"
78 | command: "git reflog expire --expire=now --all && git gc --prune=now"
79 |
80 | - save_cache:
81 | name: "Saving Cache - Git"
82 | key: v2-cache-git-{{ .Branch }}-{{ .Revision }}
83 | paths:
84 | - ~/draft/.git
85 |
86 | - save_cache:
87 | name: "Saving Cache - Drafts"
88 | key: v1-cache-references-{{ epoch }}
89 | paths:
90 | - ~/.cache/xml2rfc
91 |
92 |
93 | workflows:
94 | version: 2
95 | build:
96 | jobs:
97 | - build:
98 | filters:
99 | tags:
100 | only: /.*?/
101 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Automatically generated CODEOWNERS
2 | # Regenerate with `make update-codeowners`
3 | draft-ietf-masque-quic-proxy.md tpauly@apple.com eric_rosenberg@apple.com dschinazi.ietf@gmail.com
4 |
--------------------------------------------------------------------------------
/.github/workflows/archive.yml:
--------------------------------------------------------------------------------
1 | name: "Archive Issues and Pull Requests"
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * 0,2,4'
6 | repository_dispatch:
7 | types: [archive]
8 | workflow_dispatch:
9 | inputs:
10 | archive_full:
11 | description: 'Recreate the archive from scratch'
12 | default: false
13 | type: boolean
14 |
15 | jobs:
16 | build:
17 | name: "Archive Issues and Pull Requests"
18 | runs-on: ubuntu-latest
19 | steps:
20 | - name: "Checkout"
21 | uses: actions/checkout@v4
22 |
23 | # Note: No caching for this build!
24 |
25 | - name: "Update Archive"
26 | uses: martinthomson/i-d-template@v1
27 | env:
28 | ARCHIVE_FULL: ${{ inputs.archive_full }}
29 | with:
30 | make: archive
31 | token: ${{ github.token }}
32 |
33 | - name: "Update GitHub Pages"
34 | uses: martinthomson/i-d-template@v1
35 | with:
36 | make: gh-archive
37 | token: ${{ github.token }}
38 |
39 | - name: "Save Archive"
40 | uses: actions/upload-artifact@v4
41 | with:
42 | path: archive.json
43 |
--------------------------------------------------------------------------------
/.github/workflows/ghpages.yml:
--------------------------------------------------------------------------------
1 | name: "Update Editor's Copy"
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - README.md
7 | - CONTRIBUTING.md
8 | - LICENSE.md
9 | - .gitignore
10 | pull_request:
11 | paths-ignore:
12 | - README.md
13 | - CONTRIBUTING.md
14 | - LICENSE.md
15 | - .gitignore
16 |
17 | jobs:
18 | build:
19 | name: "Update Editor's Copy"
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: "Checkout"
23 | uses: actions/checkout@v4
24 |
25 | - name: "Setup"
26 | id: setup
27 | run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT"
28 |
29 | - name: "Caching"
30 | uses: actions/cache@v4
31 | with:
32 | path: |
33 | .refcache
34 | .venv
35 | .gems
36 | node_modules
37 | .targets.mk
38 | key: i-d-${{ steps.setup.outputs.date }}
39 | restore-keys: i-d-
40 |
41 | - name: "Build Drafts"
42 | uses: martinthomson/i-d-template@v1
43 | with:
44 | token: ${{ github.token }}
45 |
46 | - name: "Update GitHub Pages"
47 | uses: martinthomson/i-d-template@v1
48 | if: ${{ github.event_name == 'push' }}
49 | with:
50 | make: gh-pages
51 | token: ${{ github.token }}
52 |
53 | - name: "Archive Built Drafts"
54 | uses: actions/upload-artifact@v4
55 | with:
56 | path: |
57 | draft-*.html
58 | draft-*.txt
59 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: "Publish New Draft Version"
2 |
3 | on:
4 | push:
5 | tags:
6 | - "draft-*"
7 | workflow_dispatch:
8 | inputs:
9 | email:
10 | description: "Submitter email"
11 | default: ""
12 | type: string
13 |
14 | jobs:
15 | build:
16 | name: "Publish New Draft Version"
17 | runs-on: ubuntu-latest
18 | steps:
19 | - name: "Checkout"
20 | uses: actions/checkout@v4
21 |
22 | # See https://github.com/actions/checkout/issues/290
23 | - name: "Get Tag Annotations"
24 | run: git fetch -f origin ${{ github.ref }}:${{ github.ref }}
25 |
26 | - name: "Setup"
27 | id: setup
28 | run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT"
29 |
30 | - name: "Caching"
31 | uses: actions/cache@v4
32 | with:
33 | path: |
34 | .refcache
35 | .venv
36 | .gems
37 | node_modules
38 | .targets.mk
39 | key: i-d-${{ steps.setup.outputs.date }}
40 | restore-keys: i-d-
41 |
42 | - name: "Build Drafts"
43 | uses: martinthomson/i-d-template@v1
44 | with:
45 | token: ${{ github.token }}
46 |
47 | - name: "Upload to Datatracker"
48 | uses: martinthomson/i-d-template@v1
49 | with:
50 | make: upload
51 | env:
52 | UPLOAD_EMAIL: ${{ inputs.email }}
53 |
54 | - name: "Archive Submitted Drafts"
55 | uses: actions/upload-artifact@v4
56 | with:
57 | path: "versioned/draft-*-[0-9][0-9].*"
58 |
--------------------------------------------------------------------------------
/.github/workflows/update.yml:
--------------------------------------------------------------------------------
1 | name: "Update Generated Files"
2 | # This rule is not run automatically.
3 | # It can be run manually to update all of the files that are part
4 | # of the template, specifically:
5 | # - README.md
6 | # - CONTRIBUTING.md
7 | # - .note.xml
8 | # - .github/CODEOWNERS
9 | # - Makefile
10 | #
11 | #
12 | # This might be useful if you have:
13 | # - added, removed, or renamed drafts (including after adoption)
14 | # - added, removed, or changed draft editors
15 | # - changed the title of drafts
16 | #
17 | # Note that this removes any customizations you have made to
18 | # the affected files.
19 | on: workflow_dispatch
20 |
21 | jobs:
22 | build:
23 | name: "Update Files"
24 | runs-on: ubuntu-latest
25 | steps:
26 | - name: "Checkout"
27 | uses: actions/checkout@v4
28 |
29 | - name: "Update Generated Files"
30 | uses: martinthomson/i-d-template@v1
31 | with:
32 | make: update-files
33 | token: ${{ github.token }}
34 |
35 | - name: "Push Update"
36 | run: git push
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 | *.pdf
3 | *.redxml
4 | *.swp
5 | *.txt
6 | *.upload
7 | *~
8 | .tags
9 | /*-[0-9][0-9].xml
10 | /.gems/
11 | /.refcache
12 | /.targets.mk
13 | /.venv/
14 | /.vscode/
15 | /lib
16 | /node_modules/
17 | /versioned/
18 | Gemfile.lock
19 | archive.json
20 | draft-ietf-masque-quic-proxy.xml
21 | package-lock.json
22 | report.xml
23 | !requirements.txt
24 |
--------------------------------------------------------------------------------
/.note.xml:
--------------------------------------------------------------------------------
1 |
2 | Source for this draft and an issue tracker can be found at
3 | https://github.com/tfpauly/quic-proxy.
4 |
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | This repository relates to activities in the Internet Engineering Task Force
4 | ([IETF](https://www.ietf.org/)). All material in this repository is considered
5 | Contributions to the IETF Standards Process, as defined in the intellectual
6 | property policies of IETF currently designated as
7 | [BCP 78](https://www.rfc-editor.org/info/bcp78),
8 | [BCP 79](https://www.rfc-editor.org/info/bcp79) and the
9 | [IETF Trust Legal Provisions (TLP) Relating to IETF Documents](http://trustee.ietf.org/trust-legal-provisions.html).
10 |
11 | Any edit, commit, pull request, issue, comment or other change made to this
12 | repository constitutes Contributions to the IETF Standards Process
13 | (https://www.ietf.org/).
14 |
15 | You agree to comply with all applicable IETF policies and procedures, including,
16 | BCP 78, 79, the TLP, and the TLP rules regarding code components (e.g. being
17 | subject to a Simplified BSD License) in Contributions.
18 | ## Working Group Information
19 |
20 | Discussion of this work occurs on the [Multiplexed Application Substrate over QUIC Encryption
21 | Working Group mailing list](mailto:masque@ietf.org)
22 | ([archive](https://mailarchive.ietf.org/arch/browse/masque/),
23 | [subscribe](https://www.ietf.org/mailman/listinfo/masque)).
24 | In addition to contributions in GitHub, you are encouraged to participate in
25 | discussions there.
26 |
27 | **Note**: Some working groups adopt a policy whereby substantive discussion of
28 | technical issues needs to occur on the mailing list.
29 |
30 | You might also like to familiarize yourself with other
31 | [Working Group documents](https://datatracker.ietf.org/wg/masque/documents/).
32 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # License
2 |
3 | See the
4 | [guidelines for contributions](https://github.com/ietf-wg-masque/draft-ietf-masque-quic-proxy/blob/main/CONTRIBUTING.md).
5 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | LIBDIR := lib
2 | include $(LIBDIR)/main.mk
3 |
4 | $(LIBDIR)/main.mk:
5 | ifneq (,$(shell grep "path *= *$(LIBDIR)" .gitmodules 2>/dev/null))
6 | git submodule sync
7 | git submodule update $(CLONE_ARGS) --init
8 | else
9 | git clone -q --depth 10 $(CLONE_ARGS) \
10 | -b main https://github.com/martinthomson/i-d-template $(LIBDIR)
11 | endif
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QUIC-Aware Proxying Using HTTP
2 |
3 | This is the working area for the IETF [MASQUE Working Group](https://datatracker.ietf.org/wg/masque/documents/) Internet-Draft, "QUIC-Aware Proxying Using HTTP".
4 |
5 | * [Editor's Copy](https://ietf-wg-masque.github.io/draft-ietf-masque-quic-proxy/#go.draft-ietf-masque-quic-proxy.html)
6 | * [Datatracker Page](https://datatracker.ietf.org/doc/draft-ietf-masque-quic-proxy)
7 | * [Working Group Draft](https://datatracker.ietf.org/doc/html/draft-ietf-masque-quic-proxy)
8 | * [Compare Editor's Copy to Working Group Draft](https://ietf-wg-masque.github.io/draft-ietf-masque-quic-proxy/#go.draft-ietf-masque-quic-proxy.diff)
9 |
10 |
11 | ## Contributing
12 |
13 | See the
14 | [guidelines for contributions](https://github.com/ietf-wg-masque/draft-ietf-masque-quic-proxy/blob/main/CONTRIBUTING.md).
15 |
16 | Contributions can be made by creating pull requests.
17 | The GitHub interface supports creating pull requests using the Edit (✏) button.
18 |
19 |
20 | ## Command Line Usage
21 |
22 | Formatted text and HTML versions of the draft can be built using `make`.
23 |
24 | ```sh
25 | $ make
26 | ```
27 |
28 | Command line usage requires that you have the necessary software installed. See
29 | [the instructions](https://github.com/martinthomson/i-d-template/blob/main/doc/SETUP.md).
30 |
31 |
--------------------------------------------------------------------------------
/draft-ietf-masque-quic-proxy.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: QUIC-Aware Proxying Using HTTP
3 | abbrev: QUIC Proxy
4 | category: exp
5 | docname: draft-ietf-masque-quic-proxy-latest
6 | submissiontype: IETF
7 | number:
8 | date:
9 | consensus: true
10 | v: 3
11 | area: Transport
12 | wg: MASQUE
13 | venue:
14 | group: "MASQUE"
15 | type: "Working Group"
16 | mail: "masque@ietf.org"
17 | arch: "https://mailarchive.ietf.org/arch/browse/masque/"
18 | github: "ietf-wg-masque/draft-ietf-masque-quic-proxy"
19 | latest: "https://ietf-wg-masque.github.io/draft-ietf-masque-quic-proxy/draft-ietf-masque-quic-proxy.html"
20 | keyword:
21 | - quic
22 | - http
23 | - datagram
24 | - udp
25 | - proxy
26 | - tunnels
27 | - quic in quic
28 | - turtles all the way down
29 | - masque
30 | - http-ng
31 |
32 | author:
33 | -
34 | ins: T. Pauly
35 | name: Tommy Pauly
36 | org: Apple Inc.
37 | street: One Apple Park Way
38 | city: Cupertino, California 95014
39 | country: United States of America
40 | email: tpauly@apple.com
41 |
42 | -
43 | ins: E. Rosenberg
44 | name: Eric Rosenberg
45 | org: Apple Inc.
46 | street: One Apple Park Way
47 | city: Cupertino, California 95014
48 | country: United States of America
49 | email: eric_rosenberg@apple.com
50 |
51 | -
52 | ins: "D. Schinazi"
53 | name: "David Schinazi"
54 | organization: "Google LLC"
55 | street: "1600 Amphitheatre Parkway"
56 | city: "Mountain View, California 94043"
57 | country: "United States of America"
58 | email: dschinazi.ietf@gmail.com
59 |
60 | normative:
61 | SP800-38A:
62 | author:
63 | name: Morris Dworkin
64 | org: National Institute of Standards and Technology
65 | title: >
66 | Recommendation for Block Cipher Modes of Operation: Methods and Techniques
67 | date: 2001-12-01
68 | target: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38a.pdf
69 |
70 | --- abstract
71 |
72 | This document extends UDP Proxying over HTTP to add optimizations for proxied
73 | QUIC connections. Specifically, it allows a proxy to reuse UDP 4-tuples for multiple
74 | proxied connections, and adds a mode of proxying in which QUIC short header packets
75 | can be forwarded and transformed through a HTTP/3 proxy rather than being fully
76 | re-encapsulated and re-encrypted.
77 |
78 | --- middle
79 |
80 | # Introduction {#introduction}
81 |
82 | UDP Proxying over HTTP {{!CONNECT-UDP=RFC9298}}
83 | defines a way to send datagrams through an HTTP proxy, where UDP is used to communicate
84 | between the proxy and a target server. This can be used to proxy QUIC
85 | connections {{!QUIC=RFC9000}}, since QUIC runs over UDP datagrams.
86 |
87 | This document uses the term "target" to refer to the server that a client is
88 | accessing via a proxy. This target may be an origin server hosting content, or
89 | another proxy for cases where proxies are chained together.
90 |
91 | This document extends the UDP proxying protocol to add signalling about QUIC
92 | Connection IDs. QUIC Connection IDs are used to identify QUIC connections in
93 | scenarios where there is not a strict one-to-one mapping between QUIC
94 | connections and UDP 4-tuples (pairs of IP addresses and ports).
95 |
96 | If a client permits proxy port reuse, once a proxy is aware of QUIC Connection IDs,
97 | it can reuse UDP 4-tuples between itself and a target for multiple proxied QUIC connections.
98 |
99 | For proxies that are themselves running on HTTP/3 {{!HTTP3=RFC9114}}, and thus
100 | are accessed by clients over QUIC, QUIC Connection IDs can be used to treat
101 | packets differently on the link between clients and proxies. New QUIC Connection IDs
102 | can be assigned to perform transformations to the packets that allow for efficient
103 | forwarding of packets that don't require full re-encapsulation and re-encryption
104 | of proxied QUIC packets within datagrams inside the QUIC connection between
105 | clients and proxies.
106 |
107 | This document defines two modes for proxying QUIC connections, "tunnelled" and
108 | "forwarded":
109 |
110 | 1. Tunnelled is the default mode for UDP proxying, defined in {{CONNECT-UDP}}.
111 | In this mode, packets in QUIC connection between the client and target are
112 | encapsulated inside the QUIC connection between the client and proxy.
113 | These packets use multiple layers of encryption and congestion control.
114 |
115 | 2. Forwarded is the mode of proxying added by this document. In this mode,
116 | packets in the QUIC connection between the client and target are sent with dedicated
117 | QUIC Connection IDs between the client and proxy, and use special-purpose
118 | tranforms instead of full re-encapsulation and re-encryption.
119 |
120 | QUIC long header packets between clients and targets MUST be proxied in tunnelled
121 | mode. QUIC short header packets between clients and targets MAY be proxied in
122 | forwarded mode, subject to negotiation between a client and a proxy.
123 |
124 | Forwarded mode is an optimization to reduce CPU and memory cost to clients and
125 | proxies and avoid encapsulation overhead for packets on the wire that reduce
126 | the effective MTU (Maximum Transmission Unit). This makes it suitable for
127 | deployment situations that otherwise relied on cleartext TCP
128 | proxies, which cannot support QUIC and have inferior security and privacy
129 | properties.
130 |
131 | The properties provided by the forwarded mode are as follows:
132 |
133 | - All packets sent between the client and the target traverse through the proxy
134 | device.
135 | - The target server cannot know the IP address of the client solely based on the
136 | proxied packets the target receives.
137 | - Observers of either or both of the links between client and proxy and between
138 | proxy and target are not able to learn more about the client-to-target
139 | communication than if no proxy was used.
140 |
141 | Forwarded mode does not prevent correlation of packets on the link between
142 | client and proxy and the link between proxy and target by an entity that
143 | can observe both links. The precise risks depend on the negotiated transform
144 | ({{transforms}}). See {{security}} for further discussion.
145 |
146 | Both clients and proxies can unilaterally choose to disable forwarded mode for
147 | any client-to-target connection.
148 |
149 | The forwarded mode of proxying is only defined for HTTP/3 {{HTTP3}} and not
150 | any earlier versions of HTTP.
151 |
152 | QUIC proxies only need to understand the Header Form bit, and the connection ID
153 | fields from packets in client-to-target QUIC connections. Since these fields
154 | are all in the QUIC invariants header {{!INVARIANTS=RFC8999}}, QUIC proxies can
155 | proxy all versions of QUIC.
156 |
157 | ## Conventions and Definitions {#conventions}
158 |
159 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
160 | "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this
161 | document are to be interpreted as described in BCP 14 {{!RFC2119}} {{!RFC8174}}
162 | when, and only when, they appear in all capitals, as shown here.
163 |
164 | ## Terminology
165 |
166 | This document uses the following terms:
167 |
168 | - Client: the client of all QUIC connections discussed in this document.
169 | - Proxy: the endpoint that responds to the UDP proxying request.
170 | - Target: the server that a client is accessing via a proxy.
171 | - Client-to-proxy 4-tuple: the UDP 4-tuple (client IP address, client UDP port,
172 | proxy IP address, proxy UDP port) used to communicate between the client and
173 | the proxy.
174 | - Proxy-to-target 4-tuple: the UDP 4-tuple (proxy IP address, proxy UDP port,
175 | target IP address, target UDP port) used to communicate between the proxy and
176 | the target.
177 | - Client Connection ID (CID): a QUIC Connection ID that is chosen by the client, and
178 | is used in the Destination Connection ID field of packets from the target to
179 | the client.
180 | - Target Connection ID (CID): a QUIC Connection ID that is chosen by the target, and
181 | is used in the Destination Connection ID field of packets from the client to
182 | the target.
183 | - Virtual Connection ID (VCID): a fake QUIC Connection ID chosen by the proxy
184 | that is used on the client-to-proxy 4-tuple in forwarded mode.
185 | - Client VCID: a VCID used by the proxy to send forwarded packets from the target
186 | to the client.
187 | - Target VCID: a VCID used by the client to send forwarded packets to the target
188 | via the proxy.
189 | - Packet Transform: the procedure used to modify packets before they enter the
190 | client-proxy link.
191 |
192 | # Protocol Overview
193 |
194 | QUIC-aware proxying involves a client, a proxy, and a target. Although
195 | multiple proxies can be chained together in sequence (which is
196 | a main motivation for enabling forwarded mode), each subsequent proxy
197 | is just treated as a target by the preceding proxy.
198 |
199 | ~~~ aasvg
200 | +--------+ +-----------+ +--------+
201 | | | | | | |
202 | | Client +--------------+ Proxy +--------------+ Target |
203 | | | | | | |
204 | +--------+ +-----------+ +--------+
205 |
206 | <----------- Client-to-target connection ----------->
207 |
208 |
209 | <-- Client-to-proxy --> <-- Proxy-to-target -->
210 | 4-tuple 4-tuple
211 | ~~~
212 | {: #overview-diagram title="Diagram of roles in QUIC-aware proxying"}
213 |
214 | All QUIC-aware proxying relies on the proxy learning information about
215 | QUIC connection IDs on the client-to-target QUIC connection ({{cid-awareness}}).
216 |
217 | Forwarded mode adds the concept of Virtual Connection IDs that are assigned
218 | by the proxy to use to identify packets on the client-to-proxy 4-tuple ({{vcids}}).
219 |
220 | Negotation of modes and assignment of connection IDs is described in {{negotiation}}.
221 |
222 | ## Connection ID Awareness {#cid-awareness}
223 |
224 | For a proxy to be aware of proxied QUIC connection IDs, it needs to know and
225 | correlate three values:
226 |
227 | 1. The HTTP stream used to proxy a client-to-target QUIC connection
228 | 1. The client-chosen connection ID on the client-to-target QUIC connection,
229 | or the "client CID"
230 | 1. The target-chosen connection ID on the client-to-target QUIC connection,
231 | or the "target CID"
232 |
233 | For example, consider a proxy using HTTP/3 that has two clients (A and B) connected
234 | simultaneously, each client coming from a different IP address and port.
235 | Each client makes a request to proxy a UDP flow to "target.example.net" on
236 | port 443. If the proxy knows that client A's connection to the target
237 | has negotiated a client CID `AAAA0000` and a target CID `0000AAAA`, and client B's
238 | connection to the target has negotiated a client CID `BBBB0000` and a target CID
239 | `0000BBBB`, then the proxy would be able to use the same proxy-to-target 4-tuple
240 | for both connections, because it can route packets from the target to the
241 | correct client based on CID `AAAA0000` or `BBBB0000`.
242 |
243 | ~~~ aasvg
244 | <---------- Client A-to-target connection ---------->
245 | AAAA0000 0000AAAA
246 | +--------+ +-----------+
247 | | Client +--------------+ +
248 | | A | | |
249 | +--------+ | | +--------+
250 | | | Shared | |
251 | | Proxy +--------------+ Target |
252 | | | 4-tuple | |
253 | +--------+ | | +--------+
254 | | Client | | |
255 | | B +--------------+ |
256 | +--------+ +-----------+
257 | BBBB0000 0000BBBB
258 | <---------- Client B-to-target connection ---------->
259 | ~~~
260 | {: #sharing-tuple-diagram title="Example of sharing a proxy-to-target 4-tuple"}
261 |
262 | In order to share a proxy-to-target 4-tuple between multiple proxied connections,
263 | the proxy MUST guarantee that the client CIDs do not conflict. See {{conflicts}}
264 | for more discussion of handlng CID conflicts.
265 |
266 | ## Virtual Connection IDs {#vcids}
267 |
268 | Virtual Connection IDs (VCIDs) are QUIC Connection IDs used on the link between a
269 | client and proxy that do not belong to the QUIC connection between the client
270 | and proxy, but instead are aliases for particular client-to-target connections.
271 | VCIDs are only used in forwarded mode. They are established using HTTP capsules
272 | {{!HTTP-CAPSULES=RFC9297}} as described in {{cid-capsules}}.
273 |
274 | For example, consider a proxy using HTTP/3 that has a single client connected
275 | to it. The client-to-proxy QUIC connection has `CCCC0000` as the client CID
276 | and `0000CCCC` as the proxy CID. The client has connected to a single target
277 | through the proxy, and the client-to-target QUIC connection has `CCCC1111`
278 | as the client CID and `1111CCCC` as the target CID. In order to use
279 | forwarded mode, the proxy assigns VCIDs to use on the client-to-proxy link
280 | to represent the client-to-target connection. In this case, the VCIDs could
281 | be `CCCC2222` for the client's VCID and `2222CCCC` for the proxy's VCID that
282 | it uses to forward to the target.
283 |
284 | ~~~ aasvg
285 | +--------+ +-----------+ +--------+
286 | | | | | | |
287 | | Client +--------------+ Proxy +--------------+ Target |
288 | | | | | | |
289 | +--------+ +-----------+ +--------+
290 |
291 | CCCC0000 0000CCCC
292 | <-- Client-to-proxy -->
293 | connection
294 |
295 | CCCC1111 1111CCCC
296 | <----------- Client-to-target connection ----------->
297 |
298 | CCCC2222 (CCCC1111) 2222CCCC (1111CCCC)
299 | <-- Client-to-proxy -->
300 | VCIDs
301 | ~~~
302 | {: #vcid-diagram title="Diagram of VCIDs in forwarded mode"}
303 |
304 | In order for a proxy to correctly route packets using VCIDs from
305 | client-to-target and target-to-client, the proxy MUST guarantee that
306 | the mappings between VCIDs, CIDs, and 4-tuples are unique. Specifically,
307 | in order to route packets sent by the client, the proxy needs to be able
308 | to observe the VCID and the client-to-proxy 4-tuple, and map them
309 | to a specific target CID and proxy-to-target 4-tuple. In order to route
310 | packets sent by a target, the proxy needs to be able to observe the client
311 | CID and the proxy-to-target 4-tuple, and map them to a specific VCID
312 | and client-to-proxy 4-tuple. Since proxies choose the VCID values, they
313 | can ensure that the VCIDs are distinguishable.
314 |
315 | Servers receiving QUIC packets can employ load balancing
316 | strategies such as those described in {{?QUIC-LB=I-D.ietf-quic-load-balancers}}
317 | that encode routing information in the connection ID. When operating in
318 | forwarded mode, clients send QUIC packets destined for the target directly
319 | to the proxy. Since these packets are generated using the target CID,
320 | load balancers may not have the necessary information to route packets to the correct proxy. The target VCID
321 | is a VCID chosen by the proxy that the client uses when sending
322 | forwarded mode packets. The proxy replaces the target VCID
323 | with the target CID prior to forwarding the packet to the target.
324 |
325 | Similarly, QUIC requires that connection IDs aren't reused over multiple network
326 | paths to avoid linkability. The client VCID is a connection ID
327 | chosen by the proxy that the proxy uses when sending forwarded mode packets.
328 | The proxy replaces the client CID with the client VCID prior to
329 | forwarding the packet to the client. Clients take advantage of this
330 | to avoid linkability when migrating a client to proxy network path. The Virtual
331 | client CID allows the connection ID bytes to change on the wire
332 | without requiring the connection IDs on the client to target connection change.
333 | To reduce the likelihood of connection ID conflicts, the proxy MUST choose a
334 | client VCID that is at least as long as the original client CID. Similarly,
335 | clients multiplexing connections on the same UDP 4-tuple SHOULD
336 | choose a client CID that's sufficiently long to reduce the likelihood
337 | of a conflict with the proxy-chosen client VCID. The client VCID MUST either be
338 | constructed such that it is unpredictable to the client or to guarantee no
339 | conflicts among all proxies sharing an IP address
340 | and port. See {{security}} for more discussion on client VCID
341 | construction.
342 |
343 | Clients and Proxies not implementing forwarded mode do not need to consider
344 | VCIDs since all client-to-target datagrams will be encapsulated
345 | within the client-to-proxy connection.
346 |
347 | ## Negotiating Modes and Connection IDs {#negotiation}
348 |
349 | In order to support QUIC-aware proxying, both clients and proxies need
350 | to support capsules {{HTTP-CAPSULES}}, which is indicated by including
351 | the "Capsule-Protocol" header field in requests and responses. If this header
352 | field is not included, none of the functionality in this document can be used.
353 |
354 | ~~~
355 | capsule-protocol = ?1
356 | ~~~
357 |
358 | To permit the proxy to share target-facing ports, the client needs to include the
359 | "Proxy-QUIC-Port-Sharing" header field, as defined in {{port-sharing-header}}.
360 | This indicates that the proxy may share target-facing 4-tuples concurrently with
361 | other QUIC connections. Clients that do not want the target-facing 4-tuple shared
362 | will not include this header or will provide it with a value of "?0".
363 |
364 | ~~~
365 | proxy-quic-port-sharing = ?1
366 | ~~~
367 |
368 | To support forwarded mode, both clients and proxies need to include
369 | the "Proxy-QUIC-Forwarding" header field, as defined in {{forwarding-header}}.
370 | This indicates support for forwarded mode, and allows negotiation of
371 | packet transforms to apply when forwarding ({{transforms}}), along
372 | with any parameters for specific transforms. Clients or proxies that
373 | don't support forwarded mode will not include this header field.
374 |
375 | ~~~
376 | proxy-quic-forwarding = ?1; accept-transform=scramble,identity; \
377 | scramble-key=:abc...789=:
378 | ~~~
379 |
380 | If neither header is supplied with a value of "?1", none of the functionality
381 | in this document can be used and handling reduces to normal connect-udp.
382 |
383 | After negotiating support with header fields, clients and proxies use
384 | the capsules defined in {{cid-capsules}} to communicate information
385 | about CIDs and VCIDs.
386 |
387 | For QUIC-aware proxying without forwarded mode, the steps are as follows:
388 |
389 | 1. The client sends the `REGISTER_CLIENT_CID` capsule once it selects a
390 | CID that it will use for receiving packets from a target.
391 |
392 | 1. The proxy sends the `ACK_CLIENT_CID` capsule to acknowledge that CID,
393 | with no associated client VCID; alternatively, the proxy can send the
394 | `CLOSE_CLIENT_CID` if it detects a conflict with another CID.
395 |
396 | 1. The client sends the `REGISTER_TARGET_CID` capsule as soon as it learns
397 | the target CID on the client-to-target connection.
398 |
399 | 1. The proxy sends the `ACK_TARGET_CID` capsule to acknowledge that CID,
400 | with no associated target VCID; alternatively, the proxy can send the
401 | `CLOSE_TARGET_CID` if it detects a conflict with another CID.
402 |
403 | 1. The proxy sends the `MAX_CONNECTION_IDS` capsule to allow additional
404 | registration of new connection IDs via future `REGISTER_CLIENT_CID` and
405 | `REGISTER_TARGET_CID` capsules.
406 |
407 | 1. Whenever a client or target stops uses a particular CID, the client
408 | sends a `CLOSE_CLIENT_CID` or `CLOSE_TARGET_CID` capsule. The client
409 | can also initiate new `REGISTER_CLIENT_CID` or `REGISTER_TARGET_CID`
410 | exchanges at any time.
411 |
412 | For QUIC-aware proxying with forwarded mode, the steps are as follows:
413 |
414 | 1. The client sends the `REGISTER_CLIENT_CID` capsule once it selects a
415 | CID that it will use for receiving packets from a target.
416 |
417 | 1. The proxy sends the `ACK_CLIENT_CID` capsule to acknowledge that CID,
418 | with a client VCID; alternatively, the proxy can send the
419 | `CLOSE_CLIENT_CID` if it detects a conflict with another CID.
420 |
421 | 1. The client sends the `ACK_CLIENT_VCID` capsule to acknowledge the
422 | client VCID, which allows forwarded packets for that VCID to be used.
423 |
424 | 1. The client sends the `REGISTER_TARGET_CID` capsule as soon as it learns
425 | the target CID on the client-to-target connection.
426 |
427 | 1. The proxy sends the `ACK_TARGET_CID` capsule to acknowledge that CID,
428 | with a target VCID; alternatively, the proxy can send the
429 | `CLOSE_TARGET_CID` if it detects a conflict with another CID. Once
430 | the client receives the target VCID, it can start sending forwarded
431 | packets using the target VCID.
432 |
433 | 1. The proxy sends the `MAX_CONNECTION_IDS` capsule to allow additional
434 | registration of new connection IDs via future `REGISTER_CLIENT_CID` and
435 | `REGISTER_TARGET_CID` capsules.
436 |
437 | 1. Whenever a client or target stops uses a particular CID, the client
438 | sends a `CLOSE_CLIENT_CID` or `CLOSE_TARGET_CID` capsule. The client
439 | can also initiate new `REGISTER_CLIENT_CID` or `REGISTER_TARGET_CID`
440 | exchanges at any time.
441 |
442 | # Proxy-QUIC-Forwarding Header {#forwarding-header}
443 |
444 | A client initiates UDP proxying via a CONNECT request as defined
445 | in {{CONNECT-UDP}}. Within its request, it includes the "Proxy-QUIC-Forwarding"
446 | header to indicate whether or not the request should support forwarding.
447 | If this header is not included, the client MUST NOT send any connection ID
448 | capsules.
449 |
450 | "Proxy-QUIC-Forwarding" is an Item Structured Header {{!RFC8941}}. Its
451 | value MUST be a Boolean.
452 |
453 | If the client wants to enable QUIC packet forwarding for this request, it sets
454 | the value to "?1". If it doesn't want to enable forwarding, but instead only
455 | provide information about QUIC Connection IDs for the purpose of allowing
456 | the proxy to share a proxy-to-target 4-tuple, it sets the value to "?0".
457 |
458 | The client MUST add an "accept-transform" parameter whose value is an
459 | `sf-string` containing the supported packet transforms ({{transforms}})
460 | in order of descending preference, separated by commas. If the proxy receives a
461 | "Proxy-QUIC-Forwarding" header without the "accept-transform" parameters, it
462 | MUST ignore the header and respond as if the client had not sent the
463 | "Proxy-QUIC-Forwarding" header.
464 |
465 | If the proxy supports QUIC-aware proxying, it will include the
466 | "Proxy-QUIC-Forwarding" header in successful HTTP responses. The value
467 | indicates whether or not the proxy supports forwarding. If the client does
468 | not receive this header in responses, the client SHALL assume that the proxy
469 | does not support this extension.
470 |
471 | The proxy MUST include a "transform" parameter whose value is an `sf-string`
472 | indicating the selected transform. If the proxy does not recognize or accept
473 | any of the transforms offered by the client, it MUST omit this parameter and
474 | set the header field value to "?0", or omit the header entirely.
475 |
476 | # Proxy-QUIC-Port-Sharing Header {#port-sharing-header}
477 |
478 | A client may include the "Proxy-QUIC-Port-Sharing" header to indicate whether
479 | or not the proxy is permitted to share ports between this QUIC connection and other
480 | proxied QUIC connections.
481 |
482 | "Proxy-QUIC-Port-Sharing" is an Item Structured Header {{!RFC8941}}. Its value
483 | MUST be a Boolean.
484 |
485 | Clients SHOULD send this with a value of "?1" unless the client wishes to prohibit
486 | this behavior. Permitting the proxy to share ports may allow the proxy to conserve
487 | resources and support more clients.
488 |
489 | A proxy that does not support port sharing, SHOULD send "Proxy-QUIC-Port-Sharing"
490 | with a value of "?0". Doing so allows clients to stop sending capsules for this
491 | extension if forwarding mode is also not supported. Clients who send "?1", but do
492 | not receive any "Proxy-QUIC-Port-Sharing" header in response must assume that port
493 | sharing may be in effect and MUST continue register connection IDs in order for
494 | the proxied connection to continue to work.
495 |
496 | # Connection ID Capsules {#cid-capsules}
497 |
498 | Connection ID awareness relies on using capsules {{HTTP-CAPSULES}} to
499 | signal addition and removal of Connection IDs. Clients send capsules
500 | to let proxies know when Connection IDs on the client-to-target
501 | QUIC connection are changing. Proxies send capsules to acknowledge or
502 | reject these Connection IDs, and in forwarded mode to let clients know
503 | about Virtual Connection IDs to use on the client-to-proxy link.
504 |
505 | Note that these capsules do not register contexts. QUIC packets are encoded
506 | using HTTP Datagrams with the context ID set to zero as defined in
507 | {{CONNECT-UDP}}.
508 |
509 | The REGISTER_CLIENT_CID ({{capsule-reg-client}}) and REGISTER_TARGET_CID
510 | ({{capsule-reg-target}}) capsule types allow a client to inform
511 | the proxy about a new client CID or a new target CID,
512 | respectively. These capsule types MUST only be sent by a client. These capsule
513 | types share a sequence number space which allows the proxy to limit the
514 | number of active registrations. The first registration (of either client CID or target CID)
515 | has sequence number 0, and subsequent registrations increment the sequence number
516 | by 1.
517 |
518 | The ACK_CLIENT_CID ({{capsule-ack-client}}) and ACK_TARGET_CID
519 | ({{capsule-ack-target}}) capsule types are sent by the proxy to the client
520 | to indicate that a mapping was successfully created for a registered
521 | connection ID as well as optionally provide the Virtual Connection IDs that can be
522 | used in forwarded mode. These capsule types MUST only be sent by a proxy.
523 |
524 | The ACK_CLIENT_VCID ({{capsule-ack-virtual}}) capsule type MUST only be sent
525 | by the client and only when forwarded mode is enabled. It is sent by the client
526 | to the proxy in response to an ACK_CLIENT_CID capsule to indicate that the client
527 | is ready to receive forwarded mode packets with the specified virtual connection ID.
528 | The proxy MUST NOT send forwarded mode packets to the client prior to receiving this
529 | acknowledgement. This capsule also contains a Stateless Reset Token the client
530 | may respond with when receiving forwarded mode packets with the specified
531 | virtual connection ID.
532 |
533 | The CLOSE_CLIENT_CID and CLOSE_TARGET_CID capsule types ({{capsule-close}})
534 | allow either a client or a proxy to remove a mapping for a connection ID.
535 | These capsule types MAY be sent by either a client or the proxy. If a proxy sends a
536 | CLOSE_CLIENT_CID without having sent an ACK_CLIENT_CID, or if a proxy
537 | sends a CLOSE_TARGET_CID without having sent an ACK_TARGET_CID,
538 | it is rejecting a Connection ID registration. Similarly, if a client sends
539 | CLOSE_CLIENT_CID without having sent an ACK_CLIENT_VCID capsule, the client is
540 | either rejecting the proxy-chosen client VCID or no longer
541 | needs the connection ID registered.
542 |
543 | The MAX_CONNECTION_IDS capsule type {{capsule-max-cids}} MUST only be sent by the
544 | proxy. It indicates to the client the maximum permitted sequence number for
545 | connection ID registrations. This allows the proxy to limit the number of active
546 | registrations. The initial maximum is 1, allowing the client to send 2 registrations,
547 | one with sequence number 0 and another with sequence number 1. A proxy MUST NOT
548 | send a MAX_CONNECTION_IDS capsule with a value less than 1. Clients receiving a
549 | MAX_CONNECTION_IDS capsule with a value less than 1 MUST reset the stream with
550 | H3_DATAGRAM_ERROR error code.
551 |
552 | When port sharing {{port-sharing-header}} is supported, the client MUST register
553 | and receive acknowledgements for client and target CIDs before using them. Packets with
554 | unknown connection IDs received by the proxy on a target-facing sockets that support
555 | port sharing MUST be dropped. In order to avoid introducing an additional round trip
556 | on setup, a REGISTER_CLIENT_CID capsule SHOULD be sent at the same time as the client's
557 | first flight. If the proxy rejects the client CID, the proxy MUST drop all packets until
558 | it has sent an ACK_CLIENT_CID capsule and the client MUST NOT send any packets until
559 | receiving an ACK_CLIENT_CID. When port sharing is supported, a proxy SHOULD buffer a
560 | reasonable number of incoming packets while waiting for the first REGISTER_CLIENT_CID
561 | capsule.
562 |
563 | ## REGISTER_CLIENT_CID {#capsule-reg-client}
564 |
565 | The REGISTER_CLIENT_CID capsule is sent by the client and contains a single
566 | connection ID that is the client-provided connection ID on the client-to-target QUIC
567 | connection.
568 |
569 | ~~~
570 | Register CID Capsule {
571 | Type (i) = see {{iana}} for the value of the capsule type
572 | Length (i),
573 | Connection ID (0..2040),
574 | }
575 | ~~~
576 | {: #fig-capsule-register-client-cid title="Register CID Capsule Format"}
577 |
578 | Connection ID:
579 | : A connection ID being registered, which is between 0 and 255 bytes in
580 | length. The length of the connection ID is implied by the length of the
581 | capsule. Note that in QUICv1, the length of the Connection ID is limited
582 | to 20 bytes, but QUIC invariants allow up to 255 bytes.
583 |
584 | ## REGISTER_TARGET_CID {#capsule-reg-target}
585 |
586 | The REGISTER_TARGET_CID capsule is sent by the client and includes the
587 | target-provided connection ID on the client-to-target QUIC connection, and
588 | the corresponding Stateless Reset Token.
589 |
590 | ~~~
591 | Register Target CID Capsule {
592 | Type (i) = see {{iana}} for the value of the capsule type
593 | Length (i),
594 | Connection ID Length (i)
595 | Connection ID (0..2040),
596 | Stateless Reset Token Length (i),
597 | Stateless Reset Token (..),
598 | }
599 | ~~~
600 | {: #fig-capsule-register-target-cid title="Register Target CID Capsule Format"}
601 |
602 | Connection ID Length
603 | : The length of the connection ID being registered, which is between 0 and
604 | 255. Note that in QUICv1, the length of the Connection ID is limited to 20
605 | bytes, but QUIC invariants allow up to 255 bytes.
606 |
607 | Connection ID
608 | : A connection ID being registered whose length is equal to Connection ID
609 | Length. This is the real target CID.
610 |
611 | Stateless Reset Token Length
612 | : The length of the target-provided Stateless Reset Token.
613 |
614 | Stateless Reset Token
615 | : The target-provided Stateless Reset token allowing the proxy to correctly
616 | recognize Stateless Reset packets to be tunnelled to the client.
617 |
618 | ## ACK_CLIENT_CID {#capsule-ack-client}
619 |
620 | The ACK_CLIENT_CID capsule is sent by the proxy in
621 | response to a REGISTER_CLIENT_CID capsule. It optionally assigns a Virtual
622 | Connection ID when forwarded mode is supported.
623 |
624 | ~~~
625 | Acknowledge Client CID Capsule {
626 | Type (i) = see {{iana}} for the value of the capsule type
627 | Length (i)
628 | Connection ID Length (i)
629 | Connection ID (0..2040),
630 | Virtual Connection ID Length (i)
631 | Virtual Connection ID (0..2040),
632 | }
633 | ~~~
634 | {: #fig-capsule-ack-client-cid title="Acknowledge Client CID Capsule Format"}
635 |
636 | Connection ID Length
637 | : The length of the connection ID being acknowledged, which
638 | is between 0 and 255. Note that in QUICv1, the length of the Connection ID
639 | is limited to 20 bytes, but QUIC invariants allow up to 255 bytes.
640 |
641 | Connection ID
642 | : A connection ID being acknowledged whose length is equal to
643 | Connection ID Length. This is the real Cilent Connection ID.
644 |
645 | Virtual Connection ID Length
646 | : The length of the client VCID being provided. This MUST be a
647 | valid connection ID length for the QUIC version used in the client-to-proxy QUIC
648 | connection. When forwarded mode is not negotiated, the length MUST be zero.
649 | The Virtual Connection ID Length and Connection ID Length SHOULD be equal
650 | when possible to avoid the need to resize packets during replacement. The
651 | client VCID Length MUST be at least as large as the
652 | Connection ID to reduce the likelihood of connection ID conflicts.
653 |
654 | Virtual Connection ID
655 | : The proxy-chosen connection ID that the proxy MUST use when sending in
656 | forwarded mode. The proxy rewrites forwarded mode packets to contain the
657 | correct client VCID prior to sending them to the client.
658 |
659 | ## ACK_TARGET_CID {#capsule-ack-target}
660 |
661 | The ACK_TARGET_CID capsule is sent by the proxy in
662 | response to a REGISTER_TARGET_CID capsule. It optionally assigns a Virtual
663 | Connection ID and Stateless Reset Token if forwarded mode is enabled.
664 |
665 | ~~~
666 | Acknowledge Target CID Capsule {
667 | Type (i) = see {{iana}} for the value of the capsule type
668 | Length (i)
669 | Connection ID Length (i)
670 | Connection ID (0..2040),
671 | Virtual Connection ID Length (i)
672 | Virtual Connection ID (0..2040),
673 | Stateless Reset Token Length (i),
674 | Stateless Reset Token (..),
675 | }
676 | ~~~
677 | {: #fig-capsule-ack-target-cid title="Acknowledge Target CID Capsule Format"}
678 |
679 | Connection ID Length
680 | : The length of the connection ID being acknowledged, which
681 | is between 0 and 255. Note that in QUICv1, the length of the Connection ID
682 | is limited to 20 bytes, but QUIC invariants allow up to 255 bytes.
683 |
684 | Connection ID
685 | : A connection ID being acknowledged whose length is equal to
686 | Connection ID Length. This is the real target CID.
687 |
688 | Virtual Connection ID Length
689 | : The length of the target VCID being provided. This MUST be a
690 | valid connection ID length for the QUIC version used in the client-to-proxy QUIC
691 | connection. When forwarded mode is not negotiated, the length MUST be zero.
692 | The Virtual Connection ID Length and Connection ID Length SHOULD be equal
693 | when possible to avoid the need to resize packets during replacement.
694 |
695 | Virtual Connection ID
696 | : The proxy-chosen connection ID that the client MUST use when sending in
697 | forwarded mode. The proxy rewrites forwarded mode packets to contain the
698 | correct target CID prior to sending them.
699 |
700 | Stateless Reset Token Length
701 | : The length of the Stateless Reset Token sent by the proxy in response to
702 | forwarded mode packets in order to reset the client-to-target QUIC connection.
703 | When forwarded mode is not negotiated, the length MUST be zero. Proxies choosing
704 | not to support stateless resets MAY set the length to zero. Clients receiving a
705 | zero-length stateless reset token MUST ignore it.
706 |
707 | Stateless Reset Token
708 | : A Stateless Reset Token allowing reset of the client-to-target connection in
709 | response to client-to-target forwarded mode packets.
710 |
711 | ## ACK_CLIENT_VCID {#capsule-ack-virtual}
712 |
713 | The ACK_CLIENT_VCID capsule type is sent by the client in
714 | response to an ACK_TARGET_CID capsule that contains a virtual connection ID.
715 |
716 | ~~~
717 | Acknowledge Client VCID Capsule {
718 | Type (i) = see {{iana}} for the value of the capsule type
719 | Length (i)
720 | Connection ID Length (i)
721 | Connection ID (0..2040),
722 | Virtual Connection ID Length (i)
723 | Virtual Connection ID (0..2040),
724 | Stateless Reset Token Length (i),
725 | Stateless Reset Token (..),
726 | }
727 | ~~~
728 | {: #fig-capsule-ack-virtual-client-cid title="Acknowledge Client VCID Capsule Format"}
729 |
730 | Connection ID Length
731 | : The length of the connection ID being acknowledged, which
732 | is between 0 and 255. Note that in QUICv1, the length of the Connection ID
733 | is limited to 20 bytes, but QUIC invariants allow up to 255 bytes.
734 |
735 | Connection ID
736 | : A connection ID being acknowledged whose length is equal to
737 | Connection ID Length. This is the real Cilent Connection ID.
738 |
739 | Virtual Connection ID Length
740 | : The length of the client VCID being acknowledged.
741 |
742 | Virtual Connection ID
743 | : The proxy-chosen virtual connection ID being acknowledged whose length is
744 | equal to Virtual Connection ID Length.
745 |
746 | Stateless Reset Token Length
747 | : The length of the Stateless Reset Token that may be sent by the client in
748 | response to forwarded mode packets to reset the client-to-target connection.
749 | Clients choosing not to support stateless resets MAY set the length to zero.
750 | Proxies receiving a zero-length stateless reset token MUST ignore it.
751 |
752 | Stateless Reset Token
753 | : A Stateless Reset Token allowing reset of the target-to-client forwarding rule
754 | in response to target-to-client forwarded mode packets.
755 |
756 | ## CLOSE_CLIENT_CID and CLOSE_TARGET_CID {#capsule-close}
757 |
758 | CLOSE_CLIENT_CID and CLOSE_TARGET_CID capsule types include a single connection ID to close. They
759 | can be sent by either clients or proxies.
760 |
761 | ~~~
762 | Close CID Capsule {
763 | Type (i) = see {{iana}} for the values of the capsule types
764 | Length (i),
765 | Connection ID (0..2040),
766 | }
767 | ~~~
768 | {: #fig-capsule-close-cid title="Close CID Capsule Format"}
769 |
770 | Connection ID:
771 | : A connection ID being closed, which is between 0 and 255 bytes in
772 | length. The length of the connection ID is implied by the length of the
773 | capsule. Note that in QUICv1, the length of the Connection ID is limited
774 | to 20 bytes, but QUIC invariants allow up to 255 bytes.
775 |
776 | ## MAX_CONNECTION_IDS {#capsule-max-cids}
777 |
778 | The MAX_CONNECTION_IDS capsule is sent by the proxy
779 | to permit additional connection ID registrations.
780 |
781 | ~~~
782 | Maximum Connection IDs Capsule {
783 | Type (i) = see {{iana}} for the value of the capsule type
784 | Length (i)
785 | Maximum Sequence Number (i)
786 | }
787 | ~~~
788 | {: #fig-capsule-max-connection-ids title="Maximum Connection IDs Capsule Format"}
789 |
790 | Maximum Sequence Number
791 | : The maximum permitted sequence number for connection ID registrations. This MUST
792 | NOT be less than 1.
793 |
794 | ## Detecting Conflicts {#conflicts}
795 |
796 | In order to be able to route packets correctly in both tunnelled and forwarded
797 | mode, proxies check for conflicts before creating a new CID mapping. If a conflict
798 | is detected, the proxy will reject the client's request using a CLOSE_CLIENT_CID
799 | or CLOSE_TARGET_CID capsule.
800 |
801 | Two 4-tuples conflict if and only if all members of the 4-tuple (local IP
802 | address, local UDP port, remote IP address, and remote UDP port) are identical.
803 |
804 | Two Connection IDs conflict if and only if one Connection ID is equal to or a
805 | prefix of another. For example, a zero-length Connection ID conflicts with all
806 | connection IDs. This definition of a conflict originates from the fact that
807 | QUIC short headers do not carry the length of the Destination Connection ID
808 | field, and therefore if two short headers with different Destination Connection
809 | IDs are received on a shared 4-tuple, one being a prefix of the other prevents
810 | the receiver from identifying which mapping this corresponds to.
811 |
812 | The proxy treats two mappings as being in conflict when a conflict is detected
813 | for all elements on the left side of the mapping diagrams above.
814 |
815 | Since very short Connection IDs are more likely to lead to conflicts,
816 | particularly zero-length Connection IDs, a proxy MAY choose to reject all
817 | requests for very short Connection IDs as conflicts, in anticipation of future
818 | conflicts.
819 |
820 | ## Client Considerations
821 |
822 | The client sends a REGISTER_CLIENT_CID capsule before it advertises a new
823 | client CID to the target, and a REGISTER_TARGET_CID capsule when
824 | it has received a new target CID for the target. In order to change
825 | the connection ID bytes on the wire, a client can solicit new virtual connection
826 | IDs by re-registering the same connection IDs. The client may solicit a new
827 | target VCID by sending a REGISTER_TARGET_CID capsule with a
828 | previously registered target CID. Similarly, the client may solicit a
829 | new client VCID by sending a REGISTER_CLIENT_CID with a
830 | previously registered client CID. The client MUST acknowledge the new
831 | client VCID with an ACK_CLIENT_VCID capsule or close the
832 | registration. The proxy MUST NOT send in forwarded mode until ACK_CLIENT_VCID
833 | has been received. Clients are responsible for changing Virtual Connection IDs
834 | when the HTTP stream's network path changes to avoid linkability across network
835 | paths. Note that initial REGISTER_CLIENT_CID capsules MAY be sent prior to
836 | receiving an HTTP response from the proxy.
837 |
838 | Connection ID registrations are subject to a proxy-advertised limit. Each registration
839 | has a corresponding sequence number. The client MUST NOT send a registration
840 | capsule with a sequence number greater than what the proxy advertises via the
841 | MAX_CONNECTION_IDS capsule. The initial MAX_CONNECTION_IDS value is 1, allowing both
842 | sequence numbers 0 and 1 for a total of two registrations without receiving a
843 | MAX_CONNECTION_IDS capsule from the proxy.
844 |
845 | Clients that cannot register new connection IDs within a reasonable time due to
846 | the MAX_CONNECTION_IDS limit SHOULD abort the proxied connection by resetting the HTTP
847 | stream with error code NO_ERROR. This may happen, for example, if the target server
848 | sends a NEW_CONNECTION_ID frame with Sequence Number and Retire Prior To equal to the
849 | same value.
850 |
851 | Clients can cease receiving with forwarded mode over an existing tunnel while
852 | retaining the same client-to-target connection by creating a new tunnel with
853 | "Proxy-QUIC-Forwarding" set to "?0" and migrating the client-to-target connection.
854 |
855 | ### New Proxied Connection Setup
856 |
857 | To initiate QUIC-aware proxying, the client sends a REGISTER_CLIENT_CID
858 | capsule containing the initial client CID that the client has
859 | advertised to the target.
860 |
861 | If the mapping is created successfully, the client will receive a
862 | ACK_CLIENT_CID capsule that contains the same client CID that was
863 | requested as well as a client VCID that the client MUST use
864 | when sending forwarded mode packets, assuming forwarded mode is supported.
865 |
866 | If forwarded mode is supported, the client MUST respond with an
867 | ACK_CLIENT_VCID to signal to the proxy that it may start sending forwarded mode
868 | packets. If forwarded mode is not supported, an ACK_CLIENT_VCID capsule MUST
869 | NOT be sent.
870 |
871 | Since clients are always aware whether or not they are using a QUIC proxy,
872 | clients are expected to cooperate with proxies in selecting client CIDs.
873 | A proxy detects a conflict when it is not able to create a unique mapping
874 | using the client CID ({{conflicts}}). It can reject requests that
875 | would cause a conflict and indicate this to the client by replying with a
876 | CLOSE_CLIENT_CID capsule. In order to avoid conflicts, clients SHOULD select
877 | client CIDs of at least 8 bytes in length with unpredictable values.
878 | A client also SHOULD NOT select a client CID that matches the ID used
879 | for the QUIC connection to the proxy, as this inherently creates a conflict.
880 |
881 | If the rejection indicated a conflict due to the client CID, the
882 | client MUST select a new Connection ID before sending a new request, and
883 | generate a new packet. For example, if a client is sending a QUIC Initial
884 | packet and chooses a Connection ID that conflicts with an existing mapping
885 | to the same target server, it will need to generate a new QUIC Initial.
886 |
887 | ### Adding New Client Connection IDs
888 |
889 | Since QUIC connection IDs are chosen by the receiver, an endpoint needs to
890 | communicate its chosen connection IDs to its peer before the peer can start
891 | using them. In QUICv1, this is performed using the NEW_CONNECTION_ID frame.
892 |
893 | Prior to informing the target of a new chosen client CID, the client
894 | MUST send a REGISTER_CLIENT_CID capsule to the proxy containing the new client
895 | CID.
896 |
897 | The client should only inform the target of the new client CID once an
898 | ACK_CLIENT_CID capsule is received that contains the echoed connection ID.
899 |
900 | If forwarded mode is enabled, the client MUST reply to the ACK_CLIENT_CID with
901 | an ACK_CLIENT_VCID capsule with the real and virtual connection IDs along with
902 | an optional Stateless Reset Token.
903 |
904 | ## Proxy Considerations
905 |
906 | The proxy MUST reply to each REGISTER_CLIENT_CID capsule with either
907 | an ACK_CLIENT_CID or CLOSE_CLIENT_CID capsule containing the
908 | Connection ID that was in the registration capsule.
909 |
910 | Similarly, the proxy MUST reply to each REGISTER_TARGET_CID capsule with
911 | either an ACK_TARGET_CID or CLOSE_TARGET_CID capsule containing the
912 | Connection ID that was in the registration capsule.
913 |
914 | The proxy then determines the proxy-to-target 4-tuple to associate with the
915 | client's request. This will generally involve performing a DNS lookup for
916 | the target hostname in the CONNECT request, or finding an existing proxy-to-target
917 | 4-tuple to the authority. The proxy-to-target 4-tuple might already be open due to a
918 | previous request from this client, or another. If the 4-tuple is not already
919 | created, the proxy creates a new one. Proxies can choose to reuse proxy-to-target
920 | 4-tuples across multiple UDP proxying requests, or have a unique proxy-to-target 4-tuple
921 | for every UDP proxying request. If the client did not send a value of "?1" for the
922 | "Proxy-QUIC-Port-Sharing" header, port reuse is not permitted and the proxy MUST allocate
923 | a new UDP 4-tuple.
924 |
925 | If a proxy reuses proxy-to-target 4-tuples, it SHOULD store which authorities
926 | (which could be a domain name or IP address literal) are being accessed over a
927 | particular proxy-to-target 4-tuple so it can avoid performing a new DNS query and
928 | potentially choosing a different target server IP address which could map to a
929 | different target server.
930 |
931 | Proxy-to-target 4-tuples MUST NOT be reused across QUIC and non-QUIC UDP proxy
932 | requests, since it might not be possible to correctly demultiplex or direct
933 | the traffic. Any packets received on a proxy-to-target 4-tuple used for proxying
934 | QUIC that does not correspond to a known CID MUST be dropped.
935 |
936 | When the proxy recieves a REGISTER_CLIENT_CID capsule, it is receiving a
937 | request to be able to route traffic matching the client CID back to
938 | the client using. If the pair of this client CID and the selected
939 | proxy-to-target 4-tuple does not create a conflict, the proxy creates the mapping
940 | and responds with an ACK_CLIENT_CID capsule. If forwarded mode is enabled, the
941 | capsule contains a proxy-chosen client VCID. If forwarded mode
942 | is enabled, and after receiving an ACK_CLIENT_VCID capsule from the client, any
943 | packets received by the proxy from the proxy-to-target 4-tuple that match the
944 | client CID can to be sent to the client after the proxy has replaced
945 | the CID with the client VCID. If forwarded mode is
946 | not supported, the proxy MUST NOT send a client VCID by setting
947 | the length to zero. The proxy MUST use tunnelled mode (HTTP Datagram frames) for
948 | any long header packets. The proxy SHOULD forward directly to the client for any
949 | matching short header packets if forwarding is supported by the client, but the
950 | proxy MAY tunnel these packets in HTTP Datagram frames instead. If the mapping
951 | would create a conflict, the proxy responds with a CLOSE_CLIENT_CID capsule.
952 |
953 | When the proxy recieves a REGISTER_TARGET_CID capsule, it is receiving a
954 | request to allow the client to forward packets to the target. The proxy
955 | generates a target VCID for the client to use when sending
956 | packets in forwarded mode. If forwarded mode is not supported, the proxy MUST
957 | NOT send a target VCID by setting the length to zero. If
958 | forwarded mode is supported, the proxy MUST use a target VCID
959 | that does not introduce a conflict with any other Connection ID on the
960 | client-to-proxy 4-tuple. The proxy creates the mapping and responds with an
961 | ACK_TARGET_CID capsule. Once the successful response is sent, the proxy will
962 | forward any short header packets received on the client-to-proxy 4-tuple that use
963 | the target VCID using the correct proxy-to-target 4-tuple after
964 | first rewriting the target VCID to be the correct target CID.
965 |
966 | Proxies MUST choose unpredictable client and target VCIDs to
967 | avoid forwarding loop attacks.
968 |
969 | The proxy MUST only forward non-tunnelled packets from the client that are QUIC
970 | short header packets (based on the Header Form bit) and have mapped target VCIDs.
971 | Packets sent by the client that are forwarded SHOULD be
972 | considered as activity for restarting QUIC's Idle Timeout {{QUIC}}.
973 |
974 | In order to permit the client to change client-to-target connection IDs, the proxy
975 | SHOULD send MAX_CONNECTION_IDS capsules allowing the client additional connection ID
976 | registrations.
977 |
978 | ### Closing Proxy State
979 |
980 | For any registration capsule for which the proxy has sent an acknowledgement, any
981 | mappings last until either endpoint sends a close capsule or the either side of the
982 | HTTP stream closes.
983 |
984 | A client that no longer wants a given Connection ID to be forwarded by the
985 | proxy sends a CLOSE_CLIENT_CID or CLOSE_TARGET_CID capsule.
986 |
987 | If a client's connection to the proxy is terminated for any reason, all
988 | mappings associated with all requests are removed.
989 |
990 | A proxy can close its proxy-to-target 4-tuple once all UDP proxying requests mapped to
991 | that 4-tuple have been removed.
992 |
993 | # Using Forwarded Mode
994 |
995 | All packets sent in forwarded mode use a transform in which CIDs are switched
996 | into VCIDs, and the contents of packets are either left the same, or modified
997 | ({{transforms}}).
998 |
999 | Forwarded mode also raises special considerations for handling connection
1000 | maintenance ({{maintenance}}), connection migration ({{migration}}),
1001 | ECN markings ({{ecn}}), and stateless resets ({{resets}}).
1002 |
1003 | ## Sending With Forwarded Mode
1004 |
1005 | Support for forwarded mode is determined by the "Proxy-QUIC-Forwarding" header,
1006 | see {{forwarding-header}}.
1007 |
1008 | Once the client has learned the target server's Connection ID, such as in the
1009 | response to a QUIC Initial packet, it can send a REGISTER_TARGET_CID capsule
1010 | containing the target CID to request the ability to forward packets.
1011 |
1012 | The client MUST wait for an ACK_TARGET_CID capsule that contains the echoed
1013 | connection ID and target VCID before using forwarded mode.
1014 |
1015 | Prior to receiving the proxy server response, the client MUST send short header
1016 | packets tunnelled in HTTP Datagram frames. The client MAY also choose to tunnel
1017 | some short header packets even after receiving the successful response.
1018 |
1019 | If the target CID registration is rejected, for example with a
1020 | CLOSE_TARGET_CID capsule, it MUST NOT forward packets to the requested target CID,
1021 | but only use tunnelled mode. The request might also be rejected
1022 | if the proxy does not support forwarded mode or has it disabled by policy.
1023 |
1024 | QUIC long header packets MUST NOT be forwarded. These packets can only be
1025 | tunnelled within HTTP Datagram frames to avoid exposing unnecessary connection
1026 | metadata.
1027 |
1028 | When forwarding, the client sends a QUIC packet with the target VCID
1029 | in the QUIC short header, using the same 4-tuple between client and
1030 | proxy that was used for the main QUIC connection between client and proxy.
1031 |
1032 | When forwarding, the proxy sends a QUIC packet with the client VCID
1033 | in the QUIC short header, using the same 4-tuple between client
1034 | and proxy that was used for the main QUIC connection between client and proxy.
1035 |
1036 | Prior to sending a forwarded mode packet, the sender MUST replace the Connection
1037 | ID with the Virtual Connection ID. If the Virtual Connection ID is larger than
1038 | the Connection ID, the sender MUST extend the length of the packet by the
1039 | difference between the two lengths, to include the entire Virtual Connection ID.
1040 | If the Virtual Connection ID is smaller than the Connection ID, the sender MUST
1041 | shrink the length of the packet by the difference between the two lengths.
1042 |
1043 | Clients and proxies supporting forwarded mode MUST be able to handle Virtual
1044 | Connection IDs of different lengths than the corresponding Connection IDs.
1045 |
1046 | ## Receiving With Forwarded Mode
1047 |
1048 | If the client has indicated support for forwarded mode with the "Proxy-QUIC-Forwarding"
1049 | header, the proxy MAY use forwarded mode for any client CID for which
1050 | it has a valid mapping.
1051 |
1052 | Once a client has sent an ACK_CLIENT_VCID capsule to the proxy, it MUST be
1053 | prepared to receive forwarded short header packets on the 4-tuple between itself
1054 | and the proxy for the specified client VCID.
1055 |
1056 | The client uses the Destination Connection ID field of the received packet to
1057 | determine if the packet was originated by the proxy, or merely forwarded from
1058 | the target. The client replaces the client VCID with the real
1059 | client CID before processing the packet further.
1060 |
1061 | ## Packet Transforms {#transforms}
1062 |
1063 | A packet transform is the procedure applied to encode packets as they are sent
1064 | on the link between the client and proxy, along with the inverse decode step applied
1065 | on receipt. Simple transforms can be modeled as a function as follows:
1066 |
1067 | Inputs:
1068 |
1069 | 1. A QUIC short header packet (after Connection ID remapping).
1070 | 1. The mode (encode or decode).
1071 | 1. The direction (client-to-proxy or proxy-to-client).
1072 | 1. Any configuration information negotiated at startup.
1073 |
1074 | Output:
1075 |
1076 | * A UDP payload that conforms to the QUIC invariants {{?RFC8999}} and does not
1077 | modify the Connection ID.
1078 |
1079 | More complex transform behaviors could have internal state, but no such transforms
1080 | are presented here.
1081 |
1082 | Packet transforms are identified by an IANA-registered name, and negotiated in
1083 | the HTTP headers (see {{forwarding-header}}). This document defines two initial
1084 | transforms: the `identity` transform and the `scramble` transform.
1085 |
1086 | ### The identify transform {#identity-transform}
1087 |
1088 | The `identity` transform does not modify the packet in any way. When this transform
1089 | is in use, a global passive adversary can trivially correlate pairs of packets
1090 | that crossed the forwarder, providing a compact proof that a specific client
1091 | was communicating to a specific target.
1092 |
1093 | The `identity` transform is identified by the value "identity" {{iana-transforms}}.
1094 |
1095 | Use of this transform is NOT RECOMMENDED if the `scramble` transform is supported
1096 | by both the client and the proxy. Implementations MAY choose to not implement or
1097 | support the `identity` transform, depending on the use cases and privacy requirements of
1098 | the deployment.
1099 |
1100 | ### The scramble transform {#scramble-transform}
1101 |
1102 | The `scramble` transform implements length-preserving unauthenticated
1103 | re-encryption of QUIC packets while preserving the QUIC invariants. When
1104 | the `scramble` transform is in use, a global passive adversary cannot simply compare the packet
1105 | contents on both sides of the proxy
1106 | to link the client and target. However, the `scramble` transform does not defend against
1107 | analysis of packet sizes and timing, nor does it protect privacy against an
1108 | active attacker.
1109 |
1110 | Deployments that implement the version of the `scramble` transform defined in this
1111 | document MUST use the value "scramble-dt". The finalized version is expected
1112 | to use the reserved value "scramble" {{iana-transforms}}.
1113 |
1114 | The `scramble` transform is initialized using a 32-byte random symmetric key.
1115 | When offering or selecting this transform, the client and server each
1116 | generate the key that they will use to encrypt scrambled packets and MUST add it to the
1117 | "Proxy-QUIC-Transform" header in an `sf-binary` parameter named "scramble-key".
1118 | If either side receives a `scramble` transform without the "scramble-key" parameter,
1119 | forwarded mode MUST be disabled.
1120 |
1121 | This transform relies on the AES-128 block cipher, which is represented by the
1122 | syntax `AES-ECB(key, plaintext_block)` as in {{?RFC9001}}. The corresponding
1123 | decryption operation is written here as `AES-ECB-inv(key, ciphertext_block)`.
1124 | It also uses AES in Counter Mode ({{SP800-38A}}, Section 6.5), which is
1125 | represented by the syntax `AES-CTR(key, iv, input)` for encryption and
1126 | decryption (which are identical). In this syntax, `iv` is an array of 16 bytes
1127 | containing the initial counter block. The counter is incremented by the
1128 | standard incrementing function ({{SP800-38A}}, Appendix B.1) on the full block
1129 | width.
1130 |
1131 | In brief, the transform applies AES in counter mode (AES-CTR) using an
1132 | initialization vector drawn from the packet, then encrypts the initialization
1133 | vector with AES-ECB. The detailed procedure is as follows:
1134 |
1135 | 1. Let `k1, k2 = scramble_key[:16], scramble_key[16:32]`.
1136 | 1. Let `L` be the Connection ID length.
1137 | 1. Let `cid = packet[1:L+1]`, i.e., the Connection ID.
1138 | 1. Let `iv = packet[L+1:L+17]`, i.e., the 16 bytes following the Connection ID.
1139 | 1. Let `ctr_input = packet[0] | packet[L+17:]`.
1140 | 1. Let `ctr_output = AES-CTR(k1, iv, ctr_input)`.
1141 | 1. Let `header = ctr_output[0] & 0x7F`. This ensures that the Header Form bit
1142 | is zero, as required by the QUIC invariants ({{?RFC8999}}, Section 5.2).
1143 | 1. Encrypt `iv` with the block cipher: `encrypted_iv = AES-ECB(k2, iv)`.
1144 | 1. Produce the output packet as:\\
1145 | `header | cid | encrypted_iv | ctr_output[1:]`.
1146 |
1147 | The inverse transform operates as follows:
1148 |
1149 | 1. Decrypt the AES-CTR initialization vector:\\
1150 | `iv = AES-ECB-inv(k2, packet[L+1:L+17])`.
1151 | 1. Compute the other variables exactly as in the forward transform.
1152 | (AES-CTR encryption and decryption are identical.)
1153 | 1. Produce the output: `header | cid | iv | ctr_output[1:]`.
1154 |
1155 | The encryption keys used in this procedure do not depend on the packet contents,
1156 | so each party only needs to perform AES initialization once for each connection.
1157 |
1158 | NOTE: The security of this arrangement relies on every short-header QUIC packet
1159 | containing a distinct 16 bytes following the Connection ID. This is true
1160 | for the original ciphersuites of QUICv1, but it is not guaranteed by the QUIC
1161 | Invariants. Future ciphersuites and QUIC versions could in principle produce
1162 | packets that are too short or repeat the values at this location. When using the
1163 | `scramble` transform, clients MUST NOT offer any configuration that could
1164 | cause the client or target to violate this requirement.
1165 |
1166 | ## Connection Maintenance in Forwarded Mode {#maintenance}
1167 |
1168 | When a client and proxy are using forwarded mode, it is possible that there can be
1169 | long periods of time in which no ack-eliciting packets
1170 | (see {{Section 2 of !QUIC-RETRANSMISSION=RFC9002}}) are exchanged
1171 | between the client and proxy. If these periods extend beyond the effective idle
1172 | timeout for the client-to-proxy QUIC connection (see {{Section 10.1 of QUIC}}),
1173 | the QUIC connection might be closed by the proxy if the proxy does not use
1174 | forwarded packets as an explicit liveness signal. To avoid this, clients SHOULD
1175 | send keepalive packets to the proxy before the idle timeouts would be reached,
1176 | which can be done using a PING frame or another ack-eliciting frame as described
1177 | in {{Section 10.1.1 of QUIC}}.
1178 |
1179 | ## Handling Connection Migration {#migration}
1180 |
1181 | If a proxy supports QUIC connection migration, it needs to ensure that a migration
1182 | event does not end up sending too many tunnelled or forwarded packets on a new
1183 | path prior to path validation.
1184 |
1185 | Specifically, the proxy MUST limit the number of packets that it will proxy
1186 | to an unvalidated client address to the size of an initial congestion window.
1187 | Proxies additionally SHOULD pace the rate at which packets are sent over a new
1188 | path to avoid creating unintentional congestion on the new path.
1189 |
1190 | When operating in forwarded mode, the proxy reconfigures or removes forwarding
1191 | rules as the network path between the client and proxy changes. In the event of
1192 | passive migration, the proxy automatically reconfigures forwarding rules to use
1193 | the latest active and validated network path for the HTTP stream. In the event of
1194 | active migration, the proxy removes forwarding rules in order to not send
1195 | packets with the same connection ID bytes over multiple network paths. After
1196 | initiating active migration, clients are no longer able to send forwarded mode
1197 | packets since the proxy will have removed forwarding rules. Clients can proceed with
1198 | tunnelled mode or can request new forwarding rules via REGISTER_CLIENT_CID and
1199 | REGISTER_TARGET_CID capsules. Each of the acknowledging capsules will contain new
1200 | virtual connection IDs to prevent packets with the same connection ID bytes being
1201 | used over multiple network paths. Note that the client CID and target CID
1202 | can stay the same while the target VCID and client VCID change.
1203 |
1204 | ## Handling ECN Marking {#ecn}
1205 |
1206 | Explicit Congestion Notification marking {{!ECN=RFC3168}} uses two bits in the IP
1207 | header to signal congestion from a network to endpoints. When using forwarded mode,
1208 | the proxy replaces IP headers for packets exchanged between the client and target;
1209 | these headers can include ECN markings. Proxies SHOULD preserve ECN markings on
1210 | forwarded packets in both directions, to allow ECN to function end-to-end. If the proxy does not
1211 | preserve ECN markings, it MUST set ECN marks to zero on the IP headers it generates.
1212 |
1213 | Forwarded mode does not create an IP-in-IP tunnel, so the guidance in
1214 | {{?ECN-TUNNEL=RFC6040}} about transferring ECN markings between inner and outer IP
1215 | headers does not apply.
1216 |
1217 | A proxy MAY additionally add ECN markings to signal congestion being experienced
1218 | on the proxy itself.
1219 |
1220 | ## Stateless Resets for Forwarded Mode QUIC Packets {#resets}
1221 |
1222 | While the lifecycle of forwarding rules are bound to the lifecycle of the
1223 | client-to-proxy HTTP stream, a peer may not be aware that the stream has
1224 | terminated. If the above mappings are lost or removed without the peer's
1225 | knowledge, they may send forwarded mode packets even though the client
1226 | or proxy no longer has state for that connection. To allow the client or
1227 | proxy to reset the client-to-target connection in the absence of the mappings
1228 | above, a stateless reset token corresponding to the Virtual Connection ID
1229 | can be provided.
1230 |
1231 | Consider a proxy that initiates closure of a client-to-proxy QUIC connection.
1232 | If the client is temporarily unresponsive or unreachable, the proxy might have
1233 | considered the connection closed and removed all connection state (including
1234 | the stream mappings used for forwarding). If the client never learned about the closure, it
1235 | might send forwarded mode packets to the proxy, assuming the stream mappings
1236 | and client-to-proxy connection are still intact. The proxy will receive these
1237 | forwarded mode packets, but won't have any state corresponding to the
1238 | destination connection ID in the packet. If the proxy has provided a stateless
1239 | reset token for the target VCID, it can send a stateless reset
1240 | packet to quickly notify the client that the client-to-target connection is
1241 | broken.
1242 |
1243 | ### Stateless Resets from the Target
1244 |
1245 | Reuse of proxy-to-target 4-tuples is only possible because QUIC connection IDs
1246 | allow distinguishing packets for multiple QUIC connections received with the
1247 | same 5-tuple. One exception to this is Stateless Reset packets, in which the
1248 | connection ID is not used, but rather populated with unpredictable bits followed
1249 | by a Stateless Reset token, to make it indistinguishable from a regular packet
1250 | with a short header. In order for the proxy to correctly recognize Stateless
1251 | Reset packets, the client SHOULD share the Stateless Reset token for each
1252 | registered target CID. When the proxy receives a Stateless Reset packet,
1253 | it can send the packet to the client as a tunnelled datagram. Although Stateless Reset packets
1254 | look like short header packets, they are not technically short header packets and do not contain
1255 | negotiated connection IDs, and thus are not eligible for forwarded mode.
1256 |
1257 | # Example Exchange
1258 |
1259 | Consider a client that is establishing a new QUIC connection through the proxy.
1260 | In this example, the client prefers the `scramble` transform, but also offers the `identity`
1261 | transform. It has selected a client CID of `0x31323334`. In order to inform a proxy
1262 | of the new QUIC client CID, the client also sends a
1263 | REGISTER_CLIENT_CID capsule.
1264 |
1265 | The client will also send the initial QUIC packet with the Long Header form in
1266 | an HTTP datagram.
1267 |
1268 | ~~~
1269 | Client Server
1270 |
1271 | STREAM(44): HEADERS -------->
1272 | :method = CONNECT
1273 | :protocol = connect-udp
1274 | :scheme = https
1275 | :path = /target.example.com/443/
1276 | :authority = proxy.example.org
1277 | proxy-quic-port-sharing = ?1
1278 | proxy-quic-forwarding = ?1; accept-transform=scramble,identity; \
1279 | scramble-key=:abc...789=:
1280 | capsule-protocol = ?1
1281 |
1282 | STREAM(44): DATA -------->
1283 | Capsule Type = REGISTER_CLIENT_CID
1284 | Connection ID = 0x31323334
1285 | Stateless Reset Token = Token
1286 |
1287 | <-------- STREAM(44): DATA
1288 | Capsule Type = MAX_CONNECTION_IDS
1289 | Maximum Sequence Number = 3
1290 |
1291 | DATAGRAM -------->
1292 | Quarter Stream ID = 11
1293 | Context ID = 0
1294 | Payload = Encapsulated QUIC initial
1295 |
1296 | <-------- STREAM(44): HEADERS
1297 | :status = 200
1298 | proxy-quic-forwarding = ?1; \
1299 | transform=scramble; \
1300 | scramble-key=:ABC...321=:
1301 | capsule-protocol = ?1
1302 |
1303 | <-------- STREAM(44): DATA
1304 | Capsule Type = ACK_CLIENT_CID
1305 | Connection ID = 0x31323334
1306 | Virtual CID = 0x62646668
1307 | ~~~
1308 |
1309 | The proxy has acknowledged the client CID and provided a client VCID.
1310 | Even if there were Short Header packets to send, the proxy
1311 | cannot send forwarded mode packets because the client hasn't acknowledged the
1312 | client VCID.
1313 |
1314 | The proxy indicates to the client that it will allow connection ID registrations
1315 | with sequence numbers 0-3, allowing for registrations beyond the initial maximum
1316 | of 1.
1317 |
1318 | ~~~
1319 | STREAM(44): DATA -------->
1320 | Capsule Type = ACK_CLIENT_VCID
1321 | Connection ID = 0x31323334
1322 | Virtual CID = 0x62646668
1323 | Stateless Reset Token = Token
1324 | ~~~
1325 |
1326 | The client acknowledges the client VCID. The proxy still
1327 | doesn't have any Short Header Packets to send, but, if it did, it would be able
1328 | to send with forwarded mode.
1329 |
1330 | ~~~
1331 | /* Wait for target server to respond to UDP packet. */
1332 |
1333 | <-------- DATAGRAM
1334 | Quarter Stream ID = 11
1335 | Context ID = 0
1336 | Payload = Encapsulated QUIC initial
1337 |
1338 | /* All Client -> Target QUIC packets must still be encapsulated */
1339 |
1340 | DATAGRAM -------->
1341 | Quarter Stream ID = 11
1342 | Context ID = 0
1343 | Payload = Encapsulated QUIC packet
1344 |
1345 | /* Forwarded mode packets possible in Target -> Client direction */
1346 |
1347 | <-------- UDP Datagram
1348 | Payload = Forwarded QUIC SH packet
1349 |
1350 | ~~~
1351 |
1352 | The client may receive forwarded mode packets from the proxy with a Virtual
1353 | client CID of 0x62646668 which it will replace with the real client CID
1354 | of 0x31323334. All forwarded mode packets sent by the proxy
1355 | will have been modified to contain the client VCID instead
1356 | of the client CID, and processed by the negotiated "scramble"
1357 | packet transform. However, in the unlikely event that a forwarded packet
1358 | arrives before the proxy's HTTP response, the client will not know which
1359 | transform the proxy selected. In this case, the client will have to ignore
1360 | the packet or buffer it until the HTTP response is received.
1361 |
1362 | Once the client learns which Connection ID has been selected by the target
1363 | server, it can send a new request to the proxy to establish a mapping for
1364 | forwarding. In this case, that ID is 0x61626364. The client sends the
1365 | following capsule:
1366 |
1367 | ~~~
1368 | STREAM(44): DATA -------->
1369 | Capsule Type = REGISTER_TARGET_CID
1370 | Connection ID = 0x61626364
1371 |
1372 | <-------- STREAM(44): DATA
1373 | Capsule Type = ACK_TARGET_CID
1374 | Connection ID = 0x61626364
1375 | Virtual Connection ID = 0x123412341234
1376 | Stateless Reset Token = Token
1377 |
1378 | /* Client -> Target QUIC short header packets may use forwarded mode */
1379 |
1380 | UDP Datagram -------->
1381 | Payload = Forwarded QUIC SH packet
1382 |
1383 | ~~~
1384 |
1385 | Upon receiving an ACK_TARGET_CID capsule, the client starts sending Short Header
1386 | packets with a Destination Connection ID of 0x123412341234 directly to the proxy
1387 | (not tunnelled), and these are rewritten by the proxy to have the Destination
1388 | Connection ID 0x61626364 prior to being forwarded directly to the target. In the
1389 | reverse direction, Short Header packets from the target with a Destination
1390 | Connection ID of 0x31323334 are modified to replace the Destination Connection
1391 | ID with the client VCID of 0x62646668 and forwarded directly to
1392 | the client.
1393 |
1394 | # Packet Size Considerations
1395 |
1396 | Since Initial QUIC packets must be at least 1200 bytes in length, the HTTP
1397 | Datagram frames that are used for a QUIC-aware proxy MUST be able to carry at least
1398 | 1200 bytes.
1399 |
1400 | Additionally, clients that connect to a proxy for purpose of proxying QUIC
1401 | SHOULD start their connection with a larger packet size than 1200 bytes, to
1402 | account for the overhead of tunnelling an Initial QUIC packet within an
1403 | HTTP Datagram frame. If the client does not begin with a larger packet size than
1404 | 1200 bytes, it will need to perform Path MTU (Maximum Transmission Unit)
1405 | discovery to discover a larger path size prior to sending any tunnelled Initial
1406 | QUIC packets.
1407 |
1408 | Once a proxied QUIC connections moves into forwarded mode, the client SHOULD
1409 | initiate Path MTU discovery to increase its end-to-end MTU.
1410 |
1411 | # Security Considerations {#security}
1412 |
1413 | Proxies that support this extension SHOULD provide protections to rate-limit
1414 | or restrict clients from opening an excessive number of proxied connections, so
1415 | as to limit abuse or use of proxies to launch Denial-of-Service attacks.
1416 |
1417 | Sending QUIC packets by forwarding through a proxy without tunnelling exposes
1418 | clients to additional information exposure and deanonymization attacks which
1419 | need to be carefully considered. Analysis should consider both passive and
1420 | active attackers which may be global or localized to the network paths used
1421 | on one side of the proxy. The following sections highlight deanonymization risks with
1422 | using forwarded mode.
1423 |
1424 | ## Passive Attacks
1425 |
1426 | A passive attacker aims to deanonymize a client by correlating traffic across
1427 | both sides of the proxy. When using forwarded mode with the `identity` packet
1428 | transform (see {{identity-transform}}), such correlation is trivial by matching
1429 | a subset of QUIC packet bytes as packets enter the proxy on one side and exit
1430 | on the other. Packet transforms such as `scramble` mitigate this by
1431 | cryptographically preventing such byte comparisons
1432 | (see {{!scramble-transform=scramble-transform}}).
1433 |
1434 | Regardless of which packet transform is used, both tunnelled and forwarded mode
1435 | are still vulnerable to size and timing attacks, without the addition of techniques that go beyond the analysis
1436 | in this document, such as padding and adding chaff packets. Such techniques could be supported
1437 | in future packet transforms, subject to additional security analysis.
1438 |
1439 | Unlike tunnelled mode where packets are fully encapsulated in the client-to-proxy
1440 | connection, clients using forwarded mode to access multiple target servers
1441 | over the same client-to-proxy connection expose the number of target servers
1442 | they are communicating with on each connection to passive attackers that can
1443 | observe the client-to-proxy traffic. This additional metadata revealed on each
1444 | packet simplifies size and timing attacks.
1445 |
1446 | ## Active Attacks
1447 |
1448 | An active attacker is an adversary that can inject, modify, drop, and view
1449 | packets in the network. Some active attacks have different effects between
1450 | forwarded mode and tunnelled mode, but active attacks can be used to correlate
1451 | flows in either mode.
1452 |
1453 | Both tunnelled mode and forwarded mode (regardless of packet transform) are
1454 | vulnerable to packet injection in the target-to-client direction. An attacker
1455 | can inject a burst of packets with a known QUIC Connection ID and see which
1456 | Connection ID is used for the corresponding burst on the proxy-to-client network path.
1457 |
1458 | Packet injection with a known QUIC Connection ID can also happen in the
1459 | client-to-proxy direction, which only affects forwarded mode since
1460 | tunnelled mode sends packets within an authenticated and integrity protected
1461 | QUIC connection to the proxy (see {{?RFC9001}}). None of the packet transforms
1462 | defined in this document provide integrity protection. Even if a packet
1463 | transform did provide integrity protection, attackers can inject replayed
1464 | packets. Protection against replayed packets is similarly provided by QUIC in
1465 | tunnelled mode, but not provided by any of the forwarded mode packet transforms
1466 | defined in this document.
1467 |
1468 | An active attacker can modify packets in the client-to-proxy direction, which
1469 | would cause a tunnelling proxy to silently drop packets, while a forwarding proxy
1470 | would forward the packets. In this way, forwarded mode is less vulnerable to
1471 | flow recognition based on corrupting a portion of packets in a burst.
1472 |
1473 | Chaining of proxies using forwarded mode introduces the risk of forwarding loop
1474 | attacks. Preventing client VCID conflicts across proxies
1475 | sharing an IP address and port mitigates one such forwarding loop attack.
1476 | Conflicts can be avoided by partitioning the client VCID space
1477 | across proxies, using sufficiently long and random values, or by other means.
1478 |
1479 | [comment1]: # OPEN ISSUE: Figure out how clients and proxies could interact to
1480 | [comment2]: # learn whether an adversary is injecting malicious forwarded
1481 | [comment3]: # packets to induce rate limiting.
1482 |
1483 | # IANA Considerations {#iana}
1484 |
1485 | ## HTTP Header Field {#iana-header}
1486 |
1487 | This document registers the "Proxy-QUIC-Forwarding" header field in the
1488 | "Hypertext Transfer Protocol (HTTP) Field Name Registry"
1489 | <[](https://www.iana.org/assignments/http-fields)>.
1490 |
1491 | ~~~
1492 | +-----------------------+-----------+-----------------+---------------+----------+
1493 | | Field Name | Status | Structured Type | Reference | Comments |
1494 | +-----------------------+-----------+-----------------+---------------+----------+
1495 | | Proxy-QUIC-Forwarding | permanent | Item | This document | None |
1496 | +-----------------------+-----------+-----------------+---------------+----------+
1497 | ~~~
1498 | {: #iana-header-type-table title="Registered HTTP Header Field"}
1499 |
1500 | ## Proxy QUIC Forwarding Parameter Names
1501 |
1502 | This document establishes a new registry, "Proxy QUIC Forwarding Parameter Names",
1503 | for parameter names to use with the "Proxy-QUIC-Forwarding" header field,
1504 | in <[](https://www.iana.org/assignments/masque/masque.xhtml)>.
1505 | Registrations in this registry are assigned using the
1506 | Specification Required policy (Section 4.6 of [IANA-POLICY]).
1507 |
1508 | ~~~
1509 | +-----------------------+-------------------------------------+---------------+--------------------------------+
1510 | | Parameter Name | Description | Reference | Notes |
1511 | +-----------------------+-------------------------------------+---------------+--------------------------------+
1512 | | accept-transform | contains supported transforms | This document | Section {{forwarding-header}} |
1513 | +-----------------------+-------------------------------------+---------------+--------------------------------+
1514 | | transform | indicates selected transforms | This document | Section {{forwarding-header}} |
1515 | +-----------------------+-------------------------------------+---------------+--------------------------------+
1516 | | scramble-key | contains key for scramble transform | This document | Section {{scramble-transform}} |
1517 | +-----------------------+-------------------------------------+---------------+--------------------------------+
1518 | ~~~
1519 | {: #iana-parameter-names-table title="Initial Proxy QUIC Forwarding Parameter Names"}
1520 |
1521 | ## Packet Transform Names {#iana-transforms}
1522 |
1523 | This document establishes a new registry for packet transform names
1524 | in <[](https://www.iana.org/assignments/masque/masque.xhtml)>
1525 | and defines two initial transforms: "identity" and "scramble".
1526 | Prior to finalization, deployments that implement the version of
1527 | the `scramble` transform defined in this document should use the value
1528 | "scramble-dt". Once the design team proposal is adopted and a new draft is submitted,
1529 | the wire identifier will become "scramble-XX" where XX is the draft number.
1530 | Registrations in this registry are assigned using the
1531 | Specification Required policy (Section 4.6 of [IANA-POLICY]).
1532 |
1533 | | Transform Name | Description | Specification | Notes |
1534 | |:---------------|:------------------|:--------------|--------------------------------|
1535 | | identity | no transformation | This Document | Section {{identity-transform}} |
1536 | | scramble | Reserved (will be used for final version) | This Document | Section {{scramble-transform}} |
1537 | {: #iana-packet-transforms-table title="Initial Packet Transform Names"}
1538 |
1539 | ## Capsule Types {#iana-capsule-types}
1540 |
1541 | This document registers six new values in the "HTTP Capsule Types"
1542 | registry established by {{HTTP-CAPSULES}}. Note that the codepoints below
1543 | will be replaced with lower values before publication.
1544 |
1545 | | Capule Type | Value | Specification |
1546 | |:--------------------|:----------|:--------------|
1547 | | REGISTER_CLIENT_CID | 0xffe600 | This Document |
1548 | | REGISTER_TARGET_CID | 0xffe601 | This Document |
1549 | | ACK_CLIENT_CID | 0xffe602 | This Document |
1550 | | ACK_CLIENT_VCID | 0xffe603 | This Document |
1551 | | ACK_TARGET_CID | 0xffe604 | This Document |
1552 | | CLOSE_CLIENT_CID | 0xffe605 | This Document |
1553 | | CLOSE_TARGET_CID | 0xffe606 | This Document |
1554 | | MAX_CONNECTION_IDS | 0xffe607 | This Document |
1555 | {: #iana-capsule-type-table title="Registered Capsule Types"}
1556 |
1557 | All of these new entries use the following values for these fields:
1558 |
1559 | Status:
1560 |
1561 | : provisional (permanent when this document is published)
1562 |
1563 | Reference:
1564 |
1565 | : This document
1566 |
1567 | Change Controller:
1568 |
1569 | : IETF
1570 |
1571 | Contact:
1572 |
1573 | : masque@ietf.org
1574 |
1575 | Notes:
1576 |
1577 | : None
1578 | {: spacing="compact" newline="false"}
1579 |
1580 | --- back
1581 |
1582 | # Acknowledgments {#acknowledgments}
1583 | {:numbered="false"}
1584 |
1585 | Thanks to Lucas Pardue, Ryan Hamilton, and Mirja Kühlewind for their inputs
1586 | on this document.
1587 |
--------------------------------------------------------------------------------
/interop/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2018, Mark Nottingham
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/interop/asset/badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ietf-wg-masque/draft-ietf-masque-quic-proxy/099c163983dd6512f7db641a36b09c0abb6d5368/interop/asset/badge.png
--------------------------------------------------------------------------------
/interop/asset/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ietf-wg-masque/draft-ietf-masque-quic-proxy/099c163983dd6512f7db641a36b09c0abb6d5368/interop/asset/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/interop/asset/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ietf-wg-masque/draft-ietf-masque-quic-proxy/099c163983dd6512f7db641a36b09c0abb6d5368/interop/asset/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/interop/asset/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ietf-wg-masque/draft-ietf-masque-quic-proxy/099c163983dd6512f7db641a36b09c0abb6d5368/interop/asset/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/interop/asset/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ietf-wg-masque/draft-ietf-masque-quic-proxy/099c163983dd6512f7db641a36b09c0abb6d5368/interop/asset/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/interop/asset/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ietf-wg-masque/draft-ietf-masque-quic-proxy/099c163983dd6512f7db641a36b09c0abb6d5368/interop/asset/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/interop/asset/marked.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * marked - a markdown parser
3 | * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT Licensed)
4 | * https://github.com/markedjs/marked
5 | */
6 | !function(e){"use strict";var k={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:f,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,nptable:f,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|\\n*|\\n*|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)|(?!script|pre|style)[a-z][\\w-]*\\s*>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,table:f,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/,text:/^[^\n]+/};function a(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||m.defaults,this.rules=k.normal,this.options.pedantic?this.rules=k.pedantic:this.options.gfm&&(this.options.tables?this.rules=k.tables:this.rules=k.gfm)}k._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,k._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,k.def=i(k.def).replace("label",k._label).replace("title",k._title).getRegex(),k.bullet=/(?:[*+-]|\d{1,9}\.)/,k.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,k.item=i(k.item,"gm").replace(/bull/g,k.bullet).getRegex(),k.list=i(k.list).replace(/bull/g,k.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+k.def.source+")").getRegex(),k._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",k._comment=//,k.html=i(k.html,"i").replace("comment",k._comment).replace("tag",k._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),k.paragraph=i(k.paragraph).replace("hr",k.hr).replace("heading",k.heading).replace("lheading",k.lheading).replace("tag",k._tag).getRegex(),k.blockquote=i(k.blockquote).replace("paragraph",k.paragraph).getRegex(),k.normal=d({},k),k.gfm=d({},k.normal,{fences:/^ {0,3}(`{3,}|~{3,})([^`\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),k.gfm.paragraph=i(k.paragraph).replace("(?!","(?!"+k.gfm.fences.source.replace("\\1","\\2")+"|"+k.list.source.replace("\\1","\\3")+"|").getRegex(),k.tables=d({},k.gfm,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),k.pedantic=d({},k.normal,{html:i("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",k._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/}),a.rules=k,a.lex=function(e,t){return new a(t).lex(e)},a.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},a.prototype.token=function(e,t){var n,r,s,i,l,o,a,h,p,u,c,g,f,d,m,b;for(e=e.replace(/^ +$/gm,"");e;)if((s=this.rules.newline.exec(e))&&(e=e.substring(s[0].length),1 ?/gm,""),this.token(s,t),this.tokens.push({type:"blockquote_end"});else if(s=this.rules.list.exec(e)){for(e=e.substring(s[0].length),a={type:"list_start",ordered:d=1<(i=s[2]).length,start:d?+i:"",loose:!1},this.tokens.push(a),n=!(h=[]),f=(s=s[0].match(this.rules.item)).length,c=0;c?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:f,tag:"^comment|^[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,em:/^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:f,text:/^(`+|[^`])[\s\S]*?(?=[\\?@\\[^_{|}~",n.em=i(n.em).replace(/punctuation/g,n._punctuation).getRegex(),n._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,n._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,n._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,n.autolink=i(n.autolink).replace("scheme",n._scheme).replace("email",n._email).getRegex(),n._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,n.tag=i(n.tag).replace("comment",k._comment).replace("attribute",n._attribute).getRegex(),n._label=/(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/,n._href=/\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/,n._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,n.link=i(n.link).replace("label",n._label).replace("href",n._href).replace("title",n._title).getRegex(),n.reflink=i(n.reflink).replace("label",n._label).getRegex(),n.normal=d({},n),n.pedantic=d({},n.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:i(/^!?\[(label)\]\((.*?)\)/).replace("label",n._label).getRegex(),reflink:i(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",n._label).getRegex()}),n.gfm=d({},n.normal,{escape:i(n.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:i(n.text).replace("]|","~]|").replace("|$","|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&'*+/=?^_`{\\|}~-]+@|$").getRegex()}),n.gfm.url=i(n.gfm.url,"i").replace("email",n.gfm._extended_email).getRegex(),n.breaks=d({},n.gfm,{br:i(n.br).replace("{2,}","*").getRegex(),text:i(n.gfm.text).replace("{2,}","*").getRegex()}),h.rules=n,h.output=function(e,t,n){return new h(t,n).output(e)},h.prototype.output=function(e){for(var t,n,r,s,i,l,o="";e;)if(i=this.rules.escape.exec(e))e=e.substring(i[0].length),o+=u(i[1]);else if(i=this.rules.tag.exec(e))!this.inLink&&/^/i.test(i[0])&&(this.inLink=!1),!this.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(i[0])?this.inRawBlock=!0:this.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(i[0])&&(this.inRawBlock=!1),e=e.substring(i[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(i[0]):u(i[0]):i[0];else if(i=this.rules.link.exec(e))e=e.substring(i[0].length),this.inLink=!0,r=i[2],this.options.pedantic?(t=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(r))?(r=t[1],s=t[3]):s="":s=i[3]?i[3].slice(1,-1):"",r=r.trim().replace(/^<([\s\S]*)>$/,"$1"),o+=this.outputLink(i,{href:h.escapes(r),title:h.escapes(s)}),this.inLink=!1;else if((i=this.rules.reflink.exec(e))||(i=this.rules.nolink.exec(e))){if(e=e.substring(i[0].length),t=(i[2]||i[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){o+=i[0].charAt(0),e=i[0].substring(1)+e;continue}this.inLink=!0,o+=this.outputLink(i,t),this.inLink=!1}else if(i=this.rules.strong.exec(e))e=e.substring(i[0].length),o+=this.renderer.strong(this.output(i[4]||i[3]||i[2]||i[1]));else if(i=this.rules.em.exec(e))e=e.substring(i[0].length),o+=this.renderer.em(this.output(i[6]||i[5]||i[4]||i[3]||i[2]||i[1]));else if(i=this.rules.code.exec(e))e=e.substring(i[0].length),o+=this.renderer.codespan(u(i[2].trim(),!0));else if(i=this.rules.br.exec(e))e=e.substring(i[0].length),o+=this.renderer.br();else if(i=this.rules.del.exec(e))e=e.substring(i[0].length),o+=this.renderer.del(this.output(i[1]));else if(i=this.rules.autolink.exec(e))e=e.substring(i[0].length),r="@"===i[2]?"mailto:"+(n=u(this.mangle(i[1]))):n=u(i[1]),o+=this.renderer.link(r,null,n);else if(this.inLink||!(i=this.rules.url.exec(e))){if(i=this.rules.text.exec(e))e=e.substring(i[0].length),this.inRawBlock?o+=this.renderer.text(i[0]):o+=this.renderer.text(u(this.smartypants(i[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else{if("@"===i[2])r="mailto:"+(n=u(i[0]));else{for(;l=i[0],i[0]=this.rules._backpedal.exec(i[0])[0],l!==i[0];);n=u(i[0]),r="www."===i[1]?"http://"+n:n}e=e.substring(i[0].length),o+=this.renderer.link(r,null,n)}return o},h.escapes=function(e){return e?e.replace(h.rules._escapes,"$1"):e},h.prototype.outputLink=function(e,t){var n=t.href,r=t.title?u(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,u(e[1]))},h.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},h.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,s=0;s'+(n?e:u(e,!0))+"\n":"