Red Hat Enterprise Linux 8.3上的多路径TCP:从0到1子流

2020-08-20 03:37:37

多路径TCP(MPTCP)扩展了传统的TCP,允许在多个同时的TCP路径上进行可靠的端到端传输,并且它将作为Red Hat Enterprise Linux8.3上的技术预览版出现。这是面向希望在实时系统上练习新MPTCP功能的用户的两篇文章中的第一篇。在第一部分中,我们将向您展示如何在内核中启用该协议,并让客户机和服务器应用程序使用MPTCP套接字。然后,我们在一个示例测试网络中对内核运行诊断,其中端点使用单个子流。

多路径TCP是传输控制协议(TCP)的一个相对较新的扩展,它的正式Linux实现甚至是最近的。早期用户可能想知道RHEL 8.3中会有什么。在本文中,您将学习如何:

多路径TCP注册为TCP的上层协议(ULP)。用户可以通过检查可用的ULP来确保mptcp在内核中可用:

与上游Linux不同,MPTCP在默认的Red Hat Enterprise Linux(RHEL)8.3运行时被禁用。要启用创建套接字的可能性,系统管理员需要发出正确的sysctl命令:

在RHEL8.3内核中启用MPTCP后,用户空间程序有了一个新协议可用于套接字系统调用。新协议有两个潜在的使用案例。

本地支持MPTCP的应用程序可以打开SOCK_STREAM套接字,将IPPROTO_MPTCP指定为协议,将AF_INET或AF_INET6指定为地址族:

在应用程序创建套接字之后,内核将操作一个或多个将使用标准MPTCP选项(IANA编号=30)的TCP子流。客户端和服务器的语义与常规TCP套接字使用的语义相同(这意味着它们将使用bind()、listen()、connect()和Accept())。

大多数用户空间应用程序不了解IPPROTO_MPTCP,要修补并重新构建所有这些应用程序以添加对MPTCP的本机支持也是不现实的。因此,社区选择使用eBPF程序,该程序包装套接字()系统调用并覆盖protocol的值。

在RHEL 8.3中,此程序将在CPU组上运行,以便系统管理员可以指定哪些应用程序应该运行MPTCP,而其他应用程序则继续使用TCP。我们将在接下来的几周讨论上游的eBPF助手,但是我们希望支持早期的RHEL8.3用户,他们希望使用MPTCP尝试他们自己的应用程序。

您可以使用系统分路脚本作为解决办法,以拦截内核中对__sys_socket()的调用。然后,您可以允许内核探测将IPPROTO_TCP替换为IPPROTO_MPTCP。您需要添加程序包才能在带有stap的内核中安装探测器。您还将使用nmap-NCAT包中的老式NCAT工具来运行客户端和服务器:

#DNF-y安装\ >;内核头\ >;内核开发\ >;kernel-debuginfo >;kernel-debuginfo-common_x86_64\ >;SYSTEM TAP-客户端\ >;SYSTEMTAP-CLIENT-DEVELL\ >;nmap-NCAT。

图1所示的测试网络拓扑由运行在通过虚拟以太网设备(Veth)连接的单独名称空间中的客户端和服务器组成。

添加额外的IP地址将模拟端点之间的多条L4路径。首先,服务器打开被动套接字,侦听TCP端口:

从功能的角度来看,交互与使用具有常规TCP的NCAT相同:当用户在客户机的标准输入中编写一行时,服务器会在标准输出中显示该行。类似地,在服务器的标准输入中键入一行将导致将其传输回客户端的标准输出。在本例中,我们使用NCAT向服务器发送“hello world(1)\n”消息。它等待一秒钟,然后返回“hello world(2)\n”,然后关闭连接。

注意:当前的Linux MPTCP不支持IPv4/IPv6混合地址。因此,客户端/服务器连接中涉及的所有地址必须属于同一系列。

Red Hat Enterprise Linux8版本的tcpdump还不支持剖析TCP报头中的MPTCP v1子选项。我们可以通过从上游存储库构建二进制文件来克服这个问题。或者,我们可以用更新的二进制文件替换它。使用这两个更改中的任何一个,都可以检查MPTCP子选项。

在三次握手期间,客户端和服务器使用mp_capable子选项交换64位密钥,该密钥在支持mptcp之后的大括号({})中的tcpdump的输出中可见。这些密钥随后用于计算DSN/DACK和令牌。在成功建立连接之后,还会出现源自客户端的MP_CAPABLE子选项。它将一直存在,直到服务器使用数据序列信号(DSS)子选项明确确认:

#tcpdump-#tnnr capture.pcap 1 IP 192.0.2.2.44176>;192.0.2.1.4321:标志[S],序号1721499445,WIN 29200,选项[MS1460,SACK OK,TS VAL 33385784 ECR 0,NOP,wscale 7,支持MPTCP的v1],长度0 2 IP 192.0.2.1.4321>;192.0.2.2.44176:标志[S],序号3341831007,ACK 1721499446,WIN 28960,选项[MS1460,SACK OK,TS VAL 4061152149 ECR33385784,nop,wscale 7,支持mptcp的v1{0xbb206e3023b47a2d}],长度0 3 IP 192.0.2.2.44176>;192.0.2.1.4321:标志[.],ACK 1,WIN229,选项[NOP,NOP,TS VAL 33385785 ECR 4061152149,支持MPTCP的v1{0x41923206b75835f5,0xbb206e3023b47a2d}],长度0 4 IP 192.0.2.2.44176>;192.0.2.1.4321:标志[P],序号1:17,ACK 1,WIN229,选项[NOP,NOP,TS VAL 33385785 ECR 4061152149,支持MPTCP的v1{0x41923206b75835f5,0xbb206e3023b47a2d},nop,nop],长度16。

之后,TCP数据段将携带包含MPTCP序列号的DSS子选项。更具体地说,我们可以观察数据序列号(DSN)和数据确认(DACK)值,如下所示:

5 IP 192.0.2.1.4321>;192.0.2.2.44176:FLAGS[.],ACK 17,WIN227,OPTIONS[NOP,NOP,TS VAL 4061152149 ECR 33385785,MPTCP DSS ACK 1711754507747579648],长度0 6 IP 192.0.2.2.44176>;192.0.2.1.4321:标志[P],序号17:33,ACK 1,WIN229,选项[NOP,NOP,TS VAL 33386778 ECR 4061152149,mptcp DSS ACK 1331650533424046587 seq1711754507747579648 subseq 17 len 16,nop,nop],长度16 7 IP 192.0.2.1.4321>;192.0.2.2.44176:标志[.],ACK 33,WIN227,选项[NOP,NOP,TS VAL 4061153142 ECR 33386778,MPTCP DSS ACK 1711754507747579664],长度0。

使用单个子流时,DSN和DACK的增量与TCP序列号和确认号相同。连接结束时,子流将使用FIN数据包关闭,就像常规TCP流一样。由于它还会关闭MPTCP套接字,因此在DSS子选项中设置了数据FIN位,如下所示:

8 IP 192.0.2.2.44176>;192.0.2.1.4321:标志[F.],序号33,ACK 1,WIN229,选项[NOP,NOP,TS VAL 33387798 ECR 4061153142,mptcp DSS FIN ACK 1331650533424046587序号1711754507747579664子序号0镜头1,NOP,NOP],长度0 9 IP 192.0.2.1.4321>;192.0.2.2.44176:标志[.],ACK 34,WIN227,选项[NOP,NOP,TS VAL 4061154203 ECR 33387798,MPTCP DSS ACK 1711754507747579664],长度0 10 IP 192.0.2.1.4321>;192.0.2.2.44176:标志[F.],序号1,ACK 34,WIN227,选项[NOP,NOP,TS VAL 4061162156 ECR 33387798,MPTCP DSS FIN ACK 1711754507747579664序号1331650533424046587子序号0镜头1,NOP,NOP],长度0 11 IP 192.0.2.2.44176>;192.0.2.1.4321:标志[.],ACK 2,WIN229,选项[NOP,NOP,TS VAL 33395793 ECR 4061162156,MPTCP DSS ACK 1331650533424046587],长度0。

因为MPTCP使用TCP作为传输协议,所以网络管理员可以查询内核以检索主MPTCP套接字正在使用的TCP连接的信息。在本例中,我们在客户机上运行ss,在服务器侦听端口上进行过滤,在tcp-upp-mptcp之后可以读取与MPTCP相关的信息:

#ss-nti';(dport:4321)';dst 192.0.2.1 State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess ESTAB 0 192.0.2.2:44176 192.0.2.1:4321 立方体wscale:7,7[...]。已发送字节:32字节_已确认:33[...]。Tcp-upp-mptcp标志:mmec标记:0000(id:0)/768f615c(id:0)序号:127af91ad1b321fb sfseq:1 ssnoff:c7304b5f maplen:0

Tcp-upp-mptcp下面的行是紧跟在上一节中传输数据包6之后的客户端命名空间中ss的输出:

令牌的每个值都是远程对等方密钥的截断哈希消息验证码算法(HMAC),客户端在三次握手期间接收到该算法。进一步的MP_JOIN SYN数据包将使用该值来证明它们没有被欺骗。ID是RFC中指定的子流标识符。对于非MP_JOIN套接字,只有本地令牌和ID可用。

标志是包含有关子流状态的信息的位掩码。例如,M/m记录三次握手中是否存在MP_CAPABLE子选项。C表示客户端收到了服务器的密钥(即,它确认了SYN/ACK),而e表示两个MPTCP密钥的交换已完成。

SEQ表示端点在接收时期望的下一个MPTCP序列号,或者等效地,表示下一个发送的分组的DACK值。

Sfseq是子流序列号,这意味着它是此子流的当前TCP ACK值。

Ssnoff是此子流的TCP序列号和MPTCP序列号之间的当前差。如果您使用的是单个子流,则该值在连接期间不会更改。如果您使用多个子流同时承载数据段,则此值可能会根据路径容量的不同而增加或减少。

请注意,我们可以通过从SYN/ACK(捕获的数据包2)中的服务器密钥开始计算seq的值,并计算服务器的初始数据序列号(IDSN),然后将sha256(ntohll(Bb206e3023b47a2d))截断为RFC 8684指定的最低有效64位。

还要注意,因为客户端没有从服务器接收任何数据,所以在连接的整个生命周期中,seq保持等于IDSN。出于同样的原因,在本例中,sfseq的值始终等于1。我们可以在数据包10的DSN号和数据包6和8的DACK号(十进制格式:1331650533424046587)以及ss的输出(十六进制格式:127af91ad1b321fb)中看到IDSN。类似地,在本例中,SSN偏移量(SS输出中的c7304b5f)始终等于初始TCP序列号(SYN/ACK中的3341831007,捕获输出的数据包2)。

在现实场景中,MPTCP通常会使用多个子流。这样,即使在事件导致其中一条L4路径出现故障后,套接字也可以保持连通性。在下一篇文章中,我们将向您展示如何使用iproute2在RHEL 8.3上配置多个TCP路径,以及如何观看NCAT实际执行多路径。