RFC 8684
TCP Extensions for Multipath Operation with Multiple Addresses
RFC 8684: TCP Extensions for Multipath Operation with Multiple Addresses โจ
okay bestie, this is RFC 8684 ๐ฆ
It's a Standards Track document from March 2020 (ISSN 2070-1721) by a squad of five: A. Ford of Pexip, C. Raiciu of U. Politehnica of Bucharest, M. Handley of U. College London, O. Bonaventure of U. catholique de Louvain, and C. Paasch of Apple, Inc. ๐ It obsoletes RFC 6824, which is MPTCP v0, and it doesn't list any updates. So this is the glow-up. ๐ธ
Abstract
TCP/IP is currently stuck with one path per connection, even though there are often multiple paths between two peers ๐ Using several of those paths at the same time for one TCP/IP session would use network resources better and give users higher throughput and better resilience when something breaks. ๐ฅ
Multipath TCP is the thing that lets peers use multiple paths simultaneously. This document adds a set of extensions to regular TCP to make that possible. It still gives applications the same kind of service as TCP (a reliable bytestream โจ), and it includes the parts needed to set up and use multiple TCP flows across paths that might be totally separate from each other.
This document specifies v1 of Multipath TCP and obsoletes v0 from RFC 6824. The changes are clarifications and modifications, mostly driven by what people learned from actually deploying it ๐
Status of This Memo
This is an Internet Standards Track document. ๐
It's a product of the Internet Engineering Task Force (IETF), so it represents the consensus of the IETF community. It went through public review and got approved for publication by the Internet Engineering Steering Group (IESG). If you want more on Internet Standards, check out Section 2 of RFC 7841 ๐ง
For the current status, any errata, and how to send feedback, go to https://www.rfc-editor.org/info/rfc8684. ๐
Copyright Notice
Copyright (c) 2020 IETF Trust and the persons identified as the document authors. All rights reserved. ๐
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stand on the publication date. Read those carefully because they spell out your rights and restrictions for this document ๐ Code Components pulled out of this document have to include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come with no warranty, as described in the Simplified BSD License. ๐
Table of Contents
1. Introduction
1.1. Design Assumptions
1.2. Multipath TCP in the Networking Stack
1.3. Terminology
1.4. MPTCP Concept
1.5. Requirements Language
2. Operation Overview
2.1. Initiating an MPTCP Connection
2.2. Associating a New Subflow with an Existing MPTCP Connection
2.3. Informing the Other Host about Another Potential Address
2.4. Data Transfer Using MPTCP
2.5. Requesting a Change in a Path's Priority
2.6. Closing an MPTCP Connection
2.7. Notable Features
3. MPTCP Operations: An Overview
3.1. Connection Initiation
3.2. Starting a New Subflow
3.3. MPTCP Operation and Data Transfer
3.3.1. Data Sequence Mapping
3.3.2. Data Acknowledgments
3.3.3. Closing a Connection
3.3.4. Receiver Considerations
3.3.5. Sender Considerations
3.3.6. Reliability and Retransmissions
3.3.7. Congestion Control Considerations
3.3.8. Subflow Policy
3.4. Address Knowledge Exchange (Path Management)
3.4.1. Address Advertisement
3.4.2. Remove Address
3.5. Fast Close
3.6. Subflow Reset
3.7. Fallback
3.8. Error Handling
3.9. Heuristics
3.9.1. Port Usage
3.9.2. Delayed Subflow Start and Subflow Symmetry
3.9.3. Failure Handling
4. Semantic Issues
5. Security Considerations
6. Interactions with Middleboxes
7. IANA Considerations
7.1. TCP Option Kind Numbers
7.2. MPTCP Option Subtypes
7.3. MPTCP Handshake Algorithms
7.4. MP_TCPRST Reason Codes
8. References
8.1. Normative References
8.2. Informative References
Appendix A. Notes on Use of TCP Options
Appendix B. TCP Fast Open and MPTCP
B.1. TFO Cookie Request with MPTCP
B.2. Data Sequence Mapping under TFO
B.3. Connection Establishment Examples
Appendix C. Control Blocks
C.1. MPTCP Control Block
C.1.1. Authentication and Metadata
C.1.2. Sending Side
C.1.3. Receiving Side
C.2. TCP Control Blocks
C.2.1. Sending Side
C.2.2. Receiving Side
Appendix D. Finite State Machine
Appendix E. Changes from RFC 6824
Acknowledgments
Authors' Addresses
1. Introduction ๐ฆ
Multipath TCP (MPTCP) is a set of extensions to regular TCP [RFC0793]. It provides a Multipath TCP service [RFC6182], which lets one transport connection run across multiple paths at once. ๐
This document covers the protocol changes needed to add multipath to TCP. That means signaling and setting up multiple paths (called "subflows"), managing those subflows, putting data back together in order, and ending sessions. ๐ It isn't the only info you need to build an MPTCP implementation, though. This document has three companion docs:
- [RFC6182] (MPTCP architecture): explains why Multipath TCP exists, talks through the high-level design decisions this design is built on, and explains a functional separation that lets an extensible MPTCP implementation get developed. ๐ง
- [RFC6356] (congestion control): presents a safe congestion control algorithm that couples the behavior of the multiple paths so they "do no harm" to other network users. ๐ซถ
- [RFC6897] (application considerations): covers what MPTCP does to applications, what applications will want to do with it, and, because of all that, which API extensions an MPTCP implementation should expose. โจ
This document obsoletes the v0 spec of Multipath TCP [RFC6824]. It specifies MPTCP v1, which is not backward compatible with MPTCP v0. It also defines version negotiation procedures for implementations that support both versions. ๐ฅ
1.1. Design Assumptions ๐ธ
To keep the potentially huge design space under control, the MPTCP Working Group put two key constraints on the design in this document:
- It must be backward compatible with current, regular TCP, so it has a better shot at getting deployed. ๐
- We can assume that one or both hosts are multihomed and multiaddressed. ๐
To keep things simple, we assume that having multiple addresses at a host is enough to mean there are multiple paths. Those paths don't have to be totally disjoint. They might share one or more routers. Even then, using multiple paths helps: it improves resource utilization and resilience to some node failures. The congestion control algorithm in [RFC6356] makes sure that using multiple paths doesn't hurt anyone else. ๐ก๏ธ Also, in some setups, different TCP ports on one host can give you disjoint paths (for example, through certain Equal-Cost Multipath (ECMP) implementations [RFC2992]), so the MPTCP design also supports using ports as part of path identifiers. ๐ง
There are three aspects to the backward compatibility above (covered in more detail in [RFC6182]):
External Constraints: The protocol has to work through the vast majority of existing middleboxes, like NATs, firewalls, and proxies. So it has to look as much like existing TCP as possible on the wire. The protocol also can't assume that the segments it sends arrive at the destination unmodified. They may be split or coalesced, and TCP options may be removed or duplicated. ๐
Application Constraints: The protocol has to work with no changes to existing applications that use the common TCP API. (It's reasonable that not every feature will be available to those legacy apps.) The protocol also has to give applications the same service model as regular TCP. ๐
Fallback: The protocol should be able to fall back to standard TCP without any interference from the user, so it can talk to legacy hosts. ๐ฆ
The companion application considerations doc [RFC6897] goes over what an API needs for backward compatibility, plus API extensions that show MPTCP's behavior at the same level of control and information you'd get with regular single-path TCP. ๐
More on the design constraints and the decisions made around them is in the MPTCP architecture doc [RFC6182] and in [howhard]. ๐
1.2. Multipath TCP in the Networking Stack ๐ง
MPTCP works at the transport layer, and it's meant to be transparent to both the higher and lower layers. It's a set of extra features stacked on top of standard TCP. Figure 1 shows that layering ๐ฅ MPTCP is designed so legacy applications can use it with no changes. For a detailed look at how it interacts with applications, see [RFC6897]. ๐
+-------------------------------+
| Application |
+---------------+ +-------------------------------+
| Application | | MPTCP |
+---------------+ + - - - - - - - + - - - - - - - +
| TCP | | Subflow (TCP) | Subflow (TCP) |
+---------------+ +-------------------------------+
| IP | | IP | IP |
+---------------+ +-------------------------------+
Figure 1: Comparison of Standard TCP and MPTCP Protocol Stacks
Part 1 of 20 is done, bestie ๐ซถ Part 2 is coming with Terminology, the MPTCP Concept, and the Requirements Language ๐
1.3. Terminology
okay so before we dive in, bestie, there are a few MPTCP-specific terms we need to be on the same page about ๐ these are either brand new to MPTCP or they mean something special here:
- Path: a sequence of links between a sender and a receiver. In this context it's defined by a 4-tuple of source and destination address/port pairs ๐ฆ
- Subflow: a flow of TCP segments running over one individual path, which forms part of a bigger MPTCP connection. It gets started and terminated pretty much the same way a regular TCP connection does ๐
- (MPTCP) Connection: a set of one or more subflows that an application uses to communicate between two hosts. There's a one-to-one mapping between a connection and an application socket ๐ธ
- Data-level: the payload data is nominally transferred over a connection, which is then carried over subflows. So "data-level" is basically a synonym for "connection-level," as opposed to "subflow-level," which is about the properties of one individual subflow ๐ง
- Token: a locally unique identifier that a host gives to a multipath connection. It can also be called a "Connection ID" ๐
- Host: an end host running an MPTCP implementation that either starts or accepts an MPTCP connection ๐
In addition to these terms, MPTCP's interpretation of, and effect on, regular single-path TCP semantics is covered in Section 4. Don't skip it, it's giving important ๐
1.4. MPTCP Concept
this section is the highlight reel of how MPTCP normally works โจ it's a high-level summary, and you'll see the whole scenario in Figure 2. if you want the full details, Section 3 has them ๐ซถ
Host A Host B
------------------------ ------------------------
Address A1 Address A2 Address B1 Address B2
---------- ---------- ---------- ----------
| | | |
| (initial connection setup) | |
|----------------------------------->| |
|<-----------------------------------| |
| | | |
| (additional subflow setup) |
| |--------------------->| |
| |<---------------------| |
| | | |
| | | |
Figure 2: Example MPTCP Usage Scenario
- to a non-MPTCP-aware application, MPTCP behaves the same as normal TCP ๐ extended APIs could give MPTCP-aware applications extra control [RFC6897]. the app starts by opening a TCP socket in the normal way, and MPTCP signaling and operation are handled by the MPTCP implementation. no drama for the app ๐
- an MPTCP connection starts out just like a regular TCP connection. Figure 2 shows an MPTCP connection being set up between addresses A1 and B1 on Hosts A and B, respectively ๐ฆ
- if extra paths are available, additional TCP sessions (called MPTCP "subflows") get created on those paths and combined with the existing session. the existing session keeps looking like one single connection to the applications at both ends ๐ the extra session in the figure is being created between Address A2 on Host A and Address B1 on Host B.
- MPTCP identifies multiple paths by the presence of multiple addresses at hosts. Combinations of those addresses equal the additional paths. in the example, other potential paths that could be set up are A1<->B2 and A2<->B2. even though this extra session is shown starting from A2, it could just as easily have started from B1 or B2 ๐
- the discovery and setup of additional subflows gets done through a path management method. this document describes a mechanism where a host can start new subflows using its own additional addresses, or by signaling its available addresses to the other host ๐ง
- MPTCP adds connection-level sequence numbers so segments that arrive on multiple subflows, with differing network delays, can be reassembled ๐ธ
- subflows get terminated like regular TCP connections, with a four-way FIN handshake. the MPTCP connection itself is terminated by a connection-level FIN ๐ซถ
1.5. Requirements Language
okay, this part is the rulebook for capital letters ๐ the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. so if you see them in ALL CAPS, they're doing the official BCP 14 thing ๐ lowercase ones are just regular English, no cap ๐
2. Operation Overview
this section is one clean description of how MPTCP usually operates, with references to the protocol's behaviour. think of it as the high-level tour of the key functions ๐บ๏ธ the full specification comes later in Section 3. extensibility and negotiated features aren't covered here, so don't look for them yet ๐ธ a lot of this section refers to the symbolic names of MPTCP options. those are subtypes of the IANA-assigned MPTCP option (see Section 7), and their formats are defined in the detailed protocol specification in Section 3 ๐
okay the big picture is that a Multipath TCP connection gives you a bidirectional bytestream between two hosts that communicate like normal TCP, so it doesn't require any changes to the applications ๐ฆ but MPTCP lets the hosts use different paths with different IP addresses to swap packets that belong to the MPTCP connection. to an application, a Multipath TCP connection still looks like a normal TCP connection ๐ to the network layer, though, each MPTCP subflow looks like a regular TCP flow whose segments carry a new TCP option type โจ MPTCP handles creating, removing, and using these subflows to send data. the number of subflows isn't fixed; it can go up and down during the lifetime of the connection. that's giving flexible ๐ง
all MPTCP operations are signaled with a TCP option. it's a single numerical type for MPTCP, with "subtypes" for each MPTCP message ๐ฅ what comes next is a quick summary of what these messages are for and why they exist.
2.1. Initiating an MPTCP Connection
this is the same signaling as starting a normal TCP connection, except the SYN, SYN/ACK, and initial ACK (and data) packets also carry the MP_CAPABLE option ๐ this option has a variable length and does a few jobs. first, it checks whether the remote host supports Multipath TCP. second, it lets the hosts swap some information to authenticate the setup of additional subflows. more details are in Section 3.1 ๐
Host A Host B
------ ------
MP_CAPABLE ->
[flags]
<- MP_CAPABLE
[B's key, flags]
ACK + MP_CAPABLE (+ data) ->
[A's key, B's key, flags, (data-level details)]
okay now the retransmission part, which is lowkey important ๐ an ACK + MP_CAPABLE can be retransmitted if it isn't known whether it was received. the following diagrams show all the possible exchanges for the initial subflow setup, so the reliability is covered ๐ซถ
Host A (with data to send immediately) Host B
------ ------
MP_CAPABLE ->
[flags]
<- MP_CAPABLE
[B's key, flags]
ACK + MP_CAPABLE + data ->
[A's key, B's key, flags, data-level details]
Host A (with data to send later) Host B
------ ------
MP_CAPABLE ->
[flags]
<- MP_CAPABLE
[B's key, flags]
ACK + MP_CAPABLE ->
[A's key, B's key, flags]
ACK + MP_CAPABLE + data ->
[A's key, B's key, flags, data-level details]
Host A Host B (sending first)
------ ------
MP_CAPABLE ->
[flags]
<- MP_CAPABLE
[B's key, flags]
ACK + MP_CAPABLE ->
[A's key, B's key, flags]
<- ACK + DSS + data
[data-level details]
2.2. Associating a New Subflow with an Existing MPTCP Connection
the key exchange in the MP_CAPABLE handshake gives you material to authenticate the endpoints once new subflows get set up ๐ additional subflows start the same way a normal TCP connection does, but the SYN, SYN/ACK, and ACK packets also carry the MP_JOIN option ๐ฆ
Host A starts a new subflow between one of its addresses and one of Host B's addresses ๐ธ the token, which is generated from the key, identifies which MPTCP connection is being joined. the Hash-based Message Authentication Code (HMAC) handles authentication. the HMAC uses the keys exchanged in the MP_CAPABLE handshake, plus the random numbers (nonces) exchanged in these MP_JOIN options ๐ง MP_JOIN also carries flags and an Address ID, so the source address can be referred to without the sender needing to know whether a NAT changed it ๐ more details are in Section 3.2.
Host A Host B
------ ------
MP_JOIN ->
[B's token, A's nonce,
A's Address ID, flags]
<- MP_JOIN
[B's HMAC, B's nonce,
B's Address ID, flags]
ACK + MP_JOIN ->
[A's HMAC]
<- ACK
2.3. Informing the Other Host about Another Potential Address ๐ฆ
okay so a multihomed host's set of IP addresses can totally change while an MPTCP connection is running ๐ธ MPTCP lets you add and remove addresses both implicitly and explicitly. If Host A already has a subflow going from address/port pair IP#-A1 and wants a second one starting at IP#-A2, it just starts setting up that subflow like we talked about earlier. The remote host finds out about the new address automatically, no extra paperwork needed โจ
sometimes a host wants to tell the other side about an address without opening a new subflow yet. the classic example is when a NAT is blocking setup in one direction ๐ In the example below, Host A tells Host B about its alternative IP address/port pair (IP#-A2). Host B can then send an MP_JOIN to that new address later on. The ADD_ADDR option carries an HMAC so the address is authenticated as coming from the connection's originator, and the receiver echoes it back to the client to say "yep, got it" ๐ The full details live in Section 3.4.1.
Host A Host B
------ ------
ADD_ADDR ->
[Echo-flag=0,
IP#-A2,
IP#-A2's Address ID,
HMAC of IP#-A2]
<- ADD_ADDR
[Echo-flag=1,
IP#-A2,
IP#-A2's Address ID,
HMAC of IP#-A2]
there's a matching signal for removing an address too, and it uses the Address ID that got signaled during the ADD_ADDR handshake ๐ง More details are in Section 3.4.2.
Host A Host B
------ ------
REMOVE_ADDR ->
[IP#-A2's Address ID]
2.4. Data Transfer Using MPTCP ๐
okay this part is so clever, bestie ๐ญ To get reliable, in-order delivery over subflows that can show up and vanish at any moment, MPTCP numbers all the data on the connection with a 64-bit Data Sequence Number (DSN). Each subflow still has its own 32-bit sequence number space using the regular TCP sequence number header, and an MPTCP option maps that subflow space onto the data sequence space. So data can be retransmitted on a different subflow (mapped to the same DSN) if something fails ๐ฅ
the Data Sequence Signal (DSS) is what carries the Data Sequence Mapping. The mapping is made of the subflow sequence number, the data sequence number, and the length the mapping is valid for. The DSS can also carry a connection-level acknowledgment (the "Data ACK") for the DSN that was received ๐
all the subflows share one receive buffer and advertise the same receive window, and there are two levels of acknowledgment going on. Regular TCP ACKs happen on each subflow to acknowledge segments sent over that subflow, no matter what their DSN is. On top of that there are connection-level acknowledgments for the data sequence space. Those track how far the bytestream has moved along and slide the receive window forward ๐ฆ
More details are in Section 3.3.
Host A Host B
------ ------
DSS ->
[Data Sequence Mapping]
[Data ACK]
[Checksum]
2.5. Requesting a Change in a Path's Priority ๐
hosts can say at initial subflow setup whether a subflow should be a regular path or a backup path. A backup path only gets used if there are no regular paths available ๐ซถ During a connection, Host A can ask for a priority change on a subflow using the MP_PRIO signal sent to Host B. Further details are given in Section 3.3.8.
Host A Host B
------ ------
MP_PRIO ->
2.6. Closing an MPTCP Connection ๐ธ
if a host wants to close one subflow but not the whole connection, it can just do a regular TCP FIN/ACK exchange. easy peasy ๐
when Host A has no more data to send, it signals a "Data FIN" as part of the DSS. It works with the same semantics and behavior as a regular TCP FIN, just at the connection level instead. Once all the data on the MPTCP connection has been received successfully, the Data FIN gets acknowledged at the connection level with a Data ACK โจ More details are in Section 3.3.3.
Host A Host B
------ ------
DSS ->
[Data FIN]
<- DSS
[Data ACK]
there's also a method called "Fast Close," which is basically like closing a single-path TCP connection with a RST ๐ The MP_FASTCLOSE signal tells the peer the connection is about to be slammed shut and no more data will be accepted. You can send it on an ACK (which makes the signal reliable) or on a RST (which doesn't). Both versions are shown in the diagrams below. Further details are in Section 3.5.
Host A Host B
------ ------
ACK + MP_FASTCLOSE ->
[B's key]
[RST on all other subflows] ->
<- [RST on all subflows]
Host A Host B
------ ------
RST + MP_FASTCLOSE ->
[B's key] [on all subflows]
<- [RST on all subflows]
2.7. Notable Features ๐
MPTCP's signaling was designed with a few key requirements in mind, and these are the highlights ๐
- to deal with NATs on the path, addresses are referred to by Address IDs, in case a NAT changes the IP packet's source address. Setting up a new TCP flow isn't possible if the receiver of the SYN is behind a NAT, so to let subflows be created when either end is behind a NAT, MPTCP uses the ADD_ADDR message ๐
- MPTCP falls back to ordinary TCP if MPTCP operation isn't possible. For example, that happens if one host isn't MPTCP capable or if a middlebox alters the payload. This is discussed in Section 3.7.
- to address the threats identified in [RFC6181], the following steps are taken. Keys are sent in the clear in the MP_CAPABLE messages. MP_JOIN messages are secured with HMAC-SHA256 ([RFC2104] using the algorithm in [RFC6234]) using those keys. Standard TCP validity checks are made on the other messages, which ensures that sequence numbers are in-window [RFC5961]. Residual threats to MPTCP v0 were identified in [RFC7430], and the ones affecting the protocol (that is, modifications to ADD_ADDR) have been incorporated into this document. Further discussion of security can be found in Section 5 ๐ฅ
3. MPTCP Operations: An Overview ๐ง
this section is where the operation of MPTCP actually gets explained, and each subsection covers one key part of how the protocol works ๐
all MPTCP operations are signaled using optional TCP header fields. IANA assigned a single TCP option number ("Kind") for MPTCP (see Section 7). Individual messages are then identified by a "subtype," and those subtype values are also stored in an IANA registry (and listed in Section 7 too). Just like every TCP option, the Length field is in bytes and includes the 2 bytes of Kind and Length ๐
when this document refers to an MPTCP option by its symbolic name, like "MP_CAPABLE," it means a TCP option with the single MPTCP option type and the subtype value that matches that symbolic name, as defined in Section 7. The subtype is a 4-bit field, namely the first 4 bits of the option payload, as shown in Figure 3. The MPTCP messages themselves get defined in the sections that follow โจ
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------+-----------------------+
| Kind | Length |Subtype| |
+---------------+---------------+-------+ |
| Subtype-specific data |
| (variable length) |
+---------------------------------------------------------------+
Figure 3: MPTCP Option Format ๐ธ
some MPTCP options are tied to subflow initiation, and those get used on packets with the SYN flag set ๐ There's also one MPTCP option for signaling metadata so that segmented data can be recombined for delivery to the application.
the remaining options are signals that don't need to sit on any particular packet, like the ones for signaling extra addresses. An implementation might want to send MPTCP options as soon as possible, but it may not be possible to fit every option on one packet. That includes both the MPTCP options and the regular TCP ones, like SACK (selective acknowledgment) [RFC2018]. So an implementation may choose to send duplicate ACKs that carry the extra signaling information. That changes what a duplicate ACK means ๐ญ Duplicate ACKs are usually only sent in regular TCP as a signal of a lost segment [RFC5681]. So an MPTCP implementation that receives a duplicate ACK containing an MPTCP option MUST NOT treat it as a signal of congestion. Also, an MPTCP implementation SHOULD NOT send more than two duplicate ACKs in a row just to carry MPTCP options alone, so that no middleboxes misread it as a sign of congestion ๐
and one more thing: standard TCP validity checks (like making sure the sequence number and acknowledgment number are within the window) MUST be undertaken before any MPTCP signals get processed, as described in [RFC5961]. Initial subflow sequence numbers SHOULD be generated according to the recommendations in [RFC6528] ๐
3.1. Connection Initiation ๐
connection initiation kicks off with a SYN, SYN/ACK, ACK exchange on a single path ๐ฆ Each packet carries the Multipath Capable (MP_CAPABLE) MPTCP option (Figure 4). That option declares that its sender is capable of Multipath TCP and wants to use it on this particular connection โจ
๐ Okay bestie, so here's the MP_CAPABLE layout. Lookin' cute, right? ๐
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------+-------+---------------+
| Kind | Length |Subtype|Version|A|B|C|D|E|F|G|H|
+---------------+---------------+-------+-------+---------------+
| Option Sender's Key (64 bits) |
| (if option Length > 4) |
| |
+---------------------------------------------------------------+
| Option Receiver's Key (64 bits) |
| (if option Length > 12) |
| |
+-------------------------------+-------------------------------+
| Data-Level Length (16 bits) | Checksum (16 bits, optional) |
+-------------------------------+-------------------------------+
Figure 4: Multipath Capable (MP_CAPABLE) Option โจ
Okay so the MP_CAPABLE exchange in this spec (v1) is different from what v0 did ๐ If a host supports more than one version of MPTCP, the sender of the MP_CAPABLE option SHOULD signal the highest version number it supports. ๐ง Then the receiver answers in its own MP_CAPABLE option with the version it wants to use, and that version MUST be equal to or lower than the version number indicated in the initial MP_CAPABLE. Simple, right? ๐
There's a little caveat though, with listeners that only support v0. A listener that supports v0 expects the MP_CAPABLE option in the SYN segment to include the initiator's key. If the initiator already upgraded to v1, it won't include the key in the SYN segment, so the listener ignores that MP_CAPABLE and replies with a SYN/ACK that doesn't include an MP_CAPABLE. ๐ญ The initiator MAY choose to immediately fall back to TCP, or MAY choose to attempt a connection using MPTCP v0 (if the initiator supports v0), in order to discover whether the listener supports the earlier version. In general, an MPTCP v0 connection will likely be preferred over a TCP connection, but in a particular deployment scenario it may be known that the listener is unlikely to support MPTCP v0, so the initiator may prefer not to attempt a v0 connection. And an initiator MAY cache information for a peer about what version of MPTCP it supports, if any, and use this information for future connection attempts. ๐ฆ
The MP_CAPABLE option is of variable length, with different fields included depending on which packet the option is used on. The full MP_CAPABLE option is shown in Figure 4 above. ๐ธ
The MP_CAPABLE option is carried on the SYN, SYN/ACK, and ACK packets that start the first subflow of an MPTCP connection, as well as the first packet that carries data, if the initiator wishes to send first. Here's the data each option carries, where A = initiator and B = listener:
- SYN (A->B): only the first 4 octets (
Length = 4). - SYN/ACK (B->A): B's key for this connection (
Length = 12). - ACK (no data) (A->B): A's key followed by B's key (
Length = 20). - ACK (with first data) (A->B): A's key followed by B's key followed by Data-Level Length, and optional Checksum (
Length = 22 or 24).
The contents of the option are determined by the SYN and ACK flags of the packet, along with the option's Length field. In Figure 4, "Sender" and "Receiver" refer to the sender or receiver of the TCP packet (which can be either host). ๐
The initial SYN, containing just the MP_CAPABLE header, is used to define the version of MPTCP being requested and also to exchange flags to negotiate connection features, as described later. โจ
This option is used to declare the 64-bit keys that the end hosts have generated for this MPTCP connection. ๐ These keys are used to authenticate the addition of future subflows to this connection. This is the only time the key will be sent in the clear on the wire (unless "Fast Close" (Section 3.5) is used). All future subflows will identify the connection using a 32-bit "token", which is a cryptographic hash of this key. The algorithm for this process depends on the authentication algorithm selected, and the method of selection is defined later in this section. ๐
Upon reception of the initial SYN segment, a stateful server generates a random key and replies with a SYN/ACK. The key's method of generation is implementation specific. The key MUST be hard to guess, and it MUST be unique for the sending host across all its current MPTCP connections. ๐ Recommendations for generating random numbers for use in keys are given in [RFC4086]. Connections will be indexed at each host by the token (a one-way hash of the key). So an implementation will need a mapping from each token to the corresponding connection, and in turn to the keys for the connection.
There's a risk that two different keys will hash to the same token ๐ฌ. The risk of hash collisions is usually small, unless the host is handling many tens of thousands of connections. Therefore, an implementation SHOULD check its list of connection tokens to ensure that there is no collision before sending its key, and if there is, then it should generate a new key. This would, however, be costly for a server with thousands of connections. The subflow handshake mechanism (Section 3.2) will ensure that new subflows only join the correct connection, though, through the cryptographic handshake, as well as checking the connection tokens in both directions and ensuring that sequence numbers are in-window. So, in the worst case, if there was a token collision, the new subflow would not succeed, but the MPTCP connection would continue to provide a regular TCP service. ๐
Since key generation is implementation specific, there is no requirement that the keys simply be random numbers. An implementation is free to exchange cryptographic material out of band and generate these keys from this material, in order to provide additional mechanisms by which to verify the identity of the communicating entities. For example, an implementation could choose to link its MPTCP keys to those used in higher-layer TLS or SSH connections. ๐
If the server behaves in a stateless manner, it has to generate its own key in a verifiable fashion. This verifiable way of generating the key can be done by using a hash of the 4-tuple, sequence number, and a local secret (similar to what is done for the TCP sequence number [RFC4987]). It will thus be able to verify whether it is indeed the originator of the key echoed back in the subsequent MP_CAPABLE option. As for a stateful server, the tokens SHOULD be checked for uniqueness. However, if uniqueness is not met and there is no way to generate an alternative verifiable key, then the connection MUST fall back to using regular TCP by not sending an MP_CAPABLE in the SYN/ACK. ๐ต๏ธโโ๏ธ
The ACK carries both A's key and B's key. This is the first time that A's key is seen on the wire, although it is expected that A will have generated a key locally before the initial SYN. The echoing of B's key allows B to operate statelessly, as described above. Therefore, A's key must be delivered reliably to B, and in order to do this, the transmission of this packet must be made reliable. ๐ซถ
If B has data to send first, then the reliable delivery of the ACK + MP_CAPABLE is ensured by the receipt of this data with an MPTCP Data Sequence Signal (DSS) option (Section 3.3) containing a DATA_ACK for the MP_CAPABLE (which is the first octet of the data sequence space). If, however, A wishes to send data first, it has two options to ensure the reliable delivery of the ACK + MP_CAPABLE. If it immediately has data to send, then the first ACK (with data) would also contain an MP_CAPABLE option with additional data parameters (the Data-Level Length and optional Checksum as shown in Figure 4). If A does not immediately have data to send, it MUST include the MP_CAPABLE on the first ACK, but without the additional data parameters. When A does have data to send, it must repeat the sending of the MP_CAPABLE option from the first ACK, with additional data parameters. ๐ฏ This MP_CAPABLE option is used in place of the DSS and simply specifies (1) the Data-Level Length of the payload and (2) the checksum (if the use of checksums is negotiated). This is the minimal data required to establish an MPTCP connection. It allows validation of the payload, and given that it is the first data, the Initial Data Sequence Number (IDSN) is also known (as it is generated from the key, as described below). Conveying the keys on the first data packet allows the TCP reliability mechanisms to ensure that the packet is successfully delivered. The receiver will acknowledge this data at the connection level with a Data ACK, as if a DSS option has been received. ๐
There could be situations where both A and B attempt to transmit initial data at the same time. For example, if A did not initially have data to send but then needed to transmit data before it had received anything from B, it would use an MP_CAPABLE option with data parameters (since it would not know if the MP_CAPABLE on the ACK was received). In such a situation, B may also have transmitted data with a DSS option, but it had not yet been received at A. Therefore, B has received data with an MP_CAPABLE mapping after it has sent data with a DSS option. To ensure that these situations can be handled, it follows that the data parameters in an MP_CAPABLE are semantically equivalent to those in a DSS option and can be used interchangeably. Similar situations could occur when the MP_CAPABLE with data is lost and retransmitted. Furthermore, in the case of TCP segmentation offloading, the MP_CAPABLE with data parameters may be duplicated across multiple packets, and implementations must also be able to cope with duplicate MP_CAPABLE mappings as well as duplicate DSS mappings. ๐
Additionally, the MP_CAPABLE exchange allows the safe passage of MPTCP options on SYN packets to be determined. If any of these options are dropped, MPTCP will gracefully fall back to regular single-path TCP, as documented in Section 3.7. ๐ฆ If at any point in the handshake either party thinks the MPTCP negotiation is compromised (for example, by a middlebox corrupting the TCP options or by unexpected ACK numbers being present), the host MUST stop using MPTCP and no longer include MPTCP options in future TCP packets. The other host will then also fall back to regular TCP using the fallback mechanism. Note that new subflows MUST NOT be established (using the process documented in Section 3.2) until a DSS option has been successfully received across the path (as documented in Section 3.3). ๐จ
Like all MPTCP options, the MP_CAPABLE option starts with the Kind and Length to specify the TCP option's kind and length. This information is followed by the MP_CAPABLE option. The first 4 bits of the first octet in the MP_CAPABLE option (Figure 4) define the MPTCP Option Subtype (see Section 7; for MP_CAPABLE, this value is 0x0), and the remaining 4 bits of this octet specify the MPTCP version in use (for this specification, this value is 1). ๐ง
The second octet is reserved for flags, allocated as follows:
A: The leftmost bit, labeled "A", SHOULD be set to 1 to indicate "Checksum required", unless the system administrator has decided that checksums are not required (for example, if the environment is controlled and no middleboxes exist that might adjust the payload). ๐B: The second bit, labeled "B", is an extensibility flag. It MUST be set to 0 for current implementations. This flag will be used for an extensibility mechanism in a future specification, and the impact of this flag will be defined at a later date. It is expected, but not mandated, that this flag would be used as part of an alternative security mechanism that does not require a full version upgrade of the protocol but does require redefining some elements of the handshake. If receiving a message with the "B" flag set to 1 and this is not understood, then the MP_CAPABLE in this SYN MUST be silently ignored, which triggers a fallback to regular TCP; the sender is expected to retry with a format compatible with this legacy specification. Note that the length of the MP_CAPABLE option, and the meanings of bits "D" through "H", may be altered by setting B=1. ๐ฆC: The third bit, labeled "C", is set to 1 to indicate that the sender of this option will not accept additional MPTCP subflows to the source address and port, and therefore the receiver MUST NOT try to open any additional subflows toward this address and port. This improves efficiency in situations where the sender knows a restriction is in place -- for example, if the sender is behind a strict NAT or operating behind a legacy Layer 4 load balancer. ๐ธD through H: The remaining bits, labeled "D" through "H", are used for crypto algorithm negotiation. In this specification, only the rightmost bit, labeled "H", is assigned. Bit "H" indicates the use of HMAC-SHA256 (as defined in Section 3.2). An implementation that only supports this method MUST set bit "H" to 1 and bits "D" through "G" to 0. ๐
A crypto algorithm MUST be specified. If flag bits "D" through "H" are all 0, the MP_CAPABLE option MUST be treated as invalid and ignored (that is, it must be treated as a regular TCP handshake). ๐
The selection of the authentication algorithm also impacts the algorithm used to generate the token and the IDSN. In this specification, with only the SHA-256 algorithm (bit "H") specified and selected, the token MUST be a truncated (most significant 32 bits) SHA-256 hash [RFC6234] of the key. A different, 64-bit truncation (the least significant 64 bits) of the SHA-256 hash of the key MUST be used as the IDSN. Note that the key MUST be hashed in network byte order. Also note that the "least significant" bits MUST be the rightmost bits of the SHA-256 digest, as per [RFC6234]. Future specifications of the use of the crypto bits may choose to specify different algorithms for token and IDSN generation.
Both the crypto and checksum bits negotiate capabilities in similar ways. For the "Checksum required" bit (labeled "A"), if either host requires the use of checksums, checksums MUST be used. In other words, the only way for checksums not to be used is if both hosts in their SYNs set A=0. This decision is confirmed by the setting of the "A" bit in the third packet (the ACK) of the handshake. For example, if the initiator sets A=0 in the SYN but the responder sets A=1 in the SYN/ACK, checksums MUST be used in both directions, and the initiator will set A=1 in the ACK. The decision regarding whether to use checksums will be stored by an implementation in a per-connection binary state variable. If A=1 is received by a host that does not want to use checksums, it MUST fall back to regular TCP by ignoring the MP_CAPABLE option as if it was invalid. โจ
For crypto negotiation, the responder has the choice. The initiator creates a proposal setting a bit for each algorithm it supports to 1 (in this version of the specification, there is only one proposal, so bit "H" will always be set to 1). The responder responds with only 1 bit set -- this is the chosen algorithm. The rationale for this behavior is that the responder will typically be a server with potentially many thousands of connections, so it may wish to choose an algorithm with minimal computational complexity, depending on the load. If a responder does not support (or does not want to support) any of the initiator's proposals, it MUST respond without an MP_CAPABLE option, thus forcing a fallback to regular TCP.
The MP_CAPABLE option is only used in the first subflow of a connection, in order to identify the connection; all subsequent subflows will use the MP_JOIN option (see Section 3.2) to join the existing connection.
If a SYN contains an MP_CAPABLE option but the SYN/ACK does not, it is assumed that the sender of the SYN/ACK is not multipath capable; thus, the MPTCP session MUST operate as a regular, single-path TCP session. If a SYN does not contain an MP_CAPABLE option, the SYN/ACK MUST NOT contain one in response. If the third packet (the ACK) does not contain the MP_CAPABLE option, then the session MUST fall back to operating as a regular, single-path TCP session. This is done to maintain compatibility with middleboxes on the path that drop some or all TCP options. Note that an implementation MAY choose to attempt sending MPTCP options more than one time before making this decision to operate as regular TCP (see Section 3.9). ๐
If the SYN packets are unacknowledged, it is up to local policy to decide how to respond. It is expected that a sender will eventually fall back to single-path TCP (i.e., without the MP_CAPABLE option) in order to work around middleboxes that may drop packets with unknown options; however, the number of multipath-capable attempts that are made first will be up to local policy. It is possible that MPTCP and non-MPTCP SYNs could get reordered in the network. Therefore, the final state is inferred from the presence or absence of the MP_CAPABLE option in the third packet of the TCP handshake. If this option is not present, the connection SHOULD fall back to regular TCP, as documented in Section 3.7.
The IDSN on an MPTCP connection is generated from the key. The algorithm for IDSN generation is also determined from the negotiated authentication algorithm. In this specification, with only the SHA-256 algorithm specified and selected, the IDSN of a host MUST be the least significant 64 bits of the SHA-256 hash of its key, i.e., IDSN-A = Hash(Key-A) and IDSN-B = Hash(Key-B). This deterministic generation of the IDSN allows a receiver to ensure that there are no gaps in sequence space at the start of the connection. The SYN with MP_CAPABLE occupies the first octet of data sequence space, although this does not need to be acknowledged at the connection level until the first data is sent (see Section 3.3). ๐ง
3.2. Starting a New Subflow ๐ฆ
Once an MPTCP connection has begun with the MP_CAPABLE exchange, further subflows can be added to the connection. Hosts have knowledge of their own address(es) and can become aware of the other host's addresses through signaling exchanges as described in Section 3.4. Using this knowledge, a host can initiate a new subflow over a currently unused pair of addresses. It is permissible for either host in a connection to initiate the creation of a new subflow, but it is expected that this will normally be the original connection initiator (see Section 3.9 for heuristics).
A new subflow is started as a normal TCP SYN/ACK exchange. The Join Connection (MP_JOIN) MPTCP option is used to identify the connection to be joined by the new subflow. It uses keying material that was exchanged in the initial MP_CAPABLE handshake (Section 3.1), and that handshake also negotiates the crypto algorithm in use for the MP_JOIN handshake.
This section specifies the behavior of MP_JOIN using the HMAC-SHA256 algorithm. An MP_JOIN option is present in the SYN, SYN/ACK, and ACK of the three-way handshake, although in each case with a different format.
In the first MP_JOIN on the SYN packet, illustrated in Figure 5, the initiator sends a token, random number, and Address ID.
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------+-----+-+---------------+
| Kind | Length = 12 |Subtype|(rsv)|B| Address ID |
+---------------+---------------+-------+-----+-+---------------+
| Receiver's Token (32 bits) |
+---------------------------------------------------------------+
| Sender's Random Number (32 bits) |
+---------------------------------------------------------------+
Figure 5: Join Connection (MP_JOIN) Option (for Initial SYN)
The token is used to identify the MPTCP connection and is a cryptographic hash of the receiver's key, as exchanged in the initial MP_CAPABLE handshake (Section 3.1). In this specification, the tokens presented in this option are generated by the SHA-256 algorithm [RFC6234], truncated to the most significant 32 bits. The token included in the MP_JOIN option is the token that the receiver of the packet uses to identify this connection; i.e., Host A will send Token-B (which is generated from Key-B). Note that the hash generation algorithm can be overridden by the choice of cryptographic handshake algorithm, as defined in Section 3.1.
The MP_JOIN SYN sends not only the token (which is static for a connection) but also random numbers (nonces) that are used to prevent replay attacks on the authentication method. Recommendations for the generation of random numbers for this purpose are given in [RFC4086].
The MP_JOIN option includes an "Address ID". This is an identifier generated by the sender of the option, used to identify the source address of this packet, even if the IP header has been changed in transit by a middlebox. The numeric value of this field is generated by the sender and must map uniquely to a source IP address for the sending host. The Address ID allows address removal (Section 3.4.2) without needing to know what the source address at the receiver is, thus allowing address removal through NATs. The Address ID also allows correlation between new subflow setup attempts and address signaling (Section 3.4.1), to prevent setting up duplicate subflows on the same path, if an MP_JOIN and ADD_ADDR are sent at the same time.
The Address IDs of the subflow used in the initial SYN exchange of the first subflow in the connection are implicit and have the value zero. A host MUST store the mappings between Address IDs and addresses both for itself and the remote host. An implementation will also need to know which local and remote Address IDs are associated with which established subflows, for when addresses are removed from a local or remote host. ๐
The MP_JOIN option on SYN packets also carries 4 bits of flags ๐. Three of those are currently reserved, and the sender MUST set them to 0 ๐ โโ๏ธ. The last bit, labeled "B", tells the other host what the sender wants ๐ฆ. B=1 means "keep this subflow as a backup in case other paths fail." B=0 means "use this subflow as part of the connection right away." So by setting B=1, the sender is asking the other host to only send data on this subflow when there are no available subflows where B=0 ๐. Subflow policy gets the full tea in Section 3.3.8 โจ.
When a host gets a SYN with an MP_JOIN option that has a valid token for an existing MPTCP connection, the recipient SHOULD respond with a SYN/ACK that also has an MP_JOIN option. That option includes a random number and a truncated (leftmost 64 bits) HMAC ๐ธ. This version of the option is shown in Figure 6 ๐ . If the token is unknown, or the host doesn't want to allow the subflow (say, because it has a limit on how many subflows it'll allow), the receiver sends back a reset (RST). It's kind of like an unknown port in TCP ๐. The RST carries an MP_TCPRST option (Section 3.6) with an "MPTCP specific error" reason code ๐.
Okay so the HMAC math does take some crypto work, but the 32-bit token in the MP_JOIN SYN is considered enough protection against blind state exhaustion attacks. So there's no need for a responder to work statelessly at the MP_JOIN stage ๐ฅ.
1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +---------------+---------------+-------+-----+-+---------------+
| Kind | Length = 16 | Subtype | (rsv) | B | Address ID |
+---------------+---------------+-------+-----+-+---------------+
| Sender's Truncated HMAC (64 bits) |
+---------------------------------------------------------------+
| Sender's Random Number (32 bits) |
+---------------------------------------------------------------+
Figure 6: Join Connection (MP_JOIN) Option (for Responding SYN/ACK) ๐ฆ
An HMAC gets sent by both hosts. The initiator (Host A) sends one in the third packet (the ACK), and the responder (Host B) sends one in the second packet (the SYN/ACK) ๐. Doing the HMAC exchange at this stage lets both hosts first swap random data (in the first two SYN packets), and that random data is what gets used as the "message" ๐ง. This spec says the HMAC is defined as in [RFC2104], it uses the SHA-256 hash algorithm [RFC6234], and the output is truncated to the leftmost 160 bits (20 octets) โจ. Because of option space limits, the HMAC in the SYN/ACK gets truncated to the leftmost 64 bits. That's fine, since random numbers are used. So an attacker only gets one shot at guessing the HMAC that matches the random number the peer sent before ๐ฏ. If the HMAC is wrong, the TCP connection gets closed, so a new MP_JOIN negotiation with a new random number is required ๐.
The initiator's authentication info goes in its first ACK (the third packet of the handshake), as shown in Figure 7 ๐ซถ. This data needs to be sent reliably, since it's the only time this HMAC gets sent. So receiving this packet MUST trigger a regular TCP ACK in response, and the packet MUST be retransmitted if that ACK doesn't arrive ๐ญ. Basically, sending the ACK/MP_JOIN packet puts the subflow in the PRE_ESTABLISHED state. It only moves to the ESTABLISHED state once it gets an ACK from the receiver โจ. It is not permissible to send data while in the PRE_ESTABLISHED state ๐ซ. The reserved bits in this option MUST be set to 0 by the sender ๐ .
1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +---------------+---------------+-------+-----------------------+
| Kind | Length = 24 | Subtype | (reserved) |
+---------------+---------------+-------+-----------------------+
| Sender's Truncated HMAC (160 bits) |
+---------------------------------------------------------------+
Figure 7: Join Connection (MP_JOIN) Option (for Initiator's First ACK) ๐
For the HMAC key, when Host A sends the message, the key is Key-A followed by Key-B. When Host B sends it, the key is Key-B followed by Key-A. These are the keys that were swapped in the original MP_CAPABLE handshake ๐. The "message" is the concatenation of each host's random numbers (called R). For Host A, that's R-A followed by R-B. For Host B, it's R-B followed by R-A ๐ง .
All these MPTCP options work together to set up authenticated subflows, as shown in Figure 8 ๐.
Host A Host B ------------------------ ---------- Address A1 Address A2 Address B1 ---------- ---------- ----------
| SYN + MP_CAPABLE | |
|---|---|
| ---------------------------------------------> | |
| <--------------------------------------------- | |
| SYN/ACK + MP_CAPABLE(Key-B) | |
| ACK + MP_CAPABLE(Key-A, Key-B) | |
| ---------------------------------------------> | |
| SYN + MP_JOIN(Token-B, R-A) | |
| -------------------------------> | |
| <------------------------------- | |
| SYN/ACK + MP_JOIN(HMAC-B, R-B) | |
| ACK + MP_JOIN(HMAC-A) | |
| -------------------------------> | |
| <------------------------------- | |
| ACK |
HMAC-A = HMAC(Key=(Key-A + Key-B), Msg=(R-A + R-B)) HMAC-B = HMAC(Key=(Key-B + Key-A), Msg=(R-B + R-A))
Figure 8: Example Use of MPTCP Authentication
If the token Host B receives is unknown, or local policy won't accept the new subflow, the recipient MUST respond with a TCP RST for the subflow ๐ซ. If it makes sense, an MP_TCPRST option with an "Administratively prohibited" reason code (Section 3.6) should be included ๐ โโ๏ธ.
If the token is accepted at Host B but the HMAC sent back to Host A doesn't match what Host A expected, Host A MUST close the subflow with a TCP RST ๐ค. In this case, and in every other case in this section where a RST gets sent, the sender SHOULD send an MP_TCPRST option (Section 3.6) on that RST packet, with the reason code for an "MPTCP-specific error" ๐.
If Host B doesn't get the expected HMAC, or the MP_JOIN option is missing from the ACK, it MUST close the subflow with a TCP RST ๐.
If both HMACs check out, then both hosts have verified each other as the same peers that existed at the start of the connection, and they've agreed on which connection this subflow will join ๐ซถโจ.
If the SYN/ACK that Host A receives doesn't have an MP_JOIN option, Host A MUST close the subflow with a TCP RST ๐.
This covers all the ways an MP_JOIN can get lost. More specifically, if an MP_JOIN gets stripped from the SYN on the path from A to B, and Host B doesn't have a listener on the relevant port, it responds with a RST in the normal way ๐. If a SYN with an MP_JOIN option gets a SYN/ACK back without the MP_JOIN option, then the subflow is unusable and Host A MUST close it with a RST. That can happen because the option was stripped on the return path, or because it was stripped on the outgoing path and Host B responded as if it were a new regular TCP session ๐ตโ๐ซ.
Note that you can create additional subflows between any pair of ports (but see Section 3.9 for heuristics) ๐ฆ. You don't need any explicit application-level accept calls or bind calls to open additional subflows ๐ . To link a new subflow to an existing connection, the token in the subflow's SYN exchange is used for demultiplexing. That binds the 5-tuple of the TCP subflow to the connection's local token ๐. One consequence is that any port pairs can be used for a connection ๐ธ.
Demultiplexing subflow SYNs MUST be done using the token. This is different from traditional TCP, where the destination port is used to demultiplex SYN packets ๐. Once a subflow is set up, packets get demultiplexed using the 5-tuple, just like in traditional TCP. The 5-tuples get mapped to the local connection identifier (the token) ๐. Host A knows its own local token for the subflow even though it isn't sent on the wire. Only the responder's token is sent ๐ง.
3.3. MPTCP Operation and Data Transfer ๐
okay bestie, this is where the magic actually happens โจ MPTCP takes one data stream from an app and splits it across one or more subflows ๐. It also attaches enough control info that the other side can put everything back together, reliably and in order, and hand it to the app ๐ it's giving main character energy. The subsections below break down how that works.
The Data Sequence Mapping and the Data ACK are both signaled in the DSS option ๐ (Figure 9). You can signal either one or both in a single DSS, depending on which flags are set ๐ฆ. The Data Sequence Mapping says how the subflow sequence space maps onto the connection level, and the Data ACK says the data has been received at the connection level ๐. We'll get into each of these in the next two subsections ๐ซถ
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------+----------------------+
| Kind | Length |Subtype| (reserved) |F|m|M|a|A|
+---------------+---------------+-------+----------------------+
| Data ACK (4 or 8 octets, depending on flags) |
+--------------------------------------------------------------+
| Data Sequence Number (4 or 8 octets, depending on flags) |
+--------------------------------------------------------------+
| Subflow Sequence Number (4 octets) |
+-------------------------------+------------------------------+
| Data-Level Length (2 octets) | Checksum (2 octets) |
+-------------------------------+------------------------------+
Figure 9: Data Sequence Signal (DSS) Option ๐ธ
The flags, when set, define what's inside this option. Here's the tea ๐:
A= Data ACK presenta= Data ACK is 8 octets (if not set, Data ACK is 4 octets)M= Data Sequence Number (DSN), Subflow Sequence Number (SSN), Data-Level Length, and Checksum (if negotiated) presentm= Data Sequence Number is 8 octets (if not set, DSN is 4 octets)
ngl the "a" and "m" flags only mean something if their "A" or "M" buddies are set ๐ otherwise they get ignored. Also, the biggest this option can get, with every flag set, is 28 octets ๐
Now the "F" flag, which is giving "Data FIN" energy ๐ If it's present, it means this mapping covers the final data from the sender. It's basically the connection-level version of the FIN flag in single-path TCP ๐ง A connection doesn't get closed unless one of three things happens: a Data FIN exchange, an MP_FASTCLOSE (Section 3.5) message, or an implementation-specific connection-level send timeout โฐ The purpose of the Data FIN, and how this flag, the subflow-level FIN flag, and the Data Sequence Mapping all work together, is covered in Section 3.3.3 ๐ฅ And the remaining reserved bits MUST be set to 0 by an implementation of this specification, no exceptions bestie ๐
โโ๏ธ
Okay, the checksum is a whole thing ๐ง It only shows up in this option if MPTCP checksumming was negotiated during the MP_CAPABLE handshake (see Section 3.1) โจ The receiver can tell whether there's a checksum just by looking at the option's length ๐ If a checksum is there but nobody negotiated it in the MP_CAPABLE handshake, the receiver MUST close the subflow with a RST, because the sender is not behaving as negotiated ๐ค And if a checksum is missing even though it was negotiated, the receiver MUST close the subflow with a RST too, since that subflow is considered broken ๐ In both cases, this RST SHOULD come with an MP_TCPRST option (Section 3.6) carrying the reason code for an "MPTCP-specific error" ๐
โ ๏ธ tbh, the checksum rules are where people trip up, so read that last bit twice ๐ซถ
3.3.1. Data Sequence Mapping โจ
okay so the whole data stream gets stitched back together using the Data Sequence Mapping parts of the DSS option (Figure 9) ๐งฉ these define the mapping from the subflow sequence number to the data sequence number. the receiver uses this to make sure everything reaches the application layer in order ๐ meanwhile, the subflow-level sequence numbers (the normal ones in the TCP header) only matter to that one subflow. SACK [RFC2018] is expected at the subflow level to make things more efficient, but it is not mandated ๐ซถ
the mapping describes how the subflow sequence space maps onto the data sequence space ๐ฆ it's written as a starting sequence number for the subflow, a starting sequence number for the data level, and a length in bytes for which the mapping is valid. why map a whole range instead of signaling every packet? bestie, it's giving compatibility ๐ it still works when TCP/IP segmentation or coalescing happens outside the stack generating the data flow, like TCP segmentation offloading on network interface cards, or middleboxes such as Performance Enhancing Proxies (PEPs) [RFC3135]. one mapping can also cover loads of packets, which is super useful for bulk transfers ๐
a mapping is fixed: once it's processed, the subflow sequence number is bound to the data sequence number ๐ a sender MUST NOT change this mapping after it has been declared ๐ โโ๏ธ but the same data sequence number can be mapped by different subflows for retransmission purposes (see Section 3.3.6) ๐ this also lets the same data go out on multiple subflows at once for resilience or efficiency, especially on lossy links ๐ง๏ธ the detailed rules for that are outside the scope of this document, but an implementation SHOULD treat the first data it receives at a subflow for a given data sequence space as the data to deliver to the application, and any later data for that sequence space SHOULD be ignored ๐
the data sequence number is absolute, whereas the subflow sequence numbering is relative (the SYN at the start of the subflow has a relative subflow sequence number of 0) ๐ง this is so middleboxes can change the Initial Sequence Number (ISN) of a subflow, like firewalls doing ISN randomization. โจ
the Data Sequence Mapping also carries a checksum of the data it covers, but only if checksums were negotiated during the MP_CAPABLE exchange ๐ checksums catch the payload being changed by a non-MPTCP-aware middlebox ๐ if the checksum fails, it triggers a failure of the subflow or a fallback to regular TCP, as covered in Section 3.7, because MPTCP can no longer reliably know the subflow sequence space at the receiver to build Data Sequence Mappings ๐ญ without checksumming, corrupt data can reach the application if a middlebox changes segment boundaries, changes content, or doesn't deliver every segment covered by a Data Sequence Mapping ๐ so checksumming is RECOMMENDED, unless it is known that the network path has no such devices ๐ธ
the checksum algorithm is the standard TCP checksum [RFC0793], run over the data covered by this mapping along with a pseudo-header, as shown in Figure 10 ๐ฆ
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+--------------------------------------------------------------+
| |
| Data Sequence Number (8 octets) |
| |
+--------------------------------------------------------------+
| Subflow Sequence Number (4 octets) |
+-------------------------------+------------------------------+
| Data-Level Length (2 octets) | Zeros (2 octets) |
+-------------------------------+------------------------------+
Figure 10: Pseudo-Header for DSS Checksum
the data sequence number in the pseudo-header is always the 64-bit value, no matter what length is used in the DSS option itself ๐ข the standard TCP checksum was picked because it gets calculated for the TCP subflow anyway ๐ if you compute it over the data first, before adding the pseudo-headers, you only have to do it once. and since the TCP checksum is additive, the checksum for a DSN_MAP can be built by just adding together the checksums for the data of each constituent TCP segment plus the checksum for the DSS pseudo-header ๐งฎ mathematically iconic, no cap ๐ค
checksumming relies on the TCP subflow containing contiguous data, so a TCP subflow MUST NOT use the Urgent Pointer to interrupt an existing mapping ๐ซ but if Urgent data is received on a subflow, it SHOULD be mapped to the data sequence space and delivered to the application, just like Urgent data in regular TCP ๐
to avoid possible deadlocks, subflow-level processing should be handled separately from connection-level processing ๐ง so even if there's no mapping from the subflow space to the data-level space, the data SHOULD still be ACKed at the subflow (if it's in-window) ๐ซถ this data can't be acknowledged at the data level (Section 3.3.2) yet, because its data sequence numbers are unknown ๐ implementations MAY hold onto that unmapped data for a short while, in the hope that a mapping shows up soon โณ unmapped data doesn't count toward the connection-level receive window, since that window is relative to the data sequence numbers, so if the receiver runs out of memory holding it, it has to be discarded ๐๏ธ if a mapping for that subflow-level sequence space doesn't arrive within a receive window's worth of data, that subflow SHOULD be treated as broken, closed with a RST, and any unmapped data silently discarded ๐
data sequence numbers are always 64-bit quantities and MUST be maintained as such in implementations. that part is non-negotiable, bestie ๐
if a connection is progressing slowly, so protection against wrapped sequence numbers isn't needed, an implementation MAY include just the lower 32 bits of the data sequence number in the Data Sequence Mapping and/or Data ACK as an optimization ๐ and an implementation can make that choice independently for each packet โจ every implementation MUST be able to receive and process both 64-bit and 32-bit sequence number values, but it doesn't have to be able to send both ๐ง
an implementation MUST send the full 64-bit data sequence number if it's transmitting fast enough that the 32-bit value could wrap within the Maximum Segment Lifetime (MSL) [RFC7323] ๐ the lengths of the DSNs used in these values (which may be different) are declared with flags in the DSS option ๐ implementations MUST accept a 32-bit DSN and implicitly promote it to a 64-bit quantity by incrementing the upper 32 bits of the sequence number each time the lower 32 bits wrap ๐ and a sanity check MUST be implemented to make sure a wrap happens at an expected time (for example, the sequence number jumps from a very high number to a very low one) and isn't triggered by out-of-order packets ๐ต๏ธโโ๏ธ no false wraps allowed, periodt ๐
just like with the standard TCP sequence number, the data sequence number should not start at zero, but at a random value to make blind session hijacking harder ๐ฆ this specification requires setting the IDSN of each host to the least significant 64 bits of the SHA-256 hash of the host's key, as described in Section 3.1 ๐ this is also required so the receiver knows what the expected IDSN is and can work out whether any initial connection-level packets are missing ๐๏ธ that's especially relevant if two subflows start transmitting at the same time ๐ฏโโ๏ธ
the mapping provided by a Data Sequence Mapping MUST apply to some or all of the subflow sequence space in the TCP segment that carries the option ๐ฏ it doesn't need to be included in every MPTCP packet, as long as the subflow sequence space in that packet is covered by a mapping the receiver already knows about ๐ง that cuts down on overhead when the mapping is known in advance, like when there's a single subflow between the hosts, or when data gets scheduled in chunks bigger than one packet ๐
an "infinite" mapping can be used to fall back to regular TCP by mapping the subflow-level data to the connection-level data for the rest of the connection (see Section 3.7) ๐ that's done by setting the Data-Level Length field of the DSS option to the reserved value of 0 ๐ซง in that case, the checksum is also set to 0 ๐
3.3.2. Data Acknowledgments โจ
Okay so MPTCP has a connection-level acknowledgment, bestie ๐ซถ, and it acts as a cumulative ACK for the whole connection, not just one subflow. You get it through the "Data ACK" field of the DSS option (Figure 9) ๐
. It's basically the same energy as a regular TCP cumulative ACK: it says how much data has arrived with no holes ๐ธ. The subflow-level ACK is more like TCP SACK, because the connection can still have holes even when a subflow looks totally fine ๐ฆ. The Data ACK tells the other side the next data sequence number it expects to receive โจ.
The Data ACK can be sent as the full 64-bit value or as the lower 32 bits, just like the DSN. If data comes in with a 64-bit DSN, it MUST be acknowledged with a 64-bit Data ACK ๐. If the DSN is 32 bits, the implementation gets to choose between a 32-bit or 64-bit Data ACK, but it MUST accept either one in that situation ๐.
The Data ACK proves that the data, and all the required MPTCP signaling, got received and accepted by the remote end ๐. It's also how the left edge of the advertised receive window is marked (see Section 3.3.4). The receive window is shared by all subflows and is relative to the Data ACK, so an implementation MUST NOT use the RCV.WND field of a TCP segment at the connection level if that segment doesn't also carry a DSS option with a Data ACK field ๐. Splitting connection-level acknowledgments from subflow-level ones means each can be processed separately, and a receiver is free to drop segments after they've been acknowledged at the subflow level. An example would be memory constraints when lots of segments arrive out of order ๐ง.
An MPTCP sender MUST NOT free data from the send buffer until it has been acknowledged by both a Data ACK received on any subflow and, at the subflow level, by all subflows the data was sent on. The first condition keeps the connection alive, and the second keeps each subflow alive and self-consistent when data has to be retransmitted ๐ญ. Heads up though: if some data needs retransmitting multiple times over one subflow, the send window can get blocked. When that happens, the MPTCP sender can decide to kill the misbehaving subflow by sending a RST with an appropriate MP_TCPRST (Section 3.6) error code ๐.
The Data ACK MAY be included in all segments ๐. Advanced implementations SHOULD consider optimizations, though, where the Data ACK only shows up in segments when the Data ACK value actually moves forward, and this behavior MUST be treated as valid โจ. That way the send buffer still gets freed, and the overhead drops when data is only flowing one way ๐.
3.3.3. Closing a Connection ๐ฆ
In regular TCP, a FIN tells the receiver that the sender has no more data to send. MPTCP wants its subflows to operate independently and still look like TCP on the wire, so a FIN here only affects the subflow it was sent on ๐. That gives nodes a lot of freedom over which paths are in use at any given time ๐ . The semantics of a FIN stay exactly the same as regular TCP, so the subflow isn't fully closed until both sides have ACKed each other's FINs ๐ซถ.
When an application calls close() on a socket, it's saying it has no more data to send. In regular TCP that would produce a FIN on the connection. MPTCP needs an equivalent, and that's the DATA_FIN ๐ธ.
A DATA_FIN says the sender has no more data to send, so it can be used to check that all data was successfully received โจ. Like the FIN on a regular TCP connection, a DATA_FIN is a unidirectional signal ๐.
The DATA_FIN is signaled by setting the "F" flag in the DSS option (Figure 9) to 1 ๐ฅ. A DATA_FIN takes up 1 octet (the final octet) of the connection-level sequence space. The DATA_FIN is counted in the Data-Level Length, but not at the subflow level. For example, a segment with a DSN value of 80 and a Data-Level Length of 11, with DATA_FIN set, would map 10 octets from the subflow into data sequence space 80-89. The DATA_FIN would then be DSN 90, so this segment, DATA_FIN included, gets acknowledged with a DATA_ACK of 91 ๐ง.
When the DATA_FIN isn't attached to a TCP segment that contains data, the DSS MUST have a subflow sequence number of 0, a Data-Level Length of 1, and the data sequence number that matches the DATA_FIN itself ๐. In this case the checksum only covers the pseudo-header ๐.
A DATA_FIN has the same semantics and behavior as a regular TCP FIN, just at the connection level โจ. The big thing to know is that it only gets DATA_ACKed once all data has been successfully received at the connection level ๐. A DATA_FIN is decoupled from a subflow FIN. You can only combine these signals on one subflow if there's no data outstanding on the other subflows, because otherwise data may need to be retransmitted on different subflows ๐ญ. Basically, a host MUST NOT close all of its functioning subflows unless it's actually safe to do so. That means either all outstanding data has been DATA_ACKed, or the segment with the DATA_FIN flag set is the only one still outstanding ๐ .
Once a DATA_FIN has been acknowledged, all the remaining subflows MUST be closed with standard FIN exchanges ๐. Both hosts SHOULD send FINs on all subflows as a courtesy, so middleboxes can clean up their state even if one subflow has failed ๐ซถ. Reducing the timeouts (MSL) on subflows at the end hosts after receiving a DATA_FIN is also encouraged ๐. Any subflow that still has outstanding data queued (data that was retransmitted on other subflows to get the DATA_FIN acknowledged) MAY be closed with a RST carrying an MP_TCPRST (Section 3.6) error code for "too much outstanding data" ๐.
A connection counts as closed once both hosts' DATA_FINs have been acknowledged by DATA_ACKs ๐ฆ.
As we said above, a standard TCP FIN on one subflow only shuts down that subflow ๐. If every subflow has been closed with a FIN exchange but no DATA_FIN has been received and acknowledged, the MPTCP connection is only treated as closed after a timeout ๐ธ. So an implementation ends up with TIME_WAIT states at both the subflow level and the connection level (see Appendix D) โจ. That's what allows "break-before-make" scenarios, where connectivity is lost on every subflow before a new one can be set up again ๐ง.
3.3.4. Receiver Considerations ๐
Regular TCP advertises a receive window in every packet, which tells the sender how much data the receiver is willing to accept past the cumulative ACK. That's how flow control works: it slows down fast senders when receivers can't keep up ๐ .
MPTCP has a unique receive window that's shared between the subflows ๐. The idea is to let any subflow send data as long as the receiver is willing to accept it. The alternative, per-subflow receive windows, could end up stalling some subflows while others never use up their window ๐ญ.
The receive window is relative to the DATA_ACK. Just like in TCP, a receiver MUST NOT shrink the right edge of the receive window (that's DATA_ACK + receive window) ๐. The receiver uses the data sequence number to decide whether a packet should be accepted at the connection level ๐ซถ.
With regular TCP, the sequence number in a packet gets checked against the allowed receive window when deciding whether to accept it at the subflow level. With MPTCP, that check uses only the connection-level window ๐ฆ. A sanity check SHOULD be performed at the subflow level to make sure the subflow and mapped sequence numbers pass this test: SSN - SUBFLOW_ACK <= DSN - DATA_ACK. Here SSN is the subflow sequence number of the received packet, and SUBFLOW_ACK is the RCV.NXT (next expected sequence number) of the subflow. The connection-level definitions for DSN and DATA_ACK are equivalent โจ.
In regular TCP, once a segment is considered in-window, it goes into either the in-order receive queue or the out-of-order queue. Multipath TCP does the same thing, just at the connection level ๐. A segment goes into the connection-level in-order or out-of-order queue only if it's in-window at both the connection level and the subflow level ๐. The stack still has to remember, for each subflow, which segments were received successfully, so it can ACK them at the subflow level properly ๐ธ. This is typically done by keeping per-subflow out-of-order queues (holding only message headers, not the payloads) and remembering the value of the cumulative ACK ๐.
Implementers really need to get how big a receive buffer should be. The lower bound for full network utilization is the maximum bandwidth-delay product of any one of the paths. But that might not be enough when a packet gets lost on a slower subflow and has to be retransmitted (see Section 3.3.6) ๐ญ. A tight upper bound is the maximum round-trip time (RTT) of any path multiplied by the total bandwidth available across all paths ๐ฅ. That lets all subflows keep going at full speed while a packet is fast-retransmitted on the maximum RTT path โจ. Even that might not be enough to keep full performance if a retransmit timeout hits on the maximum RTT path ๐. How retransmission strategies relate to receive buffer sizing is left for future study ๐ง.
3.3.5. Sender Considerations ๐
okay bestie, sender side is where it gets SO juicy ๐ so the sender keeps a memory of the receive window advertisements it gets back from the receiver ๐ง. and it should ONLY bump its local receive window values when the largest sequence number allowed (that's DATA_ACK + receive window) actually goes up after a DATA_ACK comes in ๐ธ. this matters bigly because different paths have different RTTs, which means different feedback loops, and we need to let those paths do their own thing ๐ฆ
ngl this part is so clever ๐ MPTCP uses ONE receive window across ALL subflows. if that window was guaranteed to stay the same end to end, the host could always just read the newest value ๐ but nope, some middleboxes mess with the TCP-level receive window ๐ usually they SHRINK the offered window, but for short moments it could be bigger too (and it can't last forever, because the middlebox still has to keep up with delivering data to the receiver ๐). so if the receive windows are different across subflows, MPTCP SHOULD take the LARGEST of the most recent window sizes when it's doing its calculations โจ this rule is kinda hiding inside the requirement to never shrink the right edge of the window ๐
the sender MUST also remember the receive windows advertised by EACH subflow ๐ซถ the allowed window for subflow i is (ack_i, ack_i + rcv_wnd_i), where ack_i is the subflow-level cumulative ACK of subflow i ๐ this is what keeps data from being sent to a middlebox unless there's actually enough buffering for it ๐ญ
putting both rules together: a sender is allowed to send data segments with data-level sequence numbers between (DATA_ACK, DATA_ACK + receive_window) ๐ each of those segments gets mapped onto subflows, as long as the subflow sequence numbers are inside the allowed windows for those subflows ๐ฆ and fyi subflow sequence numbers don't usually affect flow control when the same receive window is advertised everywhere. they only do flow control for the subflows with a SMALLER advertised receive window ๐ฅ
last one: the send buffer MUST, at a minimum, be as big as the receive buffer, so the sender can actually hit max throughput ๐โจ
3.3.6. Reliability and Retransmissions ๐
okay this is the retransmission tea โ๐ the Data Sequence Mapping lets a sender resend data with the SAME data sequence number on a DIFFERENT subflow ๐ฆ but when it does, a host MUST still retransmit the original data on the ORIGINAL subflow ๐ซถ why? to keep the subflow's integrity (middleboxes could replay old data and/or reject holes in subflows, not cute ๐). and the receiver just ignores those retransmissions ๐ ngl it's clearly suboptimal, but for compatibility reasons it's the sensible move ๐ optimizations could be negotiated in future versions of this protocol ๐ธ also, this property means a sender could always send the SAME data with the SAME data sequence number on multiple subflows if it wants extra reliability ๐
this spec does NOT mandate any retransmission mechanism ๐ it's all local policy (more on that in Section 3.3.8 โจ). you can imagine super aggressive connection-level policies where every packet lost at the subflow level gets resent on a different subflow (wasting bandwidth but maybe cutting app-to-app delays ๐โโ๏ธ๐จ), or conservative ones where connection-level retransmissions only kick in after a few subflow-level retransmission timeouts ๐งโโ๏ธ
it's envisaged that a standard connection-level retransmission mechanism would be built around a connection-level data queue ๐ง all segments that haven't been DATA_ACKed get stored there โญ a timer gets set when the head of the connection-level queue is ACKed at the subflow level but NOT DATA_ACKed at the data level ๐ฅ and that timer guards against retransmission failures caused by middleboxes that proactively ACK data ๐
the sender MUST keep data in its send buffer as long as it hasn't been acknowledged BOTH (1) at the connection level AND (2) on all subflows it was sent on ๐ซถ that way the sender can always retransmit when needed, on the same subflow or a different one ๐ the special case is when a subflow fails: the sender will typically resend the data on other working subflows after a timeout, and it keeps trying on the failed subflow too ๐ it declares the subflow failed after a predefined upper bound on retransmissions is hit (which MAY be LOWER than the usual TCP limits of the MSL) or when an ICMP error arrives, and ONLY THEN deletes the outstanding data segments ๐ญ
if multiple retransmissions show a subflow is performing badly, this MAY lead to the host resetting that subflow with a RST ๐ but honestly, more research is needed to figure out the heuristics for how and when to reset underperforming subflows ๐ฌ a highly asymmetric path could get misdiagnosed as underperforming ๐ and a RST for this purpose SHOULD come with an "Unacceptable performance" MP_TCPRST option (Section 3.6) ๐
3.3.7. Congestion Control Considerations ๐
different subflows in an MPTCP connection have different congestion windows ๐ฆ to get fairness at bottlenecks and resource pooling, you NEED to couple the congestion windows across subflows so most traffic gets pushed onto uncongested links ๐ one algorithm for this is presented in [RFC6356] ๐ง it doesn't achieve perfect resource pooling, but it's "safe" because it's readily deployable on the current Internet โจ by safe we mean it doesn't take up more capacity on any one path than a single-path flow using only that route would, so it coexists fairly with single-path TCP at shared bottlenecks ๐ซถ
it's foreseeable that different congestion controllers will be implemented for MPTCP ๐ each one aims for different properties in the resource pooling / fairness / stability design space, plus different properties in quality of service, reliability, and resilience ๐
regardless of the algorithm, the MPTCP design aims to give congestion control implementations enough information to make the right decisions ๐ง and that info includes, for each subflow, which packets were lost and when โญ
3.3.8. Subflow Policy ๐ฏ
within a local MPTCP implementation, a host can use ANY local policy it wants to decide how to share traffic across the available paths ๐
in the typical use case, where the goal is maximizing throughput, all available paths get used at the same time for data transfer, using coupled congestion control as described in [RFC6356] ๐ other use cases are expected to show up though ๐ฆ
for instance, one option is an "all-or-nothing" approach: have a second path ready in case the first one fails ๐ alternatively, you could fully saturate one path before using an additional one (the "overflow" case) ๐ these choices would most likely be based on the monetary cost of links, but they could also depend on things like delay or jitter, where stability (of delay or bandwidth) matters more than raw throughput ๐ application requirements like these are discussed in detail in [RFC6897] ๐
the catch is that making good choices at the sender needs full knowledge of the path "cost", and that's unlikely to be the case ๐ it would be dreamy for a receiver to signal its own path preferences, since the receiver is often the multihomed party and may have to pay for metered incoming bandwidth ๐ธ๐ซถ
to make this happen, the MP_JOIN option (see Section 3.2) has the "B" bit ๐ it lets a host tell its peer that a path should be treated as a backup path, only used when other working subflows fail (so a subflow where the receiver set B=1 SHOULD NOT be used to send data unless there are no usable subflows where B=0) ๐
if the set of available paths changes, a host may want to signal a priority change to the peer ๐ (like a subflow that used to be a backup should now take priority over all the remaining subflows ๐) so the MP_PRIO option, shown in Figure 11, can change the "B" flag of the subflow it's sent on ๐ง
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------+-----+-+
| Kind | Length |Subtype|(rsv)|B|
+---------------+---------------+-------+-----+-+
Figure 11: Change Subflow Priority (MP_PRIO) Option
another use of MP_PRIO is setting the "B" flag on a subflow to cleanly "retire" it before closing it and removing it with REMOVE_ADDR (Section 3.4.2) ๐ธ for example, to support make-before-break session continuity, where new subflows get added before the old ones are closed ๐
one more thing: the backup flag is a request from a data receiver to a data sender ONLY, and the data sender SHOULD adhere to these requests ๐ซถ but a host can't just assume the sender will, since local policies (or technical difficulties) may override MP_PRIO requests ๐ฌ also, this signal only applies to one direction, so the sender of this option could keep using the subflow to send data even after it signaled B=1 to the other host ๐โจ
3.4. Address Knowledge Exchange (Path Management) โจ
okay so "path management" is our fancy name for hosts swapping info about extra paths between each other ๐บ๏ธ, and in this design it's handled by giving each host multiple addresses ๐ . if you want the big-picture architecture thinking behind all this, check out [RFC6182] ๐.
there are TWO ways hosts share this info, and both can be used on the same connection ๐ . method one is the direct setup of new subflows (see Section 3.2), where the initiator just has an extra address ๐. method two (the subsections below) explicitly signals addresses to the other host so it can open new subflows ๐ฆ. they're complementary: method one is implicit and simple, while method two is explicit and more complex but more robust ๐ช. together they let addresses change in flight (so NAT works, since the source address doesn't need to be known ๐ธ), they allow signaling of previously unknown addresses, and they allow addresses from other address families (e.g., both IPv4 and IPv6) ๐.
here's a typical run of the protocol ๐:
- an MPTCP connection is first set up between address/port
A1of Host A and address/portB1of Host B. if Host A is multihomed and multiaddressed, it can start an additional subflow from its addressA2toB1by sending aSYNwith anMP_JOINoption fromA2toB1, using B's previously declared token for this connection. or, if B is multihomed, it can try to set up a new subflow fromB2toA1, using A's previously declared token. either way, theSYNgets sent to the port already in use for the original subflow on the receiving host ๐ง. - simultaneously (or after a timeout), an
ADD_ADDRoption (Section 3.4.1) is sent on an existing subflow, telling the receiver about the sender's alternative address(es) ๐ฃ. the recipient can use that info to open a new subflow to the sender's additional address(es). in our example, A sends theADD_ADDRoption telling B about address/portA2. the mix of theSYN-based option and theADD_ADDRoption (including timeouts) is implementation specific and can be tailored to agree with local policy โญ. - if subflow
A2-B1gets set up successfully, Host B can use the Address ID in theMP_JOINoption to match this source address with theADD_ADDRoption that will also arrive on an existing subflow. now B knows not to openA2-B1, and it ignores theADD_ADDR๐. otherwise, if B hasn't received theA2-B1MP_JOINSYNbut did get theADD_ADDR, it can try to start a new subflow from one or more of its addresses to addressA2. this lets new sessions open even if one host is behind a NAT ๐ฅ.
other ways of using the two signaling mechanisms are possible too. for instance, signaling addresses in other address families can only be done explicitly, using the Add Address (ADD_ADDR) option ๐.
3.4.1. Address Advertisement ๐ฃ
the ADD_ADDR MPTCP option announces additional addresses (and, optionally, ports) where a host can be reached (Figure 12) ๐ฆ. this option can be used at any time during a connection, depending on when the sender wants to enable multiple paths and/or when paths become available โจ. as with all MPTCP signals, the receiver MUST undertake standard TCP validity checks, e.g., per [RFC5961], before acting upon it ๐ก๏ธ.
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------+-------+---------------+
| Kind | Length |Subtype|(rsv)|E| Address ID |
+---------------+---------------+-------+-------+---------------+
| Address (IPv4: 4 octets / IPv6: 16 octets) |
+-------------------------------+-------------------------------+
| Port (2 octets, optional) | |
+-------------------------------+ |
| Truncated HMAC (8 octets, if E=0) |
| +-------------------------------+
| |
+-------------------------------+
Figure 12: Add Address (ADD_ADDR) Option ๐
every address has an Address ID ๐ชช, which is used to uniquely identify the address within a connection (for address removal). the Address ID is also used to identify MP_JOIN options (see Section 3.2) that relate to the same address, even when address translators are in use. the Address ID MUST uniquely identify the address for the sender of the option (within the scope of the connection). the mechanism for allocating these IDs is implementation specific ๐
.
all Address IDs learned via either MP_JOIN or ADD_ADDR SHOULD be stored by the receiver in a data structure that gathers all the Address-ID-to-address mappings for a connection (identified by a token pair) ๐๏ธ. that way there's a stored mapping between the Address ID, observed source address, and token pair for processing future control info on that connection. note that an implementation MAY discard incoming address advertisements at will ๐๏ธ, for example to avoid updating mapping state, or because the advertised addresses are of no use to it (like IPv6 addresses when it only has IPv4). so a host MUST treat address advertisements as soft state, and it MAY choose to refresh advertisements periodically ๐. also, an implementation MAY choose to cache these advertisements even when they aren't relevant right now but might be later, like IPv4 addresses when IPv6 connectivity is available but IPv4 is waiting on DHCP ๐ง.
this option is shown in Figure 12. the illustration is sized for IPv4 addresses. for IPv6, the address length will be 16 octets (instead of 4) ๐.
the 2 octets that specify the TCP port number to use are optional, and you can tell whether they're there from the option's length ๐ข. even though most use cases will use the same port pairs as the initial subflow (e.g., port 80 stays port 80 on all subflows, and so does the ephemeral port at the client), there may be cases (like port-based load balancing) where an explicit different port is required ๐ฏ. if no port is specified, MPTCP SHOULD attempt to connect to the specified address on the same port already in use by the subflow the ADD_ADDR signal was sent on. this is covered in more detail in Section 3.9 ๐ซถ.
the Truncated HMAC parameter in this option is the rightmost 64 bits of an HMAC, negotiated and calculated the same way as for MP_JOIN (see Section 3.2) ๐. for this spec of MPTCP there's only one hash algorithm option, so this will be HMAC as defined in [RFC2104], using the SHA-256 hash algorithm [RFC6234]. just like with MP_JOIN, the HMAC key for a message sent by Host A is Key-A followed by Key-B, and for a message from Host B it's Key-B followed by Key-A. these are the keys exchanged in the original MP_CAPABLE handshake ๐ค. the HMAC message is the Address ID, IP address, and port that come before the HMAC in the ADD_ADDR option. if the port isn't in the ADD_ADDR option, the HMAC message still includes 2 octets of value zero 0๏ธโฃ. the whole point of the HMAC is to stop unauthorized entities from injecting ADD_ADDR signals to hijack a connection ๐ซ. also, the HMAC stops the address from being changed in flight unless an intermediary knows the key ๐ต๏ธ. if a host receives an ADD_ADDR option whose HMAC it can't validate, it SHOULD silently ignore the option ๐คซ.
a set of four flags sits after the subtype and before the Address ID ๐ณ๏ธ. only the rightmost bit, labeled "E", is assigned in this spec. the other bits are currently unassigned: they MUST be set to 0 by a sender and MUST be ignored by the receiver ๐ โโ๏ธ.
the "E" flag is there to add reliability for this option ๐. since this option is often sent on pure ACKs, there's no guarantee it gets through. so a receiver that gets a fresh ADD_ADDR option (where E=0) sends the same option back to the sender, but without the HMAC and with E=1, to confirm receipt ๐ฌ. depending on local policy, if that "echo" doesn't come back, the initial ADD_ADDR sender can tell that the ADD_ADDR needs to be retransmitted ๐.
with NATs everywhere, it's reasonably likely that one host will try to advertise private addresses [RFC1918] ๐๏ธ. it's not desirable to ban this, since both hosts might have extra interfaces on the same private network, so a host MAY advertise such addresses ๐ก. the MP_JOIN handshake for creating a new subflow (Section 3.2) has mechanisms to minimize security risks ๐งฏ. the MP_JOIN message contains a 32-bit token that uniquely identifies the connection to the receiving host. if the token is unknown, the host responds with a RST ๐ซ. in the unlikely event that the token is valid at the receiving host, subflow setup continues, but the HMAC exchange has to happen for authentication. the HMAC exchange will fail and that's enough protection against two unconnected hosts accidentally setting up a new subflow because of a private address signal ๐. more security considerations around ADD_ADDR messages that accidentally misdirect, or maliciously direct, new MP_JOIN attempts are discussed in Section 5 ๐จ.
a host that gets an ADD_ADDR but finds that a connection to that IP address and port number is unsuccessful SHOULD NOT try connecting to that address/port combination again for this connection ๐
. if a sender wants to trigger a new incoming connection attempt on a previously advertised address/port combination, it can refresh the ADD_ADDR info by sending the option again ๐.
a host can send an ADD_ADDR message with an already-assigned Address ID, but the address MUST be the same as the address previously assigned to that Address ID ๐งท. a new ADD_ADDR may have the same port number or a different one. if the port number is different, the receiving host SHOULD try to set up a new subflow to this new address/port combination ๐.
a host that wants to replace an existing Address ID MUST first remove the existing one (Section 3.4.2) ๐งน.
during normal MPTCP operation, it's unlikely there'll be enough TCP option space for ADD_ADDR to fit alongside the options for data sequence numbering (Section 3.3.1) ๐ฅฒ. so an MPTCP implementation is expected to send the ADD_ADDR option on separate ACKs. as discussed earlier, though, an MPTCP implementation MUST NOT treat duplicate ACKs carrying any MPTCP option (except the DSS option) as indications of congestion [RFC5681] ๐ฆ. and an MPTCP implementation SHOULD NOT send more than two duplicate ACKs in a row for signaling purposes ๐.
3.4.2. Remove Address โจ
okay so bestie, if an address that was already announced stops being valid during an MPTCP connection (like the interface just disappears, or an IPv6 address stops being the preferred one), the affected host SHOULD tell its peer so the peer can remove the subflows tied to that address ๐ซถ. Even if nobody is using that address right now, if it was announced before, an implementation SHOULD announce its removal too ๐ง. A host MAY also announce that a still-valid IP address shouldn't be used anymore, like for make-before-break session continuity ๐ฆ.
This happens through the Remove Address (REMOVE_ADDR) option (Figure 13) ๐ . It removes a previously added address (or a list of them) from a connection and terminates any subflows currently using that address.
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------+-------+---------------+
| Kind |Length = 3 + n |Subtype|(resvd)| Address ID | ...
+---------------+---------------+-------+-------+---------------+
(followed by n-1 Address IDs, if required)
Figure 13: Remove Address (REMOVE_ADDR) Option
Security time, and I need everyone listening ๐๐. If a host receives a REMOVE_ADDR option, it must ensure that the affected path or paths are no longer in use before it starts closing anything. Getting a REMOVE_ADDR SHOULD first kick off a TCP keepalive [RFC1122] on the path ๐ฅ. If something answers, the path SHOULD NOT be removed. If the path turns out to still be alive, the receiving host SHOULD no longer use that address for future connections, BUT shutting down the subflow is the job of whoever sent the REMOVE_ADDR ๐ .
Before the address is gone, the requesting host MAY also use MP_PRIO (Section 3.3.8) to ask that a path stop being used ๐ธ. The usual TCP validity tests on the subflow (like checking that the sequence and ACK numbers are correct) MUST also be run ๐ง. An implementation can use failures from these tests for intrusion detection or error logging ๐.
Sending this message SHOULD trigger RSTs from both hosts on the affected subflow(s) (if possible), as a courtesy ๐. Receiving it does too, as long as no keepalive response came back. The point is to let middlebox state get cleaned up before any local state does ๐ซง.
Address removal is handled by Address ID, which is what lets it work with NATs and other middleboxes that rewrite source addresses ๐ฆ. If an Address ID is unknown, the receiver silently ignores the request ๐คซ.
A subflow that is still functioning MUST be closed with a FIN exchange as in regular TCP, rather than using this option. For more information, see Section 3.3.3. ๐ซ
3.5. Fast Close โจ
regular TCP can send a RST to slam a connection shut abruptly ๐ฅ. With MPTCP though, a regular RST only covers its own subflow. It closes that one subflow and leaves the rest alone, because the MPTCP connection stays alive at the data level so that break-before-make handover between subflows is possible ๐ฆ. So MPTCP needs its own connection-level "reset" for abruptly closing the whole thing, and that's what the MP_FASTCLOSE option is for ๐.
MP_FASTCLOSE tells the peer that the connection is about to be abruptly closed and that no more data will be accepted ๐ โโ๏ธ. The reasons for sending one are implementation specific. Regular TCP doesn't let you send a RST while the connection is synchronized [RFC0793]. Implementations do allow it in that state if, say, the operating system is running out of resources. In those cases, MPTCP should send the MP_FASTCLOSE ๐. It looks like this (Figure 14):
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------+-----------------------+
| Kind | Length |Subtype| (reserved) |
+---------------+---------------+-------+-----------------------+
| Option Receiver's Key |
| (64 bits) |
| |
+---------------------------------------------------------------+
Figure 14: Fast Close (MP_FASTCLOSE) Option
If Host A wants to force the closure of an MPTCP connection, it has two options ๐:
- Option A (ACK): Host A sends an ACK containing the MP_FASTCLOSE option on one subflow, containing the key of Host B as declared in the initial connection handshake. On all the other subflows, Host A sends a regular TCP RST to close these subflows and tears them down. Host A now enters FASTCLOSE_WAIT state.
- Option R (RST): Host A sends a RST containing the MP_FASTCLOSE option on all subflows, containing the key of Host B as declared in the initial connection handshake. Host A can tear down the subflows and the connection immediately.
If Host A goes with Option A and sends an ACK with the MP_FASTCLOSE option, the connection shall proceed as follows ๐ :
- Upon receipt of an ACK with MP_FASTCLOSE by Host B, containing the valid key, Host B answers on the same subflow with a TCP RST and tears down all subflows also through sending TCP RST signals. Host B can now close the whole MPTCP connection (it transitions directly to CLOSED state) ๐ซถ.
- As soon as Host A has received the TCP RST on the remaining subflow, it can close this subflow and tear down the whole connection (transition from FASTCLOSE_WAIT state to CLOSED state). If Host A receives an MP_FASTCLOSE instead of a TCP RST, both hosts attempted fast closure simultaneously. Host A should reply with a TCP RST and tear down the connection ๐ญ.
- If Host A does not receive a TCP RST in reply to its MP_FASTCLOSE after one retransmission timeout (RTO) (the RTO of the subflow where the MP_FASTCLOSE has been sent), it SHOULD retransmit the MP_FASTCLOSE. To keep this connection from being retained for a long time, the number of retransmissions SHOULD be limited; this limit is implementation specific. A RECOMMENDED number is 3. If no TCP RST is received in response, Host A SHOULD send a TCP RST with the MP_FASTCLOSE option itself when it releases state, in order to clear any remaining state at middleboxes ๐งน.
If Host A goes with Option R instead and sends a RST with the MP_FASTCLOSE option, Host B will act as follows ๐ธ: upon receipt of a RST with MP_FASTCLOSE, containing the valid key, Host B tears down all subflows by sending a TCP RST. Host B can now close the whole MPTCP connection (it transitions directly to CLOSED state) โจ.
3.6. Subflow Reset โจ
An MPTCP implementation may also need to send a regular TCP RST to force the closure of a subflow ๐. A host sends a TCP RST to close a subflow, or to reject an attempt to open one (MP_JOIN). So the receiving host knows why a subflow is being closed or rejected, the TCP RST packet MAY include the MP_TCPRST option (Figure 15) ๐. The host MAY use that information to decide whether to re-establish the subflow immediately, later, or never ๐ง .
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------+-----------------------+
| Kind | Length |Subtype|U|V|W|T| Reason |
+---------------+---------------+-------+-----------------------+
Figure 15: TCP RST Reason (MP_TCPRST) Option
MP_TCPRST carries a reason code so the sender can explain why the subflow ended ๐ฌ. It uses 12 bits of option space. The first 4 bits are flags (only one of them is defined right now), and the remaining octet is the reason code, which a receiver MAY use to infer information about the usability of this path ๐งฉ.
The T flag is how the sender says whether the error is Transient (T set to 1) or Permanent (T set to 0) ๐ฆ๏ธ. If the sender considers the error Transient, the recipient MAY try to re-establish a subflow for this connection over the failed path. When to try is implementation specific, but it SHOULD take into account the properties of the failure, as defined by the provided reason code โฐ. If the error is considered Permanent, the receiver of the RST segment SHOULD NOT try to re-establish a subflow for this connection over this path ๐ซ. The U, V, and W flags are not defined by this specification and are reserved for future use ๐. An implementation of this specification MUST set these flags to 0, and a receiver MUST ignore them ๐.
Reason is an 8-bit field that holds the reason code for the termination of the subflow. The following codes are defined in this document ๐:
- Unspecified error (code 0x00). This is the default error. It implies that the subflow is no longer available. Seeing this option tells you the RST was generated by an MPTCP-aware device ๐ค.
- MPTCP-specific error (code 0x01). An error has been detected in the processing of MPTCP options. This is the usual reason code when a RST is sent to close a subflow because of an invalid response ๐.
- Lack of resources (code 0x02). The sending host doesn't have enough resources to support the terminated subflow ๐ชซ.
- Administratively prohibited (code 0x03). The requested subflow is prohibited by the sending host's policies ๐ง.
- Too much outstanding data (code 0x04). There's an excessive amount of data that needs to go over the terminated subflow, even though it has already been acknowledged over one or more other subflows. This can happen if a path was unavailable for a short while, and it's more efficient to reset and start again than to retransmit the queued data ๐.
- Unacceptable performance (code 0x05). The performance of this subflow was too low compared to the other subflows of this Multipath TCP connection ๐ข.
- Middlebox interference (code 0x06). Middlebox interference has been detected over this subflow, which makes MPTCP signaling invalid. For example, this may be sent if the checksum does not validate ๐พ.
3.7. Fallback ๐ฆ
okay so bestie, this is the part where things can go sideways ๐ญ. Middleboxes (those sneaky in-network boxes sitting on a path) can totally get in the way of MPTCP. MPTCP is built to handle plenty of middlebox shenanigans (see Section 6 ๐ ), but there are still a couple of situations where a subflow just can't keep up with the MPTCP requirements. The two big ones are: MPTCP options getting dropped on a path, and payload data getting modified. When something like that happens, the connection has to "fall back" to the last known safe way of doing things. That could mean going back to plain regular TCP, or just ditching the problematic subflow. Iconic survival move ๐โจ
At the very start of an MPTCP connection (aka the first subflow), it's super important to make sure the path is fully MPTCP capable and that the MPTCP options can actually reach each host ๐. The handshake from Section 3.1 SHOULD fall back to regular TCP if either of the SYN messages doesn't carry the MPTCP options. Honestly, this is the same behavior you want when a host isn't MPTCP capable or the path doesn't support the options, so it's giving consistency ๐. When you're trying to join an existing MPTCP connection (Section 3.2) and the MPTCP options don't make it through on the SYNs, the subflow gets closed according to the MP_JOIN logic ๐ช.
Now here's the plot twist ๐: what if the MPTCP options DO get through on the SYN, but not on regular packets after that? If the subflow is the first one, so all the data in flight is contiguous, you can sort it out with these rules:
- A sender MUST include a DSS option with Data Sequence Mapping in every segment until one of the sent segments gets acknowledged with a DSS option containing a Data ACK. Once that ACK comes back, the sender knows the DSS option survives in both directions and MAY send fewer DSS options than one per segment ๐.
- If, however, an ACK comes back for data (not just for the SYN) without a DSS option containing a Data ACK, the sender decides the path isn't MPTCP capable ๐ฌ. If this happens on an additional subflow (one started with MP_JOIN), the host MUST close the subflow with a RST, which SHOULD contain an MP_TCPRST option (Section 3.6) with a
"Middlebox interference"reason code. - If that ACK shows up on the first subflow (the one that started with MP_CAPABLE), before any additional subflows get added, the implementation MUST drop out of MPTCP mode and fall back to regular TCP. The sender sends one final Data Sequence Mapping, with the Data-Level Length value of 0 indicating an infinite mapping (so the other end knows in case the path drops options in only one direction), and then goes back to sending data on the single subflow without any MPTCP options ๐ง.
- If a subflow breaks during operation, say it gets rerouted and MPTCP options aren't allowed anymore, then once that's detected (by the subflow-level receive buffer filling up, since there's no mapping left to DATA_ACK this data ๐ต), the subflow SHOULD be treated as broken and closed with a RST. Why? Because no data can reach the application layer and no fallback signal can be reliably sent. This RST SHOULD include the MP_TCPRST option (Section 3.6) with a
"Middlebox interference"reason code.
These rules should cover every case where this kind of failure could happen, whether it's on the forward or reverse path, and whether the server or the client sends data first ๐.
So far we've been talking about MPTCP options getting lost, either right at the start or partway through the connection. As Section 3.3 explains, every chunk of data that has a mapping is protected by a checksum, as long as checksums were negotiated ๐. That's how the stack detects whether a middlebox messed with the payload: adding, removing, or changing data. A checksum fails if the data changed in any way. The checksum also catches whether the length of the data on the subflow went up or down, and that means the Data Sequence Mapping isn't valid anymore ๐. The sender no longer knows which subflow-level sequence number the receiver is actually at (the middlebox is faking the ACKs), so it can't signal any more mappings. On top of that, payload changes that are valid at the application layer are possible, and those changes could be triggered across MPTCP segment boundaries and corrupt the data ๐. So, all data from the start of the segment that failed the checksum onward is not trustworthy.
Quick note ๐: if checksum usage wasn't negotiated, this fallback mechanism cannot be used, unless some higher-layer or lower-layer signal tells the MPTCP implementation that the payload was tampered with.
When multiple subflows are in use, the data in flight on a subflow will probably include bits that aren't contiguous in the connection-level stream, since segments are spread across the subflows ๐งฉ. Because of the problems above, there's no way to figure out what adjustments were made to the data (especially changes to the subflow sequence numbering). So, it isn't possible to recover the subflow. The affected subflow MUST be closed immediately with a RST that includes an MP_FAIL option (Figure 16). That option carries the data sequence number at the start of the segment (as defined by the Data Sequence Mapping) that had the checksum failure. Also, the MP_FAIL option always uses the full 64-bit sequence number, even when 32-bit sequence numbers are normally used in the DSS signals on the path ๐ข.
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------+----------------------+
| Kind | Length=12 |Subtype| (reserved) |
+---------------+---------------+-------+----------------------+
| |
| Data Sequence Number (8 octets) |
| |
+--------------------------------------------------------------+
Figure 16: Fallback (MP_FAIL) Option
The receiver of this option MUST discard all data following the data sequence number specified. Failed data MUST NOT be DATA_ACKed, so it gets retransmitted on other subflows (Section 3.3.6) ๐.
There's a special case when there's a single subflow and it fails with a checksum error ๐ฅบ. If it's known that all the unacknowledged data in flight is contiguous (which is usually true with a single subflow), an infinite mapping can be applied to the subflow without closing it first. That basically turns off all further MPTCP signaling โจ. In this situation, if a receiver spots a checksum failure when there's only one path, it sends back an MP_FAIL option on the subflow-level ACK, pointing at the data-level sequence number of the start of the segment where the checksum error showed up. The sender gets that info and, if all the unacknowledged data in flight is contiguous, signals an infinite mapping. That infinite mapping is a DSS option (Section 3.3) on the first new packet, carrying a Data Sequence Mapping that works retroactively. It refers to the start of the subflow sequence number of the most recent segment known to be delivered intact (aka successfully DATA_ACKed) ๐ฐ๏ธ. From that point on, a middlebox can change the data without affecting MPTCP, because the stream is basically a regular, legacy TCP session. Paths might only be damaged in one direction, and the MP_FAIL signal only affects one direction of traffic. But for simplicity of implementation, the receiver of an MP_FAIL MUST also respond with an MP_FAIL in the reverse direction and fully revert to a regular TCP session ๐ซถ.
Now, the rare case where the data isn't contiguous (which could happen with only one subflow if it's retransmitting data from a subflow that was recently closed uncleanly) ๐ตโ๐ซ: the receiver MUST close the subflow with a RST with MP_FAIL. The receiver MUST discard all data that follows the data sequence number specified. The sender MAY try to create a new subflow that belongs to the same connection, and if it does, it SHOULD immediately put the single subflow into single-path mode by setting an infinite Data Sequence Mapping. That mapping begins at the data-level sequence number that was declared in the MP_FAIL ๐ฏ.
After a sender signals an infinite mapping, it MUST only use subflow ACKs to clear its send buffer ๐งน. The reason is that Data ACKs can get out of line with the subflow ACKs when middleboxes insert or delete data. The receiver SHOULD stop generating Data ACKs after it receives an infinite mapping ๐.
When a connection has fallen back with an infinite mapping, only one subflow can send data. Otherwise the receiver wouldn't know how to reorder the data ๐. In practice, that means all MPTCP subflows get terminated except one. Once MPTCP falls back to regular TCP, it MUST NOT go back to MPTCP later in the connection ๐ซ.
It's worth stressing ๐ฃ: MPTCP isn't trying to stop middleboxes that want to adjust the payload. An MPTCP-aware middlebox could provide that kind of functionality by also rewriting the checksums ๐ง.
3.8. Error Handling ๐จ
Besides the fallback mechanism we just covered, the standard classes of TCP errors may need handling in an MPTCP-specific way ๐ ๏ธ. Changing semantics, like what a RST means, are covered in Section 4 ๐. Wherever possible, we don't want to deviate from regular TCP behavior ๐.
Here's the list of possible errors and the right MPTCP behavior for each one ๐:
- Unknown token in MP_JOIN (or HMAC failure in MP_JOIN ACK, or missing MP_JOIN in SYN/ACK response): send RST (analogous to TCP's behavior on an unknown port) ๐ซ
- DSN out of window (during normal operation): drop the data; do not send Data ACKs ๐๏ธ
- Remove request for unknown Address ID: silently ignore ๐คซ
3.9. Heuristics ๐ง
There are a bunch of heuristics that are needed for performance or deployment, but they aren't required for protocol correctness ๐ . In this section, we go over those heuristics. Also, discussions of buffering and certain sender and receiver window behaviors are in Sections 3.3.4 and 3.3.5, and retransmission is covered in Section 3.3.6 โจ.
3.9.1. Port Usage ๐
Okay so, under typical operation, an MPTCP implementation SHOULD use the same ports that are already in use ๐
. The destination port of a SYN carrying an MP_JOIN option SHOULD match the remote port of the first subflow in the connection. The local port for those SYNs SHOULD also match the first subflow's port, so an implementation SHOULD reserve ephemeral ports across all local IP addresses ๐ง. Is that always doable? Nope, there may be cases where it's infeasible ๐. The whole point is to maximize the odds that the SYN gets through a firewall or NAT at the recipient, and to avoid confusing any network-monitoring software โจ.
There are also times when a host wants to signal that a specific port should be used, and that's what the ADD_ADDR option is for (see Section 3.4.1) ๐ฆ. So it's totally possible to have multiple subflows between the same two addresses using different port pairs. That could even be used to load balance inside the network based on 5-tuples (e.g., some ECMP implementations [RFC2992]) ๐.
3.9.2. Delayed Subflow Start and Subflow Symmetry ๐ธ
Lowkey, lots of TCP connections are short-lived and only send a few segments, so the overhead of MPTCP just isn't worth it ๐. That means a heuristic is required to decide when to start using additional subflows in an MPTCP connection. Experimental deployments have shown MPTCP works across a range of scenarios, so an implementation will likely need to think about stuff like the type of traffic and how long the session lasts. This information MAY be signaled by the application layer ๐ซถ.
But for regular TCP traffic, here's a suggested general-purpose heuristic that an implementation MAY choose to use ๐. If a host has data buffered for its peer (which implies the application has received a request for data), the host opens one subflow for each initial window's worth of data that is buffered.
Consideration should also be given to limiting how fast new subflows get added, plus capping the total number of subflows open for one connection ๐. A host may choose to vary these values based on its load or what it knows about traffic and path characteristics.
Heads up, this heuristic alone is probably not enough ๐ญ. Traffic for common apps like downloads is highly asymmetric, and the multihomed host might be the client that never fills its buffers, so it never uses MPTCP under this heuristic. Advanced APIs that let an application signal its traffic requirements would really help with these decisions ๐.
A time-based heuristic could be added too: open additional subflows after a given period of time has passed โฐ. That would fix the issue above and also give resilience to low-bandwidth but long-lived applications.
Another thing: both hosts might try to set up a subflow between the same pair of addresses at the same time, and that wastes resources ๐ค.
If every subflow uses the same ports (like we recommended above), standard TCP simultaneous-open logic should handle it, and only one subflow ends up between the address pairs. But that only works if both end hosts use the same ports. If a host does not support TCP simultaneous open, it is RECOMMENDED that some element of randomization be applied to the time it waits before opening new subflows, so that only one subflow is created between a given address pair ๐ฒ. But if hosts signal additional ports to use (like for leveraging ECMP on-path), this heuristic is a no-go ๐ซ.
So that's some of the factors an implementer should consider when developing MPTCP heuristics. It's not meant to be prescriptive, though ๐.
3.9.3. Failure Handling ๐ ๏ธ
The rules for handling unexpected signals live in Section 3.8, but there are other failure cases where hosts get to pick what's appropriate ๐ง.
For example, Section 3.1 suggests that a host SHOULD fall back to trying regular TCP SYNs after one or more failures of MPTCP SYNs for a connection. A host may keep a system-wide cache of that info, so it can back off from MPTCP, first for that particular destination host and eventually across a whole interface if MPTCP connections keep failing ๐. How long that cache lasts is implementation specific.
Another failure: the MP_JOIN handshake can fail ๐จ. Section 3.8 says an incorrect handshake MUST lead to the subflow being closed with a RST. A host running an active intrusion-detection system may choose to start blocking MP_JOIN packets from the source host if it sees multiple failed MP_JOIN attempts. From the connection initiator's side, if an MP_JOIN fails, it SHOULD NOT try to connect to that same IP address and port during the lifetime of the connection, unless the other host refreshes the info with another ADD_ADDR option. Just so you know, ADD_ADDR is informational only, so it doesn't guarantee the other host will actually attempt a connection ๐ .
Also, over a bunch of connections, an implementation may pick up that certain interfaces or destination addresses keep failing, and it may default to skipping MPTCP for those ๐ซ . The same goes for subflows that perform really badly or keep failing during use. Those behaviors can be learned too, so an implementation can temporarily choose not to use those paths ๐ค๏ธ.
4. Semantic Issues ๐ง
To make multipath work, some TCP components got new semantics ๐ช. So this section is a handy list of those changes for reference.
Sequence number: the in-header TCP sequence number is specific to the subflow ๐ฏ. So the receiver can reorder application data, there's an extra data-level sequence space. In that space, the initial SYN and the final DATA_FIN each take up 1 octet, so these signals get acknowledged at the connection level โ . There's an explicit mapping from data sequence space to subflow sequence space, and it's signaled through TCP options in data packets.
ACK: the ACK field in the TCP header only acknowledges the subflow sequence number, and not the data-level sequence space ๐
. Implementations SHOULD NOT attempt to infer a data-level acknowledgment from the subflow ACKs. This keeps subflow-level and connection-level processing separate at an end host.
Duplicate ACK: a duplicate ACK that carries any MPTCP signaling (except the DSS option) MUST NOT be treated as a sign of congestion ๐ซ. To keep non-MPTCP-aware entities from mistakenly reading duplicate ACKs as congestion, MPTCP SHOULD NOT send more than two duplicate ACKs containing (non-DSS) MPTCP signals in a row.
Receive Window: the receive window in the TCP header shows the free buffer space for the whole data-level connection (not just this subflow) at the receiver ๐ช. The semantics are the same as regular TCP, but to keep them, the receive window must be interpreted at the sender as relative to the sequence number given in the DATA_ACK, rather than the subflow ACK in the TCP header. That's how the original role of flow control is preserved. Note that some middleboxes may change the receive window, so a host SHOULD use the maximum of the values recently seen on the constituent subflows for the connection-level receive window. It also needs to maintain a subflow-level window for subflow-level processing.
FIN: the FIN flag in the TCP header only applies to the subflow it's sent on, not the whole connection ๐. For connection-level FIN semantics, the DATA_FIN option is used.
RST: the RST flag in the TCP header only applies to the subflow it's sent on, not the whole connection ๐ฅ. The MP_FASTCLOSE option gives you the Fast Close functionality of a RST at the MPTCP connection level.
Address List: managing the address list (i.e., knowing the local and remote hosts' lists of available IP addresses) happens per connection. It's not per subflow, per host, or per pair of communicating hosts ๐บ๏ธ. That means per-connection local policy can be applied. Adding an address to one connection (either explicitly with an ADD_ADDR message or implicitly through an MP_JOIN) has zero implications for other connections between the same pair of hosts ๐ซ.
5-tuple: the 5-tuple (protocol, local address, local port, remote address, remote port) that kernel APIs present to the application layer in a non-multipath-aware application is that of the first subflow ๐. That's true even if that subflow has since been closed and removed from the connection. This decision, plus other related API issues, gets a deeper dive in [RFC6897] ๐.
5. Security Considerations ๐โจ
okay bestie, security is where things get serious ๐ญ As identified in [RFC6181], adding multipath capability to TCP brings a whole new set of threat classes ๐. To stop those threats, [RFC6182] lays out a set of requirements for an MPTCP security solution. The main goal is for MPTCP security to be "no worse" than regular TCP today ๐ซถ. The key security requirements are:
- Provide a mechanism to confirm that the parties in a subflow handshake are the same as the parties in the original connection setup.
- Provide verification that the peer can receive traffic at a new address before using it as part of a connection.
- Provide replay protection, i.e., ensure that a request to add/remove a subflow is "fresh".
To hit these goals, MPTCP uses a hash-based handshake algorithm, as documented in Sections 3.1 and 3.2 ๐.
The security of an MPTCP connection hangs on keys that get shared once at the start of the first subflow and are never sent over the network again (unless they're used in the Fast Close mechanism (Section 3.5)) ๐๏ธ. To keep demultiplexing easy without giving away any cryptographic material, future subflows use a truncated cryptographic hash of this key as the connection identification "token" ๐. The keys are concatenated and used as keys for creating Hash-based Message Authentication Codes (HMACs) that get used on subflow setup, so the parties in the handshake can be verified as the same parties from the original connection setup. It also verifies that the peer can receive traffic at this new address ๐ฌ. Replay attacks would still be possible if only keys were used, so the handshakes use single-use random numbers (nonces) at both ends. This makes sure the HMAC will never be the same on two handshakes ๐ โโ๏ธ. Guidance on generating random numbers suitable for use as keys is given in [RFC4086] and discussed in Section 3.1. The nonces are valid for the lifetime of the TCP connection attempt. HMAC is also used to secure the ADD_ADDR option, because of the threats identified in [RFC7430] ๐ก๏ธ.
The use of crypto capability bits in the initial connection handshake lets you negotiate a particular algorithm, which means additional crypto mechanisms can be deployed in the future ๐ฎ. Ngl, this negotiation is still open to a bid-down attack, where an on-path active attacker modifies the crypto capability bits in the receiver's response to force a less secure crypto mechanism ๐ฌ. So the security mechanism presented in this document should protect against all forms of flooding and hijacking attacks discussed in [RFC6181] ๐ช.
The version negotiation specified in Section 3.1 would, if differing MPTCP versions shared a common negotiation format, allow an on-path attacker to try a theoretical bid-down attack ๐. Since the v1 and v0 protocols have different handshakes, that attack would require the client to re-establish the connection using v0 and the server to support v0. Also, an on-path attacker would have access to the raw data, which negates any other TCP-level security mechanisms ๐. As also noted in Appendix E, this document specifies the removal of the AddrID field [RFC6824] from the MP_PRIO option (Section 3.3.8). That change kills the possibility of a theoretical attack where an attacker could place a subflow in "backup" mode ๐งฏ.
During normal operation, regular TCP protection mechanisms (like making sure sequence numbers are in-window) give the same level of protection against attacks on individual TCP subflows as regular TCP has today โ . Implementations will add extra buffers compared to regular TCP, so they can reassemble data at the connection level ๐งฉ. Applying window sizing will minimise the risk of denial-of-service attacks eating up resources ๐ซง.
As discussed in Section 3.4.1, a host may advertise its private addresses, but those might point to different hosts in the receiver's network ๐ . The MP_JOIN handshake (Section 3.2) makes sure this doesn't succeed in setting up a subflow to the wrong host. It could still create unwanted TCP handshake traffic though ๐ฉ. This feature of MPTCP could be a target for denial-of-service exploits, where malicious participants in MPTCP connections encourage the recipient to target other hosts in the network ๐จ. So implementations should consider heuristics (Section 3.9) at both the sender and receiver to reduce the impact of this ๐ง .
To protect further against malicious ADD_ADDR messages sent by an off-path attacker, the ADD_ADDR includes an HMAC using the keys negotiated during the handshake. This effectively stops an attacker from diverting an MPTCP connection through an off-path ADD_ADDR injection into the stream ๐ซ.
A small security risk could theoretically exist with key reuse. But to pull off a replay attack, both the sender and receiver keys, and the sender and receiver random numbers, in the MP_JOIN handshake (Section 3.2) would have to match ๐ค.
While this specification defines a "medium" security solution that meets the criteria set out at the start of this section and in the threat analysis document [RFC6181], attacks only ever get worse ๐. So it's likely that a future version of MPTCP would need to support stronger security. There are several ways MPTCP security could be improved. Some of these would be compatible with MPTCP as defined in this document, while others may not be โจ. For now, the best approach is to gain experience with the current approach, figure out what might work, and check that the threat analysis is still accurate ๐.
Possible ways of improving MPTCP security could include:
- defining a new MPTCP cryptographic algorithm, as negotiated in MP_CAPABLE. If an implementation were deployed in a controlled environment where additional assumptions could be made, such as the ability for servers to store state during the TCP handshake, then it may be possible to use a stronger cryptographic algorithm than would otherwise be possible ๐ฐ.
- defining how to secure data transfer with MPTCP, while not changing the signaling part of the protocol ๐ฆ.
- defining security that requires more option space, perhaps in conjunction with a "long options" proposal for extending the TCP option space (such as those surveyed in [TCPLO]), or perhaps building on the current approach with a second stage of security based on MPTCP options ๐งฑ.
- revisiting the working group's decision to exclusively use TCP options for MPTCP signaling, and instead looking at the possibility of using TCP payloads as well ๐ค.
MPTCP has been designed with several methods available to indicate a new security mechanism, including:
- available flags in MP_CAPABLE (Figure 4).
- available subtypes in the MPTCP option (Figure 3).
- the Version field in MP_CAPABLE (Figure 4).
6. Interactions with Middleboxes โจ
Okay bestie, so Multipath TCP was designed to actually work in the messy world we have today ๐๐ . Its design takes into account "reasonable" existing middlebox behavior ๐ง. In this section we look at a few middlebox-related failure scenarios and how Multipath TCP handles them ๐ซถ. After that we list the design decisions it made to accommodate the different middleboxes ๐.
A primary concern is our use of a new TCP option ๐ญ. Middleboxes should forward packets with unknown options unchanged, yet some of them don't ๐. We expect these middleboxes to strip options and pass the data, drop packets with new options, copy the same option into multiple segments (e.g., when doing segmentation), or drop options during segment coalescing ๐.
MPTCP uses a single new TCP option called "Kind", and all message types are defined by "subtype" values (see Section 7) ๐. This should reduce the chances of only some types of MPTCP options getting through ๐ฆ. Instead, the key differing characteristics are the different paths and the presence of the SYN flag ๐ค๏ธ.
MPTCP SYN packets on the first subflow of a connection contain the MP_CAPABLE option (Section 3.1) ๐ธ. If this is dropped, MPTCP SHOULD fall back to regular TCP ๐. If packets with the MP_JOIN option (Section 3.2) are dropped, the paths will simply not be used ๐ซ.
If a middlebox strips options but otherwise passes the packets unchanged, MPTCP will behave safely ๐ก๏ธ. If an MP_CAPABLE option is dropped on either the outgoing path or the return path, the initiating host can fall back to regular TCP, as illustrated in Figure 17 and discussed in Section 3.1 ๐.
Host A Host B
| Middlebox M |
| | |
| SYN (MP_CAPABLE) | SYN |
|-------------------|---------------->|
| SYN/ACK |
|<------------------------------------|
a) MP_CAPABLE option stripped on outgoing path
Host A Host B
| SYN (MP_CAPABLE) |
|-------------------------------------->|
| Middlebox M |
| | |
| SYN/ACK |SYN/ACK (MP_CAPABLE)|
|<-----------------|--------------------|
b) MP_CAPABLE option stripped on return path
Figure 17: Connection Setup with Middleboxes That Strip Options
from Packets
Subflow SYNs contain the MP_JOIN option ๐งฉ. If this option is stripped on the outgoing path, the SYN will look like a regular SYN to Host B ๐. Depending on whether there is a listening socket on the target port, Host B will reply with either a SYN/ACK or a RST (and the subflow connection fails) ๐. When Host A receives the SYN/ACK, it sends a RST, because the SYN/ACK does not contain the MP_JOIN option and its token ๐ โโ๏ธ. Either way, the subflow setup fails, but it doesn't otherwise affect the MPTCP connection as a whole ๐.
We now look at data flow with MPTCP, assuming the flow is correctly set up. That implies the options in the SYN packets were allowed through by the relevant middleboxes ๐ช. If options are allowed through and there is no resegmentation or coalescing into TCP segments, Multipath TCP flows can proceed without problems ๐โโ๏ธโจ.
The case where options get stripped on data packets is discussed in Section 3.7 ๐ฌ. If only some MPTCP options are stripped, behavior is not deterministic ๐ฒ. If some Data Sequence Mappings are lost, the connection can continue as long as mappings exist for the subflow-level data (e.g., if multiple maps have been sent that reinforce each other) ๐งต. If some subflow-level space is left unmapped, however, the subflow is treated as broken and is closed, using the process described in Section 3.7 ๐จ. MPTCP should survive a loss of some Data ACKs, but performance will degrade as the fraction of stripped options increases ๐. We do not expect such cases to appear in practice, though: most middleboxes will either strip all options or let them all through ๐คทโโ๏ธ.
We end this section with a list of middlebox classes, their behavior, and the elements in the MPTCP design that allow operation through such middleboxes ๐ฆ. Issues surrounding dropping packets with options or stripping options were discussed above and are not included here ๐:
- NATs (Network Address (and port) Translators) [RFC3022] change the source address (and often the source port) of packets ๐ . This means a host will not know its public-facing address for signaling in MPTCP ๐ซฅ. Therefore, MPTCP permits implicit address addition via the MP_JOIN option, and the handshake mechanism ensures that connection attempts to private addresses [RFC1918], since they are authenticated, will only set up subflows to the correct hosts ๐. Explicit address removal is undertaken by an Address ID, so no knowledge of the source address is needed ๐.
- Performance Enhancing Proxies (PEPs) [RFC3135] might proactively ACK data to increase performance ๐๏ธ. MPTCP, however, relies on accurate congestion control signals from the end host, and non-MPTCP-aware PEPs will not be able to provide such signals ๐ก. MPTCP will, therefore, fall back to single-path TCP or close the problematic subflow (see Section 3.7) ๐.
- Traffic normalizers [norm] may not allow holes in sequence numbers, and they may cache packets and retransmit the same data ๐งน. MPTCP looks like standard TCP on the wire and will not retransmit different data on the same subflow sequence number ๐งพ. In the event of a retransmission, the same data will be retransmitted on the original TCP subflow, even if it is additionally retransmitted at the connection level on a different subflow ๐.
- Firewalls [RFC2979] might perform Initial Sequence Number (ISN) randomization on TCP connections ๐ฒ. MPTCP uses relative sequence numbers in Data Sequence Mappings to cope with this ๐ช. Like NATs, firewalls will not permit many incoming connections, so MPTCP supports address signaling (ADD_ADDR) so that a multiaddressed host can invite its peer behind the firewall/NAT to connect out to its additional interface ๐ฃ.
- Intrusion Detection Systems / Intrusion Prevention Systems (IDSs/IPSs) observe packet streams for patterns and content that could threaten a network ๐ฎโโ๏ธ. MPTCP may require the instrumentation of additional paths, and an MPTCP-aware IDS or IPS would need to read MPTCP tokens to correlate data from multiple subflows, so it keeps comparable visibility into all of the traffic between devices ๐. Without such changes, an IDS would get an incomplete view of the traffic ๐. That increases the risk of missing traffic of interest (false negatives) and increases the chances of erroneously identifying a subflow as a risk due to only seeing partial data (false positives) ๐ตโ๐ซ.
- Application-level middleboxes such as content-aware firewalls may alter the payload within a subflow, for example by rewriting URIs in HTTP traffic ๐. MPTCP will detect such changes using the checksum and close the affected subflow(s), if there are other subflows that can be used ๐ง. If all subflows are affected, MPTCP will fall back to TCP, allowing such middleboxes to change the payload ๐ซ . MPTCP-aware middleboxes should be able to adjust the payload and MPTCP metadata so they don't break the connection ๐ ๏ธ.
In addition, all classes of middleboxes may affect TCP traffic in the following ways ๐:
- TCP options may be removed, or packets with unknown options dropped, by many classes of middleboxes ๐๏ธ. It is intended that the initial SYN exchange, with a TCP option, will be sufficient to identify the path's capabilities ๐ชช. If such a packet does not get through, MPTCP will end up falling back to regular TCP ๐.
- Segmentation/coalescing (e.g., TCP segmentation offloading) might copy options between packets and might strip some options ๐งฉ. MPTCP's Data Sequence Mapping includes the relative subflow sequence number instead of using the sequence number in the segment ๐ข. In this way, the mapping is independent of the packets that carry it ๐.
- The receive window may be shrunk by some middleboxes at the subflow level ๐ช. MPTCP will use the maximum window at the data level but will also obey subflow-specific windows ๐โโ๏ธ.
7. IANA Considerations ๐
This document obsoletes [RFC6824] ๐๏ธโก๏ธโจ. As such, IANA has updated several registries to point to this document ๐. In addition, this document creates one new registry ๐. These topics are described in the following subsections ๐.
7.1. TCP Option Kind Numbers ๐ข
IANA has updated the "TCP Option Kind Numbers" registry to point to this document for Multipath TCP, as shown in Table 1 ๐ :
+------+--------+-----------------------+-----------+
| Kind | Length | Meaning | Reference |
+======+========+=======================+===========+
| 30 | N | Multipath TCP (MPTCP) | RFC 8684 |
+------+--------+-----------------------+-----------+
Table 1: TCP Option Kind Numbers
7.2. MPTCP Option Subtypes ๐
okay so the 4-bit MPTCP subtype lives in the "MPTCP Option Subtypes" subregistry under the "Transmission Control Protocol (TCP) Parameters" registry ๐ it was first defined in [RFC6824]. Since [RFC6824] is an Experimental RFC and not a Standards Track one, AND nobody has added anything beyond the entries pointing back to [RFC6824], IANA swapped the whole registry out for the contents of Table 2, plus the note below. no cap, very tidy ๐ง
Heads up: this registry covers the MPTCP Option Subtypes for MPTCP v1, which obsoletes the Experimental MPTCP v0. if you're after the v0 subtypes, go check [RFC6824] ๐
+-------+-----------------+----------------------+-------------+
| Value | Symbol | Name | Reference |
+=======+=================+======================+=============+
| 0x0 | MP_CAPABLE | Multipath Capable | RFC 8684, |
| | | | Section 3.1 |
+-------+-----------------+----------------------+-------------+
| 0x1 | MP_JOIN | Join Connection | RFC 8684, |
| | | | Section 3.2 |
+-------+-----------------+----------------------+-------------+
| 0x2 | DSS | Data Sequence Signal | RFC 8684, |
| | | (Data ACK and Data | Section 3.3 |
| | | Sequence Mapping) | |
+-------+-----------------+----------------------+-------------+
| 0x3 | ADD_ADDR | Add Address | RFC 8684, |
| | | | Section |
| | | | 3.4.1 |
+-------+-----------------+----------------------+-------------+
| 0x4 | REMOVE_ADDR | Remove Address | RFC 8684, |
| | | | Section |
| | | | 3.4.2 |
+-------+-----------------+----------------------+-------------+
| 0x5 | MP_PRIO | Change Subflow | RFC 8684, |
| | | Priority | Section |
| | | | 3.3.8 |
+-------+-----------------+----------------------+-------------+
| 0x6 | MP_FAIL | Fallback | RFC 8684, |
| | | | Section 3.7 |
+-------+-----------------+----------------------+-------------+
| 0x7 | MP_FASTCLOSE | Fast Close | RFC 8684, |
| | | | Section 3.5 |
+-------+-----------------+----------------------+-------------+
| 0x8 | MP_TCPRST | Subflow Reset | RFC 8684, |
| | | | Section 3.6 |
+-------+-----------------+----------------------+-------------+
| 0xf | MP_EXPERIMENTAL | Reserved for Private | |
| | | Use | |
+-------+-----------------+----------------------+-------------+
Table 2: MPTCP Option Subtypes
values 0x9 through 0xe are unassigned right now ๐ธ and 0xf is reserved for private experiments. its use might get formalized in a future spec, so keep an eye out โจ future assignments in this registry go through Standards Action as defined by [RFC8126]. every assignment is made up of the subtype's symbolic name, its value, and a reference to its spec. that's the whole vibe ๐
7.3. MPTCP Handshake Algorithms ๐ฆ
the "MPTCP Handshake Algorithms" subregistry, also under the "Transmission Control Protocol (TCP) Parameters" registry, was defined in [RFC6824] too. same story as before: [RFC6824] is Experimental and not Standards Track, and no new entries have shown up beyond the ones pointing at it, so IANA replaced the registry with Table 3 and a note of its own ๐
Note: this registry covers the MPTCP Handshake Algorithms for MPTCP v1, which obsoletes the Experimental MPTCP v0. for the MPTCP v0 subtypes, check [RFC6824] ๐ซถ
+----------+---------------------------------+-------------+
| Flag Bit | Meaning | Reference |
+==========+=================================+=============+
| A | Checksum required | RFC 8684, |
| | | Section 3.1 |
+----------+---------------------------------+-------------+
| B | Extensibility | RFC 8684, |
| | | Section 3.1 |
+----------+---------------------------------+-------------+
| C | Do not attempt to establish new | RFC 8684, |
| | subflows to the source address. | Section 3.1 |
+----------+---------------------------------+-------------+
| D-G | Unassigned | |
+----------+---------------------------------+-------------+
| H | HMAC-SHA256 | RFC 8684, |
| | | Section 3.2 |
+----------+---------------------------------+-------------+
Table 3: MPTCP Handshake Algorithms
the spicy bit: the meaning of bits "D" through "H" can depend on bit "B", depending on how the Extensibility parameter gets defined in future specs ๐ check Section 3.1 for the details. future assignments here are also going through Standards Action as defined by [RFC8126]. each one is made of the flags' value, a symbolic name for the algorithm, and a reference to its spec ๐
7.4. MP_TCPRST Reason Codes ๐
okay this one's fun ๐ IANA made a whole new subregistry, "MPTCP MP_TCPRST Reason Codes", under the "Transmission Control Protocol (TCP) Parameters" registry. it's based on the reason code carried in the MP_TCPRST message (Section 3.6). the starter values are in Table 4. future assignments need Specification Required as defined by [RFC8126]. each assignment is the code's value, a short description of what it means, and a reference to its spec. the max value is 0xff, so there's room but not infinite room ๐
+------+-----------------------------+-----------------------+
| Code | Meaning | Reference |
+======+=============================+=======================+
| 0x00 | Unspecified error | RFC 8684, Section 3.6 |
+------+-----------------------------+-----------------------+
| 0x01 | MPTCP-specific error | RFC 8684, Section 3.6 |
+------+-----------------------------+-----------------------+
| 0x02 | Lack of resources | RFC 8684, Section 3.6 |
+------+-----------------------------+-----------------------+
| 0x03 | Administratively prohibited | RFC 8684, Section 3.6 |
+------+-----------------------------+-----------------------+
| 0x04 | Too much outstanding data | RFC 8684, Section 3.6 |
+------+-----------------------------+-----------------------+
| 0x05 | Unacceptable performance | RFC 8684, Section 3.6 |
+------+-----------------------------+-----------------------+
| 0x06 | Middlebox interference | RFC 8684, Section 3.6 |
+------+-----------------------------+-----------------------+
Table 4: MPTCP MP_TCPRST Reason Codes
as guidance for the designated expert [RFC8126]: assignments should not normally be refused unless codepoint space is getting scarce, as long as there's a clear difference from codes that already exist, and as long as there's enough guidance for implementers on both the sending and receiving side. the expert's gotta be fair but also keep the space tidy ๐ซถโจ
8. References ๐
8.1. Normative References ๐ฅ
the normative refs, all listed out so nothing gets lost ๐
[RFC0793] Postel, J., "Transmission Control Protocol", STD 7,
RFC 793, DOI 10.17487/RFC0793, September 1981,
<https://www.rfc-editor.org/info/rfc793>.
[RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
Hashing for Message Authentication", RFC 2104,
DOI 10.17487/RFC2104, February 1997,
<https://www.rfc-editor.org/info/rfc2104>.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC5961] Ramaiah, A., Stewart, R., and M. Dalal, "Improving TCP's
Robustness to Blind In-Window Attacks", RFC 5961,
DOI 10.17487/RFC5961, August 2010,
<https://www.rfc-editor.org/info/rfc5961>.
[RFC6234] Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms
(SHA and SHA-based HMAC and HKDF)", RFC 6234,
DOI 10.17487/RFC6234, May 2011,
<https://www.rfc-editor.org/info/rfc6234>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
8.2. Informative References โจ
okay bestie, here are the receipts ๐๐ซถ these are the informative ones, so they back up everything in this doc, and they're SO worth a read ๐
[deployments]Bonaventure, O. and S. Seo, "Multipath TCP Deployments", IETF Journal 2016, November 2016,https://www.ietfjournal.org/multipath-tcp-deployments/.[howhard]Raiciu, C., Paasch, C., Barre, S., Ford, A., Honda, M., Duchene, F., Bonaventure, O., and M. Handley, "How Hard Can It Be? Designing and Implementing a Deployable Multipath TCP", Usenix Symposium on Networked Systems Design and Implementation 2012, April 2012,https://www.usenix.org/conference/nsdi12/technical-sessions/presentation/raiciu.[norm]Handley, M., Paxson, V., and C. Kreibich, "Network Intrusion Detection: Evasion, Traffic Normalization, and End-to-End Protocol Semantics", Usenix Security Symposium 2001, August 2001,https://www.usenix.org/legacy/events/sec01/full_papers/handley/handley.pdf.[RFC1122]Braden, R., Ed., "Requirements for Internet Hosts - Communication Layers", STD 3, RFC 1122, DOI 10.17487/RFC1122, October 1989,https://www.rfc-editor.org/info/rfc1122.[RFC1918]Rekhter, Y., Moskowitz, B., Karrenberg, D., de Groot, G. J., and E. Lear, "Address Allocation for Private Internets", BCP 5, RFC 1918, DOI 10.17487/RFC1918, February 1996,https://www.rfc-editor.org/info/rfc1918.[RFC2018]Mathis, M., Mahdavi, J., Floyd, S., and A. Romanow, "TCP Selective Acknowledgment Options", RFC 2018, DOI 10.17487/RFC2018, October 1996,https://www.rfc-editor.org/info/rfc2018.[RFC2979]Freed, N., "Behavior of and Requirements for Internet Firewalls", RFC 2979, DOI 10.17487/RFC2979, October 2000,https://www.rfc-editor.org/info/rfc2979.[RFC2992]Hopps, C., "Analysis of an Equal-Cost Multi-Path Algorithm", RFC 2992, DOI 10.17487/RFC2992, November 2000,https://www.rfc-editor.org/info/rfc2992.[RFC3022]Srisuresh, P. and K. Egevang, "Traditional IP Network Address Translator (Traditional NAT)", RFC 3022, DOI 10.17487/RFC3022, January 2001,https://www.rfc-editor.org/info/rfc3022.[RFC3135]Border, J., Kojo, M., Griner, J., Montenegro, G., and Z. Shelby, "Performance Enhancing Proxies Intended to Mitigate Link-Related Degradations", RFC 3135, DOI 10.17487/RFC3135, June 2001,https://www.rfc-editor.org/info/rfc3135.[RFC4086]Eastlake 3rd, D., Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, DOI 10.17487/RFC4086, June 2005,https://www.rfc-editor.org/info/rfc4086.[RFC4987]Eddy, W., "TCP SYN Flooding Attacks and Common Mitigations", RFC 4987, DOI 10.17487/RFC4987, August 2007,https://www.rfc-editor.org/info/rfc4987.[RFC5681]Allman, M., Paxson, V., and E. Blanton, "TCP Congestion Control", RFC 5681, DOI 10.17487/RFC5681, September 2009,https://www.rfc-editor.org/info/rfc5681.[RFC6181]Bagnulo, M., "Threat Analysis for TCP Extensions for Multipath Operation with Multiple Addresses", RFC 6181, DOI 10.17487/RFC6181, March 2011,https://www.rfc-editor.org/info/rfc6181.[RFC6182]Ford, A., Raiciu, C., Handley, M., Barre, S., and J. Iyengar, "Architectural Guidelines for Multipath TCP Development", RFC 6182, DOI 10.17487/RFC6182, March 2011,https://www.rfc-editor.org/info/rfc6182.[RFC6356]Raiciu, C., Handley, M., and D. Wischik, "Coupled Congestion Control for Multipath Transport Protocols", RFC 6356, DOI 10.17487/RFC6356, October 2011,https://www.rfc-editor.org/info/rfc6356.[RFC6528]Gont, F. and S. Bellovin, "Defending against Sequence Number Attacks", RFC 6528, DOI 10.17487/RFC6528, February 2012,https://www.rfc-editor.org/info/rfc6528.[RFC6824]Ford, A., Raiciu, C., Handley, M., and O. Bonaventure, "TCP Extensions for Multipath Operation with Multiple Addresses", RFC 6824, DOI 10.17487/RFC6824, January 2013,https://www.rfc-editor.org/info/rfc6824.[RFC6897]Scharf, M. and A. Ford, "Multipath TCP (MPTCP) Application Interface Considerations", RFC 6897, DOI 10.17487/RFC6897, March 2013,https://www.rfc-editor.org/info/rfc6897.[RFC7323]Borman, D., Braden, B., Jacobson, V., and R. Scheffenegger, Ed., "TCP Extensions for High Performance", RFC 7323, DOI 10.17487/RFC7323, September 2014,https://www.rfc-editor.org/info/rfc7323.[RFC7413]Cheng, Y., Chu, J., Radhakrishnan, S., and A. Jain, "TCP Fast Open", RFC 7413, DOI 10.17487/RFC7413, December 2014,https://www.rfc-editor.org/info/rfc7413.[RFC7430]Bagnulo, M., Paasch, C., Gont, F., Bonaventure, O., and C. Raiciu, "Analysis of Residual Threats and Possible Fixes for Multipath TCP (MPTCP)", RFC 7430, DOI 10.17487/RFC7430, July 2015,https://www.rfc-editor.org/info/rfc7430.[RFC8041]Bonaventure, O., Paasch, C., and G. Detal, "Use Cases and Operational Experience with Multipath TCP", RFC 8041, DOI 10.17487/RFC8041, January 2017,https://www.rfc-editor.org/info/rfc8041.[RFC8126]Cotton, M., Leiba, B., and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 8126, DOI 10.17487/RFC8126, June 2017,https://www.rfc-editor.org/info/rfc8126.[TCPLO]Ramaiah, A., "TCP option space extension", Work in Progress, Internet-Draft, draft-ananth-tcpm-tcpoptext-00, 26 March 2012,https://tools.ietf.org/html/draft-ananth-tcpm-tcpoptext-00.
Appendix A. Notes on Use of TCP Options ๐
okay so the TCP option space is seriously cramped ๐ญ the Data Offset field is only 4 bits, and it counts the TCP header length in 32-bit words. a standard TCP header is 20 bytes, which leaves a max of 40 bytes for options, and a bunch of that is already taken by stuff like timestamp and SACK.
we did a quick study of the commonly used TCP options on SYN, data, and pure ACK packets, and guess what? there's enough room for every option in this doc ๐ฆ๐ฅ
SYN packets usually carry Maximum Segment Size (MSS) (4 bytes), window scale (3 bytes), SACK permitted (2 bytes), and timestamp (10 bytes). together that's 19 bytes. some operating systems pad each option up to a word boundary, so it becomes 24 bytes (a brief survey suggests Windows XP and Mac OS X do this, while Linux doesn't). so optimistically we've got 21 bytes free, or 16 if options have to be word-aligned. either way, the SYN versions of MP_CAPABLE (12 bytes) and MP_JOIN (12 or 16 bytes) still fit in what's left ๐
ok this next bit is kind of a big deal ๐ because the connection uses a 64-bit data-level sequence space, it's feasible that MPTCP won't need the timestamp option for protection against wrapped sequence numbers (per the Protection Against Wrapped Sequences (PAWS) mechanism, as described in [RFC7323]). the data-level sequence space has way less chance of wrapping. confirming that this optimization is valid is left for further study ๐ง
TCP data packets usually carry a timestamp option in every packet, which takes 10 bytes (or 12 with padding). that leaves 30 bytes (or 28 if word-aligned). the DSS option changes size depending on (1) whether the Data Sequence Mapping, DATA_ACK, or both are included, (2) whether the sequence numbers in use are 4 or 8 octets, and (3) whether the checksum is present. the biggest DSS option is 28 bytes, so even that fits ๐ but unless a connection is both bidirectional and high-bandwidth, it's unlikely that all that option space will be needed on every DSS option.
inside the DSS option, you don't have to include the Data Sequence Mapping and DATA_ACK in every packet. in lots of cases you can alternate their presence, as long as the mapping covers the data sent in the next packet. you could also alternate between 4-byte and 8-byte sequence numbers from one option to the next ๐
on subflow and connection setup, an MPTCP option also rides on the third packet (an ACK). those are 20 bytes (for MP_CAPABLE) and 24 bytes (for MP_JOIN), and both fit in the available option space ๐ธ
pure ACKs usually only have timestamps (10 bytes). here, Multipath TCP usually just needs to encode the DATA_ACK (max 12 bytes). every now and then ACKs carry SACK information, and depending on how many packets were lost, SACK can use up the entire option space. if a DATA_ACK has to be included too, it's probably necessary to cut down the number of SACK blocks to make room for it. ngl though, the DATA_ACK is unlikely to be needed when SACK is in use, since until at least some of the SACK blocks get retransmitted, the cumulative data-level ACK won't move forward. (or if it does, because of retransmissions on another path, that path can also carry the new DATA_ACK) ๐
the ADD_ADDR option can be anywhere from 16 to 30 bytes, depending on (1) whether IPv4 or IPv6 is used and (2) whether the port number is present. it's unlikely that this signaling would fit in a data packet (though if there's space, it's totally fine to include it). it is recommended that duplicate ACKs not be used with any other payload or options, so these rare signals get sent on their own. this is also the reason duplicate ACKs with MPTCP options are not to be taken as a signal of congestion ๐ญ๐ซถ
Appendix B. TCP Fast Open and MPTCP ๐ฆ
TCP Fast Open (TFO) is an experimental TCP extension, described in [RFC7413], that lets you send data one RTT earlier than regular TCP. it's considered a huge win, because super short connections are everywhere, especially for HTTP request/response stuff ๐ the trick is that the SYN segment gets sent together with the application's data, and the listener can reply with data right after the SYN/ACK. [RFC7413] keeps this secure by using a new TCP option that carries a cookie negotiated in an earlier connection ๐
when you combine TFO with MPTCP, there are two key points you need to keep in mind, and we'll go through them below ๐
B.1. TFO Cookie Request with MPTCP โจ
okay so, TFO (TCP Fast Open) has a rule that you can't just stuff data into the SYN right away, and that's for security reasons ๐ [RFC7413]. So instead, the initiator asks the listener for a cookie first, and then uses that cookie in later connections ๐ช. This happens with the TCP cookie request and response options, which are 2 bytes and 6-18 bytes respectively (depending on how long the cookie is) ๐.
TFO and MPTCP can totally be combined ๐ , as long as all the options together don't go over the max of 40 bytes that TCP allows:
- In the SYN: MPTCP uses a 4-byte MP_CAPABLE option. The sum of the MPTCP and TFO options is 6 bytes. With typical TCP options using up to 19 bytes in the SYN (24 bytes if options are padded at a word boundary), there is enough space to combine the MP_CAPABLE with the TFO cookie request. ๐
- In the SYN + ACK: MPTCP uses a 12-byte MP_CAPABLE option, but now the TFO option can be as long as 18 bytes. Since the maximum option length might get exceeded, it's up to the listener to avoid this problem by using a shorter cookie ๐ซถ. As an example, if we consider that 19 bytes are used for classical TCP options, the maximum possible cookie length would be 7 bytes. Note that, for the SYN packet, the same limitation applies to subsequent connections (because the initiator then echoes the cookie back to the listener) ๐. Finally, if the security impact of reducing the cookie size is not deemed acceptable, the listener can reduce the amount of space used by other TCP options by omitting the TCP timestamps (as outlined in Appendix A) โจ.
B.2. Data Sequence Mapping under TFO
okay this one is giving main character energy ๐ฆ. In the TCP establishment phase, MPTCP uses a key exchange that is used to generate the Initial Data Sequence Numbers (IDSNs). Specifically, the SYN with MP_CAPABLE occupies the first octet of data sequence space. With TFO, one way to handle the data sent together with the SYN would be to consider an implicit DSS mapping that covers that SYN segment (since there's not enough space in the SYN to include a DSS option). The problem with that approach is that if a middlebox modifies the TFO data, MPTCP won't notice because there's no DSS checksum ๐ญ. For example, a TCP-aware (but not MPTCP-aware) middlebox could insert bytes at the beginning of the stream and adapt the TCP checksum and sequence numbers accordingly. With an implicit mapping, this information would give the initiator and listener a different view of the DSS mapping, and there would be no way to detect this inconsistency, because the DSS checksum isn't present ๐.
To solve this issue, the TFO data must not be considered part of the data sequence number space ๐ โโ๏ธ: the SYN with MP_CAPABLE still occupies the first octet of data sequence space, but then the first non-TFO data byte occupies the second octet. This guarantees that, if the use of the DSS checksum is negotiated, all data in the data sequence number space is checksummed ๐ฅ. We also note that this does not entail a loss of functionality, because TFO data is always only sent on the initial subflow, before any attempt to create additional subflows ๐.
B.3. Connection Establishment Examples
here are a few examples of possible "TFO + MPTCP" establishment scenarios ๐ง.
Before an initiator can send data together with the SYN, it must request a cookie from the listener, as shown in Figure 18. (Note: The sequence number and length are annotated in Figure 18 as Seq(Length) (e.g., "S. 0(0)") and used as such in the subsequent figures (e.g., "S 0(20)" in Figure 19).) This is done by simply combining the TFO and MPTCP options ๐ธ.
initiator listener
| |
| S Seq=0(Length=0) <MP_CAPABLE>, <TFO cookie request> |
| --------------------------------------------------------> |
| |
| S. 0(0) ack 1 <MP_CAPABLE>, <TFO cookie> |
| <-------------------------------------------------------- |
| |
| . 0(0) ack 1 <MP_CAPABLE> |
| --------------------------------------------------------> |
| |
Figure 18: Cookie Request
Once that's done, the received cookie can be used for TFO, as shown in Figure 19 ๐. In this example, the initiator first sends 20 bytes in the SYN. The listener immediately replies with 100 bytes following the SYN-ACK, and then the initiator replies with 20 more bytes. Note that the last segment in the figure has a TCP sequence number of 21, while the DSS subflow sequence number is 1 (because the TFO data is not part of the data sequence number space, as explained in Appendix B.2.
initiator listener
| |
| S 0(20) <MP_CAPABLE>, <TFO cookie> |
| --------------------------------------------------------> |
| |
| S. 0(0) ack 21 <MP_CAPABLE> |
| <-------------------------------------------------------- |
| |
| . 1(100) ack 21 <DSS ack=1 seq=1 ssn=1 dlen=100> |
| <-------------------------------------------------------- |
| |
| . 21(0) ack 1 <MP_CAPABLE> |
| --------------------------------------------------------> |
| |
| . 21(20) ack 101 <DSS ack=101 seq=1 ssn=1 dlen=20> |
| --------------------------------------------------------> |
| |
Figure 19: The Listener Supports TFO
In Figure 20, the listener doesn't support TFO ๐. The initiator detects that no state is created in the listener (as no data is ACKed), so it now sends the MP_CAPABLE in the third packet, so the listener can build its MPTCP context at the end of the establishment. Now, the TFO data, when retransmitted, becomes part of the Data Sequence Mapping, because it's effectively sent (in fact re-sent) after the establishment ๐ .
initiator listener
| |
| S 0(20) <MP_CAPABLE>, <TFO cookie> |
| --------------------------------------------------------> |
| |
| S. 0(0) ack 1 <MP_CAPABLE> |
| <-------------------------------------------------------- |
| |
| . 1(0) ack 1 <MP_CAPABLE> |
| --------------------------------------------------------> |
| |
| . 1(20) ack 1 <DSS ack=1 seq=1 ssn=1 dlen=20> |
| --------------------------------------------------------> |
| |
| . 0(0) ack 21 <DSS ack=21 seq=1 ssn=1 dlen=0> |
| <-------------------------------------------------------- |
| |
Figure 20: The Listener Does Not Support TFO
It's also possible that the listener only acknowledges part of the TFO data, as illustrated in Figure 21 ๐. The initiator will simply retransmit the missing data together with a DSS mapping.
initiator listener
| |
| S 0(1000) <MP_CAPABLE>, <TFO cookie> |
| --------------------------------------------------------> |
| |
| S. 0(0) ack 501 <MP_CAPABLE> |
| <-------------------------------------------------------- |
| |
| . 501(0) ack 1 <MP_CAPABLE> |
| --------------------------------------------------------> |
| |
| . 501(500) ack 1 <DSS ack=1 seq=1 ssn=1 dlen=500> |
| --------------------------------------------------------> |
| |
Figure 21: Partial Data Acknowledgment
Appendix C. Control Blocks ๐ง
conceptually, an MPTCP connection can be represented as an MPTCP protocol control block (PCB). It holds several variables that track the progress and state of the MPTCP connection, plus a set of linked TCP control blocks that correspond to the subflows that have been established ๐ฆ.
RFC 793 [RFC0793] specifies several state variables. Whenever possible, we reuse the same terminology as RFC 793 to describe the state variables that MPTCP maintains ๐.
C.1. MPTCP Control Block
the MPTCP control block contains the following variables per connection โจ.
C.1.1. Authentication and Metadata
Local.Token (32 bits): This is the token chosen by the local host on this MPTCP connection. The token must be unique among all established MPTCP connections, and it's generated from the local key ๐.
Local.Key (64 bits): This is the key sent by the local host on this MPTCP connection.
Remote.Token (32 bits): This is the token chosen by the remote host on this MPTCP connection, generated from the remote key.
Remote.Key (64 bits): This is the key chosen by the remote host on this MPTCP connection.
MPTCP.Checksum (flag): This flag is set to true if at least one of the hosts has set the "A" bit in the MP_CAPABLE options exchanged during connection establishment; otherwise, it's set to false. If this flag is set, the checksum must be computed in all DSS options ๐ฅ.
C.1.2. Sending Side
SND.UNA (64 bits): This is the data sequence number of the next byte to be acknowledged, at the MPTCP connection level. This variable is updated upon reception of a DSS option containing a DATA_ACK ๐ซถ.
SND.NXT (64 bits): This is the data sequence number of the next byte to be sent. SND.NXT is used to determine the value of the DSN in the DSS option ๐.
SND.WND (32 bits): This is the send window. It's 32 bits if the features in RFC 7323 are used, and 16 bits otherwise. MPTCP maintains the send window at the MPTCP connection level, and the same window is shared by all subflows. All subflows use the MPTCP connection-level SND.WND to compute the SEQ.WND value that is sent in each transmitted segment ๐.
C.1.3. Receiving Side
okay so this is the receiving side of the MPTCP control block, and it's giving organized ๐ฆโจ
RCV.NXT(64 bits): This is the data sequence number of the next byte that is expected on the MPTCP connection. It gets modified when in-order data shows up ๐ฅ. The value ofRCV.NXTis used to specify theDATA_ACKthat goes out in the DSS option on all subflows ๐ซถ.RCV.WND(32 bits): This is the connection-level receive window, which is the maximum of theRCV.WNDon all the subflows ๐ธ. It's 32 bits if the features in RFC 7323 are used, and 16 bits otherwise ๐ .
C.2. TCP Control Blocks
the MPTCP control block also keeps a list of the TCP control blocks that belong to the MPTCP connection ๐ฆ
bestie, notice that the TCP control block on the TCP subflows does not have RCV.WND or SND.WND. ๐ That's because those are tracked at the MPTCP connection level, not the subflow level. main character energy for the connection, lol ๐
Inside each TCP control block, the following state variables are defined.
C.2.1. Sending Side
SND.UNA(32 bits): This is the sequence number of the next byte to be acknowledged on the subflow. It gets updated every time a TCP acknowledgment comes in on the subflow ๐.SND.NXT(32 bits): This is the sequence number of the next byte to be sent on the subflow.SND.NXTis what sets the value ofSEG.SEQwhen the next segment goes out ๐.
C.2.2. Receiving Side
RCV.NXT(32 bits): This is the sequence number of the next byte that is expected on the subflow. It gets modified when in-order segments are received ๐ฌ. The value ofRCV.NXTis copied into theSEG.ACKfield of the next segments sent on the subflow ๐.RCV.WND(32 bits): This is the subflow-level receive window, and it's updated with the window field from the segments received on this subflow ๐. It's 32 bits if the features in RFC 7323 are used, and 16 bits otherwise ๐.
Appendix D. Finite State Machine โจ
the diagram in Figure 22 shows the Finite State Machine for connection-level closure ๐ง it's a really good look at how the DATA_FIN connection-level signal (shown in the diagram as the DFIN flag on a DATA_ACK) (1) interacts with subflow-level FINs, and (2) allows break-before-make handover between subflows ๐ฅ
+---------+
| M_ESTAB |
+---------+
M_CLOSE | | rcv DATA_FIN
------- | | -------
+---------+ snd DATA_FIN / \ snd DATA_ACK[DFIN] +-------+
| M_FIN |<----------------- ------------------->|M_CLOSE|
| WAIT-1 |--------------------------- | WAIT |
+---------+ rcv DATA_FIN \ +-------+
| rcv DATA_ACK[DFIN] ------- | M_CLOSE |
| -------------- snd DATA_ACK | ------- |
| CLOSE all subflows | snd DATA_FIN |
V V V
+-----------+ +-----------+ +----------+
|M_FINWAIT-2| | M_CLOSING | |M_LAST-ACK|
+-----------+ +-----------+ +----------+
| rcv DATA_ACK[DFIN] | rcv DATA_ACK[DFIN] |
| rcv DATA_FIN -------------- | -------------- |
| ------- CLOSE all subflows | CLOSE all subflows |
| snd DATA_ACK[DFIN] V delete MPTCP PCB V
\ +-----------+ +--------+
------------------------>|M_TIME WAIT|---------------->|M_CLOSED|
+-----------+ +--------+
All subflows in CLOSED
------------
delete MPTCP PCB
Figure 22: Finite State Machine for Connection Closure
Appendix E. Changes from RFC 6824 ๐ฆ
this appendix is the tea โ on the key technical changes between [RFC6824], which specifies MPTCP v0, and this document, which obsoletes [RFC6824] and specifies MPTCP v1. Note that this specification is not backward compatible with [RFC6824]. ๐
- This document incorporates lessons learned from the various implementations, deployments, and experiments gathered in the documents "Use Cases and Operational Experience with Multipath TCP" [RFC8041] and the IETF Journal article "Multipath TCP Deployments" [deployments]. ๐
- Connection initiation, through the exchange of the MP_CAPABLE MPTCP option, is different from [RFC6824]. The SYN no longer includes the initiator's key, to allow the MP_CAPABLE option on the SYN to be shorter in length and to avoid duplicating the sending of keying material. โ๏ธ
- This also ensures reliable delivery of the key on the MP_CAPABLE option by allowing its transmission to be combined with data and thus using TCP's built-in reliability mechanism. If the initiator does not immediately have data to send, the MP_CAPABLE option with the keys will be repeated on the first data packet. If the other end is the first to send, then the presence of the DSS option implicitly confirms the receipt of the MP_CAPABLE. ๐
- In the Flags field of MP_CAPABLE, "C" is now assigned to mean that the sender of this option will not accept additional MPTCP subflows to the source address and port. This improves efficiency -- for example, in cases where the sender is behind a strict NAT. ๐ก๏ธ
- In the Flags field of MP_CAPABLE, "H" now indicates the use of HMAC-SHA256 (rather than HMAC-SHA1). ๐
- Connection initiation also defines the procedure for version negotiation, for implementations that support both v0 [RFC6824] and v1 (this document). ๐ค
- The HMAC-SHA256 (rather than HMAC-SHA1) algorithm is used, as it provides better security. It is used to generate the token in the MP_JOIN and ADD_ADDR messages and to set the IDSN. ๐
- A new subflow-level option exists to signal reasons for sending a RST on a subflow (MP_TCPRST (Section 3.6)); this can help an implementation decide whether to attempt later reconnection. ๐จ
- The MP_PRIO option (Section 3.3.8), which is used to signal a change of priority for a subflow, no longer includes the AddrID field. Its purpose was to allow the changed priority to be applied on a subflow other than the one it was sent on. However, it was determined that this could be used by a man-in-the-middle to divert all traffic onto its own path, and MP_PRIO does not include a token or other type of security mechanism. ๐ค
- The ADD_ADDR option (Section 3.4.1), which is used to inform the other host about another potential address, is different in several ways. It now includes an HMAC of the added address, for enhanced security. In addition, reliability for the ADD_ADDR option has been added: the IPVer field is replaced with a flag field, and one flag is assigned ("E") that is used as an "echo" so a host can indicate that it has received the option. ๐ฃ
- This document describes an additional way of performing a Fast Close -- by sending an MP_FASTCLOSE option on a RST on all subflows. This allows the host to tear down the subflows and the connection immediately. โก
- IANA has reserved the MPTCP option subtype of value 0xf for Private Use (Section 7.2). This document doesn't define how to use that value. ๐คซ
- This document adds a new appendix (Appendix B), which discusses the usage of both MPTCP options and TFO options on the same packet. ๐ง
Acknowledgments ๐ซถ
The authors gratefully acknowledge significant input into this document from Sebastien Barre and Andrew McDonald. ๐
The authors also wish to acknowledge reviews and contributions from Iljitsch van Beijnum, Lars Eggert, Marcelo Bagnulo, Robert Hancock, Pasi Sarolahti, Toby Moncaster, Philip Eardley, Sergio Lembo, Lawrence Conroy, Yoshifumi Nishida, Bob Briscoe, Stein Gjessing, Andrew McGregor, Georg Hampel, Anumita Biswas, Wes Eddy, Alexey Melnikov, Francis Dupont, Adrian Farrel, Barry Leiba, Robert Sparks, Sean Turner, Stephen Farrell, Martin Stiemerling, Gregory Detal, Fabien Duchene, Xavier de Foy, Rahul Jadhav, Klemens Schragel, Mirja Kรผhlewind, Sheng Jiang, Alissa Cooper, Ines Robles, Roman Danyliw, Adam Roach, Eric Vyncke, and Ben Kaduk. ๐
Authors' Addresses ๐
Alan Ford
Pexip
Email: alan.ford@gmail.com
Costin Raiciu
University Politehnica of Bucharest
Splaiul Independentei 313
Bucharest
Romania
Email: costin.raiciu@cs.pub.ro
Mark Handley
University College London
Gower Street
London
WC1E 6BT
United Kingdom
Email: m.handley@cs.ucl.ac.uk
Olivier Bonaventure
Universitรฉ catholique de Louvain
Pl. Ste Barbe, 2
1348 Louvain-la-Neuve
Belgium
Email: olivier.bonaventure@uclouvain.be
Christoph Paasch
Apple, Inc.
Cupertino, CA
United States of America
Email: cpaasch@apple.com
๐ฆ where this page comes from
- ietf: rfc8684 2020-03
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms