多路径 TCP (MPTCP)

简介

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

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

用例

得益于 MPTCP,能够并行或同时使用多条路径带来了与 TCP 相比的新用例

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

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

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

概念

从技术上讲,当使用 IPPROTO_MPTCP 协议(Linux 特有)创建新的套接字时,会创建一个子流(或路径)。这个子流由一个常规的 TCP 连接组成,用于通过一个接口传输数据。主机之间稍后可以协商额外的子流。为了使远程主机能够检测到 MPTCP 的使用,在底层 TCP 子流的 TCP 选项字段中添加了一个新字段。该字段包含(除其他外)一个 MP_CAPABLE 选项,如果支持 MPTCP,则它会告诉另一个主机使用 MPTCP。如果远程主机或其间的任何中间设备不支持 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:(无效参数):MPTCP 在内核版本 < 5.6 时不可用。

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

  • ENOPROTOOPT协议不可用):MPTCP 已通过 net.mptcp.enabled sysctl 旋钮禁用;参见 MPTCP Sysfs 变量

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

对于用户空间应用程序来说,从 IPPROTO_TCP 切换到 IPPROTO_MPTCP 应该尽可能透明。

套接字选项

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 时将性能影响降至最低。