要約

  • 遅れたACKに双方が応答して現在のパケットを再送すると、古い確認応答から後続ブロックの重複が続くことがあった。
  • RFC 1123はDATA送信側に重複ACKへの再送禁止を求めた。危険は輻輳による転送タイムアウトであり、完了したファイルの破損ではない。

保持するのは一つのブロック

1981年6月、Karen SollinsはTrivial File Transfer Protocolの第2版を公開した。「trivial」は機能を絞ったインターフェースを指す。遠隔ファイルの読み書きはできるが、ディレクトリー一覧もユーザー認証もない。データは512バイトのブロックで運び、最終ブロック以外はACKを受け取るまで次へ進まない。送信側は再送用に現在の一つのパケットを保持すればよかった。ストップ・アンド・ウェイトはフロー制御にもなり、受信順の並べ替えを不要にした。

この小ささは、完全なOSが動く前にネットワークからシステムを読み込むマシンに意味があった。1989年のRFC 1123は、TFTPの最重要用途をローカルネットワーク上のホスト起動とし、EPROMに実装できるほど小さい点を挙げる。BOOTPが構成情報を渡し、その後TFTPがイメージを運ぶ。別々の役割だが、ファイル転送にも遅延とACKの問題は残った。

TFTPはUDP上で動き、信頼性を自分で扱う。期待する応答がタイマーまでに来なければ、送信側は待っているパケットを再送できる。パケットが失われた場合には単純だが、失われずに遅れた場合には別の状態が生まれる。

古いACKが新しい重複を生む

ホストAがACK X−1を受け、DATA Xを送ったとする。ホストBは受信してACK Xを返すが、そのACKが遅れる。Aのタイマーが切れ、AはDATA Xを再送する。すでにXを受け取ったBは重複と判定し、ACK Xをもう一度返す。最初の遅れたACKが届くと、Aは次へ進みDATA X+1を送る。そこへ二つ目のACK Xが届く。元の規則では、Aは古いACKに対して何もしないのではなく、自分が今送っているパケット、DATA X+1を再送する。

BはX+1を二度受け取り、ACK X+1を二つ返す。その一つがAがDATA X+2へ進んだ後に届けば、今度は新しいブロックの重複を誘発する。古いパケットの再生が古い状態にだけ作用するのではなく、各端末の現在の状態に基づいて反応を引き起こす。ひとつの重複が相手側の新たな重複を作る。

Robert BradenはRFC 1123でこれを「Sorcerer’s Apprentice syndrome」と呼んだ。例では、データグラムが失われて連鎖が切れない限り、その後のパケットも重複して送られ続ける。ファイル内容が必ず壊れるという意味ではない。RFC 1123は、転送が完了すればファイルは正しいと明記する。問題は、重複が時間と帯域を使い、完了前にタイムアウトへ追い込むことだ。

したがって「信頼性がある」と「遅延に左右されない」は同じではない。ブロック番号がどのDATAを確認したか示しても、古いブロックへの応答をすべて無害にはしない。DATA送信側もACK送信側も、重複を受け取ると自分の現在のパケットを再送できた。一方ずつでは合理的に見える動作が、組み合わさってフィードバック・ループになった。

遅延が輻輳を増幅する

1989年の要求文書は仕組みを詳しく説明する。ACKが遅れている間にタイムアウトが起き、DATA再送が送られる。重複DATAはACKを再び発生させる。最初のACKが送信側の進行後に届けば、新しいDATAが重複し、さらにACKが増える。どこかでデータグラムが失われない限り、後続ブロックも重複しうる。

輻輳は引き金にも結果にもなる。混雑した経路がACKを遅らせて連鎖を始める。重複したDATAとACKがその経路を余計に占有し、さらなる遅延を起こしやすくする。RFC 1123はこれが追加の輻輳と遅延につながると警告した。すべての遅い転送にこの症状が出た、あるいは特定の歴史的障害を引き起こしたとまでは述べていない。記録されているのはプロトコルの正のフィードバックだ。

RFC 1123は、この問題を通常のタイムアウト方式とも分けている。タイムアウトは経路変動に適応し、少なくとも指数バックオフを使うべきだと勧告する。しかしタイマーの値を良くしても、古いACKが現在のDATAの再送を命じられる規則は残る。重複ACKを受けたときの動作そのものを変えなくてはならない。

改訂に先立つ小さな規則

RFC 1123は修正をMUSTとして定めた。DATAを送る側は、重複ACKを受けても現在のDATAを再送してはならない。この応答を止めれば、重複ACKは次の重複DATAを起こさず、連鎖は続かない。受信側のACKタイマーは、ACKを再送しても相手の動作を変えないため省略できる。ただし、実際に失われたACKを置き換えられるなら残してもよい。送信側の再送タイマーは必要なままだ。

起動プログラムで動かすプロトコルにとって、受信側のタイマーをなくせるのは実装簡素化につながる。修正にウィンドウ拡大も512バイトブロックの変更もUDPの置き換えも必要なかった。危険な応答の連鎖を切りながら、一度に一つだけ保持する設計は保てた。

1992年7月にRFC 1350がRFC 783を置き換え、STD 33になった。その謝辞は、Noel Chiappaが1992年5月の改訂でこのプロトコル・バグを修正したと記す。順序を混同してはならない。RFC 1123は1989年にすでに実装に求められる動作を規定し、1992年のRFCはTFTPの改訂文書に修正を反映した。改訂日だけから、すべての実装が1992年まで壊れていた、あるいは1989年にすべて更新済みだったとは言えない。

後年の拡張は別の境界を扱った。1998年のブロックサイズ・オプションなどは、ストップ・アンド・ウェイトのスループット制約を緩和し、2015年のウィンドウ・オプションは複数ブロックを同時に送れるようにした。重複ACKの修正とは別物だ。転送量を増やすことと、古いACKが現在のトラフィックを複製するのを防ぐことは違う変更である。

TFTPの歴史からは、極小の設計が複雑さをパケット形式でなく状態遷移に押し込む様子が見える。アクティブなブロックを一つに絞れば、メモリーと実装作業は少なくなる。しかし遅延によって、両端が考える「現在のパケット」がずれる。修正も小さかった。重複ACKに、DATA送信側が今のブロックを再送するよう命じさせない。信頼性には、再試行する時機だけでなく、もう再送しない判断も必要だった。

出典