要約

  • DHCP はオプション52による宣言で、file と sname を追加のオプション領域として使える。古いフィールド名だけでは、現在の中身の意味は決まらない。
  • 借用した各領域には独自の終端があり、個々の符号化されたオプションは境界を越えられない。容量の再利用は、境界をなくすことではない。
  • 分割された値は options、file、sname という論理順序で復元する。物理的な配置順とは異なり、正しく復元できても設定の権限や安全性まで証明されるわけではない。

一つの End は、すべての情報の終わりではない

オプション列に End が現れたら、その列は終わる。ところが DHCP では、そこでメッセージ内の設定をすべて読み終えたとは限らない。別の領域もオプションとして使うという宣言があれば、そちらを読む必要がある。

この違いは、単なる終端記号の細部ではない。一つの容器の終わりと、設定全体を理解し終える位置が別になっている。以前は名前を入れていた領域が別の容器になると、解析側はそれぞれの境界と、それらをたどる順序の両方を知らなければならない。

1997年3月のRFC 2131は、その条件を明記した。通常の options 領域を先に解釈して借用の宣言を読み、指定されていれば file、次に sname を処理する。追加領域のオプション列も End で終了し、残りは Pad で埋める。

これは特定の障害を実測した話ではない。規則から分かるのは、最初の End だけを見て後続の論理領域を無視すると、送られた設定を取り落とし得るということだ。メッセージの理解は、バイトを先頭から順に眺めるだけでは完成しなくなった。

起動のための名前には専用の場所があった

1985年9月のRFC 951で定められた BOOTP は、十分に動作環境が整っていない機械に、アドレスやサーバー、起動ファイルの情報を渡すための仕組みだった。固定形式の中には、その目的に沿った場所が用意されていた。

sname は任意のサーバーホスト名を置く64オクテット、file は起動ファイル名を置く128オクテットで、いずれもゼロで終わる文字列だった。さらに64オクテットの vend 領域が、ベンダー固有の情報に使えた。

小さな起動プログラムにとって、決まった位置に名前があることは分かりやすい。これらは後の拡張のために無意味に空けておいた場所ではなく、当初の仕事を担う領域だった。

DHCP はその骨格を引き継ぎながら、より多くの設定を運ぶようになった。RFC 2131では options を可変長にし、少なくとも312オクテットのオプション領域を受け取れることをクライアントに求めた。さらに大きいメッセージの大きさを取り決める方法も別にある。単一オプションの長さの上限と、メッセージ全体の上限を混同してはならない。

先に宣言し、あとで読み方を変える

領域の再利用は2002年に突然現れたわけではない。RFC 1533は1993年10月の時点で Option Overload を定めていた。番号は52、値の長さは1オクテット。値1は file、2は sname、3は両方をオプションに使うという意味になる。

宣言が指すのは、そのメッセージで指定された領域である。見慣れないバイトがある場所を自由にオプションとして解釈してよいという一般的な許可ではない。選ばれていない領域を、空間が足りないからという理由で勝手に取り込むこともできない。

宣言を通常の options に置くのは、解析の出発点を確定させるためだ。新しい読み方が必要かどうかを、その読み方をまだ許されていない領域の中で探してはならない。双方がすでに理解している場所に、次に読む場所の変更を知らせる。

送信側には空間の使い方を選ぶ余地がある。しかし、その選択を受信側に知らせる位置と形式まで自由なのではない。この小さな共通部分が、局所的な工夫を相互運用可能な変更にする。

借りた場所にも壁は残る

通常のオプション領域は4オクテットの magic cookie から始まり、その後に番号付きの項目が続く。一般的な可変長オプションは番号、長さ、その長さ分のデータで構成される。長さには番号と長さ自身のオクテットを含めない。Pad と End は1オクテットだけの例外だ。

借用された file や sname では、最初のオクテットからオプションを置く。別の cookie を先頭に足すわけではない。各領域には End と残りの Pad が必要で、一つの符号化されたオプションはその領域の中に収まらなければならない。

壁の向こうに空きがあっても、一つの項目を物理的に流し込んでよいことにはならない。複数の領域を利用できるという約束と、一つの領域の境界を越えられるという約束は違う。

file と sname を合わせると192オクテットの場所になるが、192オクテットすべてが追加データになるわけではない。番号、長さ、終端、詰め物、宣言、移し替えた起動情報などが容量を使う。これは有限の再利用であり、無限の拡張でも IP フラグメント化でもなかった。

名前は、元の欄から移せる

古い名前を捨てなくても領域を借りられる。1997年3月のRFC 2132では、sname をオプションに使う場合の TFTP サーバー名をオプション66、file を使う場合の起動ファイル名をオプション67で表す。

専用の固定欄にあった情報が、識別番号付きのデータへ移るのである。file から読める文字列が消えたことは、起動ファイルが指定されていない証拠にはならない。監視や表示が古い置き場所だけを見ていると、移動を欠落と誤認してしまう。

現在のIANA BOOTP/DHCP 登録簿にも、52、66、67と、後の Domain Search である119が記録されている。これらはオプション番号であり、トランスポートのポート番号ではない。67が DHCP サーバーの UDP ポートと同じ数であることも、二つの意味を同一にはしない。

登録は種類を共通に認識するためのものだ。指定されたファイルの存在、転送の成功、実行の許可までは保証しない。名前を運ぶ処理と、その後に起動を行う処理の境界は残る。

長さと空き容量は別の制約だった

一般的なオプションの長さは1オクテットなので、一つの実体が持てるデータは255オクテットまでになる。メッセージ全体に余裕があっても、それより長い値を一回の長さ表示だけで表すことはできない。

逆に短い値でも、現在の領域に残る空きには収まらない場合がある。別の使用可能な領域に残りを置けるなら、値を分割する方法が必要になる。ここでは長大な設定であることが分割の条件ではない。

Ted Lemon と Stuart Cheshire による2002年11月のRFC 3396は、この二つを扱った。従来の RFC 2131にあった、原則として同じオプションは一回だけという文を明示的に削除し、分割と連結の規則を整理した。

各部分は同じオプション番号と個別の長さを持つ。データ部分の長さを合計したものが、元の論理的な値の長さになる。番号と長さまで値に混ぜたり、最初や最後の部分だけを残したり、各部分を独立した設定として扱ったりしてはならない。

例として示される起動パス/diskless/fooは13オクテットしかないが、7と6に分けられる。切れ目は diskless の途中であり、ディレクトリーの意味上の区切りではない。分割位置に意味を持たせず、受信側が完全な値を戻してから解釈するという規則が重要になる。

領域の順番は、見える順番ではない

RFC 3396の集約オプションバッファは、通常の options、file、sname という順の論理的な集合だ。選ばれていない領域は含めない。物理配置では sname、file が options より前にあるため、実際に受信した順をそのまま連結順にすることはできない。

最初に宣言を確認し、使用する領域を定め、規則の順番で同じ番号のデータを集める必要がある。フィールドの位置を変えずに意味を保つには、この読み替えを実装ごとの偶然に任せられない。

領域の借用と値の連結は、ここでも別の仕組みである。借りた領域に分割していないオプションを置くことも、通常の options 内だけで長い値を複数の実体に分けることもできる。一方は置き場所、もう一方は復元方法を定義する。

新しい文書は古い受信側を更新しない

RFC 3396は当時、多くの稼働中の DHCP プログラムが連結を実装していないと記していた。やむを得ない場合や、相手が対応すると分かる場合を除き、安易に分割しないよう求めたのである。

相手が連結を必要とするオプションを提供または要求していたり、管理者が明示的に対応を仮定する設定をしていたりすることが、その判断材料になる。新たな万能の能力ビットを作ったわけではない。この2002年の記述を、現在の機器の利用率に読み替えることもできない。

送信側が普通のオプションを分割しない方針を選んでも、受信責任まで単純になるとは限らない。連結を要するオプションに対応する実装は、ほかの受信オプションの分割も連結できなければならない。自分が出力しない形を、相手も送ってこないと決めつけてはいけないのである。

全体ができてから決まる座標

同月のRFC 3397は、DNS の検索リストを運ぶオプション119を定めた。長いリストを複数のオプションへ分けられ、共通の接尾部分を節約するために DNS 式の名前圧縮も使う。

圧縮ポインターの位置は、連結済みのデータ全体を基準にする。各部分の番号や長さは含まず、DHCP パケット先頭からの距離でも、部分ごとにゼロへ戻る距離でもない。したがって、名前を解釈する前にデータを論理的に再構成する必要がある。

この例が示すのは、圧縮一般の歴史ではなく、解釈の依存関係だ。下の層で作るべき全体がまだないのに、上の層の座標を読み始めれば、局所的な長さが正しくても意味は崩れる。

復元が成功しても、その設定が信頼できるとは限らない。RFC 3397は、検索リストによって短い名前の行き先となる完全なドメイン名が変わり得ることを指摘した。その別の名前のデータに有効な署名があっても、利用者が本来そこを選びたかった証明にはならない。

DHCP は古い領域を使い直すため、境界を消すのではなく、境界をどう越えて理解するかを明示した。一つの End の後にも仕事が残るのは、規則が曖昧だからではない。規則が複数の容器を区別しているからだった。