This document is a translation of `Why TCP Over TCP Is A Bad Idea', which was written by Olaf Titz.

この文書は、Olaf Titz氏による `Why TCP Over TCP Is A Bad Idea'の翻訳です。

なぜTCP Over TCPは悪いアイデアか

IPトンネリングアプリケーションにしばしば用いられるアイデア は、IPパケットを(モデムラインのような)ストリーム転送に適した フォーマットにカプセル化する、PPPのようなプロトコルをTCPベー スの接続上で実行することです。これは、すでにいくつかの推奨 (Linux HOWTOや、私自身のウェブサイトや、もちろん他にもいく つか)がある、PPP over SSHの実行により、容易なソリューション となるでしょう。それは、データグラムベースの圧縮が効率的な 制約で困難となる一方で、任意のIPトラフィックを圧縮する容易 な方法となります。

しかし、不運にも、それは上手くいきません。長い遅延や頻繁な接続の中 断が予想されます。ここにその理由を述べます。

TCPの再送アルゴリズム

TCPはデータストリームを、個々のIPデータグラムとして送信される セグメントに分割します。セグメントは、ストリーム 中のバイトに番号を付けるシーケンス番号(sequence number)と、他方に最後に受信されたシーケンス番号を伝え る受領番号(acknowledge number)を搬送します。[RFC793]

IPデータグラムは、失われたり、重複したり、順番が変わってしまう かもしれないため、シーケンス番号がストリームの再組み立てに利用 されます。受領番号は送信者に間接的にセグメントが失われたかどうかを 伝えます。つまり、ある一定の時間に最近送信されたセグメントに対する 受領番号が届かなかった場合、送信者はパケットが失われたと仮定し、 セグメントを再送します。

同様のアプローチを利用した他の多くのプロトコル(ほとんどが比較的一定な 帯域幅のラインで利用されるように設計されています)が、固定されているか 設定可能な「一定の時間」を持っています。しかし、インターネット上では、 帯域幅や遅延や損失率のようなパラメータは、一つの接続と他の接続で、 あるいは一つの接続でさえ違う時間で比較すると、非常に異なっています。 秒単位のある一定のタイムアウトは高速なLANでは不適切になるでしょうし、 また、輻輳した国をまたぐようなリンクでも不適切でしょう。実際、 それは輻輳を増大し「メルトダウン」として知られる効果を引き起こします。

このため、TCPはタイミング関係のパラメータのために適応性のあるタイムアウトを 利用しています。TCPでは保守的な見積りからスタートし、受信した各セグメント毎に 動的にパラメータを変化させます。実際に利用されているアルゴリズムは[RFC2001] で説明されています。詳細はここでは重要ではありませんが、一つだけ重要なことが あります。あるセグメントがタイムアウトした場合、次のタイムアウトは増加 されます。(タイムアウトは指数関数的に増加されます。これはメルトダウンを 避けるためのものです。)

TCPのスタッキング

TCPのタイムアウトポリシーはインターネット上で多様な接続の特性に対して有効に 機能しています。なぜならTCPは接続を切らないように非常によく努力し、タイムアウト は数分の単位にまで増加されるからです。これは、無人の大量のデータ転送に対して、 何と気のきいたことでしょう。(対話的なアプリケーションに対しては、このような 遅い接続はもちろん望ましくないもので、たぶんユーザは接続を切断するでしょう。)

この信頼性のための最適化はTCP接続を他のTCP接続の上に積み重ねると破綻します。 それはTCPの設計者たちが予期したものではありません。しかし、そのような状況は PPP over SSHやその他のTCPベースのプロトコルを実行すると発生します。 なぜならPPPでカプセル化されたIPデータグラムがこのようにTCPベースのペイロード を搬送するからです。

(TCP over IP over PPP over SSH over TCP over IP)

上位のレイヤーと下位レイヤーではTCPは異なるタイマーを持つことに注意して ください。上位のレイヤーの接続が高速にスタートした時、そのタイマーも高速 です。その際、下位レイヤーがより遅いタイマーを持っているかもしれません。 おそらく、低速であるか信頼性の低いベースとなる接続の時からの痕跡として。

この状況でベースとなる接続がパケットを失い始めた時、どんなことが起こるか 想像してみましょう。下位レイヤーのTCPは、再送パケットをキューに入れ、タイム アウトを増加させます。接続はこの間ブロックするため、上記のレイヤーの TCPは適時にACKを受信できず、同様に再送パケットをキューに入れます。タイムアウト は依然として下位のレイヤーよりも小さいため、上位レイヤーは下位レイヤーが 処理するよりも早く再送パケットをキューに入れます。これは上位レイヤーの 接続を非常に早く失速させ、再送は問題 - 内部的なメルトダウン効果 - を増加させる だけです。

TCPの信頼性のための用意はここでは逆効果となります。上位のレイヤーの再送は まったく不要です。なぜならキャリアが配送を保証するからです。しかし、上位レイヤー のTCPはそれを知ることができません。なぜならTCPは常に信頼できないキャリアを 仮定するからです。

実際の経験

このすべての問題がCIPE プロジェクトを始める最初の動機となりました。なぜなら私はしばらくの間 PPP over SSHソリューションを使って、それがまったく役に立たないことが 明らかになったからです。当時はしばしばパケットロスが起こる光リンク上で 実行していて、時々10-20%の時間の延長がありました。素のTCPでは、 これは我慢できました。(なぜならリンクは輻輳していたわけではなからです。) しかし、積み重ねられたプロトコルでは、接続は実に遅くなり、とてもよく 切断されました。

これがCIPEがデータグラムキャリアを採用している詳細な理由です。 (IPsecのような他のIPレベルのプロトコルではなくUDPを採用しているのには いくつかの理由があります: UDPを使うと、トンネルをポート番号によって識別 することができ、SOCKS上で実行することも可能です。) データグラムキャリアは素のIPとちょうど同じような特性を持っており、それは TCPがその上で実行されるように設計されたものです。


Olaf Titz
translated by Shugo Maeda