MPTCP 是 TCP 的多路径扩展,可将一个 tcp 连接分成多个流从多条路径传输,既可提高稳定性又可叠加带宽。比如,手机上网可以走流量,也可以走 wifi,MPTCP为的是能让两条路径可以同时使用。MPTCP需要内核支持,并不是随便一个内核就能用上的,比如 ubuntu,从 22.04 开始才集成到内核。此外,MPTCP还需要应用程序本身支持,不过即使程序不原生支持也可通过 mptcpize 程序运行之来使它支持。
好了,说下环境,主机是 ubuntu 22.04,另一台是一个 vmware 虚拟机也是 ubuntu 22.04。为了构造多路径,虚拟机需要两个网卡,我一个设为桥接模式与主机处于同一局域网,另一个为NAT模式。网络配置如下(应首先测试连通性,保证双方可以互相 ping 通):
- host:
eth0 192.168.0.10
,vmnet8 192.168.100.1
- vm:
ens0 192.168.0.15
,ens1 192.168.100.6
为了使 MPTCP 正常工作,还需要一些准备工作:
- 升级内核 系统最初的默认内核是 5.15,我主机上已经升到 6.8.0-52-generic 了,所以为了一致虚拟机上的内核也升级到 6.8.0-52-generic。
- 升级 mptcpize不过,mptcpize 程序还是和5.15内核配套的0.9版本,而6.8内核的mptcp版本应该较新,于是从 https://github.com/multipath-tcp/mptcpd 下载了最新的 0.13 版的 mptcpd,编译安装(默认路径 /usr/local)后就带有 mptcpize。编译 configure 时没有的东西先用 apt 安装相应的 dev 开发文件再重新配置就行了。关于 mptcpd 后面再说。
- 升级 iproute2 ip 工具(用于运行 ip mptcp 进行相关设置)在包 iproute2 里,然而系统带的也是针对5.15内核的较老版本,于是去 https://github.com/iproute2/iproute2/tags 下载与 6.8 内核相匹配的版本编译安装。
-
检查内核参数 通过
sysctl -a |grep mptcp
检查内核参数,确保net.mptcp.enabled = 1
以及net.mptcp.pm_type = 0
。如果不是则需要设置sysctl -w net.mptcp.enabled=1
,或修改 /etc/sysctl.conf 添加net.mptcp.enabled = 1
并运行 sysctl -p 加载。
#sysctl -a | grep mptcp
net.ipv4.tcp_available_ulp = espintcp mptcp tls
net.mptcp.add_addr_timeout = 120
net.mptcp.allow_join_initial_addr_port = 1
net.mptcp.checksum_enabled = 0
net.mptcp.close_timeout = 60
net.mptcp.enabled = 1
net.mptcp.pm_type = 0
net.mptcp.scheduler = default
net.mptcp.stale_loss_cnt = 4
- 限制带宽 为观察带宽叠加,在虚拟机的虚拟网卡上限制带宽为100Mbps。
-
安装 iperf3 安装测速工具
apt install ipef3
设置子流端点及测试
主机上设置子流端点
ip mptcp endpoint flush # 清除原有设置
ip mptcp endpoint add 192.168.0.10 dev eth1 signal
ip mptcp endpoint add 192.168.100.1 dev vmnet8 signal
ip mptcp limits set add_addr_accepted 8 subflows 8 #个数上限,可根据情况调整
ip mptcp endpoint # 显示已设端点
ip mptcp limits # 显示已设限制
虚拟机上设置子流端点
ip mptcp endpoint flush # 清除原有设置
ip mptcp endpoint add 192.168.0.15 dev ens0 signal
ip mptcp endpoint add 192.168.100.6 dev ens1 signal
ip mptcp limits set add_addr_accepted 8 subflows 8
ip mptcp endpoint # 显示已设端点
ip mptcp limits # 显示已设限制
虚拟机上通过 mptcpize 运行 iperf3 服务
mptcpize run iperf3 -s
主机上先在一个终端里运行 ip mptcp monitor
用于监测子流建立情况,然后在另一个终端里运行如下命令测速,正常情况下应该能看到 200Mbps 的速度,恰好是两个通道带宽的叠加:
mptcpize run iperf3 -c 192.168.0.15
# ip mptcp monitor 输出如下
[ CREATED] token=01d5b047 remid=0 locid=0 saddr4=192.168.0.10 daddr4=192.168.0.15 sport=42118 dport=5201
[ ESTABLISHED] token=01d5b047 remid=0 locid=0 saddr4=192.168.0.10 daddr4=192.168.0.15 sport=42118 dport=5201
[ ANNOUNCED] token=01d5b047 remid=2 daddr4=192.168.100.6 dport=5201
[ SF_ESTABLISHED] token=01d5b047 remid=2 locid=2 saddr4=192.168.100.1 daddr4=192.168.100.6 sport=50335 dport=5201 backup=0
[ CREATED] token=5136e6d4 remid=0 locid=0 saddr4=192.168.0.10 daddr4=192.168.0.15 sport=42132 dport=5201
[ ESTABLISHED] token=5136e6d4 remid=0 locid=0 saddr4=192.168.0.10 daddr4=192.168.0.15 sport=42132 dport=5201
[ ANNOUNCED] token=5136e6d4 remid=2 daddr4=192.168.100.6 dport=5201
[ SF_ESTABLISHED] token=5136e6d4 remid=2 locid=2 saddr4=192.168.100.1 daddr4=192.168.100.6 sport=36739 dport=5201 backup=0
上面的过程是,192.168.0.10 先向 192.168.0.15 发起一个连接,由于是使用 mptcpize 运行的,通过 tcpdump 抓包可以看到连接建立初始保证含有 mptcp capable v1
的内容,代表互相协商两边都支持 MPTCP。这样,服务端检查 endpoint 发现还有一个地址 192.168.100.6 可用,于是 ANNOUNCE 告诉客户端,客户端检查本地的 endpoint 发现有 192.168.100.1 与之可连通,于是客户端建立起一个 192.168.100.1->192.168.100.6 的新的子流,并把原本需要发送的数据包分成两部分,一部分通过 192.168.0.10->192.168.0.15 发送,另一部分则通过 192.168.100.1->192.168.100.6 发送。
后记
1. 关于路径管理器 pm_type
根据官方(https://www.mptcp.dev/pm.html#in-kernel-path-manager)的说明,pm_type=0 代表内核层面的管理器,是默认的; pm_type=1 是用户空间管理器。一开始用 chatGPT 或 DeepSeek 帮忙配置,虽然提供了不少帮助,但也给出了好多坑误导了我很久。而且网上搜到的说是使用 mptcpize 运行时要配合 pm_type=1 也误导了我很久,一直不成功(只有一个子流,流量不叠加)就是因为一直在用 pm_type=1,改成 pm_type=0 就好了!现在想来,用户空间或许是指用户自定义的路径管理器(自己写代码实现的?),总之没有用自定义的那就用内核默认的就行。
2. 关于 mptcpd
mptcpd 是一个自动管理 endpoint 的服务程序,但对应的 systemd 服务却是 mptcp.server,没有 d,启动用 systemctl start mptcp
。不过,开始被官网 (https://www.mptcp.dev/pm.html#userspace-path-manager)的如下描述误导:
With the userspace MPTCP path-manager –
sysctl -w net.mptcp.pm_type=1
– different rules can be applied for each connection. The path-manager will then need to be controlled by a userspace daemon, i.e.mptcpd
. In this case, the configuration has to be done on the userspace daemon side.
误以为 mptcpd 是用户空间的路径管理,必须与 pm_type=1 配合使用。其实不是,它只是自动管理 endpoint 的程序,支持通过插件的方式引入第三方(即用户空间?)的路径管理器,然而默认带的依然是内核支持的。其实,除了 mptcpd 外,其他程序比如 NetworkManager 1.40 也能自动配置 endpoint,好像二者还有冲突,需要避免同时使用。其实,像上面通过 ip mptcp 名字自己添加 endpoint 是手动设置方式,此时完全不需要 mptcpd 服务,将其关掉即可。
3. 关于标签
前面 ip mptch endpoint 设置时最后的 signal
是该端点的标签,可用的标签可通过 ip mptcp 命令查看,我的相关部分结果如下
FLAG := [ signal | subflow | backup | fullmesh ]
没有更新 iproute2 之前的 ip mptcp 则不支持 fullmesh 标签。关于标签的含义,引用官方的说法:
One of the following flags needs to be set:
signal
: The endpoint will be announced to each peer via an MPTCP ADD_ADDR sub-option. Typically, a server would be responsible for this.subflow
: The endpoint will be used to create an additional subflow using the given source IP address. A client would typically do this.Optionally, the following flags can be added next to one of the previous ones:
backup
: Subflows created from this endpoint instruct the peers to only send data on it when all non-backup subflows are unavailable.fullmesh
: The MPTCP path manager will try to create an additional subflow for each known peer address, using this endpoint as the source IP address. It requires the subflow flag, and it is not compatible with the signal one.
不过,在我上述环境的测试中发现,如果主机和虚拟机的所有端点都设成 (1)subflow
标签的话,如果用 mptcpize run iperf3 -c 192.168.0.10 发起测速的话就能带宽叠加,monitor 显示有 192.168.0.10->192.168.0.15 和 192.168.100.1->192.168.0.15 两个通道;但若使用另一个 IP 192.168.100.1 发起测速的话则无法叠加,最终流量只走 192.168.100.1->192.168.100.6 一个通道,而尝试建立 192.168.0.10->192.168.100.6 失败。通过 tcpdump 抓包能发现有 join 字段,但没有 add_addr 字段。(2)若是都是用 singal
标签,则会出现 add_addr 字段,两边有协商过程,不论使用哪个 IP 发起测速,最终都能建立 192.168.100.1->192.168.100.6 和 192.168.0.10->192.168.0.15 两个子流,成功带宽叠加。(3)若是都用 fullmesh
标签,结果怎么都不能建立两个子流,带宽不叠加,不知这是否正常。
总之,用 signal
就行了!