TIPS/Linux/iptables tutorial

Last-modified: 2006-08-04 (金) 00:54:38

予備知識
このドキュメントを理解するには、カーネルのコンパイル方法やカーネル内部に関する知識の他、Linux/Unix とシェルスクリプティングについて、或る程度の予備知識を必要とする。

ドキュメントの大枠を把握するところまでは、可能な限り予備知識の必要性を排除したつもりだが、まったく無しで理解できるようにするのは無理な話だ。


このドキュメントで用いる表記法
当ドキュメントでは、コマンド、ファイル、その他特定の事柄に関して、以下のような表記を行う。

コードの引用とコマンドの出力は、出力は固定幅フォント、ユーザが入力するコマンドは太字を使い、このように書く:

[blueflux@work1 neigh]$ ls
default eth0 lo
[blueflux@work1 neigh]$

    コマンドとプログラムの名称は太字 (bold)。

ハードウェアなどのシステムアイテム、カーネルの内部構造、ループバックインターフェースなどシステムの論理アイテムは イタリック (Italic) で示す。

テキスト内でのコンピュータの出力は this way のような様式で書く。

ファイルシステム上のファイル名とパスは /usr/local/bin/iptables のように表す。


この日本語訳で用いる表記法>
訳者は、Linux のドキュメントの翻訳物にありがちな「直訳」は本当の意味での正確な訳ではないと考えました。そのため、多少、元の構文を無視して意訳的な訳し方をした部分もあります。こうすることで、著者の表したかったことが、より正確に伝わるものと信じています。それでも、言語および言語習慣の違いから、どうしても言い表せない、あるいは、言い表そうとすれば日本語とはかけ離れた奇妙で読みにくい文章になってしまう部分もあります。そういった場合には、以下のような補足を織り込むことにしました。

[訳者註: ] または省略して [: ] は、訳者が加えた補足事項で、原文にはありません。

(masquerading) のように ( ) で囲まれた英語句は、その直前の訳語が原文でどう書かれているかを示しています。ネットワークの常識として把握しておくべき語句だったり、設定時やログメッセージなどで実際に出くわす機会があるため英語表記を知っておいたほうがいい場合などに用いています。


Chapter 1. 序文
1.1. なぜこのドキュメントを書いたか
何というか、出回っている HOWTO には、Linux 2.4.x カーネルの iptables と Netfilter の機能に関して、情報がすっぽり抜けていると感じたから。特にステート (state) マッチングのような新しい機能について沸き上がるであろう疑問に対して、できるだけ答えるようにしていく。その大部分は、 /etc/rc.d/ のスクリプトとして使える rc.firewall.txt を使って解説していく。そう、お気づきの方もおられようが、このファイルは masquerading HOWTO のものをベースにしている。

また、実際僕にも経験のあることだが、設定がぐちゃぐちゃになってしまった時のために、小さなスクリプト rc.flush-iptables.txt も用意しておいた。


1.2. どのようにして書いたか
このチュートリアルは boingworld.com に書いたほんの短い文書に端を発している。そのホームページは、数年前に僕を含めた少人数のメンバーで運営していたニュースサイトで、 Amiga や Linux、その他の全般的な事柄を扱うものだった。沢山の読者を得て多くのコメントが寄せられたのに元気づけられて、僕は執筆を続けた。初めは印刷すると A4 で 10~15 ページほどの分量だったが、少しずつ、しかし着実にボリュームアップしていった。それはもう沢山の人たちが、執筆、校正、バグチェックなどを手伝ってくれた。この文章を書いている時点で、http://iptables-tutorial.frozentux.net/ は延べ 600,000 ヒットを数えるに至った。

このドキュメントは、セットアップの行程を順を追って案内していく流れを採っている。そうすることで、 iptables パッケージをもっとよく知ってもらう手助けになれればと思う。 iptables の使い方を学ぶには例を見るのが一番だと思うので、ほとんどの要素は、例 rc.firewall ファイルに集約したつもりだ。僕は、まず基本的なチェーン構造をカバーし、そこから、パケットの通っていくチェーンのひとつひとつを記述していくことで、スクリプトの働きを説明することにした。このやり方だと、より論理的である反面、チュートリアルについていくのはやや難しくなるかもしれない。[訳者補足: スクリプトで] 理解しにくいところが出てきたら、いつでもこのチュートリアルに戻ってくればいい。


1.3. このドキュメントで使う語句
事前説明の必要な語句は、このドキュメントにはわずかしかない。このセクションでは、主立った語句と、それをドキュメント中で使う理由を説明する。

コネクション -- (Connection) このドキュメントにおいてのこの語彙は、相互に関わりを持つパケットの集まりを指す。パケット同士は、確立済み (established) の関係となるわけだ。コネクションはまた、一連のパケットの遣り取りだとも言える。 TCP 接続で言えば、これは 3 ウェイハンドシェイクを踏んで接続を確立することであり、そのコネクションは切断のためのハンドシェイクが完了するまで存続する。

DNAT -- 宛先ネットワークアドレス変換 (Destination Network Address Translation)。 DNAT とは、パケットの宛先IPアドレスを変換する技術、または変えること自体を指す。 SNAT と組み合わせて、インターネット上でルーティング可能な 1 個の IPアドレスを複数のホストでシェアしたり、サーバサービスを提供したりするのに用いられる。これは通常、インターネット上でルーティング可能な 1 個の IPアドレスに対して、別のポートを振り直し、 Linux ルータに宛先を指示することにより行われる。

カーネル空間 -- (Kernel space) いわば、ユーザ空間の反意語。カーネルの内側での出来事、カーネルの外でないところで起こるあらゆる事象を指す。

ストリーム -- (stream) この語句は、何らかの意味で関係性のあるパケットをやりとりする接続を指す。僕は元来、2 つ以上のパケットを双方向に送るあらゆる接続に対してこの語句を使ってきた。つまり TCP においては、SYN を送り SYN/ACK で応答することを意味するが、SYN を送り ICMP の Host unreachable が返って来ても、やはりこの語句に当てはまる。非常に曖昧に使う、とも言える。

SNAT -- 送信元ネットワークアドレス変換 (Source Network Address Translation)。パケットの送信元アドレスを変換する技術のこと。 IPアドレスの枯渇 (IPv6が救うだろう) に瀕した現在、この技術よって、ひとつのインターネット IP アドレスを複数のホストで共有可能にしている。

ステート -- (state) この語句は、パケットがどの状態にあるかを指す。状態とは RFC 793 - Transmission Control Protocol に照らして言う場合もあるし、 Netfilter/iptables で使用されるユーザサイドでの状態を言う場合もある。憶えておいてほしいのは、カーネル内部でのステート、外部でのステートのどちらも、 RFC793 の定義の文字通りそのままではないという点だ。その主たる理由は、 Netfilter が接続とパケットについてあれこれと推測を行うためそれらを駆使する必要があるからだ。

ユーザ空間 -- (User space) 僕はこの語句で、カーネルの外で起きるあらゆる事柄を指す。例えば、iptables -h はカーネルの外だが、iptables -A FORWARD -p tcp -j ACCEPT はルールセットに新たなルールを加えるので (部分的にしても) カーネル内の出来事だ。

ユーザランド -- (Userland) ユーザ空間を参照のこと。

パケット -- (Packet) ネットワーク伝送の最小単位であり、ひとつのヘッダとデータ部から成る。例えば IP パケットや TCP パケットがある。インターネットの規約 (Request For Comments = RFC) では、 IP パケットを表すにはデータグラム (datagram)、 TCP パケットにはセグメント (segment) という呼び名が使われるが、"パケット"という言葉は普遍化されていない。僕は、物事を単純化するために、このドキュメント内ではあらゆるものをパケットと呼ぶことにした。

セグメント -- (Segment) TCPセグメントは "パケット" とほぼ同義だが、 TCP パケットを表す時の "かしこまった" 呼び方。


Chapter 2. TCP/IPのおさらい
iptables は知識のあるなしで大違いのツールだ。別の言い方をすれば、 iptables をフル活用するにはそれなりの知識が必要だということだ。中でも TCP/IP プロトコルについては相当に精通している必要がある。

このチャプターは、とにかく TCP/IP についての「必須知識」を解説するために設けた。先へ進んで iptables と取っ組み合うのはそれからだ。ここでは特に、 IP, TCP, UDP, ICMP プロトコルとそのヘッダについてと、それぞれの用途の違い、それに、各プロトコル同士の関係について、駆け足で見ていく。 iptables はインターネット層とトランスポート層で働く。それ故に、このチャプターではそれらのレイヤー [訳者註: 層] にも焦点を当てる。

iptables はより表層のレイヤーで機能することも可能で、例えばアプリケーション層で作用することもできる。だが、 iptables は本来、そうした用途は意図しておらず、そんな使い方をすべきではない。それについては IPフィルタリングとは のチャプターでもう少し詳しく述べることにしよう。


2.1. TCP/IPのレイヤー
先にも述べたように、 TCP/IP は多層構造になっている。ということは、或る層でひとつの機能を働かせながら、別の層で別の機能を働かせ、そしてまた別の層で...ということが可能なわけだ。レイヤー構造を持つ意義は、考えてみれば単純だ。

多層構造となっている最も大きな理由は、拡張を容易にするため。例えばアプリケーション層に新たな機能を追加したい時にも、わざわざ TCP/IP スタックのコードを書き直したり、アプリケーション側に TCP/IP スタックの一切合切を装備しなくても済む。同様に、新しいネットワークインターフェースカードを創るたびにプログラムを逐一書き直すなんてことも必要ない。各レイヤーは独立して働けるように、他のレイヤーのことには必要最小限だけ関知していればいいようになっているのだ。

カーネルの備える TCP/IP プログラミングコードについて語る時、実はそれは TCP/IP スタックのことを言っている場合が多い。 TCP/IP スタックとは、ネットワークアクセス層から表層のアプリケーション層に至るまでの、サブレイヤー一切合切のことである。

レイヤーについて語る時、基礎となる構造定義がふたつある。ひとつは OSI (Open Systems Interconnect) 参照モデルで、これは TCP/IP を 7 層に分けている。ただし、我々の興味はどちらかというと TCP/IP レイヤーモデルのほうにあるので、ここではざっと眺める程度にしておこう。しかし、歴史的な意味から、OSI参照モデルにも興味深い点はある。特に、さまざまな種類の異なるネットワークを扱わなければならない場合だ。 OSI参照モデルのレイヤーは下記の一覧の通りだ。

アプリケーション層 (Application layer)

プレゼンテーション層 (Presentation layer)

セッション層 (Session layer)

トランスポート層 (Transport layer)

ネットワーク層 (Network layer)

データリンク層 (Data Link layer)

物理層 (Physical layer)

我々がひとつのパケットを送ったとすると、そのパケットはリストの上方から下方へと進み、それぞれのレイヤーが、カプセル化 (encapsulation) と呼ばれる段階で各々独自のヘッダ一式をくっつける。そして目的地へ行き着くと、今度はリストを逆順に辿り、パケットからヘッダが一個一個取り除かれていく。取り除かれる際、各ヘッダは、パケット内のデータから目的のホストの得るべき情報を全て渡していく。そうしてやっと、送信目的だったアプリケーションあるいはプログラムに辿り着くわけだ。

もうひとつの、そして我々にとって、より重要な意味を持つレイヤーモデルが、 TCP/IP構造モデルだ。それを一覧にしたのが下記の TCP/IP構造リスト。 TCP/IP構造モデルの階層数については、これといった共通認識は存在しない。とはいえ、3 ないし 5 層から成るというのが一般的な認識となっていて、図解や説明がなされる時には、たいてい 4 層として扱われているようだ。混乱を避けるため、このドキュメントでも、一般的な 4 層モデルのみを対象に話を進める。

アプリケーション層 (Application layer)

トランスポート層 (Transport layer)

インターネット層 (Internet layer)

ネットワークアクセス層 (Network Access layer)

見ての通り、 TCP/IP構造は OSI参照モデルとそっくりだ。だが、まだそう断じるのは早計だ。入る時、出る時に、ヘッダをくっつけたり取り去ったりするのは OSI 参照モデルの時と同じだ。

では、最近よくコンピュータネットワーキングの比喩に用いられるスネイルメールの手紙に喩えてみよう。[訳者註: snail-mail: 昔ながらの紙の手紙のこと。 eメールの普及によって逆に最近生まれた言葉]。物事は何でも順を追って行われる。 TCP/IP も同じだ。

ご機嫌伺いの手紙を送ろうとしているとしよう。まず初めに、内容あるいは質問する事柄を考えなければならない。実際のデータで言えば、これはアプリケーション層に置かれることになる。

そうしたら次に、便箋に内容を書いて封筒に入れ、どこの会社あるいはどこの家の誰かさん宛てだという旨を封筒に書くはずだ。例えばだが、おそらくこんな風に:

     ジョン・ドー 様

これは TCP/IP で言うところのトランスポート層にあたる。トランスポート層で、今仮に TCP を相手にしているとすれば、必ずというわけではないがこれは何らかのポートナンバーかもしれない。

ここで、今度は封筒に受取人の住所を書く。こんな具合だ:

     V. Andersgardsgatan 2
     41715 Gothenburg

これはインターネット層にあたる。インターネット層は、 TCP/IP ネットワーク上のどこへ行けば受取人あるいはホストへ行き着けるかを示す情報を格納する。ちょうど、封筒に書く宛名と同じだ。

そして最後のステップは、この封筒に収まった手紙をポストへ投函する作業だ。これとほぼ同義なのが、パケットをネットワークアクセス層へ送り込むこと。ネットワークアクセス層は、パケットの搬送される物理的なネットワークにアクセスするための、機能や手順を含有している。

受取人は、封書を受け取ったら、封筒から手紙を取り出すだろう (つまり非カプセル化(decapsulate))。受け取った手紙は返信が必要かもしれないしそうでないかもしれない。いずれにしろ、返信するとすれば、受け取った人は、手紙に書いてあった宛先と差出人を逆にして送るだろう。差出人が宛先に、宛先だった人が差出人になるようにするわけだ。

ここで頭に入れておかなければならないのは、 iptables はインターネット層とトランスポート層のヘッダに作用するべく作られているということだ。確かに、基本的フィルタの中にもアプリケーション層やネットワークアクセス層で使えるものがある。しかし、これは iptables の設計意図から外れるものだし、 iptables はそういった目的には適していない。

例えば string マッチを使ってパケットの中の文字列、例えば "GET /index.html" でパケットを捕まえるとしよう。これはうまくいくだろうか? 普通は YES だ。だが、パケットのサイズが非常に小さい時には、そうはいかない。これは iptables がパケット単位で作用するように作られているからで、もしも文字列が複数のパケットへと分断されていた場合、 iptables はそれを一続きの文字列として検出することができない。そうした理由から、アプリケーション層でのフィルタリングを行うという用途であれば、プロキシなど別のものを使うほうがずっとずっと確実だ。こうした問題については、 IPフィルタリングとはで詳しく考察する。

iptables および netfilter の活動の舞台は主にインターネット層とトランスポート層なので、このチャプターのこれ以降のセクションでも、主としてそれらの層に焦点を当てていく。インターネット層の配下には、 IP プロトコルという無視しがたい存在がある。その他に、例えば GRE プロトコルのように、言及すべきものもないことはないが、そうしたプロトコルに出会う機会は限られている。また、 iptables は (その名が示す通り) その種のプロトコルを上手に扱うようには作られていない。上に挙げた様々な理由から、このドキュメントでは、インターネット層の IP プロトコルと、トランスポート層の TCP, UDP, ICMP を扱っていく。

ICMP プロトコルは、実はふたつのレイヤーが混じり合ったものだ。 ICMP パケットは、インターネット層で活動するものであると同時に、 IP プロトコルと全く変わらないヘッダに加えて、他にも幾つかのヘッダを持ち、カプセルのすぐ内側にはデータも格納している。これについては ICMPの特徴 の中でもっと詳しく述べることにする。

2.2. IPの特徴
既に述べたように、 IP プロトコルはインターネット層に属している。 IP プロトコルは TCP/IP スタックのうちで、コンピュータやルータ、スイッチなどにパケットの行き先を教える役割を担うプロトコルだ。 IP プロトコルこそ、 TCP/IP スタックの心臓部であり、インターネットの土台を支えているプロトコルである。

IP プロトコルはトランスポート層のパケットを、トランスポート層プロトコルの種別や行き先のホスト、発信元をはじめその他幾つかの情報とともにカプセル化 (encapsulate) する。この処理は全て、微に入り細に入り規格化がなされている。こうした厳格さは、当チャプターで述べていくいずれのプロトコルにも当てはまることだ。

IP プロトコルには、必ず備えなければならない幾つかの基本的機能が定められている。 IP プロトコルは、データグラムつまり、トランスポート層の作る次の建屋を用意できなくてはならない (トランスポート層とは例えば TCP, UDP, ICMP のいずれか)。 IP プロトコルはまた、今日我々がお世話になっているインターネットの住所指定の仕組みも司る。つまり、 IP プロトコルはホスト間を往き来する方法を定め、それ故に当然、パケットをルーティングできるのも IP プロトコルのおかげというわけだ。ここで話題にしている住所 (address) とは、いわゆる IPアドレス のこと。通常我々が "IPアドレス" と言う時、ドット区切りの 4つの数字 (例えば 127.0.0.1) を指す。ただし、この形式は専ら人が見て読みやすいようにするためのもので、 IPアドレスの実体は 32 ビット長の単なる 1 と 0 の並びである (即ち 127.0.0.1 なら、ヘッダ上では 01111111000000000000000000000001 と書かれている)。

IP プロトコルがその袖口で演じなくてはならないマジックはこれだけではない。 IP データグラム (IPデータ) をカプセル化および非カプセル化できる能力、ネットワークアクセス層とでもトランスポート層とでもデータを遣り取りできる能力だ。これはことさら目につく働きではないかもしれない。しかしそれ以前に、 IP プロトコルには果たさなければならない更にふたつの重要な機能がある。しかもそれはファイヤーウォール構築とルーティングに関わり合う人なら興味を惹かれずにいられない機能だ。 IP プロトコルは、ホストからホストへのパケットばかりでなく、どこかのホストから自分以外のホスト宛てのパケットが入ってきた際の動作も司っているのだ。ひとつのネットワークにしかアクセスしないホストであれば、処理はほとんどの場合至って単純だ。選択肢はふたつしかない。自分につながっているローカルネットワーク行きのパケットか、デフォルトゲートウェイを通るパケットのどちらかだ。しかし、ひと度ファイヤーウォールやセキュリティポリシーを用いて複数のネットワークインターフェースやルーティングを操り始めると、途端に、ネットワーク管理者は頭痛に悩まされるようになる。 IP プロトコル最後の機能は、フラグメントされたデータグラムを再構成したり、接続されたネットワークのハードウェア的特性に合わせて適切なパケットサイズへとフラグメントする能力だ。パケットのフラグメントサイズが目に見えて小さい場合にもやはり、ファイヤーウォール管理者は酷い頭痛に襲われる。パケットが粉微塵にフラグメントしているがために、肝心のデータグラムばかりか、パケットヘッダを読み取るのにさえ支障を来しているわけだ。

2.4 シリーズの Linux カーネルおよび iptables では、 Linux でファイヤーウォールを運営する上でこれはもはや問題とはならなくなった。 iptables がステートマッチングや NAT に使用するコネクション追跡メカニズムは、フラグメントされたパケットを読む必要がある。そのため、パケットがカーネルの netfilter/iptables 構造部に届く以前に conntrack が全てのパケットのデフラグメンテーションを行うからだ。

IP プロトコルのもうひとつの特質は、コネクションレスなプロトコルであるという点だ。つまり、 IP は接続に際しての "ネゴシエーション (negotiation = 折衝)" を行わない。これに対して、コネクション指向のプロトコルは、"コネクション" のネゴシエーション (ハンドシェイク と呼ばれる) を行い、データを全て送り終えたら接続を締め切る。 TCP はそういったプロトコルの一例だが、その仕組みが実装されているのは IP プロトコルより一皮上だ。 IP 層の段階でコネクション指向にしない理由はいろいろあるが、特に言えるのは、このレベルで敢えて不要なオーバーヘッドを伴うハンドシェイクを実装する意味がないからだ。その重荷は他のプロトコルに背負ってもらえばよい。オーバーヘッドは、もし返答が得られなければ、伝送経路のどこかでパケットが失われたと分かり、当初のリクエストを送信しなおす、という処理から生じる。お察しの通り、この場合、リクエストを送って一定の時間だけ回答を待つほうが賢い。でなければ、まずコネクション開始の要望を知らせるパケットを送り、次にコネクションが開いたと告げる知らせのパケットを受け取り、続いてコネクションが開いたことを確かに承知しましたという承認を行い、ここでやっとこさ当の目的のリクエストを送り、そうしたらコネクションを切断するためのパケットを更に送りその返事を待つ、という手間になってしまうのだ。

IP はまた、低信頼性のプロトコルとしても知られる。簡単に言えば、パケットが届いたのか届かなかったのかには無頓着なのだ。 IP プロトコルはただ単純にトランスポート層からパケットを受け、やるべきことをやったら、ネットワークアクセス層へ渡し、それでおしまい。返答のパケットが来るかもしれないが、それがネットワークアクセス層から IP プロトコルへとやってくると、 IP はまたやるべきことをやり、上のトランスポート層へと渡す。しかし、返事のパケットが来るのかパケットが行き先まで届いたのかにはお構いなしだ。 IP のこの低信頼性についても、先ほどの "コネクションレスさ" と同様のことが言える。低信頼性を何とかしようとすれば、パケットを送るたびに追加のパケットが必要となるのだ。 DNS 検索を例にとってみよう。通常やるように、 servername.com を牽こうと DNS リクエストを送信する。返答が得られなければ、何か手違いがあったと考えてルックアップを再要求する。しかし我々は通常、リクエストを送り出すのは 1回だけで、受け取る回答も 1回だけだ。信頼性を高めようと思ったら、リクエストには 2回のパケットを要することになり (リクエスト 1回と、パケット到着確認 1回)、返答も 2回要る (回答 1回と、回答の領収の通知で 1回)。つまり、送るべきパケットの数は倍になり、流れるデータの量もほぼ 2倍になるのだ。


2.3. IPヘッダ
IP プロトコルの概要を示した前章でお分かりかと思うが、 IP パケットはヘッダに幾つかの部位を備えている。ヘッダ全体は性質毎に細分化され、オーバーヘッドを可能な限り抑えるという命題のために、それぞれの部位は機能できる必要最小限にまでサイズが切り詰められている。 IP ヘッダの諸元は、図「IPヘッダ」 にある。

ヘッダの部位に関する説明は、ほんのさわりに過ぎず、この先もその基本骨子にしか触れないという点を、ご承知おき頂きたい。これから各ヘッダについて述べる中で、当該のヘッダのより細かい説明を提供してくれる適切な RFC は紹介していく。補足の補足として書いておくが、 RFC は、Request For Comments [訳者註:「インターネット規約」と訳されることが多い。直訳すると「回答伺い」あるいは「回答募集中」] の意味だが、今日のインターネットコミュニティにおいては、名称とは全く異なる意味合いを持つようになった。研究者達が RFC を書き始めた当初とは違って、今の RFC はインターネットそのものを規定したり標準化するものとなっている。遡れば RFC はその名の通りコメントを要望する文書であり、自分の意見に対して他の研究者達に見解を乞う手段だったのである。

IP プロトコルについての大部分は RFC 791 に書かれている。ただし、この RFC は RFC 1349 - Type of Service in the Internet Protocol Suite によって一部改訂 (update) され、その RFC 1349 はまた RFC 2474 - Definition of the Differentiated Services Field (DS Field) in the IPv4 and IPv6 Headers によって廃版 (obsolete) とされ、その RFC 2474 は RFC 3168 - The Addition of Explicit Congestion Notification (ECN) to IP および RFC 3260 - New Terminology and Clarifications for Diffserv により一部改訂された。

ご覧のように、これら標準化文書は、時としてかなり追いかけにくい。関連のある RFC 群を見つけようと思った時のひとつの手段は、 RFC-editor.org の備えている検索機能を使用することだ。 IP の場合には、 RFC 791 が大元で、その他の RFC はそれを改訂したり変更を加えているに過ぎない。そうしたことについては、そういう新しい RFC によって変更されたヘッダを取り扱う際に、それぞれ詳しく述べる。

ひとつ憶えておいてほしいのは、 RFC は廃版にされる (もう一切使われなくなる) ことがあるということだ。そうなるのは大抵、その RFC が大幅に変わり、丸ごと置き換えたほうが得策だと判断された時。別の理由で廃版にされる場合もある。廃版となった RFC には、それに取って代わった新しい RFC を指し示す文言が書き添えられる。

バージョン (Version) - ビット 0-3。これは IP プロトコルのバージョンナンバーを 2進数で表している。 IPv4 は 0100、 IPv6 は 0110 で表される。このフィールドがフィルタリングに使われることはほとんど無い。 RFC 791 に書かれているのは IPv4 のほうだ。

IHL (ヘッダ長 Internet Header Length) - ビット4-7。このフィールドは IPヘッダ の長さを 32ビット毎のワード数で示したもの。お分かりの通り、 IP ヘッダの図はこれに倣って 1行当たり 32ビットとなるようヘッダを区切ってある。 Options フィールドは自由長なので、この IHL フィールド無しにはヘッダ全体の長さを特定することができない。このヘッダの長さは最小 5 ワードだ。

サービスタイプ (Type of Service), DSCP, ECN - ビット8-15。ここは IPヘッダ の中でも最も難解な部分。というのも、3度にも渡って変更されてきたからだ。いずれの改訂でも基本的な用途こそ変わらなかったものの、実装方法が度々変更になった。まず最初、このフィールドはサービスタイプ (Type of Service) フィールドと呼ばれていた。このフィールドのうちビット 0 からビット 2 までは優先区分 (Precedence) フィールドと呼ばれた。ビット 3 は遅延の中/低 (Normal/Low delay)、ビット 4 はスループットの中/高 (Normal/High throughput)、ビット 5 は信頼性の中/高 (Normal/High reliability) であり、ビット 6 から 7 は将来の用途のための未使用域とされていた。このヘッダは、現在でも旧式なハードウェアを運用している場所ではかなり使われており、そして相変わらずインターネットに弊害をもたらし続けている。特に、ビット 6 からビット 7 がゼロにセットされていなくてはならないという規定が問題だ。 ECN の改訂 (RFC) でこれらのビットが使われるようになり、ゼロ以外の値にセットするケースが出てきたからだ。ところが、旧型のファイヤーウォールやルータの中には、これらのビットが 1 になっていないかのチェック機構を内蔵しているものがあり、それに引っ掛かったパケットを破棄してしまうのである。そうした挙動は今日の RFC に完全に抵触しているわけだが、我々ができることといえば悪態をつくことくらいのもので、為す術がないのが実情だ。

2度目のでんぐり返しである RFC 2474 によって、このフィールドは DS フィールドと呼ばれることになった。 DS は Differentiated Services を表す。この規格によれば、ビット 8-13 は Differentiated Services Code Point (DSCP) となり、残りの 2 ビット (14-15) は引き続き未使用だった。 DSCP フィールドの利用法は ToS フィールドの時とさほど変わらない。つまり、区別ができるようルータを設定しておけば、このフィールドを目印にしてパケット毎に処遇を切り替えられるというものだ。この改訂で大きく変わったのは、 RFC 2474 準拠であるためには、デバイスは未使用のビットを無視しなければならなくなったという点。これによって、デバイスを作る側がこの RFC に従う限りは、前の規格で格闘を余儀なくされていた難解な設定とはおさらばできるようになった。

ToS フィールドの 3度目であり実質的に最後の変更となったのが FRC 3168。それによって、以前は未使用だったふたつのビットが ECN (Explicit Congestion Notification = 明示的輻輳通知) に使用されるようになった。 ECN は、ルータが輻輳を起こした際、実際にルータがパケットを取りこぼし始める前に、末端のノードに輻輳を知らせる役目を持つ。このおかげで、末端のノードは、ルータが実際にデータを取りこぼす前にデータの送信速度を落とすことができる。この規格ができるまでは、ルータにとっては、実際にデータを破棄するしか過負荷を知らせる術がなく、エンドノードはそれを見て速度を一旦がくんと落とし (slow restart)、破棄されたパケットをおもむろに送り直し、具合をみながら元のスピードまで上げていくという手段をとっていた。ふたつのビットは、 ECT (ECNCapable Transport) と CE (Congestion Experienced) と呼ばれる。

一連の堂々巡りで最後の変更は RFC 3260 で、 DiffServ 機構を使用する上での新しい用語の規定と、規格の明確化を行った。この改訂は技術用語の類が主で、更新や変更はそれほど沢山盛り込まれていない。 RFC は開発者達の議論を経て要点を整理するという役割も持っているのだ。

全長 (Total Length) - ビット 16-31。このフィールドは、ヘッダをはじめ一切合切を含めたパケット全体のサイズをオクテット単位で示す。パケットひとつのサイズの上限は 65535 オクテット (=バイト)。到着した時点でパケットがフラグメントしているかどうかにかかわらず、最小サイズは 576 バイトだ [訳者註: フラグメントしている時の値は再構成後の合計サイズではなくフラグメント個々のサイズ]。 RFC 791 に照らして、ホストが必ず受け取れるという確証のない限り、この制限を上回るサイズのパケットを送るのはお勧めできない。ただし、今日ではほとんどのネットワークがパケットサイズを 1500 バイトにして運用している。ほぼ全てのイーサネット接続がそうだし、ほとんどのインターネットコネクションでも同様だ。

識別子 (Identification) - ビット 32-46。このフィールドはパケットフラグメントの再構成を助けるためにある。

フラグ (Flag) - ビット 47-49。このフィールドはフラグメンテーションに関するその他のフラグを含んでいる。最初のビットは予約済みだが、まだ使用されておらず、ゼロになっていなければならない。 第2 ビットは、パケットをさらにフラグメンテーションしてもよい場合は 0 に、それ以上のフラグメンテーションを許さない場合には 1 にセットする。 3番目 (つまり最後) のビットは、それが最終フラグメントであれば 0 、同一パケットに属するフラグメントが他にもあるなら 1 にセットする。

フラグメントオフセット (Fragment Offset) - ビット 50-63。フラグメントオフセットは、そのパケットが元のデータグラムのどの位置にあたるのかを表す。フラグメントは 64 ビットで計算される。最初のフラグメントのオフセットは 0 である。

Time to live - ビット 64-72。 TTL フィールドが示すのは、そのパケットがどれだけ生きられるか。もう少し具体的に言うと、インターネットの中を何回 "ホップ" できるかだ。パケットに触れた各プロセスは TTL を必ず 1 ずつ奪い取っていき、もし TTL がゼロになったらそのパケットはかけらも残さず破棄されなくてはならない。この仕組みは、パケットがホスト間で制御不能のループを引き起こさないようにするための、一種の安全装置である。破棄の際には、ホストは送信者へ ICMP の Destination Unreachable メッセージを送るべきとされている。

プロトコル (Protocol) - ビット 73-80。このフィールドには上位レイヤーのプロトコルが記載される。一部を挙げれば TCP, UDP, ICMP などである。それらを示すナンバーは Internet Assigned Numbers Authority (IANA) で規定されている。ナンバーの全てはホームページ Internet Assigned Numbers Authority で調べることができる。

ヘッダチェックサム (Header checksum) - ビット 81-96。そのパケットのヘッダのチェックサムだ。ヘッダに変更を加えたホストはこのフィールドを必ず再構成しなければならない。実際のところ、パケットの通り道となったホストは TTL フィールドをはじめパケットのどこかをほぼ必ずいじるので、再計算はほとんどのホストで行われると思って間違いない。

送信元アドレス (Source address) - ビット 97-128。送信元アドレスを示すフィールド。一般的には、元の 2進数を 10進数に変換し、間をドットでつなげた 4 オクテットの値で記述される。例を挙げると 127.0.0.1 などだ。このフィールドはパケットがどこから来たのかを受信者に教える。

宛先アドレス (Destination address) - ビット 129-160。 Destination address フィールドは宛先のアドレスを格納しているが、あらビックリ、フォーマットは送信元アドレスと同じだ。

オプション (Options) - ビット 161-192<>478。名前の響きとは裏腹に、オプションフィールドの存在は任意ではない。正直なところ、このフィールドは IP ヘッダの中でもかなり複雑な部類。オプションフィールドは、インターネットタイムスタンプ、SACK、レコードルートオプション (record route options) などといった様々なオプションセッティングを格納するヘッダだ。これらの値を持つかどうかは全て任意なので、オプションフィールドの長さも可変であり、結果として IP ヘッダ自体の長さも変化することになる。ただし IP ヘッダでは常に 32 ビットを 1 ワードとして扱うため、ヘッダは必ず 32 の倍数となる偶数で終わらなければならない。このフィールドは 0 個以上のオプションを持つことができる。

オプションフィールドは 8 ビット長の短いフィールドから始まり、その部分はパケットでどういったオプションが用いられているかを知らせる。付録 TCPオプション の TCPオプション という表に全オプションを挙げておいた。各オプションの詳細を知りたければ、該当する RFC を読んで頂きたい。 IPオプション リストの最新情報は Internet Assigned Numbers Authority で調べるといいだろう。

パディング - ビット可変。ここは、ヘッダが 32 ビット境界で終わるように調整するための詰め物。このフィールドは頭から終わりまでゼロばかりが並んでいなければならない。


2.4. TCPの特徴
TCP プロトコルは IP プロトコルのひとつ表層に位置する。 TCP はステートフル (stateful) なプロトコルであり、データがもう一方のホストまで届いたかどうかを確認する機能を自ずから備えている。 TCP プロトコルの主な目的は、データが信頼性を損なうことなく受送信されたかを確認することであり、また、データがインターネット層とアプリケーション層との間で正しく伝搬されたかどうか、パケットデータがアプリケーション層において適切なプログラムにきちんと渡ったか、プログラムに正しい順序で届いたかを確認することである。これらはいずれも、パケットに TCP ヘッダがあってこそ可能なのだ。

TCP プロトコルは、データを、開始の合図と終了合図を持つ一続きのデータストリームとして捉えている。新たなストリームが通路の開くのを待っているということを表すのが、 TCP の SYN スリーウェイハンドシェイクであり、これは SYN ビットの立ったひとつのパケットから成る。相手方は、コネクションを受け入れたなら SYN/ACK、拒絶したと知らせるなら SYN/RST で回答する。クライアントは、受け取ったのが SYN/ACK だったら、もう一度、今度は ACK パケットを送る。この時点でコネクションが確立し、データが送れる状態になる。この TCP コネクションの間じゅう変わらず使われる ECN, SACK などといった特有のオプションも、この最初のハンドシェイクで話し合って取り決め (negotiate) がされる。

データストリームの活動中には、パケットが相手にきちんと届いたかを確かめる更なる仕組みがある。 TCP の高信頼たる部分だ。これはパケット内のシーケンスナンバー (Sequence number) を使って理路整然と行われる。パケットを送信する時には、それぞれに必ず固有のシーケンスナンバーを付けており、受け取った相手方は送り主に ACK パケットを送り返す。 ACK パケットはパケットを確かに受け取ったという受領の証 (acknowledge) だ。シーケンスナンバーはまた、パケットがデータストリーム内に順番通りに挿入されたことを保証する役目もしている。

コネクションを閉じる際には、一方が FIN パケットを送ることによって処理が始まる。すると相手は FIN/ACK パケットを送信する。 FIN を送った方はもう何のデータも送らないが、もう一方の相手はまだ残りのデータがあれば送りきることができる。後者は、いよいよコネクションを完全に閉じていい状態になると、初めに終局を言い出した方の端末に向かって FIN パケットを送る。そうすると前者が FIN/ACK パケットで応える。こうした一連の手順が完了して初めて、コネクションは正式に切断されるのだ。

後で触れることになるが、 TCP ヘッダにはチェックサム というものもある。チェックサム はパケットの単純なハッシュ値だ。このハッシュを使って、ホスト間の伝送中にパケットに損傷が起きたかどうかが、かなりの精度で検査できる。


2.5. TCPヘッダ
TCP ヘッダには、前述の役目を全て達成できる素養が求められる。幾つかのヘッダについては、それがいつ、どのように利用されるか既に説明したが、まだ明らかにしていない領域も他に沢山ある。下に、 TCP ヘッダを全て網羅した図を示した。見ての通り、1 段を 32 ビットワードとした形になっている。

送信元ポート (Source port) - ビット 0-15。パケットの送信元ポート。送信元ポートは、それを送信したシステム上のプロセスと直接結びついている。今日では、ひとつのアプリケーション (プログラム) に結びつけるポートの一意性を確保する必要性から、 IP アドレスと宛先および送信元ポートのハッシュ値を使用する場合がほとんどである。

宛先ポート (Destination port) - ビット 16-31。 TCP パケットの宛先ポート。送信元ポート同様、これは受信側システム上のプロセスと1対1で結びついている。今日では、より多くのコネクションを同時に張れるようにするため、ハッシュ値を用いることが多い。パケットを受信すると、送信元への返答の際には、元の宛先ポートが送信元ポート、送信元ポートが宛先ポートになるよう、宛先と送信元のポートを逆さにして送り返す。

シーケンスナンバー (Sequence Number) - ビット 32-63。シーケンスナンバーフィールドは各 TCP パケットに付け、 TCP ストリームが歯抜けにならないようにする (パケットが正しい順番で並ぶ、というような意味)。同じシーケンスナンバーは、パケットが問題なく届いたことを知らせるために、回答の際 ACK フィールドに載せて返される。

確認応答ナンバー (Acknowledge Number) - ビット 64-95。ホストの受け取った特定のパケットに対して応答確認を行う際に使用される。具体的に言えば、或るシーケンスナンバーを持ったパケットを受け取った時、パケットに特に問題がなかったら ACK パケットで確認の返答をするわけだが、その際、受け取ったパケットのシーケンスナンバーと同じ値をその確認応答ナンバーフィールドへセットして返答するのだ。

データオフセット (Data Offset) - ビット 96-99。このフィールドは、 TCP ヘッダの長さと、パケットのデータ部がどこから始まっているのかを示す。これは 4 ビットで表され、 TCP ヘッダを 32 ビットのワード単位で数えた値となる。ヘッダは、どんなオプションが使われていようとも常に 32ビット境界で終わっていなければならない。それを可能にしているのは、 TCP ヘッダの末端にあるパディングフィールド だ。

予約域 (Reserved) - ビット 100-103。これらのビットは将来の用途のために確保されている。 RFC 793 では、 CWR ビットと ECE ビットの位置もこの予約域こ規定されていた。 RFC 793 によれば 100-105 (つまり、ここと CWR および ECE フィールド) はゼロになっていなければ、規格に完全に整合しているとはいえないのだ。後になって ECN が提唱されると、多くのインターネットアプリケーション (例えばファイヤーウォールやルータ) がこの部分のビットの立ったパケットを破棄してしまうという障害を多発させる結果となった。こうして書いている今現在でも、この問題は継続している。

CWR - ビット 104。このビットは RFC 3268 で追加され、 ECN に利用されている。 CWR は Congestion Window Reduced を表し、データを送った側が受け取り側へ、輻輳制御ウィンドウ (congestion window) が縮小されたことを知らせるために利用される。輻輳制御ウィンドウが縮小したら、我々は時間当たりの送信データ量を減らすことによって、ネットワーク負荷の総量の折り合いを付けることができる。

ECE - ビット 105。このビットも RFC 3268 で追加され、 ECN に利用されている。 ECE は ECN Echo を表す。 ECE は受信側の TCP/IP スタックによって使用され、 CE パケットを受け取った旨を送信側ホストに知らせる役目をする。ここでも CWR ビットと同じことが言え、ここがかつて予約済みフィールドだったことから、ゼロ以外の値を持つパケットは、ネットワーク機器によっては破棄されてしまう恐れがある。残念なことだが、現実としてそうしたアプライアンスは多数存在している。

URG - ビット 106。このフィールドは、緊急ポインタ (Urgent Pointer) フィールドを使用するべきか否かを伝える。 0 ならば緊急ポインタは使うな、 1 ならば緊急ポインタを使うべし、である。

ACK - ビット 107。パケットにこのビットを立てるのは、そのパケットが他のパケットに対する返答であり、先ほどのパケットがデータをきちんと運んできたという旨を知らせる時だ。パケットを正しく受け取り、パケットにエラーもなかった場合には、そのことを伝えるために必ず受信確認 (Acknowledgement) パケットを送ることになっている。このビットが立っているのを見たら、先ほどデータを送った送り主は、承認確認ナンバー (Acknowledgment Number) を見て承認の対象がどのパケットかを確認した後、バッファに保持していたそのデータを破棄する。

PSH - ビット 108。プッシュ (PUSH) フラグは、経路途中のあらゆるホスト上の TCP プロトコルに対して、データをすぐに最終ユーザへ送れと告げるのが役目。 "ホスト上のTCP" には、データを受け取ることになるホストが実装する TCP 機構も含まれる。この指示が行われると、 TCPウィンドウ上に残留しているデータの量にかかわらず、全データが強制送出 (push) される。

RST - ビット 109。リセット (RESET) フラグをセットするのは、相手方へ TCP コネクションの切断を要求したい時だ。切断の行わる場面は幾つか考えられるが、代表的なのが、既にコネクションが存在しなくなっていたりパケットが不正なものだったりした時の強制断絶だ。

SYN - ビット 110。 SYN (Synchronize sequence numbers = 同期シーケンスナンバー) は、コネクションの初めの接続確立行程で使用される。 SYN はふたつの場面で使われる。ひとつはコネクションの皮切りのパケット、そしてもうひとつが返答の SYN/ACK パケットだ。 SYN フラグをこれ以外の場面で使用することは許されていない。

FIN - ビット 111。 FIN ビットは、FIN ビットを送ったホストはもう送るべきデータがない、ということを示す。 FIN ビットを受け取った相手は FIN/ACK で応える。このやりとりが行われると、先に FIN ビットを出したホストはもうデータを送ることができない。ただしもう一方は、送信しかけていたデータを最後まで送りきることはできる。送りきった後、ホスト [: 後者] は FIN パケットを返し、締めの FIN/ACK を待つ。この手順を経ることによって、コネクションは CLOSED ステートへと落ちる。

ウィンドウ (Window) - ビット 112-127。ウィンドウフィールドは、パケットを受け取る側が、その時点で受け取り可能なデータの量を送信側へ知らせるのが役目。これは ACK パケットによって行われるのだが、その ACK パケットには、先ほど受け取ったパケットの承認を示すシーケンスナンバー とともに、次の ACK パケットまでに送信側が指定可能なシーケンスナンバーの最大値がウィンドウフィールドとして記載される。次の ACK パケットにはまた新しいウィンドウが反映される。

チェックサム (Checksum) - ビット 128-143。このフィールドは TCP ヘッダ全体としてのチェックサムを格納している。チェックサムは、ヘッダ 16 ビット毎の相補 (one's complement = 1の補数) の和 (sum) を求め、それの相補を求めた値。もしもヘッダが 16 ビット境界で終わっていなければ、それ以降のビットはゼロで埋められる。チェックサムを計算している最中は、チェックサム はゼロになっている。チェックサム はまた、96 ビットの疑似ヘッダを内包し、この疑似ヘッダには、宛先アドレス、送信元アドレス、プロトコル、TCPパケット長が含まれる。チェックサム は TCP の信頼性を一段と高めるためにある。

緊急ポインタ (Urgent Pointer) - ビット 144-159。これは、緊急性の求められるデータの終端位置を指すポインタだ。受け取り側にいち早く処理してもらわなくてはならない重要なデータがコネクションに含まれている場合、送信側は、 URG フラグを立てた上で、緊急ポインタに緊急データの終端位置を示すのである。

オプション (Options) - ビット 160-**。オプションフィールド は可変長で、必須でないオプションを使う場合にはここに入っている。このフィールドは基本的には常に 3つのサブフィールドから成っており、最初のフィールドはオプションフィールド の長さ、2番目は、どのオプションを使用しているか、そして最後にオプションそのものが来る。 TCPオプション の全種類は付録 TCPオプション で見られる。

パディング (Padding) - ビット **。 TCP ヘッダが 32 ビット境界で終わるように終端に詰め物 (padding) をするのがパディングフィールドの役割。これがあるおかげで、パケットのデータ部は 32 ビット境界から開始することができ、パケット内のデータを隈無く読んでもらえるのだ。詰め物は常に全部ゼロ。


2.6. UDPの特徴
UDP プロトコル (User Datagram Protocol) は、 IP プロトコルの表層に存在する非常にベーシックでシンプルなプロトコルだ。 UDP はいかなるエラー検出も伴わないシンプルなデータ伝送を目して開発された。とはいえ、問い合わせと回答で成り立つ類のアプリケーションにも適している。 例えば DNS などだが、 DNS では、 DNS サーバから回答が来なければ、それは自ずと問い合わせがどこかで失敗したことを示すからだ。 TCP よりも UDP プロトコルのほうが向いている場面は他にもある。例えば、エラーやロスは検知したいけれどもパケットの順序には頓着しない場合だ。そうすれば、 TCP プロトコルに付き物のオーバーヘッドを、ほとんど排除できる。やり方は他にも考えられる。例えば、 UDP の上に、シーケンスは持ちながらエラーやロスの検出は備えない、独自のプロトコルを作るという手がある。

UDP プロトコルは RFC 768 - User Datagram Protocol で規定されている。この RFC は極めて簡潔で、いかにもこのシンプルなプロトコルに似つかわしい。


2.7. UDPヘッダ
UDP の備えているヘッダは、 TCP ヘッダの基本部分を簡略化したようなものと言える。 UDP ヘッダに含まれるのは、下図に見るように、宛先ポート、送信元ポート、ヘッダ長、チェックサムである。

送信元ポート (Source port) - ビット 0-15。これはパケットの送信元となったポートであり、返信のパケットはそこ宛てに返されなければならない。特に当てはまらない時にはゼロにしてしまうことも可能だ。返答を必要としない状況というのもあるわけで、その場合にはパケットのソースポートをゼロにセットすることもある。ほとんどの実装では、ここには何らかのポートナンバーをセットする。

宛先ポート (Destination port) - ビット 16-31。パケットの宛先ポート。これは送信元ポートとは異なりどのパケットにも必須である。

全長 (Length) - ビット 32-47。レングスフィールドには、ヘッダ部もデータ部も含めたパケット全体の長さをオクテット単位で指定する。パケット長の最小は 8 オクテット。

チェックサム (Checksum) - ビット 48-63。チェックサムは TCP ヘッダのものとほぼ同様だが、持っているデータが異なる。具体的に言うと、 IPヘッダ、 UDP のヘッダおよびデータそれぞれの相補 (one's complement = 1の補数) の和から、その相補を求めたもので、必要であれば末尾がゼロでパディングされる。


2.8. ICMPの特徴
ICMP メッセージは、ホスト同士あるいはホスト-ゲートウェイ間でのごく基本的なエラー通知に利用される。ゲートウェイ同士のエラー通知では、ゲートウェイtoゲートウェイプロトコル (GGP) を使用すべきという考え方が一般的だ。既に見てきたように、 IP プロトコルの備えるエラー処理は完全ではないが、 ICMP メッセージはそうした問題の一部を解決してくれる。或る意味 ICMP の手強い点は、ヘッダがかなり複雑で、メッセージの種類毎にも微妙に異なるという点だ。しかし、フィルタリングの観点では、これが大きな問題となることはほとんどない。

ICMP メッセージの基本的なフォーマットは、通常通りの IP ヘッダと、タイプ (type)、コード (code)、チェックサム (checksum) を備える。これらはどの ICMP メッセージでも必ず持っている。タイプは、そのパケットがどういった種類のエラーメッセージあるいは返答であるかという情報を表す。例えば destination unreachable や、 echo, echo reply, redirect などのメッセージだ。コードフィールドは、もし必要があれば、追加的な情報を指定する。仮にパケットのタイプが destination unreachable だとすれば、このコードフィールドは network unreachable, host unreachable をはじめとした何種類かの値を採り得る。チェックサムは単純にパケット全体のチェックサムだ。

気づいた方もいると思うが、上で述べた ICMP パケットのフォーマットの中で、僕は "IPヘッダ" を具体的に挙げた。なぜそうしたかと言うと、実は、 ICMP パケットは IPヘッダ そのものを構成要素のひとつにしていて、或る意味、 IP プロトコルと同じ層で働いているとも言えるからだ。 ICMP は IP プロトコルを高次の別階層として利用しているとも言えるし、同時に、 IP プロトコルと同じ階層のプロトコルだとも言えるのだ。 ICMP は IP と渾然一体の関係にあり、故に、 IP を実装する際には必ず ICMP も実装する決まりになっている。


2.9. ICMPヘッダ
前にも述べたが ICMP のヘッダはメッセージのタイプ毎に少しずつ異なる。ほとんどの ICMP タイプはヘッダを基準にして幾つかのグループに分けることができる。そういうわけで、ここからは、まず初めに基本的なヘッダ形態について述べ、それから、特記すべきタイプグループ毎に特性を見ていくことにする。

当チャプターの IPヘッダ のくだりで既に述べたように、全てのパケットが持っている基本的な値というものが幾つかある。ヘッダに関しては既にあらかた述べたので、ここでは簡単なリストにちょっとしたコメントを添えるだけに留める。

バージョン (Version) - 必ず 4 となる。

ヘッダ長 (Internet Header Length) - 32 ビット単位で数えたヘッダの長さ。

サービスタイプ (Type of Service) - 前記参照。 RFC 792 - Internet Control Message Protocol によればここに指定できる有効な値は 0 しかないので、必ず 0。

全長 (Total Length) - ヘッダとデータ部を合わせたパケット全体の長さ。単位はオクテット。

識別子 (Identification), フラグ (Flags), フラグメントオフセット (Fragment offsets) - IP プロトコルからコピーする。

Time To Live - そのパケットが生きていられるホップ (hop) 数。

プロトコル - 使用されている ICMP のバージョン (常に 1)。

ヘッダチェックサム (Header Checksum) - IP での説明 を参照のこと。

送信元アドレス (Source Address) - パケットを送ってきた者のアドレス。この表現は正確とは限らない。というのは、パケットに刻まれているアドレスが今問題にしているマシン上のものとは限らず、どこか別の所であることもあり得るからだ。

宛先アドレス (Destination Address) - そのパケットの宛先アドレス。

どのタイプの ICMP でも必ず備えるヘッダの中には、ここまでに説明した以外にも 2 ~ 3 のヘッダがある。それが以下。ここからはもう少し詳しいコメントを添えよう。

タイプ (Type) - タイプフィールドはそのパケットの ICMP タイプを収めている。これには ICMP のタイプ毎に違った値がある。例えば ICMP Destination Unreachable パケットならばここにタイプ 3 が入る。 ICMPタイプ の全リストは付録 ICMPタイプ で見られる。このフィールドは全部で 8 ビット。

コード (Code) - コードも ICMP のタイプによって別々だ。あるタイプではひとつのコードしか指定できないが、何種類かのコードを指定できるタイプもある。例えば ICMP Destination Unreachable (タイプ3) が採れるコードには少なくとも 0, 1, 2, 3, 4, 5 のバリエーションがある。組み合わせによってコードの示す意味が変わる。コードの全リストは付録の ICMPタイプ にある。このフィールドの長さは全部で 8 ビット。各コードについてはこのセクションの後半でもう少し詳しく述べる。

チェックサム (Checksum) - チェックサムは 16 ビット長のフィールドで、 ICMPタイプ をはじめとした全てのヘッダから求めた相補 (one's complement = 1の補数) の和の相補である。チェックサムの計算中には、チェックサムフィールドの値はゼロになっていなければならない。

さて、ここまで来れば、パケット毎にヘッダにも違いがあるということが見えてきたのではないだろうか。ここからは、よく出くわす ICMP タイプをひとつずつ取り上げ、そのヘッダとコードによる違いを簡単に述べていくことにしよう。


2.9.1. ICMPエコー要求/応答

僕は、ここで ICMPエコー の応答パケット (reply) と要求パケット (request) をまとめて説明することにした。ふたつは極めて密接な関係にあるからだ。違いのひとつ目はまず、エコー要求 はタイプ 8 であり、エコー応答 はタイプが 0 だという点。或るホストがタイプ 8 を受け取れば、応答はタイプ 0 で行う。

応答パケットを送る際には、送信元アドレスと宛先アドレスも入れ替わる。そうした処理を全て行った後に、チェックサム を再計算してから返答が送信される。ただしそのどちらもコードはひとつに決まっており、常に 0 にセットされる。

識別子 (Identifier) - ここは要求パケットの時にセットされ、それに対する応答エコーの中で繰り返し使用される。複数の ping の中から要求と応答の対を見分けるためである。

シーケンスナンバー (Sequence number) - ホスト毎のシーケンスナンバー。通常 1 から始まり、パケットひとつ毎に 1 ずつ加算される。

パケットにはデータ部もひとつある。デフォルトではデータ部は通常は空だが、任意の量のランダムなデータを収容することもできる。


2.9.2. ICMP到達不能メッセージ (Destination Unreachable)

図に見られる最初の 3つのフィールドは前記と同じ。 Destination Unreachable のタイプでは、以下のリストのように、基本的に 6種類のコードが利用できる。

コード 0 - ネットワーク到達不能 (Network unreachable) - 指定されたネットワークへの到達が現在不能であることを知らせる。

コード 1 - ホスト到達不能 (Host unreachable) - 指定されたホストへの到達が現在不能であることを知らせる。

コード 2 - プロトコル到達不能 (Protocol unreachable) - このコードは指定されたプロトコル (tcp, udp など) への到達が現在できないということを知らせる。

コード 3 - ポート到達不能 (Port unreachable) - このメッセージを受けるのは、ポート (ssh, http, ftp-data など) への到達ができない時。

コード 4 - フラグメント必要だがDFフラグあり (Fragmentation needed and DF set) - パケットをフラグメントする必要があるのだが "フラグメント不可" を表すビットがパケットにセットされている時に、ゲートウェイが返すメッセージ。

コード 5 - 送信元指示によるルーティング失敗 (Source route failed) - 送信元の指示によるルーティングが某かの理由で失敗に終わった時、このメッセージが返される。

コード 6 - 宛先ネットワーク発見できず (Destination network unknown) - 指定されたネットワークまで達する経路がない時、このメッセージが返される。

コード 7 - 宛先ホスト発見できず (Destination host unknown) - 指定されたホストまでの経路がない時、このメッセージが返される。

コード 8 - 発信元ホストへのルートなし (Source host isolated)(廃) - ホストが孤立している場合にはこのメッセージを返すことになっている。当コードは旧式で今日では使われなくなった。

コード 9 - 宛先ネットワーク設定によりアクセス拒否 - (Destination network administratively prohibited) - 或るネットワークがゲートウェイでブロックされ、そのせいでパケットが対象のネットワークに到達できなかった場合、この ICMP コードが返ってくることになっている。

コード 10 - 宛先ホスト設定によりアクセス拒否 (Destination host administratively prohibited) - ホストが規制 (例えばルーティング規制) によってアクセス禁止となっているがために行き着けなかった時、返事としてこのメッセージが返ってくる。

コード 11 - TOS種別によりネットワーク到達不能 (Network unreachable for TOS) - 送信したパケットの "不適切" な TOS のせいでネットワークまで届かなかった時、リターンパケットはこのコードで作られる。

コード 12 - TOS種別によりホスト到達不能 (Host unreachable for TOS) - パケットの TOS のせいで送信パケットがホストまで届かなかった時、返事に受け取るのがこのメッセージ。

コード 13 - フィルタリング設定により通信禁止 (Communication administratively prohibited by filtering) - 何らかのフィルタリング (例えばファイヤーウォール) によってパケットの着信が禁止されていた場合に、我々送信側はコード 13 を受け取る。

コード 14 - ホスト優先区分侵害 (Host precedence violation) - このメッセージは第 1 ホップのルータが送ってくる。パケットの採用している優先区分 (precedence) が当該の宛先/送信元の組み合わせにおいては許容されないということを、接続してきたホストに伝える。

コード 15 - 優先区分により遮断発動 (Precedence cutoff in effect) - 渡されたデータグラムにセットされていた優先レベル (precedence level) が低すぎたた場合に、第 1 ホップのルータはホストへこのメッセージを送ってもよいことになっている。

これに加えて、小さなデータ部を持つこともでき、その中身はインターネットヘッダ (IPヘッダ) のヘッダ全てと、元の IP データグラムのうちの 64 ビットを写したものとなる。次のレベルのプロトコルがポート定義などを持つのなら 64 ビットデータ部からそれが読み取れるはずだと解釈される。


2.9.3. ソースクエンチ (Source Quench)

パケットまたはそのストリームの発信元に対して、送信を継続するのならパケットの送信間隔を緩めてくれと伝えるために、ソースクエンチパケットが送られることがある。ただし、パケットが渡り歩いていくそうしたゲートウェイやホストはソースクエンチパケットなど送らずに黙ってパケットを破棄しても構わない、という点に注意が必要だ。

このパケットには特別なヘッダはなく、データ部に特徴がある。そのデータ部は、元のデータのインターネットヘッダ と、データグラムのうちの 64 ビット分を格納している。それによって、そのソースクエンチメッセージが、問題のゲートウェイや宛先ホストを通じてデータを送ろうとしているどのプロセス に対するものなのかが、適切に割り出せる。

ソースクエンチパケットは ICMP タイプが必ず 4 である。コードは 0 しかあり得ない。

ゲートウェイや宛先のホストの過負荷を送信ホストや受信ホストへ知らせるには、今日では 2 ~ 3 種類の手段がある。そのひとつとして ECN (Explicit Congestion Notification = 明示的輻輳通知) という仕組みがある。

2.9.4. リダイレクト

ICMPリダイレクト パケットが使用されるケースはひとつしかない。こういう場合を考えてみよう。ここに、何台かのクライアントやサーバとふたつのゲートウェイを持つネットワーク (192.168.0.0/24) があるとする。ゲートウェイのひとつは 10.0.0.0/24 のネットワーク、もうひとつのゲートウェイはその他のインターネット全般へつながっている。さてここで、 192.168.0.0/24 内に、デフォルトゲートウエイは知っているが 10.0.0.0/24 へのルートを知らない 1 台のホストがあったとしよう。そのホストはパケットをデフォルトゲートウェイへ送る。デフォルトゲートウエイは当然 10.0.0.0/24 ネットワークの存在を知っている。デフォルトゲートウエイは、そのパケットはどうせ 10.0.0.0/24 のインターフェイスから出入りすることになるのだから、直接 10.0.0.0/24 用のゲートウェイへ送ったほうが早いと判断することができる。そこでデフォルトゲートウエイは、正しいゲートウェイを知らせる ICMPリダイレクト パケットをホストへと送り、パケットは 10.0.0.0/24 のゲートウェイへ渡す。これによって先ほどのホストは 10.0.0.0/24 ゲートウェイという近道を知るので、次回からはもしかするとそちらを使うようになるかもしれないというわけだ。

リダイレクトタイプのパケットの主役となるヘッダはゲイトウェイインターネットアドレス (Gateway Internet Address) フィールドだ。このフィールドは、その時使用すべき適切なゲートウェイについての情報をホストに伝える。パケットはそれ以外にも、元のパケットの IPヘッダ と、そのデータ部の初めの 64 ビット分も収めている。その情報は、当のリダイレクトパケットと、データを発したプロセスとを適切に結びつけるために利用される。

リダイレクトタイプは 4 種類のコードを採ることができる。それらを以下に挙げる。

コード 0 - Redirect for network - (上記の例のように) ネットワーク丸ごとのリダイレクトである時にだけ使用される。

コード 1 - Redirect for host - 特定のホストに関するリダイレクトの際にだけ使用される (例えばホストルーティング)。

コード 2 - Redirect for TOS and network - サービスタイプとネットワーク丸ごとのリダイレクトの際にのみ使用される。意味合いとしてはコード 0 と同じだが TOS にも関係している場合だ。

コード 3 - Redirect for TOS and host - サービスタイプとホストのリダイレクトである時に使用される。つまり、扱いとしてはコード 1 と同じだが TOS にも絡んでいる場合だ。


2.9.5. TTL equals 0

TTL equals 0 の ICMP タイプは別名 "時間超過メッセージ (Time Exceeded Message)" とも呼ばれる。セットされるタイプは 11 で、コードは 2 種類のうちから選べる。ゲートウェイでの伝送過程や宛先ホストでのフラグメント再構成中に TTL フィールドが 0 に達してしまった場合には、そのパケットは破棄されなければならない。パケットを送ってきたホストにそれを知らせるために使うのが TTL equals 0 の ICMP パケットだ。これにより、送信者は、次にそこへパケットを送る必要が生じた時に、パケットの TTL を大きく設定することができる。

このタイプのパケットは付加データ部だけを備える。このデータフィールドには、元のパケットのインターネットヘッダ と 64 ビット分のデータが入っており、相手方はその情報を頼りに送信元となったプロセスを正しく割り出すことができる。先にも述べたように、 TTL equals 0 タイプは 2 種類のコードを採ることができる。

コード 0 - TTL equals 0 during transit - いずれかのゲートウェイで転送を行おうとした時点で TTL が 0 になった場合に、このコードが送信元ホストへ送られる。

コード 1 - TTL equals 0 during reassembly - パケットがフラグメントされており、その再構成中に TTL が 0 に達してしまった場合に、このコードが送られる。このコードは最終目的地のホストからしか送られてこない。


2.9.6. パラメータ障害

パラメータ障害 (parameter problem) の ICMP はタイプ 12 を使用し、 2 種類のコード値も採り得る。パラメータ障害メッセージは、ゲートウェイか受信ホストがエラー等によって IPヘッダ の一部を理解できない場合や、必要とされるオプションが見あたらない場合に、送信元のホストにそれを知らせるためにある。

パラメータ障害タイプの ICMP メッセージは或る特殊なヘッダを備える。それは、元のパケットにおけるエラー原因フィールドへのポインタだ (コードが 0 の場合)。有効なコードを以下に述べる:

コード 0 - IPヘッダ異常 (IP header bad)(全てのエラーを包括) - これは、上述したような全てのエラーを包括したエラーメッセージだ。このコードをポインタと組み合わせると、エラーが IPヘッダ のどの部位にあるかを伝えることができる。

コード 1 - 必須オプション欠如 (Required options missing) - 必要なオプションが欠けている時、このコードで知らせる。


2.9.7. タイムスタンプ要求/応答

タイムスタンプタイプは今日では廃れて使われなくなっているが、ここでは簡単に取り上げておく。応答も要求も同じコード (0) を持つ。要求の場合はタイプ 13、応答はタイプ 14 である。タイムスタンプパケットは UT (Universal Time) 午前 0 時からの時間をミリ秒で表した 32 ビットのタイムスタンプを 3つ格納している。

最初のタイムスタンプはオリジナルタイムスタンプ、つまり送信元が最後にパケットに触れた時を表す。レシーブタイムスタンプ はエコーを返そうとしたホストが初めてパケットに触れた時、トランスミットタイム は、パケットがエコー送信者の手を離れる瞬間のタイプスタンプである。

いずれのタイムスタンプメッセージも、述べたもの以外に、ICMPエコー パケット同様の識別子 とシーケンスナンバー を持つ。


2.9.8. インフォメーション要求/応答

インフォメーション要求とインフォメーション応答というタイプは、今日では、必要であれば IP プロトコルの上に同目的で利用できるプロトコル (DHCP など) があるため、既に廃止されている。インフォメーション要求は、それを受け取ったネットワーク上に存在する回答可能な全ホストから応答を発生させる。

情報を要求したホストは、送信元アドレスを自分の属するネットワーク (例えば 192.168.0.0)、宛先を 0 にしてパケットを送る。応答のパケットにはいろいろな数値情報 (ネットマスクと IPアドレス) が書かれている。

インフォメーション要求は ICMP タイプが 15、かたや応答はタイプ 16 で送られる。


2.10. TCP/IP宛先誘導型ルーティング
ルーティングという面では、 TCP/IP はかなり複雑になってきている。当初は誰もが、宛先誘導型ルーティング (destination driven routing) だけで充分だと考えていた。ところがここ数年で、あっという間に複雑さを増してきた。今日では、 Linux でも、基本的には IP ヘッダのフィールドやビットのどれを基準にしてでもルーティングができるし、そればかりか TCP, UDP, ICMP のヘッダでもルーティングが可能になっている。そうした仕組みをポリシールーティング (policy based routing)、あるいは高度ルーティング (advanced routing) と呼ぶ。

この章は、宛先誘導型ルーティングがどのように行われるかの簡略な説明だ。送信元ホストからパケットを送信する時、パケットが生まれる。すると次に、コンピュータはパケットの宛先アドレスを見て、コンピュータ自身の保持しているルーティングテーブルと照合を行う。宛先アドレスがローカルなものならば、パケットは直接、ハードウェア MACアドレス を使って送られる。宛先がゲートウェイの向こう側だった場合には、パケットはゲートウェイの MAC アドレスへと送られる。するとゲートウェイはパケットの IP ヘッダを読み、パケットの宛先アドレスを知る。宛先アドレスはここでまたルーティングテーブルと照合され、パケットは次のゲートウェイへ送られる...という行程が、宛先のアドレスの属する向こう側 のネットワークに到達するまで繰り返される。

お分かりの通り、この仕組みのルーティングは基本原則だけで成り立つ単純なものだ。高度ルーティングやポリシールーティングになると話は別で、仕組みはもっと複雑だ。例えば送信元アドレスや TOS 値などに基づいてパケットのルーティングを変えたりできるのだ。


2.11. まとめ
このチャプターでは、次から始まるチャプターを理解できるようにあなたの知識をアップデートしてきた。要点を簡潔にまとめると:

TCP/IPの構造

IPプロトコルの機能とヘッダ

TCPプロトコルの機能とヘッダ

UDPプロトコルの機能とヘッダ

ICMPプロトコルの機能とヘッダ

TCP/IP宛先誘導型ルーティング

このどれもが、この先、実際にファイヤーウォールのルールセットに取り組む際に役に立つはずだ。これらをパズルの駒のようにぴったりと組み合わせることで、より良いファイヤーウォールを設計することができる。