Multipath TCPとL4バランシングのI-D

一年ほど前に書いた「MPTCP(MultiPath TCP)と負荷分散ってどうなんだろう」関連の話。


先日、まさに「Multipath TCP behind Layer-4 loadbalancers」と言うI-Dが出てました。iOSのsiriではMPTCPを使っているようですが、Appleの方もAuthorに含まれています。


I-D中にmillions of MPTCP connectionsという表現も出てきており大規模なインフラが伺えます。

問題


図のように、L4バランサの後ろにReverse Proxyが居てTCPの終端を行っています。Reverse ProxyがMPTCPをしゃべるイメージです。L4バランサは(送信元IPや送信元ポートを見て)TCPの通信を適切Reverse Proxyに割り振ります。MPTCPでは異なるIPからTCP subflowsが来ますが、MPTCPコネクションを受けてるRevese Proxyにちゃんと割り振る必要があります。


この時、L4バランサはTCP subflowsを同一のReverse Proxyに振り分ける必要があります。L4バランサがMPTCPを考慮するようにし、MP_CAPABLEハンドシェイクを見てトークンとバックエンドサーバ(Reverse Proxy)を紐付けておく必要があります。その紐付けに基づいてSYN+MP_JOINを適切なバックエンドサーバに振り分ける事ができます。


しかし、複数のL4バランサがあると状況が異なってきます(VIPなのか、経路が違うと別のL4バランサに繋がる場合がある想定のようです)。同じMPTCP コネクションに属するTCP subflowsが別のL4バランサにルーティングされる可能性があります。そうなると、トークンとバックエンドサーバ(Reverse Proxy)の紐付け情報を共有する必要が出てきます。他の全てのL4バランサと情報を供するのは非常に実装が複雑ですし、レイテンシを引き起こします。


もう一つの問題として多くのサーバがあると、違うMPTCPコネクションで同一のトークンが生成される可能性があります。数百万ものMPTCPコネクションを扱っていると、その確率は高くなります。トークンが衝突するとSYN+MP_JOINを適切なReverse Proxyに振り分けることができなくなってしまいます。

提案

基本的なアイディアは、L4バランサがトークンからバックエンドサーバを推測できるようにするというものである。

  • バックエンドサーバ毎にある整数のレンジを決める
  • L4バランサとYと言う秘密値を共有する。


バックエンドサーバは自身に割り当てられた整数レンジからXを一つ選び、Yで暗号化しトークンとする。これで、L4バランサはトークンよりバックエンドサーバを推測することが出来る。さらに、各バックエンドサーバでトークンが衝突することもなくなる。


このアプローチを用いて、問題を解決する別々の手法を2つ提案する。MP_CAPABLEハンドシェーク中に相手にトークンを伝達する方法が異なります。

Explicitly announcing the token

一つは単純に、MP_CAPABLEハンドシェイク中に平文で通知する方法です。しかし、これを可能とするためにはMP_CAPABLEハンドシェイクのワイヤフォーマットを変更する必要があります。トークンを付加するために、MP_CAPABLEを4バイト増やします。

mptcp-syncookiesがデプロイされていれば、TCPオプションの40byte中36byteが使用されます。この残りの4byteを使用してSYNセグメント中でトークンを通知します。

        SYN + MP_CAPABLE_SYN (Token_A)
    ------------------------------------->
      (the client announces the 4-byte locally
       unique token to the server in the
       SYN-segment).


       SYN/ACK + MP_CAPABLE_SYNACK (Token_B, Key_B)
    <-------------------------------------
      (the server replies with a SYN/ACK announcing
       as well a 4-byte locally unique token and a 4-byte key)


       ACK + MP_CAPABLE_ACK (Key_A, Key_B)
    -------------------------------------->
       (third ack, the client replies with a 12-byte Key_A
        and echoes the 4-byte Key_B as well).
Changing the token generation

もう一つの方法は、MP_CAPABLEハンドシェイクに大きな変更は加えません。


ホストによって生成されるMPTCPキーの上位32bitをZと定義し、下位32bitをKと定義すると、K鍵で32bitブロック暗号方式で暗号化されたZととしてトークンを生成する。


MPTCPキーのサイズは変化せず、コネクション毎に異なっているKとZを連結したものになり、64bitのランダムなbit列を提供する。


このように、バックエンドサーバはランダムに生成したK及び、Kでトークンを暗号化したものであるZを単純に生成するだけでよい。