Slide 1

Slide 1 text

Goで体感するMultipath TCP ― Go 1.24 時代の MPTCP Listener を理解する 2025/09/28 Takeru Hayasaka Go Conference2025 Day2

Slide 2

Slide 2 text

目的 - MPTCP の仕組みとポイントを押さえて、MPTCPの活用法を知る - アプリ側から: Go で MPTCP を活用する方法を知る - カーネル側から: Linux で MPTCP を活用する方法を知る 背景と目的 背景 - Go 1.24 からは、TCP Listen すると自動的にMPTCPとして動作する - そもそも MPTCP とは何か、Go1.24で何が変わるのかを知る必要がある

Slide 3

Slide 3 text

- サーバ側: OS が対応していれば 自動で MPTCP Listen - (OSが MPTCP 非対応なら自動で TCP フォールバック) - クライアント側: 何も変わらない。TCPでDialする場合はTCP接続 明示的にMPTCPを有効化した場合にはMPTCP 接続が可能 - 困る場合: 以下の様なケースだと、無効化しておく判断も必要 - MPTCPが非対応の特定のSocketOption依存 - 無効化方法:コードでoff ListenConfig.SetMultipathTCP(false) または環境変数でoff GODEBUG=multipathtcp=0 TL;DR(Go 1.24 で何が変わる?)

Slide 4

Slide 4 text

Agenda ● 概説: MPTCPとは ● アプリ: GoにおけるMPTCPサポートとその他のMPTCPの実装状況 ● カーネル: LinuxでMPTCPを使いこなすノウハウ

Slide 5

Slide 5 text

● 早坂 彪流 (Hayasaka Takeru|@takemioIO) ● 社会人5年目 ● さくらインターネット に所属 現在は、BBSakura Networksへ出向中 モバイルコアや仮想化基盤の開発・運用を担当 ○ 前職はゲーム会社でゲーム機のファームを書いていた ● 大学院生: JAIST M2 ○ 長期履修制度でM3までいる予定になっています ...😇 ● 好きなgo pkg ○ cilium/ebpf: ebpfが好き ○ kelseyhightower/envconfig: ディフォルト値が便利で好き(何もしなくていい) 自己紹介

Slide 6

Slide 6 text

概説: MPTCPとは

Slide 7

Slide 7 text

- Multipath TCP(MPTCP)とは、TCPのマルチパス化技術のことを指す - アプリ側は従来のTCPの使い勝手のまま、下層で複数のTCPコネクション (subflow)を束ねる仕組みを持っている。 - 例えば、複数のNetwork Interfaceを持つクライアントのTCPコネクション を束ねて通信の帯域を拡張し、冗長性を高めることができる - 詳しくはこちらを参照 - MPTCP v0: RFC6824 (2013年にRFC化) - MPTCP v1: RFC8684 (2020年にRFC化) - v0は実質的には廃止されて、v1でsubflowの追加の拒否等の状態を示すFlagが 追加されたり、ハンドシェイクが変わってるので何も考えずv1を読むのが良い Multipath TCP(MPTCP)とは

Slide 8

Slide 8 text

- シームレスなハンドオーバー: Wi‑Fi/セルラ間の切替でも接続を維持 - 帯域幅の集約: 複数の回線を束ねてスループット向上 - 最適経路選択: 経路の品質に応じて利用するsubflowをスケジューリング MPTCPで何が嬉しい?

Slide 9

Slide 9 text

シームレスなハンドオーバーの例 - AppleはSiriやApple Musicの接続でMPTCPの利活用をしている - サーバーにストリーミングして投げてその結果を受け取る。 基本的にモバイル端末は多くの人が移動しながら使ってるので、Wifiアクセス ポイントから遠ざかると接続切れて最終的に失敗してエラーを起こす体験の悪さがあった AppleMusicにおいてはMPTCPにしたことで停止する回数、時間が短くなって嬉しくなった cf. http://blog.multipath-tcp.org/blog/html/2018/12/15/apple_and_multipath_tcp.html cf. https://developer.apple.com/videos/play/wwdc2020/10111/

Slide 10

Slide 10 text

帯域幅の集約の例 Korea Telecommunicationはスマホで800Mbps overを出すために Wifi + 4GLTEの回線を束ねるサービスを提供している cf. http://blog.multipath-tcp.org/blog/html/2015/12/25/commercial_usage_of_multipath_tcp.html cf. https://www.ietf.org/proceedings/91/slides/slides-91-mptcp-5.pdf

Slide 11

Slide 11 text

実際どの様なモノが対応してるのか? - MPTCPv1の開発してるグループの 公式サイトにはLinuxで動くMPTCP対応 してるアプリが載っている - ネットワーク検証ツール: curlやiperf3 - Web プロキシ / サーバー: HAProxy, Apache server,Envoy - 仮想化ソフトウェア: QEMU - e.g. Livemigrationなどで使える - KVストア: Valkey - e.g. レプリケーションとかで使える cf. https://www.mptcp.dev/apps.html

Slide 12

Slide 12 text

- Multipath TCP Measurement Serviceという2021年の論文を眺めると、 数年で 20 倍に増加したという話が書かれている - 実際に彼らが継続的に測定して公開してるページを見てみると IPv4/MPTCPv1だけでも68万IP程度あることがわかる(2025/09現在) 実際どれくらい使われてるのか? cf. https://mptcp.io/

Slide 13

Slide 13 text

TCPとMPTCPの具体的な違いは? TCP - 単一のコネクション、1つのsubflowとも言える - 4Tuple:で定義 subflow A subflow B subflow C … MPTCP subflowを動的に 追加するシーケンスが必要? MPTCP - 複数のTCPコネクション(subflow)を束ねる - アプリからは1つの TCP socketに見える - TCPと強いコンパチビリティを持つ 疑問 - どの様にコンパチビリティを保ったままネゴシエーションするか? - 既存のsubflowコネクションとしてどの様に紐付けて集約するのか?

Slide 14

Slide 14 text

実際にMPTCPのネゴシエーションをみてみる - サーバーとネゴシエーションを行って以下の流れがどうなるのかをみる - ※細かいparam, その他のoptionやDataplane(e.g. DSS)で必要なパラメータの説明は省略 - 1. 最初のネゴシエーションで作られるsubflowはどうやってできるのか? - 2. 2つ目以降のsubflowはどの様に追加されるのか? - 3. 追加されるべきsubflowはどの様にして分かるのか? cf. https://datatracker.ietf.org/doc/html/rfc8684

Slide 15

Slide 15 text

復習: TCPのコネクションの確立例 SYN SYN+ACK ACK TCPの3Way Handshakeはこんな感じ SYN->SYN+ACK->ACKでお互いを確認してから通信する

Slide 16

Slide 16 text

step1: init subflow作成(subflow作成&鍵交換) my_key = β your_key = α MPTCPは(互換性のある形で)TCPの3Way Handshakeを拡張する ※SYN->SYN+ACK->ACKの形式で基本行なっている my_key = α your_key = β SYN+MP_CAPABLE[flags,keyβ] SYN+ACK+MP_CAPABLE[flags,keyα] ACK+MP_CAPABLE[flags,keyβ,keyα]

Slide 17

Slide 17 text

step1: init subflow作成(subflow作成&鍵交換) my_key = β your_key = α MPTCPの有効化ネゴシエーションをTryするために、 MP_CAPABLEというTCP Optionを与えて試行する その時、お互いのNodeたちは鍵生成と交換を行い、最初のsubflowが出来る ※TCP Optionなので、ダメならプレーンTCPにフォールバック my_key = α your_key = β SYN+MP_CAPABLE[flags,keyβ] SYN+ACK+MP_CAPABLE[flags,keyα] ACK+MP_CAPABLE[flags,keyβ,keyα]

Slide 18

Slide 18 text

step2: token生成をする my_key = β your_key = α mytoken = token(β) yourtoken = token(α) my_key = α your_key = β mytoken = token(α) yourtoken = token(β) 鍵交換したものからtokenを生成する。 これが実質的なセッション IDとして使われる。 ※お互いの鍵からtokenは生成可能なのでtokenの交換はしなくて良い ※token(x) := MSB32(sha256(x))

Slide 19

Slide 19 text

step3: HMACを作りMP_JOINしてsubflowを作る SYN+MP_JOIN[token(α), NonceB] SYN+ACK+MP_JOIN[HMACβ,NonceA] ACK+MP_JOIN[HMACα] my_key = β your_key = α mytoken = token(β) yourtoken = token(α) my_key = α your_key = β mytoken = token(α) yourtoken = token(β) 追加のsubflowをMP_JOINを利用して作成する。 MP_CAPABLEと同様にTCP Option上で実現してるので、 互換性のある形でTCPの3Way Handshakeを拡張してる ※HMAC(Secret, Message) = HMAC-SHA256(Secret, Message)

Slide 20

Slide 20 text

step3: HMACを作りMP_JOINしてsubflowを作る my_key = β your_key = α mytoken = token(β) yourtoken = token(α) my_key = α your_key = β mytoken = token(α) yourtoken = token(β) MP_JOINは新しいsubflowを既存のと紐づけるために、Tokenを指定しつつ、 最初に交換したKeyとNonceを材料にHMACを作って提示する。 相手も同じ計算で照合するので、正しい相手でなければJOINは成立しない ※Tokenは偽造しやすいので、HMACで正しいkeyの所持を証明してる ※HMAC(Secret, Message) = HMAC-SHA256(Secret, Message) SYN+MP_JOIN[token(α), NonceB] SYN+ACK+MP_JOIN[HMACβ,NonceA] ACK+MP_JOIN[HMACα]

Slide 21

Slide 21 text

2つのsubflowをもつMPTCPの接続が完成! my_key = β your_key = α mytoken = token(β) yourtoken = token(α) my_key = α your_key = β mytoken = token(α) yourtoken = token(β) SYN+MP_CAPABLE[flags,keyβ] SYN+ACK+MP_CAPABLE[flags,keyα] ACK+MP_CAPABLE[flags,keyβ,keyα] SYN+MP_JOIN[token(α), NonceB] SYN+ACK+MP_JOIN[HMACβ,NonceA] ACK+MP_JOIN[HMACα]

Slide 22

Slide 22 text

- MPTCPはTCPと強い互換性をもつプロトコル! - Appleを始めとした会社がモビリティの安定や帯域の集約のために使ってる - MPTCPがsubflowを追加するときはTCPと互換性のある形で拡張して行う - 1. 最初のネゴシエーションで作られるsubflowはどうやってできるのか? => MP_CAPABLEというTCP Optionを加えて実施し、subflowを作る - 2. 2つ目以降のsubflowはどの様に追加されるのか? =>MP_JOINを使って、既存subflowが存在してるセッションに紐づける ここまでのサマリー

Slide 23

Slide 23 text

- 先ほどのパターンは、以下の図のケースであれば実現可能 - HostAが持つ2つのAddress(A1,A2)を利用 - HostBのInitで接続したAddress(B1)に対して接続を行う - {A1->B1, A2->B1}のsubflow作成は初回の接続情報でTry可能 - 課題: HostAがHostBの別アドレスに接続したい場合の方法 - e.g. A1->B1へのTupleを作りたい - そもそもホストBが持ってるAddressを HostAは知らないので教える必要がある - では別のIfaceにあるAddressB2に対して subflowを作ることを考えてみよう サーバーが持つ別のAddrを使うには? cf. https://datatracker.ietf.org/doc/html/rfc8684

Slide 24

Slide 24 text

step4: ADD_ADDRで別のアドレスを通知する ADD_ADDR[Addr=B2,echo=0,HMACβ] my_key = β your_key = α mytoken = token(β) yourtoken = token(α) my_key = α your_key = β mytoken = token(α) yourtoken = token(β) ADD_ADDRは、自分が持っているaddressに関して通知をする仕組み サーバーがB2というAddrについて通知をクライアントに行う この時Ackとして受信したADD_ADDRをechoする ※受け取り側だけが正しく検証できれば問題ないので、中身はecho=1にするだけでecho replyする ちなみに同様にREMOVE_ADDRというモノもあります。 自分が持てなくなったアドレスがあるときに教えてあげましょう:) newAddr=B2 ADD_ADDR[Addr=B2,echo=1,HMACβ]

Slide 25

Slide 25 text

step5: Addr=B2に対してsubflowを作成する my_key = β your_key = α mytoken = token(β) yourtoken = token(α) my_key = α your_key = β mytoken = token(α) yourtoken = token(β) 再びMP_JOINすればsubflowが無事作られて、 3つのTCPセッションを実質束ねてる状態になった! newAddr=B2 SYN+MP_JOIN[token(α), NonceB] SYN+ACK+MP_JOIN[HMACβ,NonceA] ACK+MP_JOIN[HMACα]

Slide 26

Slide 26 text

MPTCPでsubflowを作る方法のざっくりまとめ - MP_CAPABLEというTCP Optionを加えて実施し、(init)subflowを作る - MP_JOINを使って、新規のsubflowを作成し、既存のsubflowが存在してる セッションに紐づける - ADD_ADDRを使って、subflowを作ることができるアドレスを通知できる

Slide 27

Slide 27 text

MPTCP と Middlebox の相性問題 - FWやLB,NATなどは一般にMiddleBoxと呼ばれている - 伝送ポリシーを強制的に適用するためのNetwork装置 - Middleboxが MPTCP Optionを理解できず恩恵が得れないパターンも多い - FW (Firewall) - TCP Option を削除 -> MPTCP の制御情報が消える(TCPフォールバックされる) - NAT (Network Address Translation) - NATセッションが切れてしまう場合 add_addrなどでコントロールする必要がある - LB (Load Balancer) - 多くの L4LB はステートレスに動作(特にクラウド環境など...) - Consistent Hash で Per-Flow にサーバーへ振り分ける - MPTCPが分からない装置から見たら普通のTCPなので、別々の通信Flowと見做してしまう - 高確率でMP_JOIN を別のサーバーに送ってしまうので、2本以上のsubflowは持てない😇

Slide 28

Slide 28 text

LBとMPTCP相性問題のワークアラウンド - cf. draft-duchene-mptcp-load-balancing-01 - 1. LB経由でServerに繋ぎに行く - 2. ServerはMP_CAPABLEをClientと交換し、 ネゴシエーションを成功させる - 3. ADD_ADDRで持ってるAddressをclientに通知 - 条件: 通知したアドレスはClientから直接到達可能 - 4. clientはそのAddressに対して新規接続を行う - Clientは通知してきたアドレスのみに対してSubflowを 作る戦略にしてあげる(LBを2回以上通せないため) cf. MultipathTCP + 分散型ロードバランサー(2) · hrntknr's blogの図を改訂して利用 Client Server LB subflow init subflow 203.0.113.1 (VIP) 10.0.0.100 203.0.113.2

Slide 29

Slide 29 text

アプリ: GoにおけるMPTCPサポートと その他のMPTCPの実装状況

Slide 30

Slide 30 text

- Go 1.21(2023): MPTCP サポートが入った。明示的な有効化のみ使用OK - cf. https://github.com/golang/go/issues/56539 - 明示的な有効化方法は2つで、「環境変数 or コード(後述)」の書き換え - 環境変数: GODEBUG=multipathtcp= - "0": 全体オフ - "1": Dial+Listen Enable - "2": Listen のみEnable - "3": DialのみEnable - cf.https://github.com/golang/go/blob/d7a38adf4c81f0fa83203e37844192182b2268 0a/doc/godebug.md?plain=1#L215 Goでのサポートタイムライン: 1/2

Slide 31

Slide 31 text

- Go 1.24(2025): TCP ListenのディフォルトがMPTCPのListenになった - GODEBUG=multipathtcp の既定値が 2(ListenのみOnに) - ※Go 1.23ではmultipathtcp=1を設定しても正しく動いてない問題があった が、無事修正済み - (バックポートはされない方針なので1.24以降を使う必要がある) - cf.https://github.com/golang/go/commit/97ae1817fb22913f0bbfa0be2b8181e806c 26853 Goでのサポートタイムライン: 2/2

Slide 32

Slide 32 text

x86のレジスタのマッピング例 呼び出し規約に合致していることがわかる // Server側 func (*ListenConfig) SetMultipathTCP(enabled bool) func (*ListenConfig) MultipathTCP() bool // Client側 func (*Dialer) SetMultipathTCP(enabled bool) func (*Dialer) MultipathTCP() bool MPTCPの有効・無効は上記の様な コードを利用して制御することも可能 32

Slide 33

Slide 33 text

x86のレジスタのマッピング例 呼び出し規約に合致していることがわかる // Client側 d := &net.Dialer{} d.SetMultipathTCP(true) c, err := d.Dial("tcp", "host:port") // Server側 lc := &net.ListenConfig{} lc.SetMultipathTCP(true) ln, err := lc.Listen(context.Background(), "tcp", ":8080") 有効化の利用例 33

Slide 34

Slide 34 text

つまりGo1.24でMPTCPがデフォルトになるとは - クライアントがもしもMPTCPで繋いできた時に勝手に対応する話です。 - 無理だったらTCPにフォールバック! - 既存のコード修正は基本は一切不要で、何もしなくてもOK - 前述のアプリやネットワークのコンパチビリティがしっかりしてるため、 (おそらく大体の場合)クライアントでもMPTCPを喋らせても問題さそう。 - ちなみに...MPTCPの開発公式はディフォルトで有効にするのをお勧めしてる - cf.https://www.mptcp.dev/faq.html#why--when-should-mptcp-be-enabled-by-de fault

Slide 35

Slide 35 text

- MPTCPはだいたいの主要なSocketOptionの実装を満たしているが、、、 一部足りてない機能も存在している - その場合、set_sockoptをした時にENOPROTOOPTを返すケースがある - 実際TCP_MD5 Optionが動かなくて GoBGP というBGPdが動かなくて困った ケースがあり、GoBGP側でSetMultipathTCP(false)する対応が入った - このパターンのケースはLinux側にサポートが入らない限り有効化することができない - cf. https://github.com/multipath-tcp/mptcp_net-next/issues/575 - 他のサービスでも動かなくなったと言う報告もある ただし、Socket Optionで特殊なモノは要注意 cf.https://github.com/VictoriaMetrics/VictoriaMetrics/issues/8981 cf.https://github.com/golang/go/issues/74643

Slide 36

Slide 36 text

- Damien Neil(@Go Team)的には、ロールバックすると混乱を招くので、 もし問題になりそうだったらアプリで無効化しておいてねという感じに - ListenConfig.SetMultipathTCP(false) - 言及はないが、MPTCP使わなくていいなら、GODEBUGやKernelで MPTCPを無効化してTCPフォールバックする手もある - GODEBUG=multipathtcp=”0” - sudo sysctl net.mptcp.enabled=0 ちなみに...ワークアラウンドとしては cf. https://github.com/golang/go/issues/74643#issuecomment-3175797473

Slide 37

Slide 37 text

x86のレジスタのマッピング例 呼び出し規約に合致していることがわかる static bool mptcp_supported_sockopt(int level, int optname) { if (level == SOL_IP) {/* 中略 (Multicast関連等なので基本的に問題にならなさそう ) */} if (level == SOL_IPV6) {/* 中略 (Multicast関連等なので基本的に問題にならなさそう ) */} if (level == SOL_TCP) { /* TCP_MD5SIG, TCP_MD5SIG_EXT are not supported, MD5 is not compatible with MPTCP */ /* TCP_REPAIR, TCP_REPAIR_QUEUE, TCP_QUEUE_SEQ, TCP_REPAIR_OPTIONS, * TCP_REPAIR_WINDOW are not supported, better avoid this mess */ ... } } ちなみにsockoptで問題になるかどうかはlinux kernelの mptcp_supported_sockoptを眺めると対応状況がわかって便利 最新kernelのコメント的には(TCP Optにおいて) CRIUとBGPd関連のアプリを使う時のみ影響が出そう。 37 cf. https://github.com/torvalds/linux/blob/cec1e6e5d1ab33403b809f79cd20d6aff124ccfe/net/mptcp/sockopt.c#L434

Slide 38

Slide 38 text

- MPTCP socketを利用することで動作可能になる - socket(AF_INET(6), SOCK_STREAM, IPPROTO_MPTCP); - つまり基本的にsocketが使えるならどれでも動く - cf. https://github.com/mptcp-apps/mptcp-hello - c, elixir,erlang,python,perl,rustこの辺りの例が載っている。 - ちなみにmacosでも動作するため、objc, swiftなどでも動く - let connection = NWConnection(to: server, using: { let p = NWParameters.tcp; p.multipathServiceType = .handover; return p }()) - macos環境だとベースのKernelがOSSになってるのでMPTCPの実装が見れる - sysctl -a | grep mptcp - cf. http://github.com/apple-oss-distributions/xnu 他のプログラミング言語でのMPTCP利用

Slide 39

Slide 39 text

- 既存のソフトウェアのTCP Socketを強制的にMPTCP Socketに書き換えて、 動作させる仕組みがある。これでrebuildせずともMPTCPの恩恵が得れる。 - mptcpize: LD_PRELOAD を使って、libcがもつ socket() や connect() の 標準のlib関数を横取りして、MPTCP化させることができる - mptcpize run - cf.https://github.com/multipath-tcp/mptcpd/blob/c1951db79f1c7103874061df6b220fb00 a669d62/src/mptcpize.c#L3 - mptcpify: Linux v6.6~で使える, eBPFを使って強制的にMPTCP化させる Androidの様な環境設定やアプリ起動方法を変えづらい環境とかであっても使える 様に改善したモノ - python ./mptcpify.py -t - BPF_CGROUP_INET_SOCK_CREATE でhookして、書き換えている - cf. https://github.com/multipath-tcp/mptcp_net-next/issues/79 - cf. https://github.com/iovisor/bcc/pull/5274 任意のソフトウェアのMPTCP対応

Slide 40

Slide 40 text

カーネル: LinuxでMPTCPを使いこなす ノウハウ

Slide 41

Slide 41 text

- Linux v5.6 から MPTCPv1がサポートされてる - それ以前はv0(v3.4~)のサポートがされている - 公式ページを見ると、どのLinux Distributionで いつぐらいに対応してるのかが分かる - Ubuntuでは22.04, RaspberryPiなら231004 と基本サポートはどのDistributionでも行われて いることが分かる - ※v5.6以降それぞれのマイナーバージョンごとに色々と 機能が追加されてるので厳密なサポートはKernel バージョンごとに異なる - sysctl -w net.mptcp.enabled=1 で MPTCP Socketが作れる様になる LinuxにおけるMPTCP サポート cf. https://www.mptcp.dev/apps.html

Slide 42

Slide 42 text

- パスマネージャ: subflowの接続の増減を決定する - e.g. initのネゴシエーション終了時に、ADD_ADDRで追加のアドレスを通知することが できるが、これはパスマネージャーの働きで可能になっている - また、MP_JOINする対象をどの様に決めるかのストラテジーをコントロールするのも パスマネージャーのお仕事 - パケットスケジューラ: どのsubflowを介して送信されるかを決定する - 複数存在するsubflowに対してどれくらいパケットを流し込むかを決めている - 実質的に通信性能を出すための話なので、輻輳制御アルゴリズムの話なども関わります - cf. MPTCPのスケジューラーをeBPFで書けるようになった話 - BBSakura Networks Blog - eBPFで改造可能になった話を以前書いたので興味あればぜひ MPTCPを成り立たせるための機能

Slide 43

Slide 43 text

パスマネージャーを使ったsubflowの追加方法 - 手動でsubflowを増やす - 自動でMP_JOINを行う例 - sudo ip mptcp limits set subflow 2 add_addr_accepted 2 - 自分から作れる subflow は 3 本まで(= init1 + subflow2 = 最大3本の同時フロー) - 相手から受け入れる追加アドレスは 2 個まで - subflow: 1セッションで最大何本までsubflowを作るか - add_addr_accepted: 1セッションのピア受け入れるADD_ADDRの数 - MP_JOINする例(手動でインタフェースをsubflowとして追加できる) - sudo ip mptcp endpoint add 198.51.100.20 dev wlan0 subflow - サーバが受け入れ可能 (add_addr_accepted > 0) ならsubflowが追加される - アドレスの通知をする - ADD_ADDRの例(追加のアドレスを通知する) - sudo ip mptcp endpoint add 192.0.2.10 dev eth1 signal - signalではなくbackupにすると予備のパスとして広告しないで待機させてくれる 例えばメインのsubflowが死んだ時に切り替えてくれる

Slide 44

Slide 44 text

LBとMPTCP相性問題のワークアラウンド - cf. draft-duchene-mptcp-load-balancing-01 - 1. LB経由でServerに繋ぎに行く - 2. ServerはMP_CAPABLEをClientと交換し、 ネゴシエーションを成功させる - 3. ADD_ADDRで持ってるAddressをclientに通知 - 条件: 通知したアドレスはClientから直接到達可能 - 4. clientはそのAddressに対して新規接続を行う - Clientは通知してきたアドレスのみに対してSubflowを 作る戦略にしてあげる(LBを2回以上通せないため) cf. MultipathTCP + 分散型ロードバランサー(2) · hrntknr's blogの図を改訂して利用 Client Server LB subflow init subflow 203.0.113.1 (VIP) 10.0.0.100 203.0.113.2 再掲

Slide 45

Slide 45 text

MPTCPをLBに対応させる例 - 初回の宛先はLBのVIPなので、そこに別のsubflowを流そうとするとMP_JOIN が失敗する。そこでLBを通るVIPに新規subflowを作らないことにする. そこで、Serverで以下のことを実現したい - 1. 初回Subflowを行ったpathにはMP_JOINをさせない要求設定を渡す - 2. Clientから直接到達可能な自サーバのaddrをADD_ADDRで渡す 1. sysctl -w net.mptcp.allow_join_initial_addr_port=0 2. ip mptcp endpoint add dev signal 公式でも同様の紹介がなされている ※公式では、LB配下にサーバーを並べれるように変更されていて、 その代わりにLB->Server間にはStaticなPortForwardの様なことを 入れれるLB実装の前提で書いている。どちらも肝はclientから Serverに対して確実に疎通できるaddr:portの組み必要なのがわかる cf. https://www.mptcp.dev/load-balancer.html Client Server LB subflow init subflow 203.0.113.1 (VIP) 10.0.0.100 203.0.113.2

Slide 46

Slide 46 text

まとめ

Slide 47

Slide 47 text

公開用資料につけたオマケたち - Appendix-A: MPTCPのsubflowを実際に作る - cf. https://github.com/takehaya/mptcp_playground - MPTCPをちょっと触るためplaygroundを用意しました。 - MPTCPが有効な簡単なServer/ClientのGoのbin - 細かくsubflowを制御してみる例 - Appendix-B: MPTCP自体をさらに知りたい人向け - Appendix-C: MPTCP Option Format - Appendix-D: LB配下にMPTCP Aware Nodeを置く例

Slide 48

Slide 48 text

Appendix-A: MPTCPのsubflowを実際に作る https://github.com/takehaya/mptcp_playground MPTCPをちょっと触るためplaygroundを用意しました 以下の様な内容が含まれています。 - MPTCPが有効な簡単なServer/ClientのGoのbin - 細かくsubflowを制御してみる例 - 先ほどのLBで必要な例なども確かめれます。 持ち帰れる知見お土産セットなのでぜひご覧ください (starとかもよければぜひ。。。🥺)

Slide 49

Slide 49 text

Appendix-B: MPTCP自体をさらに知りたい人向け - SIGCOMM’20 Tutorial Multipath Transport Protocols - MPTCPのLinux版実装を作ってるUCLouvainの人たちがSIGCOMMでやってたtutorial Workshopの資料。網羅的で神。 - https://mptcp-apps.github.io/mptcp-doc/index.html - ユースケースからどういう思想なのかがざっくりまとまってるMPTCPの開発者向けの チュートリアルドキュメント - FOSDEM 23: MPTCP Upstream - Linuxを中心に実装において最近どんな感じなのか書いてる。過去の歴史パートが面白い。 - https://github.com/multipath-tcp/mptcp_net-next/wiki - 開発者ドキュメントwiki, ChangeLogにv5.6~からどういうものがupdateされてるのかが 載ってる。例えばスケジューラーのeBPFのサポートとか最近になったことが窺える。 - ちなみに最新の開発の進行具合はProjectBoardを見るのがおすすめ

Slide 50

Slide 50 text

まとめ - MPTCPってどの様なもの?という紹介をしました。 - Go1.24からTCP Listenでディフォルトで有効化されるが基本問題ないよ! - ただし、変わったSocketOptionを使う場合は気をつけてね! - ダメだったらSetMultipathTCP(false)にしておけばOK。 - MPTCPで使いたいTCP Option対応がLinuxKernelでされるまで動かないため - Linux方面から色々成熟してきてるので今後のサポートに期待! ヨロシク! 


Slide 51

Slide 51 text

Enabling a Connected Future.

Slide 52

Slide 52 text

Appendix-C MPTCP Option Format

Slide 53

Slide 53 text

MPTCP Option Format - RFC8684 - MPTCP はTCP Option として表現されている - MPTCPの制御情報を送る仕組み - Kind: 30(MPTCP) - Subtype: MP_CAPABLE みたい なOptionが入る 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +---------------+---------------+-------+-----------------------+ | Kind | Length |Subtype| | +---------------+---------------+-------+ | | Subtype-specific data | | (variable length) | +---------------------------------------------------------------+

Slide 54

Slide 54 text

Multipath Capable (MP_CAPABLE) Option - Kind: MPTCP(30) - Length - Subtype: MP_CAPABLE - Version: v1 - flags - A: Csum required - B: Extensibilityは0 - C: Do not attempt to establish new subflows to the source address. - D-G: Unassigned - H: HMAC-SHA256 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +---------------+---------------+-------+-------+---------------+ | Kind | Length |Subtype|Version|A|B|C|D|E|F|G|H| +---------------+---------------+-------+-------+---------------+ | Option Sender's Key (64 bits) | | (if option Length > 4) | | | +---------------------------------------------------------------+ | Option Receiver's Key (64 bits) | | (if option Length > 12) | | | +-------------------------------+-------------------------------+ | Data-Level Length (16 bits) | Checksum (16 bits, optional) | +-------------------------------+-------------------------------+

Slide 55

Slide 55 text

Join Connection (MP_JOIN) Option (for Initial SYN) - Kind: MPTCP(30) - Length: 12 - Subtype: MP_JOIN - flags - B: バックアップパスにす るかどうか(1 or 0) - AddressID: NATでアドレスが変 わってもわかる様にするID - Recv TokenID - Send Rand(Nance) 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +---------------+---------------+-------+-----+-+---------------+ | Kind | Length = 12 |Subtype|(rsv)|B| Address ID | +---------------+---------------+-------+-----+-+---------------+ | Receiver's Token (32 bits) | +---------------------------------------------------------------+ | Sender's Random Number (32 bits) | +---------------------------------------------------------------+

Slide 56

Slide 56 text

Join Connection (MP_JOIN) Option (for Responding SYN/ACK) - Kind: MPTCP(30) - Length: 16(長さで区別する) - Subtype: MP_JOIN - flags - B: バックアップパスにす るかどうか(1 or 0) - AddressID: NATでアドレスが変 わってもわかる様にするID - Sender HMAC - Send Rand(Nance) 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +---------------+---------------+-------+-----+-+---------------+ | Kind | Length = 16 |Subtype|(rsv)|B| Address ID | +---------------+---------------+-------+-----+-+---------------+ | | | Sender's Truncated HMAC (64 bits) | | | +---------------------------------------------------------------+ | Sender's Random Number (32 bits) | +---------------------------------------------------------------+

Slide 57

Slide 57 text

Join Connection (MP_JOIN) Option (for Initiator's First ACK) - Kind: MPTCP(30) - Length: 24(長さで区別する) - Subtype: MP_JOIN - Sender HMAC(160bit) 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +---------------+---------------+-------+-----------------------+ | Kind | Length = 24 |Subtype| (reserved) | +---------------+---------------+-------+-----------------------+ | | | | | Sender's Truncated HMAC (160 bits) | | | | | +---------------------------------------------------------------+

Slide 58

Slide 58 text

Add Address (ADD_ADDR) Option - Kind: MPTCP(30) - Length - Subtype: ADD_ADDR - flags - E: echo flag(replyで1) - Address (v4:4oct or v6:16 oct) - Port(2oct) - HMAC 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +---------------+---------------+-------+-------+---------------+ | Kind | Length |Subtype|(rsv)|E| Address ID | +---------------+---------------+-------+-------+---------------+ | Address (IPv4: 4 octets / IPv6: 16 octets) | +-------------------------------+-------------------------------+ | Port (2 octets, optional) | | +-------------------------------+ | | Truncated HMAC (8 octets, if E=0) | | +-------------------------------+   | |   +-------------------------------+

Slide 59

Slide 59 text

Data Sequence Signal (DSS) Option - Kind: MPTCP(30) - Length - Subtype: DSS - flags - F: datefin(データの終わ りを示す) - m: DSNが8oct - M:DSN,SSN,Dlvl,Csumが 存在する - a:Data Ackがある - A:DataAckは8oct - Data ACK - Data SeqNum - Subflow SeqNum - Data Level - Csum 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +---------------+---------------+-------+----------------------+ | Kind | Length |Subtype| (reserved) |F|m|M|a|A| +---------------+---------------+-------+----------------------+ | Data ACK (4 or 8 octets, depending on flags) | +--------------------------------------------------------------+ | Data Sequence Number (4 or 8 octets, depending on flags) | +--------------------------------------------------------------+ | Subflow Sequence Number (4 octets) | +-------------------------------+------------------------------+ | Data-Level Length (2 octets) | Checksum (2 octets) | +-------------------------------+------------------------------+

Slide 60

Slide 60 text

Appendix-D LB配下にMPTCP Aware Nodeを置く例

Slide 61

Slide 61 text

LBとMPTCP相性問題のワークアラウンドその2:1/3 - なるべく普通のMPTCPのLB対応を考えたい - ありそうな要件 - LB配下にServerを全て置きたい - セキュリティ的にGlobalアドレスを直接晒したくない - 単一のNICで実現したい - コンテナとかで扱いたいためワンアーム構成にしたい - 考え方 - Serverに対して確実に疎通できる接続Endpointが提供 出来れば2本目以上のSubflowが作れる - 登場人物 - Client: 2つのAddrを持つ - LB: 1つのVIPを持つ - Server: 1つのAddrを持つ Client Server LB subflow init subflow 203.0.113.1 (VIP) 10.0.0.100 203.0.110.1 203.0.110.2

Slide 62

Slide 62 text

LBとMPTCP相性問題のワークアラウンドその2:2/3 - 導入したい制約 - 1. 初回Subflowを行ったpathにはMP_JOINをさせない要求設定を渡す - 2. Clientから直接到達可能な自サーバのaddrとportをADD_ADDRで渡す - 3. LBに対してaddr:portが来た時はサーバーに対してportforward - 1.init subflowはECMPしてLBしてサーバー接続して ネゴシエーションを完了させる - 2. サーバーはVIPとportをクライアントに通知 - 事前に通知するportを決めて、そのportに対応するサーバーは一台 だけに絞っておく、それでadd_addrする - 3. クライアントは、subflowを作るために通知されたVIP とportでLBに繋ぎに行ってサーバーと接続して ネゴシエーションを完了させる Client Server LB subflow init subflow 203.0.113.1 (VIP) 10.0.0.100:80 203.0.110.1 203.0.110.2 10.0.0.100:8081

Slide 63

Slide 63 text

LBとMPTCP相性問題のワークアラウンドその2:3/3 Client - 実際にどの様に設定を入れる考える - 1. sysctl -w net.mptcp.allow_join_initial_addr_port=0 - 2. ip mptcp endpoint add dev [ port ] signal - 3. port forwardを追加する - nginxのようなものだと、一旦終端してTCP Optionを無くしてしまうの で、IPVSのようなパススルー型のL4LBを使う必要がある - 他のクラウドLBでもupstream一つに絞ってproxypassで良いけど、 HealthCheckに関しては別のportを指定できるものである必要がありそう - ここで考えた方法はLB配下に置くことでも実現できるが、 サーバーNodeのスケールという観点だとやや厳しいので、 この部分には課題がありそうに見える... Server LB subflow init subflow 203.0.113.1 (VIP) 10.0.0.100:80 203.0.110.1 203.0.110.2 10.0.0.100:8081

Slide 64

Slide 64 text

e.g. IPVSの設定例 ip netns exec LB ipvsadm -C ip netns exec LB ipvsadm -A -t 203.0.113.1:80 -s rr ip netns exec LB ipvsadm -a -t 203.0.113.1:80 -r 10.0.0.100:80 -m ip netns exec LB ipvsadm -d -t 203.0.113.1:80 -r 10.0.0.101:80 -m ip netns exec LB ipvsadm -A -t 203.0.113.1:8081 -s sh ip netns exec LB ipvsadm -A -t 203.0.113.1:8082 -s sh ip netns exec LB ipvsadm -a -t 203.0.113.1:8081 -r 10.0.0.100:8081 -g ip netns exec LB ipvsadm -a -t 203.0.113.1:8082 -r 10.0.0.101:8082 -g ip netns exec LB sysctl -w net.ipv4.conf.all.accept_local=1 ip netns exec LB sysctl -w net.ipv4.conf.veth-lb-rt.accept_local=1 ip netns exec LB sysctl -w net.ipv4.conf.veth-lb-br.accept_local=1 例えばあるVIP配下にサーバーが2つある場合だとこのような例になる (Netnsで雰囲気がわかるようにしてる)