従来、LinuxでMPTCPを利用する場合「Linux Kernel MultiPath TCP project」が公開しているLinuxカーネルを使う必要がありましたが、アップストリームのLinuxカーネル 5.6以降でMPTCPが利用可能になりました。
現在、MPTCPの仕様は「RFC6824」で規定された”MPTCP v0″と「RFC8684」で規定された”MPTCP v1″の2つのバージョンが存在します。従来版は”MPTCP v0″にのみ、アップストリーム版は”MPTCP v1″のみに対応しており、”MPTCP v1″は後方互換性を持たないので、現時点では従来版とアップストリーム版でMPTCP通信を行うことができません。
従来版および”MPTCP v0″については、本ブログでも過去に以下の投稿しています。
今回は、Ubuntuでアップストリーム版を使った複数サブフローでのMPTCP通信を試してみました。
参考サイト
今回、参考にしたサイトは次の通りです。
環境構築
VirtualBoxを使って、以下のような構成を作成しました。
新規VM作成
OSインストール
今回は次の理由から「Ubuntu Server 21.04 (Hirsute Hippo) Daily Build」としました。
注意点として、カーネルデバッグ情報のインストールに7GB程度とられるのでVMの仮想ハードディスク容量をデフォルトの10GBから最低でも15GBに変更しておく必要があります。
カーネル更新
インストール完了時点でLinuxカーネルは5.8であり、MPTCP通信自体は可能ですが複数サブフローで送信を行うには5.10以降に更新する必要があります。
- 「
/etc/apt/sources.list
」に以下を追記します。deb http://archive.ubuntu.com/ubuntu hirsute-proposed main restricted universe multiverse
- カーネルを5.10に更新します。
$ sudo apt update $ sudo apt install linux-headers-5.10.0-14-generic linux-image-5.10.0-14-generic $ sudo reboot $ uname -a Linux mps 5.10.0-14-generic #15-Ubuntu SMP Fri Jan 29 15:10:03 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux $ cat /proc/sys/net/mptcp/enabled 1
なお、デフォルトでMPTCPは有効になっています。
SystemTap導入
このままMPTCP通信を試すこともできるのですが、アップストリーム版でMPTCP通信を行うには、従来版と違い次のようにソケット生成時に「IPPROTO_MPTCP
」を指定する必要があります。
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
今回はRedhatの記事を参考に「SystemTap」を使った既存アプリをMPTCP通信に対応させてみます。
- Systemtapの実行にカーネルデバッグ情報が必要なのでインストールします。
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C8CAB6595FDFF622 $ codename=$(lsb_release -c | awk '{print $2}') $ sudo tee /etc/apt/sources.list.d/ddebs.list << EOF deb http://ddebs.ubuntu.com/ ${codename} main restricted universe multiverse deb http://ddebs.ubuntu.com/ ${codename}-security main restricted universe multiverse deb http://ddebs.ubuntu.com/ ${codename}-updates main restricted universe multiverse deb http://ddebs.ubuntu.com/ ${codename}-proposed main restricted universe multiverse EOF $ sudo apt-get update $ sudo apt-get install linux-image-$(uname -r)-dbgsym
- Systemtapインストール
$ sudo apt install build-essential $ sudo apt install systemtap
- Systemtap実行時にgccが使われるのでbuild-essentialを入れてます。
- スクリプト「mptcp.stap」を作成
#! /usr/bin/env stap %{ #include <linux/in.h> #include <linux/ip.h> %} function mptcpify () %{ if (CONTEXT->kregs->si == SOCK_STREAM && (CONTEXT->kregs->dx == IPPROTO_TCP || CONTEXT->kregs->dx == 0)) { CONTEXT->kregs->dx = IPPROTO_MPTCP; STAP_RETVALUE = 1; } else { STAP_RETVALUE = 0; } %} probe kernel.function("__sys_socket") { if (mptcpify() == 1) { printf("command %16s mptcpified\n", execname()); } }
- 本スクリプトを実行することで、socket()コール時に
IPPROTO_TCP
をIPPROTO_MPTCP
に入れ替えます。
- 本スクリプトを実行することで、socket()コール時に
tcpdump更新
必須ではありませんが、OSインストールで含まれる「tcpdump」はMPTCP v1に未対応なので対応している最新版に更新します。
- 最新版ソースをビルドしてインストール
$ sudo apt install libpcap-dev $ wget https://www.tcpdump.org/release/tcpdump-4.99.0.tar.gz $ tar xf tcpdump-4.99.0.tar.gz $ cd tcpdump-4.99.0/ $ ./configure $ make $ sudo make install $ sudo tcpdump --version tcpdump version 4.99.0 libpcap version 1.9.1 (with TPACKET_V3)
通信アプリインストール
Redhatの記事に倣い「ncat」、そして複数サブフロー送信の確認用に「iperf」を導入します。
$ sudo apt install ncat iperf
ここまででMPTCP通信に必要な一式をVMに導入できました。このVMイメージをクローンして”Client”と”Server”とします。
動作確認
さて、それでは”Client”と”Server”で通信させてみましょう。
単一サブフロー
まずはRedhatの記事の手順に従って、単一サブフローを試してみます。
Server
- 予めSystemTapで「mptcp.stap」を実行しておきます。
$ sudo stap -vg mptcp.stap Pass 1: parsed user script and 480 library scripts using 115216virt/101484res/7724shr/93660data kb, in 300usr/10sys/314real ms. Pass 2: analyzed script: 1 probe, 2 functions, 1 embed, 0 globals using 185872virt/172964res/8824shr/164316data kb, in 1300usr/100sys/1388real ms. Pass 3: using cached /root/.systemtap/cache/7e/stap_7e56f03f2ab5129c2608bd38944e60b3_1608.c Pass 4: using cached /root/.systemtap/cache/7e/stap_7e56f03f2ab5129c2608bd38944e60b3_1608.ko Pass 5: starting run.
- 次にncatをサーバとして起動します。
$ ncat -4lk 4321
- SystemTapを実行したコンソール上に以下の出力が追加されます。
command ncat mptcpified
Client
- Server同様に予めSystemTapで「mptcp.stap」を実行しておきます。
$ sudo stap -vg mptcp.stap Pass 1: parsed user script and 480 library scripts using 115220virt/101760res/8000shr/93664data kb, in 290usr/20sys/313real ms. Pass 2: analyzed script: 1 probe, 2 functions, 1 embed, 0 globals using 185876virt/173188res/9052shr/164320data kb, in 1270usr/90sys/1359real ms. Pass 3: using cached /root/.systemtap/cache/7e/stap_7e56f03f2ab5129c2608bd38944e60b3_1608.c Pass 4: using cached /root/.systemtap/cache/7e/stap_7e56f03f2ab5129c2608bd38944e60b3_1608.ko Pass 5: starting run.
- 実際にMPTCP通信しているかの確認用にtcpdumpを実行します。
$ sudo tcpdump -i any port 4321 -n -nn tcpdump: data link type LINUX_SLL2 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
- 次にncatをクライアントとして起動します。
$ ncat 192.168.30.20 4321
- SystemTapを実行したコンソール上に以下の出力が追加されます。
command ncat mptcpified
- tcpdumpを実行したコンソール上にmptcpオプション付のパケットが出力されます。
06:17:53.978527 enp0s3 Out IP 192.168.10.20.35644 > 192.168.30.20.4321: Flags [S], seq 804167068, win 64240, options [mss 1460,sackOK,TS val 1521023865 ecr 0,nop,wscale 7,mptcp capable v1], length 0 06:17:53.979072 enp0s3 In IP 192.168.30.20.4321 > 192.168.10.20.35644: Flags [S.], seq 2369976460, ack 804167069, win 65160, options [mss 1460,sackOK,TS val 251220121 ecr 1521023865,nop,wscale 7,mptcp capable v1 {0x7948c13f4e14a250}], length 0 06:17:53.979103 enp0s3 Out IP 192.168.10.20.35644 > 192.168.30.20.4321: Flags [.], ack 1, win 502, options [nop,nop,TS val 1521023865 ecr 251220121,mptcp capable v1 {0xcc3b1bc6232e7271,0x7948c13f4e14a250}], length 0
これで単一サブフローでMPTCP通信ができることが確認できました。
複数サブフロー
いよいよ、複数サブフロー(ネットワークインタフェース)を同時に使って”Client”と”Server”で通信させてみます。
Server
- 予めSystemTapで「mptcp.stap」を実行しておきます。
- 許容するサブフロー数を設定します。
$ sudo ip mptcp limits set subflow 2 $ sudo ip mptcp limits show add_addr_accepted 0 subflows 2
- iperfをサーバとして起動します。
$ iperf -s
- SystemTapを実行したコンソール上に以下の出力が追加されます。
command iperf mptcpified
Client
- 予めSystemTapで「mptcp.stap」を実行しておきます。
- それぞれのネットワークインタフェース指定でtcpdumpを実行します。
- enp0s3
$ sudo tcpdump -i enp0s3 dst port 5001 -n -nn tcpdump: data link type LINUX_SLL2 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
- enp0s8
$ sudo tcpdump -i enp0s8 dst port 5001 -n -nn | more tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on enp0s8, link-type EN10MB (Ethernet), snapshot length 262144 bytes
- enp0s3
- サブフローの設定をします。
- 今回試した時のClientの経路設定は次のとおりです。
$ ip route default via 192.168.10.1 dev enp0s3 proto static metric 100 default via 192.168.20.1 dev enp0s8 proto static metric 200 192.168.10.0/24 dev enp0s3 proto kernel scope link src 192.168.10.20 192.168.20.0/24 dev enp0s8 proto kernel scope link src 192.168.20.20
- enp0s3のデフォルトゲートウェイ設定の優先度が高いので、先程は何も設定しなくてもenp0s3がMPTCP通信に使われていました。
- enp0s8(192.168.20.20)をサブフローとして使うように設定します。
$ sudo ip mptcp limits set subflow 2 $ sudo ip mptcp limits show add_addr_accepted 0 subflows 2 $ sudo ip mptcp endpoint add 192.168.20.20 subflow $ sudo ip mptcp endpoint show 192.168.20.20 id 1 subflow
- 複数サブフローからパケットが送信されるようにポリシールーティングを設定します。
$ sudo ip rule add from 192.168.10.20 table 1 $ sudo ip route add 192.168.10.0/24 dev enp0s3 scope link table 1 $ sudo ip route add default via 192.168.10.1 dev enp0s3 table 1 $ sudo ip rule add from 192.168.20.20 table 2 $ sudo ip route add 192.168.20.0/24 dev enp0s8 scope link table 2 $ sudo ip route add default via 192.168.20.1 dev enp0s8 table 2 $ ip route show table 1 default via 192.168.10.1 dev enp0s3 192.168.10.0/24 dev enp0s3 scope link $ ip route show table 2 default via 192.168.20.1 dev enp0s8 192.168.20.0/24 dev enp0s8 scope link
- この設定を行わない場合は、送信元が192.168.20.20のパケットもenp0s3から送信されます。
- 公式wikiでさりげなく書いてあるのですが、割と見落としがちですね。
Configure the routing rules to use multiple subflows from multiple interfaces: Configure Routing
- 今回試した時のClientの経路設定は次のとおりです。
- iperfをクライアントとして起動します。
$ iperf -c 192.168.30.20
- SystemTapを実行したコンソール上に以下の出力が追加されます。
command iperf mptcpified
- tcpdumpを実行したそれぞれのネットワークインタフェースからmptcpオプション付のパケットが出力されるのが確認できます。
いくつかハマりポイントがありましたが、なんとかUbuntuで複数サブフローを使ったMPTCP通信を行うことができました。みなさんもぜひこれを機会にMPTCPを試してみてはいかがでしょうか。