多路径 TCP (MPTCP)

简介

多路径 TCP 或 MPTCP 是标准 TCP 的扩展,在 RFC 8684 (MPTCPv1) 中进行了描述。它允许设备同时使用多个接口,通过单个 MPTCP 连接发送和接收 TCP 数据包。MPTCP 可以聚合多个接口的带宽,或者优先选择延迟最低的接口。它还允许在一条路径中断时进行故障转移,并将流量无缝地重新注入到其他路径上。

有关 Linux 内核中多路径 TCP 的更多详细信息,请参阅官方网站:mptcp.dev

用例

与 TCP 相比,由于 MPTCP 能够并行或同时使用多个路径,因此带来了新的用例。

  • 无缝切换:在保持已建立的连接的同时,从一条路径切换到另一条路径,例如,用于智能手机等移动用例。

  • 最佳网络选择:根据一些条件(例如,延迟、丢包、成本、带宽等)使用“最佳”可用路径。

  • 网络聚合:同时使用多个路径以获得更高的吞吐量,例如,结合固定网络和移动网络以更快地发送文件。

概念

从技术上讲,当使用 IPPROTO_MPTCP 协议(Linux 特有)创建新套接字时,会创建一个子流(或路径)。此子流由一个常规 TCP 连接组成,该连接用于通过一个接口传输数据。稍后可以在主机之间协商额外的子流。为了使远程主机能够检测到 MPTCP 的使用,在底层 TCP 子流的 TCP 选项字段中添加了一个新字段。此字段包含 MP_CAPABLE 选项,该选项告诉其他主机如果支持,则使用 MPTCP。如果远程主机或中间的任何中间设备不支持,则返回的 SYN+ACK 数据包的 TCP 选项字段将不包含 MPTCP 选项。在这种情况下,连接将“降级”为普通 TCP,并将在单个路径上继续。

这种行为是通过两个内部组件实现的:路径管理器和数据包调度器。

路径管理器

路径管理器负责子流,从创建到删除,以及地址宣告。通常,客户端发起子流,服务器端通过 ADD_ADDRREMOVE_ADDR 选项宣告其他地址。

路径管理器由 net.mptcp.pm_type sysctl 旋钮控制 - 请参阅 MPTCP Sysfs 变量。有两种类型:内核内置类型(类型 0),其中相同的规则应用于所有连接(请参阅:ip mptcp);以及用户空间类型(类型 1),由用户空间守护进程(即 mptcpd)控制,其中可以为每个连接应用不同的规则。路径管理器可以通过 Netlink API 控制;请参阅 Family mptcp_pm netlink specification

为了能够在主机上使用多个 IP 地址来创建多个子流(路径),默认的内核内置 MPTCP 路径管理器需要知道可以使用哪些 IP 地址。例如,可以使用 ip mptcp endpoint 配置此项。

数据包调度器

数据包调度器负责选择要使用哪个可用的子流来发送下一个数据包。它可以决定最大化可用带宽的使用,仅选择延迟较低的路径,或者根据配置选择任何其他策略。

数据包调度器由 net.mptcp.scheduler sysctl 旋钮控制 - 请参阅 MPTCP Sysfs 变量

套接字 API

创建 MPTCP 套接字

在 Linux 上,可以通过在创建 socket 时选择 MPTCP 而不是 TCP 来使用 MPTCP

int sd = socket(AF_INET(6), SOCK_STREAM, IPPROTO_MPTCP);

请注意,IPPROTO_MPTCP 定义为 262

如果不支持 MPTCP,则 errno 将设置为

  • EINVAL:(无效参数):在内核版本 < 5.6 上,MPTCP 不可用。

  • EPROTONOSUPPORT (不支持协议):在内核版本 >= v5.6 上,MPTCP 未编译。

  • ENOPROTOOPT (协议不可用):已使用 net.mptcp.enabled sysctl 旋钮禁用了 MPTCP;请参阅 MPTCP Sysfs 变量

MPTCP 是可选的:应用程序需要明确请求它。请注意,可以使用不同的技术强制应用程序使用 MPTCP,例如 LD_PRELOAD(请参阅 mptcpize)、eBPF(请参阅 mptcpify)、SystemTAP、GODEBUG (GODEBUG=multipathtcp=1) 等。

对于用户空间应用程序,切换到 IPPROTO_MPTCP 而不是 IPPROTO_TCP 应该尽可能透明。

套接字选项

MPTCP 支持 TCP 处理的大多数套接字选项。可能不支持一些不太常见的选项,但欢迎贡献。

通常,相同的值会传播到所有子流,包括在调用 setsockopt() 之后创建的子流。可以使用 eBPF 为每个子流设置不同的值。

SOL_MPTCP (284) 级别有一些 MPTCP 特有的套接字选项来检索信息。它们会填充 getsockopt() 系统调用的 optval 缓冲区。

  • MPTCP_INFO:使用 struct mptcp_info

  • MPTCP_TCPINFO:使用 struct mptcp_subflow_data,后跟一个 struct tcp_info 数组。

  • MPTCP_SUBFLOW_ADDRS:使用 struct mptcp_subflow_data,后跟一个 mptcp_subflow_addrs 数组。

  • MPTCP_FULL_INFO:使用 struct mptcp_full_info,其中一个指针指向 struct mptcp_subflow_info 的数组(包括 struct mptcp_subflow_addrs),另一个指针指向 struct tcp_info 的数组,后跟 struct mptcp_info 的内容。

请注意,在 TCP 级别,可以使用 TCP_IS_MPTCP 套接字选项来了解当前是否正在使用 MPTCP:如果正在使用,该值将设置为 1。

设计选择

为面向用户空间的套接字添加了一种新的 MPTCP 套接字类型。内核负责创建子流套接字:它们是使用 TCP-ULP 修改其行为的 TCP 套接字。

如果客户端的连接请求没有请求 MPTCP,则 MPTCP 监听套接字将创建“普通”的接受 TCP 套接字,从而在默认启用 MPTCP 时使性能影响最小。