Network Working Group
Request for Comments: 2525
Category: Informational
V. Paxson
Editor
ACIRI / ICSI
M. Allman
NASA Glenn Research Center/Sterling Software
S. Dawson
Real-Time Computing Laboratory
W. Fenner
Xerox PARC
J. Griner
NASA Glenn Research Center
I. Heavens
Spider Software Ltd.
K. Lahey
NASA Ames Research Center/MRJ
J. Semke
Pittsburgh Supercomputing Center
B. Volz
Process Software Corporation
March 1999

Known TCP Implementation Problems

Status of this Memo

このメモは、インターネット社会の為の情報を定義している。このメモは、いかなる種類のインターネット標準も規定しない。このメモの配布は制限されない。

Copyright Notice

  Copyright (C) The Internet Society (1999). All Rights Reserved.

Table of Contents

1. 導入

2. 実装の既知の問題
  2.1 初期スロースタート無し
  2.2 再送タイムアウト後のスロースタート無し
  2.3 初期化されない CWND
  2.4 整合性のない再送
  2.5 シーケンスを超えたデータを保持しない
  2.6 輻輳回避で固定値を余分に追加
  2.7 小さ過ぎる初期 RTO
  2.8 損失回復後にウィンドウを小さくしない
  2.9 短すぎる keepalive タイムアウト
  2.10 再送タイムアウトの緩和無し
    2.11 keepalive 間の間隔不十分
    2.12 ウィンドウプローブのデッドロック
    2.13 ACK 伸張違反
    2.14 複数パケットの再送
    2.15 FIN 通知を即座に送信しない
    2.16 半二重クローズ後 RST 送信なし
    2.17 未処理のデータがあるクローズ時に RST 無し
    2.18 TCP MSS 算出からオプション抜け
3. セキュリティの考慮
4. 謝辞
5. 参照
6. 作者のアドレス
7. 完全なコピーライト宣言


1. 導入

このメモは、数多くの TCP 実装の既知の問題を分類している。これを行う目的は、現在の TCP/IP 実装の品質を高めることによって、既存のインターネットの状況を改善することである。パフォーマンスと正当性の問題は、実装者に問題と解決方法を知ってもらうことによって解決できると期待される。長いレンジで見ると、これはネットワーク上の不要なトラフィックや、プロトコルエラーによるコネクション障害率、ネットワークサーバの負荷、異常なコネクションや再送データの両方を処理するために費やす時間を減らすことに貢献することが期待される。これは、グローバルなインターネットの安定性の保証に役立つであろう。

各々の問題は以下のように定義されている。

問題の名前:
その問題に割当てられた名前。このメモでは、その名前はサブセクションの見出しとして与えられている。

分類:
問題を分類するための一つ以上のカテゴリ。"輻輳"、"パフォーマンス"、"信頼性"、"リソース管理"。

説明:
その問題の定義、簡潔だが必要な背景資料を含む。

重要性:
問題が重要である環境の種類の簡潔な要約。

実装:
なぜその問題が問題と見られるのか。

関連 RFC:
問題が対立する TCP の規約を定義した RFC。これらの RFC はしばしば、MUST、SHOULD、MAY や他の大文字で書かれた言葉を使用して振る舞いを規定している。これらの言葉の正確な解釈に付いては、RFC2119 を参照されたい。

その問題を実演するトレースファイル
適用可能ならば、一つ以上の ASCII トレースファイルが問題を実演している。

正しい動作を実演するトレースファイル
適用可能ならば、一つ以上の ASCII トレースファイルが正しい動作を実演している。

参照:
問題を更な論じている参照。

検出方法:
その問題を示すか否かをチェックするための、実装体の試験方法。この議論は、その問題を明らかにすることや、問題があることを検出するためのトレースの解釈に関する困難さや繊細さを含むかもしれない (適用可能なら)。

修正方法:
既知の問題の原因に対する実装の修正方法。

2. 実装の既知の問題

2.1. 初期スロースタート無し

問題の名前:
初期スロースタート無し

分類:
輻輳制御

説明:
TCP がデータ送信を開始する時、輻輳ウィンドウ cwnd を 1 パケット (最大長の 1 セグメント) に初期化することによって、"スロースタート" を行うことが、RFC1122, 4.2.2.15 で要求されている。([RFC2414] でドキュメント化されている TCP の実験的変更は、初期値を 1 パケットのデータよりも幾分大きくすることを許している)。その後、新しいデータに対して受信する ACK の 1 パケット毎に cwnd を加算する。cwnd の最小値と受信側が伝えたウィンドウは、TCP が送信できるシーケンス番号の最大値に結びつく。この方法で cwnd の初期化と加算を行わない TCP は、"初期スロースタート無し" を示す。

重要性:
輻輳した環境では、他のコネクションや恐らく自身のコネクションのパフォーマンスに有害である。

実装:
コネクション開始時にスロースタートに失敗した TCP は、ネットワークを圧迫し得るトラフィックバーストを招き、極端なキューイング遅延やパケット損失を導く。

この問題を示す実装体は、必要な輻輳ウィンドウを含まない一般的な問題の被害を受けているためにそうしているかもしれない。これらの実装体は、"再送タイムアウト後のスロースタート無し" の被害も受けるかもしれない。

"初期スロースタート無し" には異なる陰がある。ネットワークを圧迫するという見地から、最悪は輻輳ウィンドウを別個に考慮せずに、必ず受信側の伝えたウィンドウに基づいて単に送信するコネクションである。別の形態は、以下の "初期化されない CWND" で説明されている。

関連 RFC:
RFC1122 はスロースタートの使用を必要としている。RFC2001 はスロースタートの詳細を示している。

その問題を実演するトレースファイル
コネクションの応答側で tcpdump [Jacobson89] を使用して記録した。パケットフィルタによって通知された損失はない。

  10:40:42.244503 B > A: S 1168512000:1168512000(0) win 32768
                          <mss 1460,nop,wscale 0> (DF) [tos 0x8]
  10:40:42.259908 A > B: S 3688169472:3688169472(0)
                          ack 1168512001 win 32768 <mss 1460>
  10:40:42.389992 B > A: . ack 1 win 33580 (DF) [tos 0x8]
  10:40:42.664975 A > B: P 1:513(512) ack 1 win 32768
  10:40:42.700185 A > B: . 513:1973(1460) ack 1 win 32768
  10:40:42.718017 A > B: . 1973:3433(1460) ack 1 win 32768
  10:40:42.762945 A > B: . 3433:4893(1460) ack 1 win 32768
  10:40:42.811273 A > B: . 4893:6353(1460) ack 1 win 32768
  10:40:42.829149 A > B: . 6353:7813(1460) ack 1 win 32768
  10:40:42.853687 B > A: . ack 1973 win 33580 (DF) [tos 0x8]
  10:40:42.864031 B > A: . ack 3433 win 33580 (DF) [tos 0x8]

三つ目のパケットの後、コネクションが確立する。コネクション応答側の A は、コネクションの起動側である B への送信を開始する。ホスト A は、SYN の交換で 1460 バイトの MSS を同意したにもかかわらず (1 セグメントの初期輻輳ウィンドウが 1460 バイトに相当することを意味する)、即座に 7812 バイト分の 6 個のパケット送信している。A は最大 1460 バイト送信すべきであった。

B によって A に送信された最後の 2 行の ACK は、このトレースがサイズ上の異常はないことを示している。(スロースタートは実際に発生しているが、パケットフィルタによって対応する ACK が落とされた)。

2 番目のトレースは問題が再現し得ることを実証した。

正しい動作を実演するトレースファイル:
コネクションの起動側で tcpdump を使用して記録した。パケットフィルタによって通知された損失はない。

  12:35:31.914050 C > D: S 1448571845:1448571845(0)
                           win 4380 <mss 1460>
  12:35:32.068819 D > C: S 1755712000:1755712000(0)
                           ack 1448571846 win 4096
  12:35:32.069341 C > D: . ack 1 win 4608
  12:35:32.075213 C > D: P 1:513(512) ack 1 win 4608
  12:35:32.286073 D > C: . ack 513 win 4096
  12:35:32.287032 C > D: . 513:1025(512) ack 1 win 4608
  12:35:32.287506 C > D: . 1025:1537(512) ack 1 win 4608
  12:35:32.432712 D > C: . ack 1537 win 4096
  12:35:32.433690 C > D: . 1537:2049(512) ack 1 win 4608
  12:35:32.434481 C > D: . 2049:2561(512) ack 1 win 4608
  12:35:32.435032 C > D: . 2561:3073(512) ack 1 win 4608
  12:35:32.594526 D > C: . ack 3073 win 4096
  12:35:32.595465 C > D: . 3073:3585(512) ack 1 win 4608
  12:35:32.595947 C > D: . 3585:4097(512) ack 1 win 4608
  12:35:32.596414 C > D: . 4097:4609(512) ack 1 win 4608
  12:35:32.596888 C > D: . 4609:5121(512) ack 1 win 4608
  12:35:32.733453 D > C: . ack 4097 win 4096

参照:
この問題は、[Paxson97] でドキュメント化されている。

検出方法:
常にこの問題が現れる実装体では、上記で示したようなパケットトレースかシーケンス図ですぐ分かる。

修正方法:
もし根元の問題が実装体の輻輳ウィンドウの考えの欠如ならば、不幸にも修正を施す重要な作業が必要である。しかし、そのような実装体は "再送タイムアウト後のスロースタート無し" も現れるので、修正することは重要である。

2.2. 再送タイムアウト後のスロースタート無し

問題の名前:
再送タイムアウト後のスロースタート無し

分類:
輻輳制御

説明:
TCP が再送タイムアウトを経験する時、輻輳ウィンドウ cwnd を 1 パケット (最大長の 1 セグメント) に初期化することによって、"スロースタート" を行うことが、RFC1122, 4.2.2.15 で要求されている。その後、"輻輳回避" の限界ポイント ssthresh に達するまで、新しいデータに対して受信する ACK の 1 パケット毎に cwnd を加算する。その限界ポイントで輻輳回避アルゴリズムにウィンドウの更新が引き継がれる。タイムアウト時にスロースタートに入らない TCP は、"再送タイムアウト後のスロースタート無し" を示す。

重要性:
輻輳した環境では、他のコネクションや恐らく自身のコネクションのパフォーマンスに極めて有害である。

実装:
タイムアウト時にスロースタートに入ることは、[Jacobson88] で概説されているように、インターネット輻輳の安定性の基本の一つを形成する。もし TCP がそれに失敗すると、ネットワークが "輻輳崩壊" [RFC896] の被害を受ける危険性がある。

関連 RFC:
RFC1122 は損失後のスロースタートの使用を必要としている。RFC2001 はスロースタートの実装方法の詳細を示している。

ここで論じられている再送タイムアウトを、RFC2001 で論じられている別の "高速回復" 再送メカニズムと混乱すべきではない。

その問題を実演するトレースファイル:
送信側 TCP (A) で tcpdump を使用して記録した。パケットフィルタによって通知された損失はない。

  10:40:59.090612 B > A: . ack 357125 win 33580 (DF) [tos 0x8]
  10:40:59.222025 A > B: . 357125:358585(1460) ack 1 win 32768
  10:40:59.868871 A > B: . 357125:358585(1460) ack 1 win 32768
  10:41:00.016641 B > A: . ack 364425 win 33580 (DF) [tos 0x8]
  10:41:00.036709 A > B: . 364425:365885(1460) ack 1 win 32768
  10:41:00.045231 A > B: . 365885:367345(1460) ack 1 win 32768
  10:41:00.053785 A > B: . 367345:368805(1460) ack 1 win 32768
  10:41:00.062426 A > B: . 368805:370265(1460) ack 1 win 32768
  10:41:00.071074 A > B: . 370265:371725(1460) ack 1 win 32768
  10:41:00.079794 A > B: . 371725:373185(1460) ack 1 win 32768
  10:41:00.089304 A > B: . 373185:374645(1460) ack 1 win 32768
  10:41:00.097738 A > B: . 374645:376105(1460) ack 1 win 32768
  10:41:00.106409 A > B: . 376105:377565(1460) ack 1 win 32768
  10:41:00.115024 A > B: . 377565:379025(1460) ack 1 win 32768
  10:41:00.123576 A > B: . 379025:380485(1460) ack 1 win 32768
  10:41:00.132016 A > B: . 380485:381945(1460) ack 1 win 32768
  10:41:00.141635 A > B: . 381945:383405(1460) ack 1 win 32768
  10:41:00.150094 A > B: . 383405:384865(1460) ack 1 win 32768
  10:41:00.158552 A > B: . 384865:386325(1460) ack 1 win 32768
  10:41:00.167053 A > B: . 386325:387785(1460) ack 1 win 32768
  10:41:00.175518 A > B: . 387785:389245(1460) ack 1 win 32768
  10:41:00.210835 A > B: . 389245:390705(1460) ack 1 win 32768
  10:41:00.226108 A > B: . 390705:392165(1460) ack 1 win 32768
  10:41:00.241524 B > A: . ack 389245 win 8760 (DF) [tos 0x8]

一番目のパケットは、ack ポイントが 357125 であることを示している。ACK を受信してから 130 ミリ秒後、A は ACK ポイントの後ろのパケット 357125:358585 を送信している。これを送信してから 640 ミリ秒後、明白な再送タイムアウトで 357125:358585 を再送している。この時点で A はスロースタートに入るので、A の cwnd は 1 MSS、あるいは 1460 バイトになるべきである。トレースは、この可能性については矛盾はない。

B は 364425 の ACK で応答し、A がシーケンスの穴を埋めたことを示している。この時点で A の cwnd は 1460*2 = 2920 バイトであるべきである。しかしその後 A は 19 個連続してパケットを発行した。それはスロースタートに矛盾していることを示す。

2 番目のトレースは問題が再現し得ることを実証した。

正しい動作を実演するトレースファイル:
送信側 TCP (C) で tcpdump を使用して記録した。パケットフィルタによって通知された損失はない。

  12:35:48.442538 C > D: P 465409:465921(512) ack 1 win 4608
  12:35:48.544483 D > C: . ack 461825 win 4096
  12:35:48.703496 D > C: . ack 461825 win 4096
  12:35:49.044613 C > D: . 461825:462337(512) ack 1 win 4608
  12:35:49.192282 D > C: . ack 465921 win 2048
  12:35:49.192538 D > C: . ack 465921 win 4096
  12:35:49.193392 C > D: P 465921:466433(512) ack 1 win 4608
  12:35:49.194726 C > D: P 466433:466945(512) ack 1 win 4608
  12:35:49.350665 D > C: . ack 466945 win 4096
  12:35:49.351694 C > D: . 466945:467457(512) ack 1 win 4608
  12:35:49.352168 C > D: . 467457:467969(512) ack 1 win 4608
  12:35:49.352643 C > D: . 467969:468481(512) ack 1 win 4608
  12:35:49.506000 D > C: . ack 467969 win 3584

C が D に見せる最初のパケットを送信した後、D の 461825 に対する ACK の応答として何の動作も行っていない。なぜなら一番目のパケットが、461825 以降の 4096 バイトの伝えられたウィンドウ限界に既に達していたからである。一番目のパケットを送信してから 600 ミリ秒後、恐らくタイムアウトによって C は 461825:462337 を再送している。その輻輳ウィンドウは現在 MSS (512 バイト) である。

D は 465921 に ack し、C の再送がシーケンスの穴を埋めたことを示している。この ACK により C の cwnd は 512 から 1024 に進む。そのすぐ後に、D は申し出たウィンドウを 2048 から 4096 に更新するために、再び 465921 に ack している。この ACK は cwnd を増やさない。なぜなら、それは新しいデータに対するものではないからである。そのすぐ後に、C は新たに大きくなったウィンドウに応えて、2 個のパケットを送信している。D は両方とも ack し、cwnd が 1024 から 1536 に進む。次に C は 3 個のパケットを送信する。

参照:
この問題は、[Paxson97] でドキュメント化されている。

検出方法:
パケット損失のために再送せざる得ないインターネットパスを見つけることは通常困難ではないほど、パケット損失は十分に一般的である。

しかし、もし損失前の有効なウィンドウが十分大きければ、TCP は RFC2001 に規定されている "高速回復" メカニズムを使用して再送してもよい。パケットトレースでは、高速回復の重要性は、パケットの再送が 3 個の重複 ACK 受信に対する応答で発生することである。続く重複 ACK により、肯定応答ポイントとこれまで送信された最大のシーケンス番号の両方の上に新しいデータを送信してよい。再送前に 3 個の重複 ACK が無いことは、タイムアウトと高速回復再送を区別するのに十分である。高速回復再送を観測するだけの面では、通常、タイムアウト再送を観測するまで、データ転送を繰り返すことは難しくない。

一旦、タイムアウト再送を示すトレースが備えられたら、TCP がスロースタートに従っているか否かを決定することは、cwnd の正しい進み具合を計算し、それをタイムアウト再送に続いて TCP により送信されたデータの総量と比較することによって行われる。

修正方法:
もし根元の問題が実装体の輻輳ウィンドウの考えの欠如ならば、不幸にも修正を施す重要な作業が必要である。しかし上記で概説した理由により、修正することは重要である。

2.3. 初期化されない CWND

問題の名前:
初期化されない CWND

分類:
輻輳制御

説明:
上記の "初期スロースタート無し" で説明されているように、TCP コネクションが開始する時は、cwnd は 1 セグメント (あるいは [RFC2414] で実験しているならば恐らく 2, 3 セグメント) に初期化される。バグとして別個に言及する価値があり、かなり広く普及している "初期スロースタート無し" の一つのある特定の形態は、"初期化されない CWND" である。すなわち、TCP は適切なスロースタートメカニズムを提供していても、cwnd を適切に初期化しないためにスロースタートが実際に発生しないことがあり得る。

バグが発生し得る一つの方法は、コネクション確立のハンドシェークの間に、SYN ACK パケットが MSS オプション無しで到着する場合である。欠陥の在る実装体は、cwnd を 1 に初期化するために、受信した MSS オプションを使用する。もしそのオプションが到着しないと、代わりに cwnd を非常に大きな値に初期化する。

重要性:
輻輳した環境では、他のコネクションや恐らく自身のコネクションのパフォーマンスに有害である。バーストが大規模になり得るので (下記参照)、輻輳の無い環境でさえも有害な影響を受ける。

暗黙の意味:
この振る舞いを見せる TCP は、パケットの大規模なバーストを以ってネットワークに負荷をかけ、ネットワークの損失の原因になり得る。

関連 RFC:
RFC1122 はスロースタートの使用を必要としている。RFC2001 はスロースタートの詳細を示している。

その問題を実演するトレースファイル:
このトレースはホスト A で tcpdump を使用して作成した。ホスト A が送信側であり、ホスト B が受信側である。伝播されるウィンドウとタイムスタンプオプションは、ホスト A が送信する一版最初のセグメントを除いて、明確化のために省略されている。A は自身の初期 SYN の中で MSS オプションを送信するが、B はその応答にそれを含めないことに注意されたい。

  16:56:02.226937 A > B: S 237585307:237585307(0) win 8192
        <mss 536,nop,wscale 0,nop,nop,timestamp[|tcp]>
  16:56:02.557135 B > A: S 1617216000:1617216000(0)
        ack 237585308 win 16384
  16:56:02.557788 A > B: . ack 1 win 8192
  16:56:02.566014 A > B: . 1:537(536) ack 1
  16:56:02.566557 A > B: . 537:1073(536) ack 1
  16:56:02.567120 A > B: . 1073:1609(536) ack 1
  16:56:02.567662 A > B: P 1609:2049(440) ack 1
  16:56:02.568349 A > B: . 2049:2585(536) ack 1
  16:56:02.568909 A > B: . 2585:3121(536) ack 1

     [54 他のバーストセグメントは簡略化のために削除]

  16:56:02.936638 A > B: . 32065:32601(536) ack 1
  16:56:03.018685 B > A: . ack 1

3 方向ハンドシェークの後、一番最初のセグメントに対する重複 ACK が再送を発生させる間に、ホスト A は 61 個のセグメントをネットワークに流入させている。ホスト A は追加のセグメントを送信する前に一番最初のセグメントに対する ACK を待たなかった。それは、"初期化されない CWND" を示している。

正しい動作を実演するトレースファイル:
"初期スロースタート無し" の例を参照されたい・

参照:
この問題は、[Paxson97] でドキュメント化されている。

検出方法:
この問題は、送信側か受信側のいずれかで記録されたパケットトレースをチェックすることによって検出できる。しかし、SYN ACK の MSS オプションを送信しないリモート TCP の相手を見つける必要があるので、そのバグを引き起こすことは難しい。

修正方法:
この問題は、たとえ SYN ACK が MSS オプションを含んでいなくとも、SYN ACK の受信時に cwnd が初期化されることを保証することによって修正できる。

2.4. 整合性のない再送

問題の名前:
整合性のない再送

分類:
信頼性

説明:
もし指定されたシーケンス番号に対して、送信側 TCP が前に送信したデータとは異なるデータを再送すると、受諾したシーケンス番号のインスタンスによって、受信側の TCP が送信側のアプリケーションによって送信されたものとは異なるバイトストリームを再構成する可能性が強く起こる。

そのような送信側 TCP は、"整合性のない再送" を示す。

重要性:
すべての環境に重大である。

暗黙の意味:
データの信頼性のある配送は TCP の基本的な特性である。

関連 RFC:
RFC793 セクション 1.5 は、TCP オペレーションの信頼性の中心的な役割について論じている。

その問題を実演するトレースファイル:
受信側 TCP (B) で tcpdump を使用して記録した。パケットフィルタによって通知された損失はない。

 12:35:53.145503 A > B: FP 90048435:90048461(26)
                           ack 393464682 win 4096
                                      4500 0042 9644 0000
                  3006 e4c2 86b1 0401 83f3 010a b2a4 0015
                  055e 07b3 1773 cb6a 5019 1000 68a9 0000
 data starts here>504f 5254 2031 3334 2c31 3737*2c34 2c31
                  2c31 3738 2c31 3635 0d0a
 12:35:53.146479 B > A: R 393464682:393464682(0) win 8192
 12:35:53.851714 A > B: FP 90048429:90048463(34)
                        ack 393464682 win 4096
                                      4500 004a 965b 0000
                  3006 e4a3 86b1 0401 83f3 010a b2a4 0015
                  055e 07ad 1773 cb6a 5019 1000 8bd3 0000
 data starts here>5041 5356 0d0a 504f 5254 2031 3334 2c31
                  3737*2c31 3035 2c31 3431 2c34 2c31 3539
                  0d0a

このトレースに現れているシーケンス番号は完全に ISN を反映して調整されていない。4 つ数字の 16 進の値はパケットの IP と TCP ヘッダのダンプだけでなく、ペイロードも示している。A は最初に B に 90048435:90048461 を送信した。それに対応するデータは、16 進ワードの 504f, 5254 等から始まっている。

B は RST で応答した。記録している場所が B 側であったので、A が RST を受信したか否かは分からない。

その後 A は 90048429:90048463 を送信した。それは前に送信したものより 6 小さいシーケンス位置と、前に送信した 26 全て、そして 2 つの追加のシーケンス位置を含んでいる。

再送データは、上記の '*' で示されている 90048447 の直後から一致していない。これらの 2 バイトは元々 16 進の 2c34 として送信されているが、16 進の 2c31 として再送されている。後続の位置もまた一致していない。

この振る舞いは、異なるホストを含む他のトレースで観測された。その再現方法は不明である。

このインスタンスでは異常は発生しなかった。なぜなら、B は既に A からの後続パケットを受信しないことを指示したからである。

二番目の例は、この問題とはわずかに異なるインスタンスを示している。トレースは再度 tcpdump を用いて、受信側 TCP (D) で生成した。

  22:23:58.645829 C > D: P 185:212(27) ack 565 win 4096
                                       4500 0043 90a3 0000
                   3306 0734 cbf1 9eef 83f3 010a 0525 0015
                   a3a2 faba 578c 70a4 5018 1000 9a53 0000
  data starts here>504f 5254 2032 3033 2c32 3431 2c31 3538
                   2c32 3339 2c35 2c34 330d 0a
  22:23:58.646805 D > C: . ack 184 win 8192
                                       4500 0028 beeb 0000
                   3e06 ce06 83f3 010a cbf1 9eef 0015 0525
                   578c 70a4 a3a2 fab9 5010 2000 342f 0000
  22:31:36.532244 C > D: FP 186:213(27) ack 565 win 4096
                                       4500 0043 9435 0000
                   3306 03a2 cbf1 9eef 83f3 010a 0525 0015
                   a3a2 fabb 578c 70a4 5019 1000 9a51 0000
  data starts here>504f 5254 2032 3033 2c32 3431 2c31 3538
                   2c32 3339 2c35 2c34 330d 0a

このトレースでは、シーケンス番号が相対的である。C は 185:212 を送信したが、D は 184 の ACK しか送信していない (従ってシーケンス番号の 184 が無い)。その後 C は 186:213 を送信した。パケットのペイロードは前のペイロードと同じだが、基のシーケンス番号が 1 大きいため結果的に整合性の無い再送になっている。

いずれのトレースもチェックサムエラーは示していない。

正しい動作を実演するトレースファイル:
(正しい振る舞いは恐らく明白なので省略、)

参照:
不明。

検出方法:
経験則によると、この問題が明らかになることは非常にまれなので、不幸にも検出することはとても難しい。問題を再現するために使用できる "トリガ" は無い。

修正方法:
既知の "トリガ" が無い状態では、その問題の修正方法を評価することは必ずしも可能ではない。

ある (上記で図示されているものではない) 実装体では、次の場合に問題が表面化した。(1) 送信側が 0 ウィンドウを受信して停止した時、(2) 停止時よりも大きなウィンドウを提供する ACK が結局到着し、(3) 送信側が、停止時に保持していたデータのバッファから送信すしたが、(4) この送信のバッファ長を限定できず、代わりに新たに通知された (より大きな) 提供ウィンドウを使用した。最終的に正しいバッファ内容に加えて、バッファの最後以降にあるごみの値を送信した。そして、もしそれに対応するシーケンス番号を送信したら、正しいデータを送信したポイントで、整合性の無い再送を結果的に引き起こす。その問題のこのインスタンスは、より一般的な問題である最初に不正なデータを送信することが影響していることに注意されたい。

2.5. シーケンスを超えたデータを保持しない

問題の名前:
シーケンスを超えたデータを保持しない

分類:
輻輳制御、パフォーマンス

説明:
TCP が、RCV.NXT を超えているが RCV.NXT+RCV.WND 以下であるシーケンス番号を持つものを意味する "シーケンスを超えた" セグメントを受信した場合、後の配送のためにそのセグメントをキューイングすべきである (RFC1122, 4.2.2.20)。(RCV.NXT と RCV.WND の定義については、RFC793 を参照されたい)。それを行わない TCP は、"シーケンスを超えたデータを保持しない" ことを示す。

メモリを再利用するために TCP がシーケンスを超えたデータを破棄することは、ある意味適切かもしれない。もしそれを滅多に行わないならば、この問題を示すものとは見なさないだろう。しかし特に問題になるのは、シーケンスを超えたデータを常に破棄する TCP である。

重要性:
パケットを損失する傾向のある環境では、他方のコネクションと自身のコネクションの両方のパフォーマンスに有害である。

暗黙の意味:
輻輳時にシーケンスを超えたデータを保持しないことは、大量の異なる不要な再送や、輻輳を悪化、多くの要因による潜在的にパフォーマンスの低下を招くだろう。

関連 RFC:
RFC1122 は、この問題における後者の MAYSHOULD に更新することによって RFC793 を改訂している。

その問題を実演するトレースファイル:
受信側 TCP で tcpdump を使用して記録した。パケットフィルタによって通知された損失はない。

B は TCP 送信側である。A がシーケンスを超えたデータを保持しないことを示している。

  10:38:10.164860 B > A: . 221078:221614(536) ack 1 win 33232 [tos 0x8]
  10:38:10.170809 B > A: . 221614:222150(536) ack 1 win 33232 [tos 0x8]
  10:38:10.177183 B > A: . 222150:222686(536) ack 1 win 33232 [tos 0x8]
  10:38:10.225039 A > B: . ack 222686 win 25800

この時点では、B は (相対的な) シーケンス番号 222686 までシーケンス内で送信し、A はそれに従って肯定応答を出している。

  10:38:10.268131 B > A: . 223222:223758(536) ack 1 win 33232 [tos 0x8]
  10:38:10.337995 B > A: . 223758:224294(536) ack 1 win 33232 [tos 0x8]
  10:38:10.344065 B > A: . 224294:224830(536) ack 1 win 33232 [tos 0x8]
  10:38:10.350169 B > A: . 224830:225366(536) ack 1 win 33232 [tos 0x8]
  10:38:10.356362 B > A: . 225366:225902(536) ack 1 win 33232 [tos 0x8]
  10:38:10.362445 B > A: . 225902:226438(536) ack 1 win 33232 [tos 0x8]
  10:38:10.368579 B > A: . 226438:226974(536) ack 1 win 33232 [tos 0x8]
  10:38:10.374732 B > A: . 226974:227510(536) ack 1 win 33232 [tos 0x8]
  10:38:10.380825 B > A: . 227510:228046(536) ack 1 win 33232 [tos 0x8]
  10:38:10.387027 B > A: . 228046:228582(536) ack 1 win 33232 [tos 0x8]
  10:38:10.393053 B > A: . 228582:229118(536) ack 1 win 33232 [tos 0x8]
  10:38:10.399193 B > A: . 229118:229654(536) ack 1 win 33232 [tos 0x8]
  10:38:10.405356 B > A: . 229654:230190(536) ack 1 win 33232 [tos 0x8]

A は今 13 個の追加のパケットを B から受信した。222686:223222 は損失したので、これらはシーケンスを超えている。しかし、パケットは提示ウィンドウの範囲内に合っている。A はそれらに対して全く重複 ACK を生成していない。

トレースの提供者 (V. Paxson) は、これらの 13 個のパケットが正しい IP と TCP のチェックサムを持っていることを確認した。

  10:38:11.917728 B > A: . 222686:223222(536) ack 1 win 33232 [tos 0x8]
  10:38:11.930925 A > B: . ack 223222 win 32232

B は 222686:223222 に対してタイムアウトし、それを再送した。それを受信した時、A は 223222 にしか肯定応答を出していない。もしシーケンスを超えたデータを保持していたならば、代わりに 230190 に対して肯定応答を出しただろう。

  10:38:12.048438 B > A: . 223222:223758(536) ack 1 win 33232 [tos 0x8]
  10:38:12.054397 B > A: . 223758:224294(536) ack 1 win 33232 [tos 0x8]
  10:38:12.068029 A > B: . ack 224294 win 31696

B はさらに 2 個のパケットを送信し、A はそれらにしか応答していない。このパターンは、以前に受信したパケットのセット全体を B が再送するまで続く。

二番目のトレースは、この問題が再現可能であることを確認している。

正しい動作を実演するトレースファイル:
受信側 TCP (C) で tcpdump を使用して記録した。パケットフィルタによって通知された損失はない。

  09:11:25.790417 D > C: . 33793:34305(512) ack 1 win 61440
  09:11:25.791393 D > C: . 34305:34817(512) ack 1 win 61440
  09:11:25.792369 D > C: . 34817:35329(512) ack 1 win 61440
  09:11:25.792369 D > C: . 35329:35841(512) ack 1 win 61440
  09:11:25.793345 D > C: . 36353:36865(512) ack 1 win 61440
  09:11:25.794321 C > D: . ack 35841 win 59904

35841:36353 が落ちたため、シーケンスホールが発生した。

  09:11:25.794321 D > C: . 36865:37377(512) ack 1 win 61440
  09:11:25.794321 C > D: . ack 35841 win 59904
  09:11:25.795297 D > C: . 37377:37889(512) ack 1 win 61440
  09:11:25.795297 C > D: . ack 35841 win 59904
  09:11:25.796273 C > D: . ack 35841 win 61440
  09:11:25.798225 D > C: . 37889:38401(512) ack 1 win 61440
  09:11:25.799201 C > D: . ack 35841 win 61440
  09:11:25.807009 D > C: . 38401:38913(512) ack 1 win 61440
  09:11:25.807009 C > D: . ack 35841 win 61440
  (多くの追加行省略)
  09:11:25.884113 D > C: . 52737:53249(512) ack 1 win 61440
  09:11:25.884113 C > D: . ack 35841 win 61440

C が D から受信した各々の追加の、シーケンスを超えたパケットは、35841 重複 ACK を誘発している。

  09:11:25.887041 D > C: . 35841:36353(512) ack 1 win 61440
  09:11:25.887041 C > D: . ack 53249 win 44032

D は 35841:36353 を再送し、C は 53249 まで受信した全てのデータに対して肯定応答した。

参照:
この問題は、[Paxson97] でドキュメント化されている。

検出方法:
シーケンスを超えたパケットが結果的に到着するインターネットパスを見つけることは通常困難ではないほど、パケット損失は十分に一般的である。"シーケンスを超えたデータを保持しない" を示す TCP は、これらのパケットに対して重複 ACK を生成しないかもしれない。しかし、シーケンスを超えたデータを保持する幾つかの TCP もまた、重複 ACK を生成しないので、重複 ACK を生成しないことがその問題を決定的に識別するわけではない。代わりにその鍵となる観測は、落とされたパケットの再送時に、前に送信されたシーケンスを超えたデータに対して肯定応答されるか否かである。

パケットトレースを使用してこの問題を検出する際の二つの考慮点は。どのパケットが正常に到着したかや、もしチェックサムエラーを持って到着したらそうしたパケットが正常に破棄されたことを曖昧なく決定するために、TCP 受信側での検出が最も容易であるということである。後者はパケットの内容全体を捕捉し、IP と TCP のチェックサムアルゴリズムをそれらの完全性を保証するために実行するか、あるいは送信側 TCP が送信するパケットのチェックサムを正常に算出していると仮定して、到着したパケットが送信されたものと同じチェックサムや内容を持っているかを確認することによってテストできる。

実装体がこの問題を示していないことを証明することは、かなり容易である。それは、データの送信側でトレースを記録し、時々再送後に、再送されたものよりも大きいシーケンス番号に対して受信側が肯定応答していることを観測することによって行うことができる。

修正方法:
もし根元の問題が実装体のバッファ欠如ならば、不幸にも修正を施す重要な作業が必要である。しかし上記で概説した理由により、修正することは重要である。

2.6 輻輳回避で固定値を余分に追加

問題の名前:
輻輳回避で固定値を余分に追加

分類:
輻輳制御 / パフォーマンス

説明:
RFC1122 セクション 4.2.2.15 は、Jacobson "輻輳回避" アルゴリズム [Jacobson88] を TCP は実装しなければならない (MUST) と明言している。そのアルゴリズムは、輻輳ウィンドウ、cwnd を、新しいデータに対して受信した各 ACK に対して、以下の式によって増やす [RFC2001]

  MSS * MSS / cwnd

これは、各々のラウンドトリップ時間でおよそ 1 セグメント分の cwnd を増やす効果がある。

ある TCP 実装は、新しいデータで受信した各々の ACK に対して、セグメントの追加の分数 (通常 MSS/8) を cwnd に追加する [Stevens94], [Wright95]

  (MSS * MSS / cwnd) + MSS/8

これらの実装体は、"輻輳回避で固定値を余分に追加" を示している。

重要性:
完全に輻輳の無い環境でさえも (暗黙の意味参照)、パフォーマンスに有害であるかもしれない。

輻輳のある環境では、他のコネクションのパフォーマンスに有害である。

暗黙の意味:
余分な期間加算は、より積極的に TCP が自身の輻輳ウィンドウを開く (一次直線的な加算ではなく二次曲線的な加算) ことを可能にする。輻輳したネットワークでは、これは積極的な TCP とボトルネックを共有する全ての接続によって経験される損失の割合を増やす。

しかし、完全に輻輳の無いネットワークでさえも、余分な期間加算は次のようにパフォーマンスの低下を招き得る。輻輳回避では、TCP 送信側は利用できる容量を決定するためにネットワークパスをプローブする。それはしばしば、ボトルネック回線で利用できるバッファの個数と等しくなる。直線的な輻輳回避では、TCP は RTT につき一つの余分なパケットを保持するのに十分な容量 (バッファ) をプローブするだけである。

従って、利用可能な容量を超えた時、通常 1 つのパケットしか損失しないだろう (直前の RTT で、そのパスが一つのウィンドウを一個以下のパケットで維持できることが既に分かっていたから)。もし輻輳ウィンドウが十分に大きいならば、TCP はこの一つの損失から高速再送を使用して回復し、 (パフォーマンスの点で) 高価な再送タイムアウトを回避するだろう。

しかし、さらに期間加算が使用されると、TCP がより積極的にプローブする場合、cwnd は RTT につき 1 パケット以上加算され得る。もし以前の RTT でパスの利用可能な受容能力に達したら、余分な増加のための過剰により再び損失し、今度は単一の損失ではなく複数の損失を招くだろう。SACK [RFC2018] を利用しない TCP は、再送タイムアウトを受けずに複数の損失から回復することは通常なく [Fall96], [Hoe96]、パフォーマンスの際立った低下を招くだろう。

関連 RFC:
RFC1122 は、"輻輳回避" アルゴリズムの使用を必要としている。
RFC2001 は、高速再送/高速回復アルゴリズムを概説している。
RFC2018 は、SACK オプションについて論じている。

その問題を実演するトレースファイル:
ホスト A と同じ FDDI LAN 上で tcpdump を使用して記録した。ホスト A が送信側でホスト B が受信側である。コネクションの確立は、4312 バイトの MSS と 4 のウィンドウ幅係数を示している。この問題はウィンドウが大きな値になった時に最もよく実演されるので、確立と最初の 2.5 MB のデータ転送は省略する。抜粋したトレースの開始時点では、輻輳ウィンドウは 31 パケットである。そのコネクションは受信側ウィンドウの制限はないので、分かりやすくするためにウィンドウの通知をトレースから省略している。

  11:42:07.697951 B > A: . ack 2383006
  11:42:07.699388 A > B: . 2508054:2512366(4312)
  11:42:07.699962 A > B: . 2512366:2516678(4312)
  11:42:07.700012 B > A: . ack 2391630
  11:42:07.701081 A > B: . 2516678:2520990(4312)
  11:42:07.701656 A > B: . 2520990:2525302(4312)
  11:42:07.701739 B > A: . ack 2400254
  11:42:07.702685 A > B: . 2525302:2529614(4312)
  11:42:07.703257 A > B: . 2529614:2533926(4312)
  11:42:07.703295 B > A: . ack 2408878
  11:42:07.704414 A > B: . 2533926:2538238(4312)
  11:42:07.704989 A > B: . 2538238:2542550(4312)
  11:42:07.705040 B > A: . ack 2417502
  11:42:07.705935 A > B: . 2542550:2546862(4312)
  11:42:07.706506 A > B: . 2546862:2551174(4312)
  11:42:07.706544 B > A: . ack 2426126
  11:42:07.707480 A > B: . 2551174:2555486(4312)
  11:42:07.708051 A > B: . 2555486:2559798(4312)
  11:42:07.708088 B > A: . ack 2434750
  11:42:07.709030 A > B: . 2559798:2564110(4312)
  11:42:07.709604 A > B: . 2564110:2568422(4312)
  11:42:07.710175 A > B: . 2568422:2572734(4312) *

  11:42:07.710215 B > A: . ack 2443374
  11:42:07.710799 A > B: . 2572734:2577046(4312)
  11:42:07.711368 A > B: . 2577046:2581358(4312)
  11:42:07.711405 B > A: . ack 2451998
  11:42:07.712323 A > B: . 2581358:2585670(4312)
  11:42:07.712898 A > B: . 2585670:2589982(4312)
  11:42:07.712938 B > A: . ack 2460622
  11:42:07.713926 A > B: . 2589982:2594294(4312)
  11:42:07.714501 A > B: . 2594294:2598606(4312)
  11:42:07.714547 B > A: . ack 2469246
  11:42:07.715747 A > B: . 2598606:2602918(4312)
  11:42:07.716287 A > B: . 2602918:2607230(4312)
  11:42:07.716328 B > A: . ack 2477870
  11:42:07.717146 A > B: . 2607230:2611542(4312)
  11:42:07.717717 A > B: . 2611542:2615854(4312)
  11:42:07.717762 B > A: . ack 2486494
  11:42:07.718754 A > B: . 2615854:2620166(4312)
  11:42:07.719331 A > B: . 2620166:2624478(4312)
  11:42:07.719906 A > B: . 2624478:2628790(4312) **

  11:42:07.719958 B > A: . ack 2495118
  11:42:07.720500 A > B: . 2628790:2633102(4312)
  11:42:07.721080 A > B: . 2633102:2637414(4312)
  11:42:07.721739 B > A: . ack 2503742
  11:42:07.722348 A > B: . 2637414:2641726(4312)
  11:42:07.722918 A > B: . 2641726:2646038(4312)
  11:42:07.769248 B > A: . ack 2512366

受信側の肯定応答指針は 2 つのパケット受信につき 1 つの ACK である。よって、ホスト A に到着した各 ACK 毎に 2 つの新たなパケットが送信される。ただしこれは、cwnd が輻輳回避のために増える場合を除く。cwnd が増えた場合、新たに 3 つのパケットが送信される。

2 つのパケット毎に ACK を送る指針では、cwnd は 2 RTT につき 1 MSS のみ増えるはずである。しかし "*" の印が付いた個所では、7 つの ACK 到着した後にウィンドウが増えており、その後 6 つの ACK を受けた "**" の個所で再び増えている。

その影響を示すスペースは無いが、このトレースは単一の RTT の間に複数のパケットが損失したことによるタイムアウト再送を繰り返した。

正しい動作を実演するトレースファイル:
上記と同じホストとトレースの設定で、A の TCP が MSS/8 を一定して追加することを削除する修正のみ施した。Tcpdump は 77 パケットの損失を通知した。以下の抜粋は完全に自己矛盾は無いので、抜粋した中でこれらが発生したということではなさそうである。

再び cwnd が 31 パケットの時から始める (これはトレースではかなり遅れて発生する。なぜなら、輻輳回避は今、ウィンドウを開くのにあまり積極的ではないからである)。

   14:22:21.236757 B > A: . ack 5194679
   14:22:21.238192 A > B: . 5319727:5324039(4312)
   14:22:21.238770 A > B: . 5324039:5328351(4312)
   14:22:21.238821 B > A: . ack 5203303
   14:22:21.240158 A > B: . 5328351:5332663(4312)
   14:22:21.240738 A > B: . 5332663:5336975(4312)
   14:22:21.270422 B > A: . ack 5211927
   14:22:21.271883 A > B: . 5336975:5341287(4312)
   14:22:21.272458 A > B: . 5341287:5345599(4312)
   14:22:21.279099 B > A: . ack 5220551
   14:22:21.280539 A > B: . 5345599:5349911(4312)
   14:22:21.281118 A > B: . 5349911:5354223(4312)
   14:22:21.281183 B > A: . ack 5229175
   14:22:21.282348 A > B: . 5354223:5358535(4312)
   14:22:21.283029 A > B: . 5358535:5362847(4312)
   14:22:21.283089 B > A: . ack 5237799
   14:22:21.284213 A > B: . 5362847:5367159(4312)
   14:22:21.284779 A > B: . 5367159:5371471(4312)
   14:22:21.285976 B > A: . ack 5246423
   14:22:21.287465 A > B: . 5371471:5375783(4312)
   14:22:21.288036 A > B: . 5375783:5380095(4312)
   14:22:21.288073 B > A: . ack 5255047
   14:22:21.289155 A > B: . 5380095:5384407(4312)
   14:22:21.289725 A > B: . 5384407:5388719(4312)
   14:22:21.289762 B > A: . ack 5263671
   14:22:21.291090 A > B: . 5388719:5393031(4312)
   14:22:21.291662 A > B: . 5393031:5397343(4312)
   14:22:21.291701 B > A: . ack 5272295
   14:22:21.292870 A > B: . 5397343:5401655(4312)
   14:22:21.293441 A > B: . 5401655:5405967(4312)
   14:22:21.293481 B > A: . ack 5280919
   14:22:21.294476 A > B: . 5405967:5410279(4312)
   14:22:21.295053 A > B: . 5410279:5414591(4312)
   14:22:21.295106 B > A: . ack 5289543
   14:22:21.296306 A > B: . 5414591:5418903(4312)
   14:22:21.296878 A > B: . 5418903:5423215(4312)
   14:22:21.296917 B > A: . ack 5298167
   14:22:21.297716 A > B: . 5423215:5427527(4312)
   14:22:21.298285 A > B: . 5427527:5431839(4312)
   14:22:21.298324 B > A: . ack 5306791
   14:22:21.299413 A > B: . 5431839:5436151(4312)
   14:22:21.299986 A > B: . 5436151:5440463(4312)
   14:22:21.303696 B > A: . ack 5315415
   14:22:21.305177 A > B: . 5440463:5444775(4312)
   14:22:21.305755 A > B: . 5444775:5449087(4312)
   14:22:21.308032 B > A: . ack 5324039
   14:22:21.309525 A > B: . 5449087:5453399(4312)
   14:22:21.310101 A > B: . 5453399:5457711(4312)
   14:22:21.310144 B > A: . ack 5332663           ***

   14:22:21.311615 A > B: . 5457711:5462023(4312)
   14:22:21.312198 A > B: . 5462023:5466335(4312)
   14:22:21.341876 B > A: . ack 5341287
   14:22:21.343451 A > B: . 5466335:5470647(4312)
   14:22:21.343985 A > B: . 5470647:5474959(4312)
   14:22:21.350304 B > A: . ack 5349911
   14:22:21.351852 A > B: . 5474959:5479271(4312)
   14:22:21.352430 A > B: . 5479271:5483583(4312)
   14:22:21.352484 B > A: . ack 5358535
   14:22:21.353574 A > B: . 5483583:5487895(4312)
   14:22:21.354149 A > B: . 5487895:5492207(4312)
   14:22:21.354205 B > A: . ack 5367159
   14:22:21.355467 A > B: . 5492207:5496519(4312)
   14:22:21.356039 A > B: . 5496519:5500831(4312)
   14:22:21.357361 B > A: . ack 5375783
   14:22:21.358855 A > B: . 5500831:5505143(4312)
   14:22:21.359424 A > B: . 5505143:5509455(4312)
   14:22:21.359465 B > A: . ack 5384407
   14:22:21.360605 A > B: . 5509455:5513767(4312)
   14:22:21.361181 A > B: . 5513767:5518079(4312)
   14:22:21.361225 B > A: . ack 5393031
   14:22:21.362485 A > B: . 5518079:5522391(4312)
   14:22:21.363057 A > B: . 5522391:5526703(4312)
   14:22:21.363096 B > A: . ack 5401655
   14:22:21.364236 A > B: . 5526703:5531015(4312)
   14:22:21.364810 A > B: . 5531015:5535327(4312)
   14:22:21.364867 B > A: . ack 5410279
   14:22:21.365819 A > B: . 5535327:5539639(4312)
   14:22:21.366386 A > B: . 5539639:5543951(4312)
   14:22:21.366427 B > A: . ack 5418903
   14:22:21.367586 A > B: . 5543951:5548263(4312)
   14:22:21.368158 A > B: . 5548263:5552575(4312)
   14:22:21.368199 B > A: . ack 5427527
   14:22:21.369189 A > B: . 5552575:5556887(4312)
   14:22:21.369758 A > B: . 5556887:5561199(4312)
   14:22:21.369803 B > A: . ack 5436151
   14:22:21.370814 A > B: . 5561199:5565511(4312)
   14:22:21.371398 A > B: . 5565511:5569823(4312)
   14:22:21.375159 B > A: . ack 5444775
   14:22:21.376658 A > B: . 5569823:5574135(4312)
   14:22:21.377235 A > B: . 5574135:5578447(4312)
   14:22:21.379303 B > A: . ack 5453399
   14:22:21.380802 A > B: . 5578447:5582759(4312)
   14:22:21.381377 A > B: . 5582759:5587071(4312)
   14:22:21.381947 A > B: . 5587071:5591383(4312) ****

"***" は最初のラウンドトリップの最後を示している。cwnd は増えなかったことに注意されたい (二つの新しいデータパケットを引き出す各々の ACK で明らかなように)。二回目のラウンドトリップのほぼ最後になる "****" の時点でのみ、cwnd が 1 パケット増加している。

このトレースはタイムアウトの再送を全く被らなかった。これは、最初のトレースと同じ量のデータを約半分の時間で送信した。この相違はホスト A と B との間で再現性がある。

参照:
[Stevens94][Wright95] がこの問題について論じている。再送タイムアウトを除き、複数の損失から回復できない Reno TCP の問題については、[Fall96], [Hoe96] で論じられている。

検出方法:
もしソースコードが入手できるならば、この問題を検出方法として、それが一般的に最も容易な方法である。cwnd 変数を変更している個所を検索せよ。これらのうち (少なくとも) 一つは輻輳回避のためのものであり、関係あるコードの調べることによって、即座にこの問題の有無を確認できるはずである。

この問題は、送信側の近くで採取したパケットトレースを厳密に調査することによって検出することもできる。輻輳回避の間は、損失が無ければ (通常) 8 個の肯定応答を受信した時に、cwnd の追加セグメントが 1 つ増えるだろう。この増加は、ラウンドトリップ時間 (あるいは、もし受信側が遅延 ACK を使用しているならば、2 ラウンドトリップ時間) につき 1 セグメント加算される。

さらに、パケットトレースから作成したシーケンス番号 vs 時間のグラフは、輻輳回避の間は通常一次直線である。この問題を表している送信側からの送信のパケットトレースを見ると、そのグラフは一次直線ではなく二次曲線として見えるだろう。

最後に、トレースは、十分に大きなウィンドウでは、ほぼ全ての損失がタイムアウトを招くことを示すだろう。

修正方法:
この問題は、新しいデータの ACK を受信するたびに cwnd を増やす輻輳回避のコードから、"+ MSS/8" の部分を削除することによって修正できるかもしれない。

2.7 小さ過ぎる初期 RTO

問題の名前:
小さ過ぎる初期 RTO

分類:
パフォーマンス

説明:
TCP は最初にデータ送信を開始するとき、適切な再送タイムアウト (RTO) を算出するために必要な RTT 計測を行わない。RFC1122, 4.2.3.1 は、TCP は RTO を 3 秒に初期化すべき (SHOULD) であると述べている。より低い値を使用する TCP は "小さ過ぎる初期 RTO" を示す。

重要性:
大きい RTT を持つ環境では ("大きい" とは、初期 RTO よりも大きい値を意味する)、TCP は非常に貧弱なパフォーマンスを経験するだろう。

暗黙の意味:
RTO < RTT の場合は常に、不必要にパケットが再送され (なぜならパケットに対する ACK が到着する前に、RTO が満了するから)、コネクションがスロースタートや輻輳回避に入るため、パフォーマンスが非常に貧弱になるだろう。通常、RTO を算出するアルゴリズムは、RTT を見積もるために実名辞を追加することによってこの問題を回避する。しかし、コネクションが最初に開始される時は、ある程度見積もった RTO を使用しなければならず、もし RTT よりも小さい値が採用されたら、上記の問題が起きるだろう。

さらに初期 RTO < RTT ならば、RTT 測定を適用することによって TCP がこの問題を修正するのに長い時間がかかり得る。なぜなら、最初のタイムアウト後に、Karn のアルゴリズム (RFC1122, 4.2.3.1 で必須とされている) を使用することによって、RTT の測定候補の多くが破棄されるからである。というのも、それらは再送されたセグメントの測定だからである。

関連 RFC:
RFC1122 は、TCP は RTO を 3 秒に初期化すべきであり (SHOULD)、Karn のアルゴリズムを実装しなければならない (MUST) ことを述べている。

その問題を実演するトレースファイル:
以下のトレースファイルは、データの送信側であるホスト A で、tcpdump を使用して採取された。分かりやすくするためにウィンドウの通知と SYN オプションは省略した。

  07:52:39.870301 A > B: S 2786333696:2786333696(0)
  07:52:40.548170 B > A: S 130240000:130240000(0) ack 2786333697
  07:52:40.561287 A > B: P 1:513(512) ack 1
  07:52:40.753466 A > B: . 1:513(512) ack 1
  07:52:41.133687 A > B: . 1:513(512) ack 1
  07:52:41.458529 B > A: . ack 513
  07:52:41.458686 A > B: . 513:1025(512) ack 1
  07:52:41.458797 A > B: P 1025:1537(512) ack 1
  07:52:41.541633 B > A: . ack 513
  07:52:41.703732 A > B: . 513:1025(512) ack 1
  07:52:42.044875 B > A: . ack 513
  07:52:42.173728 A > B: . 513:1025(512) ack 1
  07:52:42.330861 B > A: . ack 1537
  07:52:42.331129 A > B: . 1537:2049(512) ack 1
  07:52:42.331262 A > B: P 2049:2561(512) ack 1
  07:52:42.623673 A > B: . 1537:2049(512) ack 1
  07:52:42.683203 B > A: . ack 1537
  07:52:43.044029 B > A: . ack 1537
  07:52:43.193812 A > B: . 1537:2049(512) ack 1

SYN/SYN-ACK の交換から、RTT は 600 ミリ秒以上であることに注意されたい。しかし、3 行目と 4 行目の間 (最初にパケットが送信され、続いて再送された) で経過した時間より、RTO が 200 ミリ秒以下に初期化されたように見える。次の行は、この値が 2 倍されて 400 ミリ秒になった (正しい指数関数的な RTO の緩和である) ことを示している。しかし、依然として不要な再送を避けるのに十分機能していない。

最終的にに、最初のセグメントに対する B からの ACK が到着した。その後の 513 に対する 2 つの重複 ACK は B に到着した。これは、大元と 2 つの再送の両方を示している。(実際、B で同時に採取したトレースによると、コネクション全体に渡って損失したパケットは無い)。この ACK により輻輳ウィンドウが 2 パケット開き、そのパケットは連続して送信されている。しかし、200 ミリ秒を少し過ぎた後の 07:52:41.703732 に、RTO が再び満了し、不要な再送を招いている。上記で抜粋されたトレースの最後までに、A から B に 1536 バイト正常に送信されたが、2 秒以上の間隔が経過しており、ひどいパフォーマンスをもたらしている。

正しい動作を実演するトレースファイル:
以下のトレースかファイルは、データ送信側であるホスト C で tcpdump を使用して採取された。分かりやすくするためにウィンドウの通知と SYN オプションは省略した。

  17:30:32.090299 C > D: S 2031744000:2031744000(0)
  17:30:32.900325 D > C: S 262737964:262737964(0) ack 2031744001
  17:30:32.900326 C > D: . ack 1
  17:30:32.910326 C > D: . 1:513(512) ack 1
  17:30:34.150355 D > C: . ack 513
  17:30:34.150356 C > D: . 513:1025(512) ack 1
  17:30:34.150357 C > D: . 1025:1537(512) ack 1
  17:30:35.170384 D > C: . ack 1025
  17:30:35.170385 C > D: . 1537:2049(512) ack 1
  17:30:35.170386 C > D: . 2049:2561(512) ack 1
  17:30:35.320385 D > C: . ack 1537
  17:30:35.320386 C > D: . 2561:3073(512) ack 1
  17:30:35.320387 C > D: . 3073:3585(512) ack 1
  17:30:35.730384 D > C: . ack 2049

最初のSYN/SYN-ACK の交換は、RTT が 800 ミリ秒以上であることを示しており、後続のパケットの幾つかは 1 秒を超えている。しかし C の再送タイマは一度も満了していないパケットの幾つかは 1 秒を超えている。しかし C の再送タイマは一度も満了していない。

参照:
この問題は、[Paxson97] でドキュメント化されている。

検出方法:
この問題は、遅延の大きいパス上で生成された TCP コネクションの開始時のパケットトレースを検査することによって容易に検出される。それは、送信側か受信側のいずれかのトレースを分析することで可能である。遅延の大きいパスは、リモートサイトを別大陸に配置することによって大抵見つかる。

修正方法:
この問題は欠陥のある初期化が原因なので、それを修正するには TCP のソースコードを 1 行変更する必要があるだろう

2.8 損失回復後にウィンドウを小さくしない

問題の名前:
損失回復後にウィンドウを小さくしない

分類:
輻輳制御 / パフォーマンス

説明:
高速回復アルゴリズムにより、TCP 送信側は損失回復の間に新しいセグメントの送信を継続することが可能になる。まず最初に、高速再送アルゴリズムは、TCP 送信側が 3 つの重複 ACK を受信した後に起動される。この時点で再送が行なわれ、cwnd が半分になる。そして、高速回復アルゴリズムは、さらに追加の重複 ACK が到着した時に、追加のセグメント送信を許している。ある高速再送の実装では、人工的に cwnd を加算することによって、追加のセグメントを送信タイミングを算出する。最初に、3 つのセグメントによって高速再送を引き起こした 3 つの重複 ACK を加算し、続いて新しい重複 ACK が到着する毎に 1 MSS を加算する。cwnd が許せば、送信側は新しいデータセグメントを送信する。

新しいデータをカバーする ACK が到着すると、cwnd は人工的に加算した分を減らすことになる。しかし、ある TCP の実装はウィンドウを "引き下げる" ことをせず、回復後に不適切な量のデータのネットワークへの流入を引き起こしている。この問題の一つの原因は "ヘッダ予測" コードであり、それは若干の動作を必要とする入力セグメントを扱うために使用される。TCP の幾つかの実装では、ヘッダ予測コードで、cwnd が人工的に膨れ上がらないことを確かめるチェックをしていない。そのため、人工的に加算された cwnd を適時減らさないのである。

重要性:
この問題を示す TCP 送信側は、回復直後にデータを突発的に送信する。それはネットワークの安定性だけでなく、パフォーマンスも下げる可能性がある。効果的に、送信側は可能な限り (損失が検出されたとき、その値を半分にする) cwnd のサイズをほとんど減らさない。これは TCP フローの競争だけでなく、TCP コネクション自身のパフォーマンスも損なうかもしれない。

暗黙の意味:
この問題を示す TCP 送信側は輻輳時に cwnd を適切に減らさないので、輻輳による崩壊をもたらかすもしれない。

関連 RFC:
RFC2001 は高速再送/高速回復アルゴリズムについて概説している。[Brakmo95] はこの実装の問題を概説し、修正することを提案している。

その問題を実演するトレースファイル:
以下のトレースファイルは、データ送信側であるホスト A で tcpdump を使用して採取された。分かりやすくするために、各ホストによって一番最初に送信されたパケットを除いて、ウィンドウ通知 (変更されてはいない) は省略している。

  08:22:56.825635 A.7505 > B.7505: . 29697:30209(512) ack 1 win 4608
  08:22:57.038794 B.7505 > A.7505: . ack 27649 win 4096
  08:22:57.039279 A.7505 > B.7505: . 30209:30721(512) ack 1
  08:22:57.321876 B.7505 > A.7505: . ack 28161
  08:22:57.322356 A.7505 > B.7505: . 30721:31233(512) ack 1
  08:22:57.347128 B.7505 > A.7505: . ack 28673
  08:22:57.347572 A.7505 > B.7505: . 31233:31745(512) ack 1
  08:22:57.347782 A.7505 > B.7505: . 31745:32257(512) ack 1
  08:22:57.936393 B.7505 > A.7505: . ack 29185
  08:22:57.936864 A.7505 > B.7505: . 32257:32769(512) ack 1
  08:22:57.950802 B.7505 > A.7505: . ack 29697 win 4096
  08:22:57.951246 A.7505 > B.7505: . 32769:33281(512) ack 1
  08:22:58.169422 B.7505 > A.7505: . ack 29697
  08:22:58.638222 B.7505 > A.7505: . ack 29697
  08:22:58.643312 B.7505 > A.7505: . ack 29697
  08:22:58.643669 A.7505 > B.7505: . 29697:30209(512) ack 1
  08:22:58.936436 B.7505 > A.7505: . ack 29697
  08:22:59.002614 B.7505 > A.7505: . ack 29697
  08:22:59.003026 A.7505 > B.7505: . 33281:33793(512) ack 1
  08:22:59.682902 B.7505 > A.7505: . ack 33281
  08:22:59.683391 A.7505 > B.7505: P 33793:34305(512) ack 1
  08:22:59.683748 A.7505 > B.7505: P 34305:34817(512) ack 1 ***
  08:22:59.684043 A.7505 > B.7505: P 34817:35329(512) ack 1
  08:22:59.684266 A.7505 > B.7505: P 35329:35841(512) ack 1
  08:22:59.684567 A.7505 > B.7505: P 35841:36353(512) ack 1
  08:22:59.684810 A.7505 > B.7505: P 36353:36865(512) ack 1
  08:22:59.685094 A.7505 > B.7505: P 36865:37377(512) ack 1

トレースの最初の 12 行は、データセグメントのウィンドウを記録している入力 ACK を示している。送信のこの時点で、cwnd は 7 セグメントである。トレースの次の 4 行は、受信側から到着した重複 ACK と、続いて送信側からの再送を示している。この時点で、cwnd は半分にされ (3 セグメント)、到着した 3 つの重複 ACK によって人工的に加算される (cwnd は 6 セグメント)。次の 2 行は 2 つの更なる重複 ACK の到着を示しており、いずれも cwnd を 1 セグメントずつ加算する。従って、これらの 2 つの重複 ACK が到着した後、cwnd は 8 セグメントになっており、送信側は新たに 1 セグメント送信することが許される (7 セグメントは未処理なので)。トレースのその次の行は、この新たなセグメントが送信されたことを示している。トレースに表れる次のパケットは、最初の 7 つの未処理のセグメントをカバーした、ホスト B からの ACK である (回復中に送信された新しいセグメント以外の全て)。これにより、cwnd は 3 セグメントに減らされて、2 セグメント送信されることになる (既に未処理のセグメントがネットワーク中に存在するため)。しかし、トレースの最後の 7 行に見られるように、cwnd が減らされておらず、7 つの新たなセグメントの連続的な突発的送信を引き起こしている。

正しい動作を実演するトレースファイル:
トレースは、上記のトレースと同じように見えるだろう。唯一の違いは、"***" とマークされた行の後ろで止まることである。なぜなら、この時点でホスト A は回復後に正しく cwnd を減らし、7 セグメントを突発的に送信するのではなく、2 セグメントのみ送信することが可能だからである。

参照:
この問題は、[Brakmo95] でドキュメント化され、パフォーマンスとの関連性が分析されている。

検出方法:
損失回復後にウィンドウを小さくしない問題は、損失を弱める期間中に記録された (つまり cwnd は、損失が発生したときの高速回復を可能するだけの十分な大きさになっている) 送信側のパケットトレースを調べることによって検出することができる

修正方法:
このバグが正しくないヘッダ予測によるものである場合、修正は cwnd が増やされているか否かをチェックするヘッダ予測テストに、次の述部を追加することである。もし増やされていればヘッダ予測テストは失敗し、通常の ACK 処理を行なう。それは (この場合)、ウィンドウの加算に注意を払う。詳細は [Brakmo95] を参照されたい。

2.9 短すぎる keepalive タイムアウト

問題の名前:
短すぎる keepalive タイムアウト

分類:
信頼性

説明:
Keep-alive は、アイドルコネクションがまだ生きているか否かをチェックするためのメカニズムである。RFC1122 に従って、keepalive はクライアントがクラッシュするか、あるいはネットワーク障害の間にコネクションをアボートしたら、永久にハングして不必要にリソースを消費することがあり得るサーバアプリケーションでのみ起動されるべきである。

RFC1122 は、もし keep-alive メカニズムを実装したら、特定のプローブに対して応答がないことを死んだコネクションとして解釈してはならない (MUST NOT) ということも規定している。その RFC は、keepalive のプローブに対して応答が無い時に、コネクションをタイムアウトする特定のメカニズムを規定していない。しかし、もしそのメカニズムがネットワーク輻輳や遅延から回復するための必要十分な時間を考慮していなければ、コネクションが不要にタイムアウトされるかもしれない。

重要性:
輻輳したネットワークでは、コネクションの不当な終了を招く可能性がある。

暗黙の意味:
二つのペアマシン間のネットワークコネクションは輻輳する可能性があるし、あるいは keep-alive プローブがコネクション上に送信された時に、パケット損失が発生する可能性がある。もし keep-alive メカニズムが、肯定応答のないプローブに直面してコネクションを落とす前に十分な時間を認めていなければ、たとえコネクションの両方のペアがまだ生きていても、コネクションが落とされるかもしれない。

関連 RFC:
RFC1122 は、keep-alive メカニズムを提供してもよいと規定している。RFC1122 は、keepalive プローブが確認されない場合に、死んだコネクションを検出するためのメカニズムは提供していない。

その問題を実演するトレースファイル:
keep-alive を使用するマシンのペアで、Orchestra ツールを使用して採取した。死んだコネクションを擬似するために、コネクション確立後に受信した keep-alive を、Orchestra によって落とした。

  22:11:12.040000 A > B: 22666019:0 win 8192 datasz 4 SYN
  22:11:12.060000 B > A: 2496001:22666020 win 4096 datasz 4 SYN ACK
  22:11:12.130000 A > B: 22666020:2496002 win 8760 datasz 0 ACK
  (2 時間以上経過)
  00:23:00.680000 A > B: 22666019:2496002 win 8760 datasz 1 ACK
  00:23:01.770000 A > B: 22666019:2496002 win 8760 datasz 1 ACK
  00:23:02.870000 A > B: 22666019:2496002 win 8760 datasz 1 ACK
  00:23.03.970000 A > B: 22666019:2496002 win 8760 datasz 1 ACK
  00:23.05.070000 A > B: 22666019:2496002 win 8760 datasz 1 ACK

最初の 3 つのパケットは、コネクション設定のための SYN の交換である。約 2 時間後に、コネクションがアイドルになったため、keepalive タイマが点火された。Keepalive プローブは計 5 回送信され、そのプローブの間隔は 1 秒である。その後コネクションは落とされた。これは、一番最初のプローブの時に 5 秒間ネットワークが停止されると、コネクションが殺される結果を招くので問題である。

正しい動作を実演するトレースファイル:
keep-alive を使用するマシンのペアで、Orchestra ツールを使用して採取した。死んだコネクションを擬似するために、コネクション確立後に受信した keep-alive を、Orchestra によって落とした。

  16:01:52.130000 A > B: 1804412929:0 win 4096 datasz 4 SYN
  16:01:52.360000 B > A: 16512001:1804412930 win 4096 datasz 4 SYN ACK
  16:01:52.410000 A > B: 1804412930:16512002 win 4096 datasz 0 ACK
  (2 時間経過)
  18:01:57.170000 A > B: 1804412929:16512002 win 4096 datasz 0 ACK
  18:03:12.220000 A > B: 1804412929:16512002 win 4096 datasz 0 ACK
  18:04:27.270000 A > B: 1804412929:16512002 win 4096 datasz 0 ACK
  18:05:42.320000 A > B: 1804412929:16512002 win 4096 datasz 0 ACK
  18:06:57.370000 A > B: 1804412929:16512002 win 4096 datasz 0 ACK
  18:08:12.420000 A > B: 1804412929:16512002 win 4096 datasz 0 ACK
  18:09:27.480000 A > B: 1804412929:16512002 win 4096 datasz 0 ACK
  18:10:43.290000 A > B: 1804412929:16512002 win 4096 datasz 0 ACK
  18:11:57.580000 A > B: 1804412929:16512002 win 4096 datasz 0 ACK
  18:13:12.630000 A > B: 1804412929:16512002 win 4096 datasz 0 RST ACK

このトレースでは、keep-alive タイマが満了した時、75 秒間隔で 9 つの keep-alive プローブが送信された。最後のプローブが送信されてから 75 秒後に最後の RST セグメントが送信され、コネクションのクローズが示された。最初の実装体は 5 秒しか許していないのに対して、この実装体はコネクションがタイムアウトするまで約 11 分待っている。

参照:
この問題は、[Dawson97] でドキュメント化されている。

検出方法:
この問題が表れる実装では、keepalive を受信した相手のマシンが応答しなければ、この問題が keepalive タイマが点火した後のパケットトレース上に現れる。通常 keepalive タイマは、keepalive が開始してから少なくとも 2 時間後に点火するだろう。しかし、もしタイマ値がより小さく設定されているか、keepalive メカニズムが規約に反していれば (keepalive 間の間隔不十分の問題参照)、より早いかもしれない。この例では、Orchestra ツールキットを使用して、keepalive プローブに対する相手側の応答を抑止することによって実現した。Orchestra はパケットを落とすよう設定することができる。また、コネクションを生成して keepalive を開始し、受信側マシン側でネットワークコネクションを切断することによっても実現できる。

修正方法:
この問題は、コネクションを落とす前に、より長い時間の経過を許すような異なる方法を、keepalive のタイムアウトとして使用することによって修正できる。例えば、落とされたデータをタイムアウトするためのアルゴリズムを使用することもできる。または、上記のトレースに見られるようなアルゴリズムでも可能である。そこでは、コネクションをクローズする前に 75 秒間隔で 9 つのプローブを送信し、さらに 75 秒間応答を待っている。

2.10 再送タイムアウトの緩和無し

問題の名前:
再送タイムアウトの緩和無し

分類:
輻輳制御/信頼性

説明:
再送タイムアウトは、パケットがネットワーク中でいつ落とされたかを決定するために使用される。ACK が到着せずにこのタイムアウトが満了した場合、そのセグメントは再送される。セグメントが再送される毎に、タイムアウトは指数関数的な緩和アルゴリズム、再送する度に 2 倍、に従って調整される。もし TCP が、同じセグメントを再送する試みを数多く行なっても ACK を受信しなければ、コネクションを終了する。タイムアウトの繰り返し時に再送タイムアウトを 2 倍しない TCP は、"再送タイムアウトの緩和無し" を示すものと呼ばれる。

重要性:
再送タイムアウトの緩和は、輻輳のあるネットワークの安定性の基礎である。つまり、このバグは輻輳したネットワークにひどい悪影響を及ぼす可能性がある。また、次のセクションで論じられるように、輻輳したネットワークにおける TCP の信頼性にも影響する可能性がある。

暗黙の意味:
再送がコネクション上に送信される時に、二つの TCP ペア間のネットワークコネクションが輻輳するか、あるいはパケット損失を示す可能性がある。もし再送メカニズムが、未確認のセグメントに直面してコネクションを落とす前に十分な時間を許していなければ、たとえコネクションがもう少し長く待つことによって継続しても、コネクションは落とされるかもしれない。

関連 RFC:
RFC 1122 は再送タイムアウトの指数関数的な緩和を必須とし、幾つかの周期後 (少なくとも 100 秒) にコネクションを終了するよう規定している。

その問題を実演するトレースファイル:
中間ホストで tcpdump を使用して作成した。

  16:51:12.671727 A > B: S 510878852:510878852(0) win 16384
  16:51:12.672479 B > A: S 2392143687:2392143687(0)
                           ack 510878853 win 16384
  16:51:12.672581 A > B: . ack 1 win 16384
  16:51:15.244171 A > B: P 1:3(2) ack 1 win 16384
  16:51:15.244933 B > A: . ack 3 win 17518  (DF)

  <受信側ホストが切断される>

  16:51:19.381176 A > B: P 3:5(2) ack 1 win 16384
  16:51:20.162016 A > B: P 3:5(2) ack 1 win 16384
  16:51:21.161936 A > B: P 3:5(2) ack 1 win 16384
  16:51:22.161914 A > B: P 3:5(2) ack 1 win 16384
  16:51:23.161914 A > B: P 3:5(2) ack 1 win 16384
  16:51:24.161879 A > B: P 3:5(2) ack 1 win 16384
  16:51:25.161857 A > B: P 3:5(2) ack 1 win 16384
  16:51:26.161836 A > B: P 3:5(2) ack 1 win 16384
  16:51:27.161814 A > B: P 3:5(2) ack 1 win 16384
  16:51:28.161791 A > B: P 3:5(2) ack 1 win 16384
  16:51:29.161769 A > B: P 3:5(2) ack 1 win 16384
  16:51:30.161750 A > B: P 3:5(2) ack 1 win 16384
  16:51:31.161727 A > B: P 3:5(2) ack 1 win 16384

  16:51:32.161701 A > B: R 5:5(0) ack 1 win 16384

最初の 3 つのパケットはコネクション設定のための SYN の交換であり、その次はデータが送信できることを確かめるために単一のデータパケットである。そして、宛先ホストへのコネクションが切断され、さらにデータが送信される。再送は 12 秒もの間、1 秒毎に発生しており、その後コネクションは RST で終了される。接続が 12 秒停止すると結果としてコネクションの終了を招くので、これは問題を含んでいる。

正しい動作を実演するトレースファイル:
また、第三のホストで tcpdump で採取した。

  16:59:05.398301 A > B: S 2503324757:2503324757(0) win 16384
  16:59:05.399673 B > A: S 2492674648:2492674648(0)
                          ack 2503324758 win 16384
  16:59:05.399866 A > B: . ack 1 win 17520
  16:59:06.538107 A > B: P 1:3(2) ack 1 win 17520
  16:59:06.540977 B > A: . ack 3 win 17518  (DF)

  <受信側ホストが切断される>

  16:59:13.121542 A > B: P 3:5(2) ack 1 win 17520
  16:59:14.010928 A > B: P 3:5(2) ack 1 win 17520
  16:59:16.010979 A > B: P 3:5(2) ack 1 win 17520
  16:59:20.011229 A > B: P 3:5(2) ack 1 win 17520
  16:59:28.011896 A > B: P 3:5(2) ack 1 win 17520
  16:59:44.013200 A > B: P 3:5(2) ack 1 win 17520
  17:00:16.015766 A > B: P 3:5(2) ack 1 win 17520
  17:01:20.021308 A > B: P 3:5(2) ack 1 win 17520
  17:02:24.027752 A > B: P 3:5(2) ack 1 win 17520
  17:03:28.034569 A > B: P 3:5(2) ack 1 win 17520
  17:04:32.041567 A > B: P 3:5(2) ack 1 win 17520
  17:05:36.048264 A > B: P 3:5(2) ack 1 win 17520
  17:06:40.054900 A > B: P 3:5(2) ack 1 win 17520

  17:07:44.061306 A > B: R 5:5(0) ack 1 win 17520

このトレースでは再送タイマが満了する時、間隔の値が 64 秒、間隔を大きくすることを停止する時間、に達するまで指数関数的に増加して、12 個の再送が送信された。最後に再送されてから 64 秒後に、コネクションが閉じたことを示す最後の RST セグメントが送信された。最初の実装体は 12 秒しか許していないが、この実装体はコネクションをタイムアウトする前に約 9 分待っている。

参照:
不明

検出方法:
単純な送信は、受信側ホストをネットワークから切り離すことによって容易に中断できる。tcpdump か他の適切なツールによって、再送が送信されていることが分かるはずである。バグを立証するには、低い rtt 環境で何回か試みる必要があるかもしれない。

修正方法:
実装研究の一つによると、この問題は、古い Jacobson アルゴリズムは常に正の値を返却するが、0 の値を返却する可能性のある Brakmo-Peterson RTO アルゴリズム [Brakmo95] を追加して持ちこまれたエラーの結果のようである。Brakmo と Peterson は、この問題を回避するために min(rtt + 2, RTO) の追加ステップを規定した。不幸にも、その実装では RTO の指数関数的な緩和を計算する際に、このステップが省略された。結果的に緩和の要素があるにも関わらず、 0 秒の RTO が緩和に乗算され、再度 0 になり、その後の MAX 処理でそれに 1 秒が加算されることになる。

同様な TCP 永久障害は、同じ原因である。

2.11 keepalive 間の間隔不十分

問題の名前:
keepalive 間の間隔不十分

分類:
信頼性

説明:
Keep-alive は、アイドルコネクションがまだ生きているか否かをチェックするためのメカニズムである。RFC1122 に従って、keep-alive を実装体に含めてもよい。もし含めるならば、keep-alive パケットの間隔は設定可能にしなければならず (MUST)、デフォルトは 2 時間以上で無ければならない (MUST)。

重要性:
輻輳したネットワークで、コネクションの正しくない終了を招く可能性がある。

暗黙の意味:
RFC1122 によると、keep-alive は、(1) 一時的なインターネット障害の間に、完璧に正常なコネクションを切断する可能性がある、(2) 不要な帯域を消費する ("もし誰もそのコネクションを使用していなければ、それが正常か否か誰が分かるか?")、(3) パケット課金のインターネットパスではお金がかかる、ので実装する必要は無い。この最後の点に関して、経路中に要求時ダイアル回線が存在すると過剰な keepalive のコスト料金が非常に拡大し得ることに加えて、keepalive を使用しなければ 1 日に数分しか接続しない回線で、潜在的に常時接続を強いる可能性があることを注記しておく。

もし keepalive が提供されたら、RFC は必要な内部 keepalive 間隔は 2 時間以上でなければならない (MUST) と述べている。さもなくば、コネクション切断の可能性が増え、そしてパケット毎に課金するパス上ではコストが増える。

関連 RFC:
RFC 1122 は、keep-alive メカニズムを提供してもよいと規定している。また、keepalive プローブのデフォルトの間隔は、最低 2 時間であるとも規定している。

その問題を実演するトレースファイル:
keep-alive を使用しているマシンの両方で、Orchestra ツールを使用して作成した。マシン A は keepalive タイマのデフォルト設定を使うよう設定されている。

  11:36:32.910000 A > B: 3288354305:0      win 28672 datasz 4 SYN
  11:36:32.930000 B > A: 896001:3288354306 win 4096  datasz 4 SYN ACK
  11:36:32.950000 A > B: 3288354306:896002 win 28672 datasz 0 ACK

  11:50:01.190000 A > B: 3288354305:896002 win 28672 datasz 0 ACK
  11:50:01.210000 B > A: 896002:3288354306 win 4096  datasz 0 ACK

  12:03:29.410000 A > B: 3288354305:896002 win 28672 datasz 0 ACK
  12:03:29.430000 B > A: 896002:3288354306 win 4096  datasz 0 ACK

  12:16:57.630000 A > B: 3288354305:896002 win 28672 datasz 0 ACK
  12:16:57.650000 B > A: 896002:3288354306 win 4096  datasz 0 ACK

  12:30:25.850000 A > B: 3288354305:896002 win 28672 datasz 0 ACK
  12:30:25.870000 B > A: 896002:3288354306 win 4096  datasz 0 ACK

  12:43:54.070000 A > B: 3288354305:896002 win 28672 datasz 0 ACK
  12:43:54.090000 B > A: 896002:3288354306 win 4096  datasz 0 ACK

最初の 3 つのパケットは、コネクション設定のための SYN の交換である。約 13 分後、コネクションがアイドルであるために keepalive タイマが満了した。keepalive は肯定応答を返され、約 13 分後に再びタイマが満了した。この振る舞いはコネクションがクローズするまで永久に続き、それは規約に違反している。

正しい動作を実演するトレースファイル:
keep-alive を使用しているマシンの両方で、Orchestra ツールを使用して作成した。マシン A は keepalive タイマのデフォルト設定を使うよう設定されている。

  17:37:20.500000 A > B: 34155521:0       win 4096 datasz 4 SYN
  17:37:20.520000 B > A: 6272001:34155522 win 4096 datasz 4 SYN ACK
  17:37:20.540000 A > B: 34155522:6272002 win 4096 datasz 0 ACK

  19:37:25.430000 A > B: 34155521:6272002 win 4096 datasz 0 ACK
  19:37:25.450000 B > A: 6272002:34155522 win 4096 datasz 0 ACK
  21:37:30.560000 A > B: 34155521:6272002 win 4096 datasz 0 ACK
  21:37:30.570000 B > A: 6272002:34155522 win 4096 datasz 0 ACK

  23:37:35.580000 A > B: 34155521:6272002 win 4096 datasz 0 ACK
  23:37:35.600000 B > A: 6272002:34155522 win 4096 datasz 0 ACK

  01:37:40.620000 A > B: 34155521:6272002 win 4096 datasz 0 ACK
  01:37:40.640000 B > A: 6272002:34155522 win 4096 datasz 0 ACK

  03:37:45.590000 A > B: 34155521:6272002 win 4096 datasz 0 ACK
  03:37:45.610000 B > A: 6272002:34155522 win 4096 datasz 0 ACK

最初の 3 つのパケットは、コネクション設定のための SYN の交換である。ちょうど 2 時間後に、コネクションがアイドルであるために keepalive タイマが満了した。keepalive は肯定応答を返され、ちょうど 2 時間後に再びタイマが満了した。この振る舞いはコネクションがクローズするまで永久に続く。

参照:
この問題は、[Dawson97] でトキュメント化されている。

検出方法:
この問題を表す実装体では、パケットトレースに表れる。もしコネクションがアイドルのままであれば、keepalive プローブは最低 2 時間よりも短くなって互いに到着するだろう。

2.12 ウィンドウプローブのデッドロック

問題の名前:
ウィンドウプローブのデッドロック

分類:
信頼性

説明:
アプリケーションが満杯のウィンドウから 1 バイトを読み込む時、馬鹿げたウィンドウ症候群 (SWS: Silly Window Syndrome [RFC813] 参照) を避けるために、ウィンドウを更新すべきではない。もしリモートの相手がウィンドウをプローブするために 1 バイトのデータを使うならば、そのバイトをバッファの中に入れることができる。ある実装では、この時点で符号付き整数の比較への負値のアーギュメントにより、後続する新しい全てのデータがウィンドウの範囲外として見なされ、結果的に破棄される (再度同期をとるために ACK を送信した後)。これらの破棄はローカルの TCP によって送信されたパケットに対する ACK を含むので、TCP はデータが未確認であると見なすことになる。

つまりアプリケーションは、ローカルの TCP で利用可能な送信バッファが枯渇したがゆ故に、リモートの相手への新しいデータの送信を完了することができない。そして、入力 ACK は破棄されるのでバッファ空間は決して解放されない。もしアプリケーションがそれ以上データを読み込まなければ、そのような送信は完了しないため、結果としてデッドロックが起きる。

重要性:
この問題が発生するようなやり方で TCP を使うことは、相対的に稀である。大半のアプリケーションは、他方の終端がデータを受信する準備ができていることを知っていれば、大量のデータを送信するだけである。しかし、もしクライアントがデータを消費せず、永久モードでサーバに置いて、その後少量のデータを消費したら、誤って負値のウィンドウを算出する可能性がある。この時点で符号付き整数の比較への負値のアーギュメントにより、クライアント自身のデータの ACK を含めて、そのサーバからの後続する全てのパケットを破棄するだろう。なぜなら、それらはウィンドウ内 (あり得ないサイズ) ではないからである。もし、続いてクライアントがウィンドウ更新をサーバに送信するのに十分なデータを受信すれば、その状況は修正される。すなわち、この状況はクライアントが、ウィンドの更新を起こさない 1 < N < MSS バイトを消費して、その後サーバに向けてウィンドウ値以上の自分自身のデータの送信を開始する場合に限って発生する。

暗黙の意味:
TCP コネクションはハングアップし、最終的にタイムアウトになるだろう。

関連 RFC:
RFC793 は 0 ウィンドウのプローブについて規定している。RFC813 は馬鹿げたウィンドウ症候群について規定している。

その問題を実演するトレースファイル:
トレースは、データが存在していなくても、ACK に割り当てられたシーケンス番号を表示するよう修正された tcpdump のバージョンで作成した。修正されていない tcpdump は seq:seq(0) は表示しない。しかしこのバグでは、ACK 中のシーケンス番号はどのように TCP が動作するかを明確に決定するために重要である。

  [ B から A への正常なコネクション開始とデータ転送。オプションで  
  16344 の MSS を両方向で含んでいる。簡略化のため省略]

  16:07:32.327616 A > B: S 65360807:65360807(0) win 8192
  16:07:32.327304 B > A: S 65488807:65488807(0) ack 65360808 win 57344
  16:07:32.327425 A > B: . 1:1(0) ack 1 win 57344
  16:07:32.345732 B > A: P 1:2049(2048) ack 1 win 57344
  16:07:32.347013 B > A: P 2049:16385(14336) ack 1 win 57344
  16:07:32.347550 B > A: P 16385:30721(14336) ack 1 win 57344
  16:07:32.348683 B > A: P 30721:45057(14336) ack 1 win 57344
  16:07:32.467286 A > B: . 1:1(0) ack 45057 win 12288
  16:07:32.467854 B > A: P 45057:57345(12288) ack 1 win 57344

   [ B は A が申し出たウィンドウが一杯になる ]
   16:07:32.667276 A > B: . 1:1(0) ack 57345 win 0

   [ B は A のウィンドウを 1 バイトでプローブする ]
   16:07:37.467438 B > A: . 57345:57346(1) ack 1 win 57344

   [ A はそのバイトを受諾せずに再度同期をとる ]
   16:07:37.467678 A > B: . 1:1(0) ack 57345 win 0

   [ B は再度 A のウィンドウをプローブする ]
   16:07:45.467438 B > A: . 57345:57346(1) ack 1 win 57344

   [ A は再度同期をとり、そのバイトを受諾する (ACK フィールドより) ]
   16:07:45.667250 A > B: . 1:1(0) ack 57346 win 0

   [ A 上のアプリケーションは、データの生成を開始する。A が送信す 
     る最初のパケットは、メモリ割り当てバグのため小さい ]
   16:07:51.358459 A > B: P 1:2049(2048) ack 57346 win 0

   [ B は A の最初のパケットに肯定応答する ]
   16:07:51.467239 B > A: . 57346:57346(0) ack 2049 win 57344

   [ これはあたかも A が B の ACK を受諾し、その応答として別のパケッ
     トを送信しているように見える。実際、A は B と再度同期をとろう
     と試みて、たまたま送信するデータがあり、最初の小さなパケット 
     は cwnd を使い果たしていなかったので、送信することができた ]
   16:07:51.467698 A > B: . 2049:14337(12288) ack 57346 win 0

   [ B は A が送信したデータの全てに肯定応答する ]
   16:07:51.667283 B > A: . 57346:57346(0) ack 14337 win 57344

   [ A は再度同期をとろうと試みる。ネットワークに見られるパケット 
     によると、A と B は実際に同期がとれている。A がそうではないと
     思っているだけである ]
   16:07:51.667477 A > B: . 14337:14337(0) ack 57346 win 0

   [ A の再送タイマは満了して、B は A が再度同期をとろうと試みて送
     信したデータの全てに肯定応答する ]
   16:07:52.467682 A > B: . 1:14337(14336) ack 57346 win 0
   16:07:52.468166 B > A: . 57346:57346(0) ack 14337 win 57344
   16:07:52.468248 A > B: . 14337:14337(0) ack 57346 win 0

   [ A の再送タイマが再び満了して、B は A が再度同期をとろうと試み
     て送信したデータの全てに肯定応答する ]
   16:07:55.467684 A > B: . 1:14337(14336) ack 57346 win 0
   16:07:55.468172 B > A: . 57346:57346(0) ack 14337 win 57344
   16:07:55.468254 A > B: . 14337:14337(0) ack 57346 win 0

正しい動作を実演するトレースファイル:
以下で述べられているバグの修正を適用した後、同じ二つのホスト間で作成した (そして同じ修正版 tcpdump を使用した)。

  [ コネクションは B から A にデータ転送して開始する。別のバグによ
    って (実際、A と B はループバックドライバ上で通信している)、B 
    は誤ってスロースタートを飛ばす。 ]

   17:38:09.510854 A > B: S 3110066585:3110066585(0) win 16384
   17:38:09.510926 B > A: S 3110174850:3110174850(0)
                            ack 3110066586 win 57344
   17:38:09.510953 A > B: . 1:1(0) ack 1 win 57344
   17:38:09.512956 B > A: P 1:2049(2048) ack 1 win 57344
   17:38:09.513222 B > A: P 2049:16385(14336) ack 1 win 57344
   17:38:09.513428 B > A: P 16385:30721(14336) ack 1 win 57344
   17:38:09.513638 B > A: P 30721:45057(14336) ack 1 win 57344
   17:38:09.519531 A > B: . 1:1(0) ack 45057 win 12288
   17:38:09.519638 B > A: P 45057:57345(12288) ack 1 win 57344

   [ B は A が申し出たウィンドウが一杯になる ]
   17:38:09.719526 A > B: . 1:1(0) ack 57345 win 0

   [ B は A のウィンドウを 1 バイトでプローブする。A はそのバイトを 
     受諾せずに再度同期をとる ]
   17:38:14.499661 B > A: . 57345:57346(1) ack 1 win 57344
   17:38:14.499724 A > B: . 1:1(0) ack 57345 win 0

   [ B は再度 A のウィンドウをプローブする。A は再度同期をとり、ACK 
     フィールドによって示されているように、そのバイトを受諾する ]
   17:38:19.499764 B > A: . 57345:57346(1) ack 1 win 57344
   17:38:19.519731 A > B: . 1:1(0) ack 57346 win 0

   [ B は A のウィンドウを 1 バイトでプローブする。A はそのバイトを 
     受諾せずに再度同期をとる ]
   17:38:24.499865 B > A: . 57346:57347(1) ack 1 win 57344
   17:38:24.499934 A > B: . 1:1(0) ack 57346 win 0

   [ A 上のアプリケーションは、データの生成を開始する。B は A のデ
     ータに肯定応答し、A はその ACK を受信してデータ転送を継続する ]

   17:38:28.530265 A > B: P 1:2049(2048) ack 57346 win 0
   17:38:28.719914 B > A: . 57346:57346(0) ack 2049 win 57344

   17:38:28.720023 A > B: . 2049:16385(14336) ack 57346 win 0
   17:38:28.720089 A > B: . 16385:30721(14336) ack 57346 win 0
   17:38:28.720370 B > A: . 57346:57346(0) ack 30721 win 57344

   17:38:28.720462 A > B: . 30721:45057(14336) ack 57346 win 0
   17:38:28.720526 A > B: P 45057:59393(14336) ack 57346 win 0
   17:38:28.720824 A > B: P 59393:73729(14336) ack 57346 win 0
   17:38:28.721124 B > A: . 57346:57346(0) ack 73729 win 47104

   17:38:28.721198 A > B: P 73729:88065(14336) ack 57346 win 0
   17:38:28.721379 A > B: P 88065:102401(14336) ack 57346 win 0

   17:38:28.721557 A > B: P 102401:116737(14336) ack 57346 win 0
   17:38:28.721863 B > A: . 57346:57346(0) ack 116737 win 36864

参照:
不明

検出方法:
クライアントからサーバへコネクションを起動する。ウィンドウが枯渇するしてバッファが一杯になるまで、サーバに連続的にデータを送信させる。次に、クライアントに 1 バイト読み込ませ、サーバ側 TCP がウィンドウプローブを送信するまで遅らせる。そして、クライアントがデータの送信を開始する。この時点で、もしクライアントがサーバの ACK を無視したら、クライアント側の TCP はこの問題を煩っている。

修正方法:
この問題を示すことが知られている一つの実装 (4.2-Reno からの派生) では、この問題は、受信ウィンドウの空間量を算出するためのマクロ MAX() が、関数呼出し max() に置きかえられた時に導入された。

    tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt));

他方に通知したサイズを超えたウィンドウ rcv_nxt > rcv_adv にデータを受信した時、これが負の値になる。これは意図的であることは (int) のキャストより明白である。しかし、unsigned max() 関数が符号拡張するため、負の数値は "大きな" 値になる。その修正は、max() を imax() に変更することである。

    tp->rcv_wnd = imax(win, (int)(tp->rcv_adv - tp->rcv_nxt));

4.3-Tahoe 以前にはこのバグは無かった。なぜなら、その計算にマクロ MAX() を使用していたからである。

2.13. ACK 伸張違反

問題の名前:
ACK 伸張違反

分類:
輻輳制御/パフォーマンス

説明:
効率 (コンピュータとネットワークの両方) を改善するために、[RFC1122] に従って、データ受信側は各々の入力セグメントに対する ACK の送信を控えてもよい。しかし、ACK は過度の時間遅らせるべきではない。特に、二個目のフルサイズセグメントが到着するたびに ACK を送信すべきである。もし、二個目のフルサイズセグメントが所定のタイムアウト (0.5 秒程度) 以内に到着しなければ、[RFC1122] に従って ACK を送信すべきである。二個目のフルサイズセグメント毎に ACK を生成しない TCP 受信側は、"伸張 ACK 違反" を示す。

重要性:
この振る舞いを示している TCP 受信側は、TCP の送信側がトラフィック爆発を生む原因になり、輻輳のある環境においてパフォーマンスを低下する可能性がある。加えて、ACK の生成が少ないと、適切なポイントに輻輳ウィンドウをオープンするためのスロースタートアルゴリズムに必要な時間が増す。それは、大きな帯域遅延製品のある環境においてパフォーマンスを低下させる。最後に、ACK の生成が少ないと、損失のある環境で不要な再送タイムアウトを引き起こすかもしれない。なぜなら ACK のウィンドウ全体が消失する可能性が増し、再送タイムアウトが強制されるからである。

暗黙の意味:
損失回復にない時、TCP 送信側が受信した全ての ACK は、新しいデータセグメントの送信の引き金になる。バーストサイズは、各々の ACK がカバーする以前に未確認のセグメントの数によって決定される。従って、一度に 2 セグメント以上の ACK を返却する TCP 受信側は、TCP 送信側が ACK の受信時に、トラフィックのより大きな爆発を生む原因になるだろう。このトラフィックの大きな爆発は、中継するゲートウェイを沈めたり、そのコネクションと輻輳したゲートウェイを経由する他のコネクションの両方の損失レートを高める可能性がある。

加えて、TCP スロースタートアルゴリズムは、各々の ACK 受信に対して 1 セグメントずつ輻輳ウィンドウを増やす。従って、ACK の間隔を増やす (つまり ACK の送信レートが減少する) ことにより、適切な処理ポイントまで輻輳ウィンドウを増やすためのスロースタートにかかる時間が増す。結果的にコネクションはパフォーマンスの低下を被る。これは、大きなウィンドウを使用しているコネクションにおいて特に当てはまる。

関連 RFC:
RFC1122 は、遅延 ACK を推奨されたメカニズムとして概説している。

その問題を実演するトレースファイル:
トレースファイルは、データの受信側 (ACK の起動側) であるホスト B で tcpdump を使用して採取した。分かりやすくするために、A が一番最初に送信したパケットを除いて、ウィンドウ通知 (変更なし) とタイムスタンプオプションは省略している。

  12:09:24.820187 A.1174 > B.3999: . 2049:3497(1448) ack 1
      win 33580 <nop,nop,timestamp 2249877 2249914> [tos 0x8]
  12:09:24.824147 A.1174 > B.3999: . 3497:4945(1448) ack 1
  12:09:24.832034 A.1174 > B.3999: . 4945:6393(1448) ack 1
  12:09:24.832222 B.3999 > A.1174: . ack 6393
  12:09:24.934837 A.1174 > B.3999: . 6393:7841(1448) ack 1
  12:09:24.942721 A.1174 > B.3999: . 7841:9289(1448) ack 1
  12:09:24.950605 A.1174 > B.3999: . 9289:10737(1448) ack 1
  12:09:24.950797 B.3999 > A.1174: . ack 10737
  12:09:24.958488 A.1174 > B.3999: . 10737:12185(1448) ack 1
  12:09:25.052330 A.1174 > B.3999: . 12185:13633(1448) ack 1
  12:09:25.060216 A.1174 > B.3999: . 13633:15081(1448) ack 1
  12:09:25.060405 B.3999 > A.1174: . ack 15081

トレースのこの部分は、受信側 (ホスト B) が三個目のフルサイズパケット受信毎に ACK を送信していることをはっきりと表している。さらにこの実装体を調べると、ACK 間隔の増やされた原因は、使用されている TCP オプションだったことが分かった。実装体は、未確認のデータを 2*MSS 相当分保持した後に ACK を送信した。上記のケースでは、MSS は 1460 バイトである。よって受信側は、未確認のデータを少なくとも 2920 バイト保持した後に ACK を送信する。しかし、[RFC1323] を使用している TCP オプションの長さは、各パケットのデータ部から 12 バイト離れている。これは、1448 バイトのデータを含むパケットを生成した。しかしヘッダのオプションによって使用される追加バイトが、ACK をいつ送信するかを決定するときに考慮されていない。従って、ACK を送信するのに十分な未確認データ (2*MSS 以上、上記の例では 2920 バイト) を保持する前に、データの受信側が 3 データセグメントかかったのである。

正しい動作を実演するトレースファイル:
トレースファイルは、データの受信側 (ACK の起動側) であるホスト B で tcpdump を使用して採取した。また、一番目のデータを除いてウィンドウとタイムスタンプ情報を省略している。

  12:06:53.627320 A.1172 > B.3999: . 1449:2897(1448) ack 1
      win 33580 <nop,nop,timestamp 2249575 2249612> [tos 0x8]
  12:06:53.634773 A.1172 > B.3999: . 2897:4345(1448) ack 1
  12:06:53.634961 B.3999 > A.1172: . ack 4345
  12:06:53.737326 A.1172 > B.3999: . 4345:5793(1448) ack 1
  12:06:53.744401 A.1172 > B.3999: . 5793:7241(1448) ack 1
  12:06:53.744592 B.3999 > A.1172: . ack 7241
  12:06:53.752287 A.1172 > B.3999: . 7241:8689(1448) ack 1
  12:06:53.847332 A.1172 > B.3999: . 8689:10137(1448) ack 1
  12:06:53.847525 B.3999 > A.1172: . ack 10137

TCP 受信側 (ホスト B) は、[RFC1122] に従って二個目のフルサイズのパケット毎に ACK を送信していることを、このトレースは表している。これは上記で示された実装と同じであり、ACK を送信するタイミングを決める時に、受信側がオプションの長さを考慮するようわずかに修正した。

参照:
この問題は、[Allman97][Paxson97] でドキュメント化されている。

検出方法:
ACK 伸張違反は、大量送信の受信側パケットトレースに即座に表れる。しかし TCP コネクションの送信側で生成されたパケットトレースは、ACK 損失の可能性があるため、この問題を診断するときに曖昧さを招くかもしれない。

2.14. 複数パケットの再送

問題の名前:
複数パケットの再送

分類:
輻輳制御

説明:
TCP が、タイムアウト満了か高速再送シーケンスの開始によってセグメントを再送する時、TCP は一つのセグメントのみ送信すべきである。一つ以上のセグメントを送信する TCP は、"複数パケットの再送" を示す。

この問題の実体は、TCP オプションの使用に伴う算出誤りよって発生することが知られている。TCP オプションは、通常サイズである 20 バイトを超えて TCP ヘッダを増やす。ヘッダの全体長はパケットの再送時も考慮しなければならない。もし、データの再送量を決める時に、TCP ヘッダで TCP オプションの長さを考慮しなければ、単一のパケットに相当するよりも大きいデータを送信するだろう。この場合、正しい再送は、再送する必要はないかもしれないデータを含む短いセグメント (タイニーグラム) が後ろに続くだろう。

特定のケースは、RFC1323 タイムスタンプオプションを使用する TCP であり、それは標準的な 20 バイト TCP ヘッダに 12 バイト追加する。パケットの再送時、12 バイトオプションはセグメントのデータ部の一部として誤って解釈される。標準的な TCP ヘッダと新たな 12 バイトオプションはデータに追加され、元のセグメントに含まれているよりも 12 バイト多いデータの送信を招く。このオーバフローは 12 データバイトを持つ、より小さいパケットを送信させる原因である。

重要性:
TCP 実装は適切な量よりも多くのパケットをネットワークに注入するので、この問題は輻輳した環境では若干重大である。しかし、タイニーグラムは高速再送かタイムアウトの応答で送信されるので、送信レートへの影響は無い。

暗黙の意味:
この振る舞いを示している TCP は、適切な量よりも多くのトラフィックでネットワークを圧迫し、処理しなければならないパケットの個数を増やすことによってルータを圧迫する。冗長なタイニーグラムもまた受信側から重複 ACK を導き、別の不要な送信を招く結果になる。

関連 RFC:
RFC1122 は損失後のスロースタートの使用を必要とし、RFC2001 はスロースタートについて解説している。RFC1323 は、この問題を示す幾つかの実装の要因として観測された、タイムスタンプオプションについて説明している。

その問題を実演するトレースファイル:
ホスト A と同じサブネット上のマシンで記録する tcpdump を使用して作成した。ホスト A は送信側であり、ホスト B は受信側である。分かりやすくするために、ホスト A が一番最初に送信したパケットを除いて、ウィンドウ通知とタイムスタンプオプションは省略している。加えて、問題のパケットに関係しないトレースファイルの部分は削除している (省略したパケットはトレース中では "[...}" で示している)。

  11:55:22.701668 A > B: . 7361:7821(460) ack 1
      win 49324 <nop,nop,timestamp 3485348 3485113>
  11:55:22.702109 A > B: . 7821:8281(460) ack 1
  [...]

  11:55:23.112405 B > A: . ack 7821
  11:55:23.113069 A > B: . 12421:12881(460) ack 1
  11:55:23.113511 A > B: . 12881:13341(460) ack 1
  11:55:23.333077 B > A: . ack 7821
  11:55:23.336860 B > A: . ack 7821
  11:55:23.340638 B > A: . ack 7821
  11:55:23.341290 A > B: . 7821:8281(460) ack 1
  11:55:23.341317 A > B: . 8281:8293(12) ack 1
  11:55:23.498242 B > A: . ack 7821
  11:55:23.506850 B > A: . ack 7821
  11:55:23.510630 B > A: . ack 7821

  [...]

  11:55:23.746649 B > A: . ack 10581

上記トレースの第二行目は、後で損失する元のセグメントの送信を示している。3 つの重複 ACK の後、トレースの 9 行目は 460 バイトのペイロードを持つ損失したパケット (7821:8281) の送信を示している。この再送の直後に、12 バイトのペイロードを持つパケットが無駄に送信される。

正しい動作を実演するトレースファイル:
トレースファイルは、以下の一行が省略されることを除いて、上記と同じである

  11:55:23.341317 A > B: . 8281:8293(12) ack 1

参照:
[Brakmo95]

検出方法:
この問題は、TCP オプションを使用している TCP コネクションの、パケットが再送される間のパケットトレースをチェックすることによって検出できる。

2.15 FIN 通知を即座に送信しない

問題の名前:
FIN 通知を即座に送信しない

分類:
パフォーマンス

説明:
アプリケーションがコネクションをクローズした時、対応する TCP は相手側に即座に FIN 通知を送信すべきである (輻輳ウィンドウによって妨げられなければ)。もし TCP 実装が例えば未確認のデータが確認されるまで待つことによって、FIN 通知の送信を遅らせるならば、"FIN 通知を即座に送信しない" を示す。

また、厳密に要求はされていないが、受信側における未処理のデータの迅速な配送を保証するために、FIN セグメントは PSH フラグを含むべきである。

重要性:
最大のインパクトは短命のコネクションで発生する。なぜなら、これらのために、コネクションをクローズするために必要な付加時間が最も大きな関連遅延を招くからである。

受信側によって送らされている ACK を待っている送信側の一般的なケースで、追加時間が重要になり得る。

暗黙の意味:
コネクションの終了は完了するまで時間がかかるので、アプリケーション層で見られる全体のスループットが減る可能性がある。

関連 RFC:
RFC793 は、受信側は入力 FIN フラグをプッシュ機能が含まれているものとして扱うべきであると述べている。

その問題を実演するトレースファイル:
tcpdump を使用して作成した (パケットフィルタによって、損失は通知されていない)。

  10:04:38.68 A > B: S 1031850376:1031850376(0) win 4096
                  <mss 1460,wscale 0,eol> (DF)
  10:04:38.71 B > A: S 596916473:596916473(0) ack 1031850377
                  win 8760 <mss 1460> (DF)
  10:04:38.73 A > B: . ack 1 win 4096 (DF)
  10:04:41.98 A > B: P 1:4(3) ack 1 win 4096 (DF)
  10:04:42.15 B > A: . ack 4 win 8757 (DF)
  10:04:42.23 A > B: P 4:7(3) ack 1 win 4096 (DF)
  10:04:42.25 B > A: P 1:11(10) ack 7 win 8754 (DF)
  10:04:42.32 A > B: . ack 11 win 4096 (DF)
  10:04:42.33 B > A: P 11:51(40) ack 7 win 8754 (DF)
  10:04:42.51 A > B: . ack 51 win 4096 (DF)
  10:04:42.53 B > A: F 51:51(0) ack 7 win 8754 (DF)
  10:04:42.56 A > B: FP 7:7(0) ack 52 win 4096 (DF)
  10:04:42.58 B > A: . ack 8 win 8754 (DF)

上記トレースにおける マシン B は、もしも未処理のデータが幾つか存在すると、FIN 通知を即座に送出しない。代わりに、FIN セグメントを送信する前に、全ての未確認のデータが確認されるのを待つ。コネクションは 40 バイトの送信を要求した後、10:04.42.33 にクローズされた。しかし、FIN 通知は 40 バイトのデータの (遅らされた) 確認を受信後の 10:04.42.51 まで送信されていない。

正しい動作を実演するトレースファイル:
tcpdump を使用して作成した (パケットフィルタによって、損失は通知されていない)。

  10:27:53.85 C > D: S 419744533:419744533(0) win 4096
                  <mss 1460,wscale 0,eol> (DF)
  10:27:53.92 D > C: S 10082297:10082297(0) ack 419744534
                  win 8760 <mss 1460> (DF)
  10:27:53.95 C > D: . ack 1 win 4096 (DF)
  10:27:54.42 C > D: P 1:4(3) ack 1 win 4096 (DF)
  10:27:54.62 D > C: . ack 4 win 8757 (DF)
  10:27:54.76 C > D: P 4:7(3) ack 1 win 4096 (DF)
  10:27:54.89 D > C: P 1:11(10) ack 7 win 8754 (DF)
  10:27:54.90 D > C: FP 11:51(40) ack7 win 8754 (DF)
  10:27:54.92 C > D: . ack 52 win 4096 (DF)
  10:27:55.01 C > D: FP 7:7(0) ack 52 win 4096 (DF)
  10:27:55.09 D > C: . ack 8 win 8754 (DF)

ここでは、マシン D は元の 10 オクテットが確認される前であっても、40 バイトのデータの FIN を送信している。最大のパフォーマンスを提供するので、これは正しい動作である。

参照:
この問題は [Dawson97] でドキュメント化されている。

検出方法:
この問題が表れる実装の場合、パケットトレースにそれが表れる。

2.16 半二重クローズ後 RST 送信なし

問題の名前:
半二重クローズ後 RST 送信なし

分類:
資源管理

説明:
RFC 1122 4.2.2.13 は、もし TCP が "半二重クローズ" 後にデータを受信したら、すなわちもしデータをアプリケーションに配送できないならば、RST を送信すべきである (SHOULD) と述べている。それを行わない TCP は、"半二重クローズ後 RST 送信なし" を示すものと呼ばれる。

重要性:
コネクション状態を管理するために利用できるメモリやプロセススロットの枯渇を招くため、非常に多くのコネクションを管理する TCP の終点にとって潜在的に重大である。

暗黙の意味:
RST を送信しないと、永久的な TCP コネクションのハングを招く可能性がある。この問題は、一般にユーザが現ページのダウンロードが終了する前に新しいページに移動する時に、HTTP クライアントがコネクションをアボートする時に表れている。サーバが画像やテキスト等を送信している間に、HTTP クライアントは FIN を送信することによってクローズする。サーバ側 TCP は FIN を受信するが、アプリケーションは全てのデータを送信のためにキューイングするまで、そのコネクションをクローズしない。サーバは先行するデータが全て送信されるまで FIN を送信しないので、もしクライアント TCP が未処理のデータを全て受信し尽くさないか、コネクションを壊せば、クライアントがデータをアプリケーションに渡せないので、ウィンドウが 0 に減少する。結果的にデッドロックになり、サーバはプローブセグメントを送信する。クライアントはプローブセグメントに対して 0 ウィンドウで肯定応答する。RFC1122 4.2.2.17 で規定されているように、プローブセグメントは永久に送信される。サーバコネクション状態は CLOSE_WAIT のままであり、最終的にはサーバプロセスが枯渇する。

これには、二つのバグがある。第一に、もしウィンドウが継続して増えなければプローブセグメントは無視すべきである。第二に、半二重クローズ後にデータを受信したら RST を送信すべきである。一つ目のバグを修正して二つ目を修正しないと、プローブセグメントが最終的にコネクションをタイムアウトするが、サーバは重要で不要な時間の間 CLOSE_WAIT のままである。

関連 RFC:
RFC1122 セクション 4.2.2.13 と 4.2.2.17

その問題を実演するトレースファイル:
未詳のネットワークアナライザを使用して作成した。有効な情報の欠落はない。

  client.1391 > server.8080: S 0:1(0) ack: 0 win: 2000 <mss: 5b4>
  server.8080 > client.1391: SA 8c01:8c02(0) ack: 1 win: 8000 <mss:100>
  client.1391 > server.8080: PA
  client.1391 > server.8080: PA 1:1c2(1c1) ack: 8c02 win: 2000
  server.8080 > client.1391: [DF] PA 8c02:8cde(dc) ack: 1c2 win: 8000
  server.8080 > client.1391: [DF] A 8cde:9292(5b4) ack: 1c2 win: 8000
  server.8080 > client.1391: [DF] A 9292:9846(5b4) ack: 1c2 win: 8000
  server.8080 > client.1391: [DF] A 9846:9dfa(5b4) ack: 1c2 win: 8000
  client.1391 > server.8080: PA
  server.8080 > client.1391: [DF] A 9dfa:a3ae(5b4) ack: 1c2 win: 8000
  server.8080 > client.1391: [DF] A a3ae:a962(5b4) ack: 1c2 win: 8000
  server.8080 > client.1391: [DF] A a962:af16(5b4) ack: 1c2 win: 8000
  server.8080 > client.1391: [DF] A af16:b4ca(5b4) ack: 1c2 win: 8000
  client.1391 > server.8080: PA
  server.8080 > client.1391: [DF] A b4ca:ba7e(5b4) ack: 1c2 win: 8000
  server.8080 > client.1391: [DF] A b4ca:ba7e(5b4) ack: 1c2 win: 8000
  client.1391 > server.8080: PA
  server.8080 > client.1391: [DF] A ba7e:bdfa(37c) ack: 1c2 win: 8000
  client.1391 > server.8080: PA
  server.8080 > client.1391: [DF] A bdfa:bdfb(1) ack: 1c2 win: 8000
  client.1391 > server.8080: PA

  [ HTTP クライアントはアボートし、FIN_WAIT_1 に遷移する ]

  client.1391 > server.8080: FPA

  [ サーバは FIN に対して ACK を送信し CLOSE_WAIT 状態に遷移する ]

  server.8080 > client.1391: [DF] A

  [ クライアントは FIN_WAIT_2 に遷移する ]

  server.8080 > client.1391: [DF] A bdfa:bdfb(1) ack: 1c3 win: 8000

  [ サーバは継続してデータの送信を試みる ]

  client.1391 > server.8080: PA < window = 0 >
  server.8080 > client.1391: [DF] A bdfa:bdfb(1) ack: 1c3 win: 8000
  client.1391 > server.8080: PA < window = 0 >
  server.8080 > client.1391: [DF] A bdfa:bdfb(1) ack: 1c3 win: 8000
  client.1391 > server.8080: PA < window = 0 >
  server.8080 > client.1391: [DF] A bdfa:bdfb(1) ack: 1c3 win: 8000
  client.1391 > server.8080: PA < window = 0 >
  server.8080 > client.1391: [DF] A bdfa:bdfb(1) ack: 1c3 win: 8000
  client.1391 > server.8080: PA < window = 0 >

  [ ... 枯渇するまで繰り返し ... ]

正しい動作を実演するトレースファイル:
未詳のネットワークアナライザを使用して作成した。有効な情報の欠落はない。

  client > server D=80 S=59500 Syn Seq=337 Len=0 Win=8760
  server > client D=59500 S=80 Syn Ack=338 Seq=80153 Len=0 Win=8760
  client > server D=80 S=59500 Ack=80154 Seq=338 Len=0 Win=8760

  [ ... 通常のデータは省略 ... ]

  client > server D=80 S=59500 Ack=14559 Seq=596 Len=0 Win=8760
  server > client D=59500 S=80 Ack=596 Seq=114559 Len=1460 Win=8760

  [ クライアントがコネクションをクローズ ]

  client > server D=80 S=59500 Fin Seq=596 Len=0 Win=8760
  server > client D=59500 S=80 Ack=597 Seq=116019 Len=1460 Win=8760

  [ クライアントは RST を送信する (RFC1122 4.2.2.13) ]

  client > server D=80 S=59500 Rst Seq=597 Len=0 Win=0
  server > client D=59500 S=80 Ack=597 Seq=117479 Len=1460 Win=8760
  client > server D=80 S=59500 Rst Seq=597 Len=0 Win=0
  server > client D=59500 S=80 Ack=597 Seq=118939 Len=1460 Win=8760
  client > server D=80 S=59500 Rst Seq=597 Len=0 Win=0
  server > client D=59500 S=80 Ack=597 Seq=120399 Len=892 Win=8760
  client > server D=80 S=59500 Rst Seq=597 Len=0 Win=0
  server > client D=59500 S=80 Ack=597 Seq=121291 Len=1460 Win=8760
  client > server D=80 S=59500 Rst Seq=597 Len=0 Win=0

"クライアント" は "サーバ" からの各入力パケットに対して 1 つずつ、たくさんの RST を送信している。なぜ "サーバ" が "クライアント" から RST を受信した後も、データパケットを送信し続けるのか不思議に思うかもしれない。その説明は、"サーバ" が最初の RST を "クライアント" から受信する前に、データパケットを 5 個全て既に送信したので、それらの送信を防ぐには遅過ぎたということである。

検出方法:
この問題は、大容量の転送で中断したパケットトレースを解析することによって検出できる。

2.17 未処理のデータがあるクローズ時に RST 無し

問題の名前:
未処理のデータがあるクローズ時に RST 無し

分類:
資源管理

説明:
アプリケーションが、もう受信したデータを読むことができないような方法でコネクションをクローズする時、RFC 1122 4.2.2.13 により、TCP はもし未読の受信データが存在するか新しいデータを受信したならば、RST を送信すべきである (SHOULD)。それを行わない TCP は "未処理のデータかあるクローズ時に RST 無し" を示す。

幾つかの TCP では、この状況は相手がデータを送信しようしている間に、アプリケーション "クラッシュ" が発生することによって引き起こされる可能性がある。

我々は、この問題を示す数多くの TCP を見かけた。もし、今クローズされたコネクション終点に送信された後続のデータによって RST が出されれば (以下の図参照)、その問題は重大ではない。

重要性:
この問題は大多数のコネクションと連動している終点にとっては、資源を漏らすのと同様にそれを行う能力が減るので最も重要である。

暗黙の意味:
コネクションをリセットしないと、永久にコネクションをハングさせる可能性がある。その場合、リモートの終点はローカルの TCP が最初に何かの動作を行うことを待つため、コネクションを壊す動作を行わない。これは、ローカル TCP が通知ウィンドウを 0 にでき、リモート TCP がプローブの "持続" に従事する (以下の例参照) 時にコネクションを壊さない場合、特にそうである。

関連 RFC:
RFC 1122 セクション 4.2.2.13。また、以下の 0 ウィンドウプローブの議論については 4.2.2.17。

その問題を実演するトレースファイル:
tcpdump を使用して作成。入手可能な情報の欠落は無い。

  13:11:46.04 A > B: S 458659166:458659166(0) win 4096
                      <mss 1460,wscale 0,eol> (DF)
  13:11:46.04 B > A: S 792320000:792320000(0) ack 458659167
                      win 4096
  13:11:46.04 A > B: . ack 1 win 4096 (DF)
  13:11.55.80 A > B: . 1:513(512) ack 1 win 4096 (DF)
  13:11.55.80 A > B: . 513:1025(512) ack 1 win 4096 (DF)
  13:11:55.83 B > A: . ack 1025 win 3072
  13:11.55.84 A > B: . 1025:1537(512) ack 1 win 4096 (DF)
  13:11.55.84 A > B: . 1537:2049(512) ack 1 win 4096 (DF)
  13:11.55.85 A > B: . 2049:2561(512) ack 1 win 4096 (DF)
  13:11:56.03 B > A: . ack 2561 win 1536
  13:11.56.05 A > B: . 2561:3073(512) ack 1 win 4096 (DF)
  13:11.56.06 A > B: . 3073:3585(512) ack 1 win 4096 (DF)
  13:11.56.06 A > B: . 3585:4097(512) ack 1 win 4096 (DF)
  13:11:56.23 B > A: . ack 4097 win 0
  13:11:58.16 A > B: . 4096:4097(1) ack 1 win 4096 (DF)
  13:11:58.16 B > A: . ack 4097 win 0
  13:12:00.16 A > B: . 4096:4097(1) ack 1 win 4096 (DF)
  13:12:00.16 B > A: . ack 4097 win 0
  13:12:02.16 A > B: . 4096:4097(1) ack 1 win 4096 (DF)
  13:12:02.16 B > A: . ack 4097 win 0
  13:12:05.37 A > B: . 4096:4097(1) ack 1 win 4096 (DF)
  13:12:05.37 B > A: . ack 4097 win 0
  13:12:06.36 B > A: F 1:1(0) ack 4097 win 0
  13:12:06.37 A > B: . ack 2 win 4096 (DF)
  13:12:11.78 A > B: . 4096:4097(1) ack 2 win 4096 (DF)
  13:12:11.78 B > A: . ack 4097 win 0
  13:12:24.59 A > B: . 4096:4097(1) ack 2 win 4096 (DF)
  13:12:24.60 B > A: . ack 4097 win 0
  13:12:50.22 A > B: . 4096:4097(1) ack 2 win 4096 (DF)
  13:12:50.22 B > A: . ack 4097 win 0

上記トレース中のマシン B は、ソケットがアプリケーションによって "クローズ" された時に受信したデータを落としていない (このケースでは、アプリケーションプロセスは終了した)。これはほぼ 13:12:06.36 に発生し、結果的にクローズの応答として FIN を送信している。しかし、データを配送すべきアプリケーションが最早存在しないので、TCP は代わりに RST を送信すべきであった。

注記: マシン A の 0 ウィンドウプローブもまた違反している。A は、新しいデータではなく古いデータを再送している。RFC793 のセクション 3.7 と RFC1122 のセクション 4.2.2.17 は、0 ウィンドウプローブについて論じている。

より良い動作を実演するトレースファイル:
tcpdump を使用して作成。入手可能な情報の欠落は無い。

下記で論じるが、より良いがまだ完全には正しいわけではない動作である。異なる多くの TCP 実装で見かけるため、これを示す。

  13:48:29.24 C > D: S 73445554:73445554(0) win 4096
                      <mss 1460,wscale 0,eol> (DF)
  13:48:29.24 D > C: S 36050296:36050296(0) ack 73445555
                      win 4096 <mss 1460,wscale 0,eol> (DF)
  13:48:29.25 C > D: . ack 1 win 4096 (DF)
  13:48:30.78 C > D: . 1:1461(1460) ack 1 win 4096 (DF)
  13:48:30.79 C > D: . 1461:2921(1460) ack 1 win 4096 (DF)
  13:48:30.80 D > C: . ack 2921 win 1176 (DF)
  13:48:32.75 C > D: . 2921:4097(1176) ack 1 win 4096 (DF)
  13:48:32.82 D > C: . ack 4097 win 0 (DF)
  13:48:34.76 C > D: . 4096:4097(1) ack 1 win 4096 (DF)
  13:48:34.84 D > C: . ack 4097 win 0 (DF)
  13:48:36.34 D > C: FP 1:1(0) ack 4097 win 4096 (DF)
  13:48:36.34 C > D: . 4097:5557(1460) ack 2 win 4096 (DF)
  13:48:36.34 D > C: R 36050298:36050298(0) win 24576
  13:48:36.34 C > D: . 5557:7017(1460) ack 2 win 4096 (DF)
  13:48:36.34 D > C: R 36050298:36050298(0) win 24576

このトレースでは、アプリケーションプロセスは、マシン D でほぼ 13:48:36.34 に終了している。その TCP は、(以前に受信したデータを破棄したので) ウィンドウを再び開けて FIN を送信した。マシン C は即座に更にデータを送信し、それによってマシン D はコネクションをリセットした。なぜなら D はデータをアプリケーションに配送できないからである。理想的には、マシン D はデータを破棄して受信ウィンドウを再オープンする代わりに RST を送信すべきである。

注記: マシン C の 0 ウィンドウプローブは違反している。上の例と同じである。

正しい動作を実演するトレースファイル:

tcpdump を使用して作成。パケットフィルタによって通知された欠損は無い。

   14:12:02.19 E > F: S 1143360000:1143360000(0) win 4096
   14:12:02.19 F > E: S 1002988443:1002988443(0) ack 1143360001
                       win 4096 <mss 1460> (DF)
   14:12:02.19 E > F: . ack 1 win 4096
   14:12:10.43 E > F: . 1:513(512) ack 1 win 4096
   14:12:10.61 F > E: . ack 513 win 3584 (DF)
   14:12:10.61 E > F: . 513:1025(512) ack 1 win 4096
   14:12:10.61 E > F: . 1025:1537(512) ack 1 win 4096
   14:12:10.81 F > E: . ack 1537 win 2560 (DF)
   14:12:10.81 E > F: . 1537:2049(512) ack 1 win 4096
   14:12:10.81 E > F: . 2049:2561(512) ack 1 win 4096
   14:12:10.81 E > F: . 2561:3073(512) ack 1 win 4096
   14:12:11.01 F > E: . ack 3073 win 1024 (DF)
   14:12:11.01 E > F: . 3073:3585(512) ack 1 win 4096
   14:12:11.01 E > F: . 3585:4097(512) ack 1 win 4096
   14:12:11.21 F > E: . ack 4097 win 0 (DF)
   14:12:15.88 E > F: . 4097:4098(1) ack 1 win 4096
   14:12:16.06 F > E: . ack 4097 win 0 (DF)
   14:12:20.88 E > F: . 4097:4098(1) ack 1 win 4096
   14:12:20.91 F > E: . ack 4097 win 0 (DF)
   14:12:21.94 F > E: R 1002988444:1002988444(0) win 4096

14:12:21.94 にアプリケーションが終了した時、F は即座に RST を送信している。

注記: マシン E の 0 ウィンドウプローブは、(とうとう) 正しい。

検出方法:
この問題は、アプリケーションの異常終了を受けた送信側のパケットトレースを調べることによって、しばしば検出できる。それを行う際、受信側 TCP が最早配送することができない未読のデータを本当に持っているか否か、(トレースを見ただけでは) 曖昧かもしれない。これを発生させるには、受信側のアプリケーションを停止させることが、如何なるデータも消費しないので助けになるかもしれない。この時点で通知ウィンドウが 0 になるので、受信側 TCP がデータバッファを上位に配送していないことが分かる。そしてアプリケーションプロセスを終了させれば、TCP の振る舞いの正しさをテストできるはずである。

2.18 TCP MSS 算出からオプション抜け

問題の名前:
TCP MSS 算出からオプション抜け

分類:
信頼性/パフォーマンス

説明:
TCP が一パケットにつきどの位のデータを送信するかを決めるとき、パスの MTU に基づいてセグメント長を計算する。その際 TCP は、その MTU からパケット中の IP と TCP ヘッダのサイズを引かなければならない。もし、この計算で IP オプションと TCP オプションを正しく考慮していなければ、結果として算出されるセグメント長は大き過ぎるかもしれない。そのような振る舞いを行う TCP は "TCP MSS 算出からオプション抜け" を示すと言われる。

重要性:
幾つかの実装では、これは妙に分割されたパケットの送信を引き起こす。パス MTU (PMTU) 検出 [RFC1191] の幾つかの実装では、この問題は、環境に関わらず (下記参照) データを全く送信できない結果を実際に招く可能性がある。

特にファイアウォールが広く配置されるので、恐らく IP オプションは通常の処理ではほとんど表れない。

暗黙の意味:
PMTU 検出を使用している実装では、この問題は出力インタフェースにとって大き過ぎるパケットを結果として引き起こす可能性があり、それには IP ヘッダに DF (分割無し) ビットが設定される。従って、ローカルマシン上の IP 層は、パケットを分割してインタフェースに送出することができない。代わりに、インタフェースの正しい MTU サイズを TCP 層に通知する。TCP 層は IP オプションのサイズを考慮しないので再び MSS の計算を誤り、データが流れることなくこの問題が繰り返される。

関連 RFC:
RFC1122 は、効率的な送信 MSS の計算について説明している。RFC1191 は、パス MTU 検出について説明している。

その問題を実演するトレースファイル:
トレースファイルは、ホスト C で tcpdump を使用して採取した。最初のトレースファイルは、パス MTU 検出なしで発生した分割を表している。

  13:55:25.488728 A.65528 > C.discard:
          P 567833:569273(1440) ack 1 win 17520
          <nop,nop,timestamp 3839 1026342>
          (frag 20828:1472@0+)
          (ttl 62, optlen=8 LSRR{B#} NOP)

  13:55:25.488943 A > C:
          (frag 20828:8@1472)
          (ttl 62, optlen=8 LSRR{B#} NOP)

  13:55:25.489052 C.discard > A.65528:
          . ack 566385 win 60816
          <nop,nop,timestamp 1026345 3839> (DF)
          (ttl 60, id 41266)

ホスト A は、1440 オクテットデータセグメントを繰り返し送信しているが、これらは二つのパケットに分割されており、一つが 1432 オクテットのデータで、もう一つが 8 オクテットのデータである。

二番目のトレースは、如何なるデータセグメントも送信していない。時々ホストがパス MTU 検出を行っていることが見える。

  13:55:44.332219 A.65527 > C.discard:
          S 1018235390:1018235390(0) win 16384
          <mss 1460,nop,wscale 0,nop,nop,timestamp 3876 0> (DF)
          (ttl 62, id 20912, optlen=8 LSRR{B#} NOP)

  13:55:44.333015 C.discard > A.65527:
          S 1271629000:1271629000(0) ack 1018235391 win 60816
          <mss 1460,nop,wscale 0,nop,nop,timestamp 1026383 3876> (DF)
          (ttl 60, id 41427)

  13:55:44.333206 C.discard > A.65527:
          S 1271629000:1271629000(0) ack 1018235391 win 60816
          <mss 1460,nop,wscale 0,nop,nop,timestamp 1026383 3876> (DF)
          (ttl 60, id 41427)

これはこのコネクション上で見られる動作の全てである。最終的にホスト C は、コネクションを確立する試みをタイムアウトする。

検出方法:
送信元経路パケットを生成するには、"netcat" ユーティリティ [Hobbit96] が便利である。

  1% nc C discard
  (interactive typing)
  ^C
  2% nc C discard < /dev/zero
  ^C
  3% nc -g B C discard
  (interactive typing)
  ^C
  4% nc -g B C discard < /dev/zero
  ^C

1 行目から 3 行目は、適切なパケットを生成するはずであり、tcpdump を使用して確かめることができる。もし問題があれば、4 行目で上記の 2 種類のパケットトレースのうちの 1 つを生成するはずである。

修正方法:
RFC1122 で実装体は、効率的な送信 MSS 計算が IP と TCP のオプションの量を含むことを保証すべきである。

つづく