Tatsuya Nonogaki - 日本語訳
Japanese translation v.1.0.1Copyright © 2001-2006 Oskar Andreasson
Copyright © 2005-2008 Tatsuya Nonogaki
この文書を、フリーソフトウェア財団発行の GNU フリー文書利用許諾契約書バージョン1.1 が定める条件の下で複製、頒布、あるいは改変することを許可する。序文とその副章は変更不可部分であり、「Original Author: Oskar Andreasson」は表カバーテキスト、裏カバーテキストは指定しない。この利用許諾契約書の複製物は「GNU フリー文書利用許諾契約書」という章に含まれている。
このチュートリアルに含まれるすべてのスクリプトはフリーソフトウェアです。あなたはこれを、フリーソフトウェア財団によって発行された GNU 一般公衆利用許諾契約書バージョン2の定める条件の下で再頒布または改変することができます。
これらのスクリプトは有用であることを願って頒布されますが、*全くの無保証* です。商業可能性の保証や特定の目的への適合性は、言外に示されたものも含め全く存在しません。詳しくはGNU 一般公衆利用許諾契約書をご覧ください。
あなたはこのチュートリアルと共に、GNU 一般公衆利用許諾契約書の複製物を一部受け取ったはずです。もし受け取っていなければ、フリーソフトウェア財団まで請求してください(宛先は the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA)。
僕はこのドキュメントを僕の素晴らしい妹と姪、それに義弟に捧げる。彼らは僕を応援し、インスピレーションを与えてくれた。彼らは、なくてはならない幸せの源であり一筋の光だ。感謝!
それに、僕に執筆の励みを与え、以心伝心で世話をしてくれる Ninel に言葉を贈らないわけにはいかない。ありがとう。
そして、僕はこの作品を、とてつもなくきつい仕事をしている Linux 開発者達と維持管理者達に捧げたい。この素晴らしいオペレーティングシステムを世に送り出してくれている人々へ。
Iptablesチュートリアルの著者は xxxx年に生まれ...
なんて、冗談はやめておこう。僕は 8 歳の時のクリスマスプレゼントで最初のコンピュータ Commondore 64 をもらった。C-1541 ディスクドライブの付いたやつで、8 ドットのドットプリンタと幾つかのゲームソフトも一緒だった。父は、なんとか動くところまで漕ぎ着け、二日掛かりで遂にゲームをロードする術を会得し、一人でもできるように僕にやり方を教えてくれた。今思えば、これが僕のコンピュータ漬け人生の始まりだった。その時点ではまだ、ほぼゲームで遊ぶだけ。しかし、何度かおもしろ半分で C-64 ベーシック言語もいじってみたりした。数年後には、Amiga 500 を手に入れた。主にゲームと宿題に使いながら、いじくり回した。その次は Amiga 1200 だ。
1993年か1994年のある日、父は Amiga には (残念ながら) 明日はないということを悟った。明日があるのは PC、i386だと。僕の涙の訴えも空しく、父は 50MHz の 486 と 16MB の RAM を積んだ Compaq PC を買い与えた。これがまさしく最悪の設計で、スピーカーからモニタから何から何まで一体化されていた。一世を風靡していた Apple のデザインを真似たつもりだろうが、見事に失敗していた。とはいえ、このマシンこそ、僕を本格的にコンピュータにのめり込ませたマシンだったことは間違いない。コーディングを本格的にやりだしたのも、インターネットを使い始めたのも、最初に実際に Linux をインストールしたのもこのマシンだった。
僕はもう長いこと、熱烈な Linux ユーザであり Linux アドミニストレータだ。僕の Linux 経験は、知り合いから借りた CD でインストールした slackware とともに 1994 年に始まる。この初めてのインストールはほとんど実験的なものだった。全く未経験なため、モデムやら何やらを動作させるだけでえらく時間が掛かった。しかもデュアルブートで別の OS を温存していた。2回目のインストールは circa 1996 だったが、メディアを持っていなかったので 28k8 モデムで slackware の A, AP, D, Nディスクを FTPダウンロードする羽目になった。グラフィカルインターフェイスからは何も学べないと悟っていた僕は、基本に立ち戻った。コンソールあるのみ。svgalib こそあれ、X11 もグラフィックもかなぐり捨てた。顧みれば、この経験がどれほど役に立ったかは疑うべくもない。何かを学ぼうと思ったら、やるしかない状態に自分を追い込んでしまうのが一番ではないだろうか。勉強するしかなかった。そんな状態でほぼ 2年が過ぎた。それからやっと、 XFree86 をスクラッチでインストールした。24時間掛かってやっとコンパイルした挙げ句、設定が出鱈目だったことに気づいて、また一からコンパイルしなおす羽目になった。人間、間違いはつきものだ。間違いは起こる時には起こるものであって、慣れるしかない。それに、こつこつと積み上げていくプロセスは忍耐というものを教えてくれる。急がば回れというやつだ。
2000年から2001年、僕はニュースサイトを運営する或る小集団の一員だった。内容は Amiga 関係のニュースが中心だったが、Linux やその他コンピュータ全般についても扱っていた。 BoingWorld という名称で、サイトは www.boingworld.com にあった (残念ながら今はもう見られない)。当時、カーネル 2.3 がリリース間近で、カーネル 2.4 が姿を現そうとしていた。その中に、今までのコンセプトをほぼ一新するファイヤーウォールが盛り込まれていることを僕は知った。確かに僕も ipfwadm や ipchains は或る程度触ったことがあったが、突き詰めてやった経験はなかった。また同時に、そのドキュメントがほとんどないに等しいということに気づいた。そこで、boingworld に iptables のチュートリアルを書いてみてはどうかと思ったわけだ。有言実行、僕は今あなたの読んでいるものの最初の 5〜10ページを書いた。これがスマッシュヒットとなり、新しい材料をチュートリアルにどんどん加えていった。当初のページはもうこのチュートリアル/ドキュメントには影も形もない。しかしコンセプトは今も引き継がれている。
僕は幾つかの企業で仕事をし、Linux やネットワークの管理をしたり、ドキュメントを書いたり、教材を作ったり、iptables や netfilter やその他諸々に関する質問の Email をくれた数千とは言わないまでも数百人の人たちをサポートしてきた。2度の CERTconf に参加し、それぞれのカンファレンスで 3つのプレゼンテーションを行った。Netfilter ワークショップ 2003 にも参加した。iptables チュートリアルを維持していくのはきつい仕事で、時には虚無感に襲われることさえあるのだが、出来上がってみれば、僕はその仕上がりに満足しているし、作り上げたことを誇りに思っている。筆を持っているこの 2006 年暮れまで、このプロジェクトが数年間に渡ってほとんど休止状態だったことは残念に思う。来年は状況を変え、たくさんの人がこの作品を長きにわたって役立つものと認めて、有用なドキュメントのひとつに加えてくれることを願っている。
当ドキュメントはリファレンス辞書として使ってもらってもいいし、頭から通して読んでもらってもいい。この文書は、当初は iptables と、少々だが netfilter を紹介するだけの小さなドキュメントだった。しかし、その目的は年月を経て変化し、今では、iptables および netfilter について可能な限り幅広く網羅したリファレンスであると同時に、少なくともこの領域を理解するために必要となる基礎知識を、的を絞って学習あるいはおさらいしてもらおうという内容となっている。断っておかなければならないのは、このドキュメントは iptables と netfilter の内外どちらのバグについては扱わないし、今後も扱うことはないという点だ。同様に、そういったバグへの対処法にも触れることはない。
iptablesやそのサブコンポーネントにバグやおかしな挙動を見つけた時は、Netfilter メーリングリストに上げてほしい。そうすれば、それが本当にバグなのかや、既に修正されているかどうか、答えが得られるだろう。 iptables と Netfilter にもセキュリティ関係のバグは存在し、時たま 1 個か 2 個のバグも紛れ込むことはある。それは避けられないことだ。こういった話は Netfilterのメインページにきちんと書いてあるので、そこで調べていただくといいだろう。
ひいては、このチュートリアルで網羅しているルールセットも、 Netfilter 内のバグを踏まえたものにはなっていないことになる。ここで示すルールセットの目的は、我々が遭遇するであろう課題に対処するためのルールを、なるべくきれいでシンプルにセットアップするやり方を示すことだ。例えば、 HTTP ポートを閉じる方法は扱っていない。理由は単純、これは Apache 1.2.12 がたまたま持っている脆弱性の問題だからだ (実際のところ網羅しているのだが、それは別の理由から)。
このドキュメントは、iptables をこれから始めるに人たちに質の高いシンプルな下地を提供することを目的としている。しかし同時に、iptables について可能な限り幅広く網羅するよう努めている。この文書では patch-o-matic に含まれるターゲットやマッチは網羅していない。理由は単純で、patch-o-matic リストのアップデートに合わせていくのは大変な労力だからだ。 patch-o-maticアップデートに関する情報が知りたければ、 patch-o-matic に付属する info や、 Netfilterのメインページに載っている他のドキュメントを読んでほしい。
漏れている項目や、iptables と netfilter に関する間違いや問題点を見つけた気がしたら、気兼ねなく知らせてほしい。僕は喜んで目を通すし、不足している事柄は追加させていただくかもしれない。
このドキュメントを理解するには、カーネルのコンパイル方法やカーネル内部に関する知識の他、Linux/Unix とシェルスクリプティングについて、或る程度の予備知識を必要とする。
ドキュメントの大枠を把握するところまでは、可能な限り予備知識の必要性を排除したつもりだが、まったく無しで理解できるようにするのは無理な話だ。
当ドキュメントでは、コマンド、ファイル、その他特定の事柄に関して、以下のような表記を行う。
長文となるコードの引用とコマンドの出力は、下記のように表す。スクリーンのダンプや、コンソールから採った長い出力例もこれに含まれる。
[blueflux@work1 neigh]$ ls
default eth0 lo
[blueflux@work1 neigh]$
コマンドとプログラムの名称は太字 (bold) で表す。タイプするコマンド全体やその一部もこれに含まれる。
ハードウェアなどのシステムアイテム、カーネルの内部構造、ループバックインターフェイスなどシステムの論理アイテムは イタリック (Italic) で示す。
コンピュータの出力は、テキスト内で this way のような様式で書く。つまりコンピュータがコンソールへ出力するもの全てがこれに該当する。
ファイルシステム上のファイル名とパスは /usr/local/bin/iptables のように表す。
訳者は、Linux のドキュメントの翻訳物にありがちな「直訳」は本当の意味での正確な訳ではないと考えました。そのため、多少、元の構文を無視して意訳的な訳し方をした部分もあります。こうすることで、著者の表したかったことが、より正確に伝わるものと信じています。それでも、言語および言語習慣の違いから、どうしても言い表せない、あるいは、言い表そうとすれば日本語とはかけ離れた奇妙で読みにくい文章になってしまう部分もあります。そういった場合には、以下のような補足を織り込むことにしました。
[訳者註: ] または省略して [: ] は、訳者が加えた補足事項で、原文にはありません。
(masquerading) のように ( ) で囲まれた英語句は、その直前の訳語が原文でどう書かれているかを示しています。ネットワークの常識として把握しておくべき語句だったり、設定時やログメッセージなどで実際に出くわす機会があるため英語表記を知っておいたほうがいい場合などに用いています。
何というか、出回っている HOWTO には、Linux 2.4.x カーネルの iptables と Netfilter の機能に関して、情報がすっぽり抜けていると感じたから。特に、ステート (state) マッチングのような新しい機能について沸き上がるであろう疑問に対しては、できるだけ答えるようにしていこう。その大部分は、 /etc/rc.d/ のスクリプトとして使える rc.firewall.txt を使って解説していく。そう、お気づきの方もおられようが、このファイルは masquerading HOWTO のものをベースにしている。
また、実際僕にも経験のあることだが、設定がぐちゃぐちゃになってしまった時のために、小さなスクリプト rc.flush-iptables.txt も用意しておいた。
このチュートリアルは boingworld.com に書いたほんの短い文書に端を発している。それは数年前に、僕を含めた少人数のメンバーで運営していたニュースサイトで、 Amiga や Linux、その他の全般的な事柄を扱うものだった。沢山の読者を得て多くのコメントが寄せられたのに元気づけられて、僕は執筆を続けた。初めは印刷すると A4 で 10〜15 ページほどの分量だったが、少しずつ、しかし着実にボリュームアップしていった。それはもう沢山の人たちが、執筆、校正、バグチェックなどを手伝ってくれた。この文章を書いている時点で、http://iptables-tutorial.frozentux.net/ は延べ 600,000 ヒットを数えるに至った。
このドキュメントは、セットアップの行程を順を追って案内していく流れを採っている。そうすることで、 iptables パッケージを少しでもよく理解してもらう手助けになればと思う。 iptables の使い方を学ぶには例を見るのが一番だと思うので、ほとんどの要素は、例 rc.firewall ファイルに集約したつもりだ。僕は、まず基本的なチェーン構造をカバーし、そこから、パケットの通っていくチェーンのひとつひとつを記述していくことで、スクリプトの働きを説明することにした。このやり方だと、より論理的である反面、チュートリアルについていくのはやや難しくなるかもしれない。[訳者補足: スクリプトで] 理解しにくいところが出てきたら、いつでもこのチュートリアルに戻ってくればいい。
事前説明の必要な用語は、このドキュメントにはわずかしかない。このセクションでは、主立った用語と、それをドキュメント中で使う理由を説明する。
コネクション -- (Connection) このドキュメントにおいてのこの語彙は、相互に関わりを持つパケットの集まりを指す。パケット同士は、確立済み (established) の関係となるわけだ。コネクションはまた、一連のパケットの遣り取りだとも言える。 TCP 接続で言えば 3 ウェイハンドシェイクを踏んで接続を確立することを主に指し、切断のためのハンドシェイクが完了するまでがひとつのコネクションと解釈される。
DNAT -- 宛先ネットワークアドレス変換 (Destination Network Address Translation)。 DNAT とは、パケットの宛先IPアドレスを変換する技術、または変えること自体を指す。 SNAT と組み合わせて、インターネット上でルーティング可能な 1 個の IPアドレスを複数のホストでシェアしたり、サーバサービスを提供したりするのに用いられる。これは通常、インターネット上でルーティング可能な 1 個の IPアドレスに対して、別のポートを振り直し、 Linux ルータに宛先を指示することにより行われる。
IPSEC - インターネット・プロトコル・セキュリティ (Internet Protocol Security) は IPv4 パケットを暗号化してインターネットへ安全に送信するためのプロトコル。 IPSEC についてもっとよく知りたければ、付録 その他の資料とリンク を参照して他の資料を探してみるといいだろう。
カーネル空間 -- (Kernel space) いわば、ユーザ空間の反意語。カーネルの内側での出来事、カーネルの外でないところで起こるあらゆる事象を指す。
パケット -- (Packet) ネットワーク伝送の最小単位であり、ひとつのヘッダとデータ部から成る。例えば IP パケットや TCP パケットがある。インターネットの規約 (Request For Comments = RFC) では、 IP パケットを表すにはデータグラム (datagram)、 TCP パケットにはセグメント (segment) という呼び名が使われるが、"パケット"という言葉は普遍化されていない。僕は、物事を単純化するために、このドキュメント内ではどれもこれもパケットと呼ぶことにした。
QoS - サービス品質 (Quality of Service) とは、特定のパケットの送信に際してパケット処遇の優先度や提供する品質を指定するひとつの方法だ。この話題に関しては チャプター TCP/IPのおさらい でも述べているし、付録 その他の資料とリンク からは外部資料にアクセスすることができる。
セグメント -- (Segment) TCPセグメントは "パケット" とほぼ同義だが、 TCP パケットを表す時の "かしこまった" 呼び方。
ストリーム -- (stream) この語句は、某かの意味で互いに関係性のあるパケットをやりとりする接続を指す。僕は元来、2つ以上のパケットを双方向に送るあらゆる接続に対してこの語句を使ってきた。つまり TCP においては、SYN を送り SYN/ACK で応答することを意味するが、SYN を送り ICMP Host unreachable を受け取ることも、やはりこの語句に当てはまる。非常に曖昧に使う、とも言える。
SNAT -- 送信元ネットワークアドレス変換 (Source Network Address Translation)。パケットの送信元アドレスを変換する技術のこと。 IPアドレスの枯渇 (IPv6が救うだろう) に瀕した現在、この技術よって、ひとつの IPv4インターネットIPアドレスを複数のホストで共有可能にしている。
ステート -- (state) この語句は、パケットがどの状態にあるかを指す。状態とは RFC 793 - Transmission Control Protocol に照らして言う場合もあるし、 Netfilter/iptables で使用されるユーザ空間上での状態を言う場合もある。憶えておいてほしいのは、iptables の利用するステート は、カーネル内外のどちらのステート も、 RFC793 の定義とは異なるという点だ。その主たる理由は、 Netfilter が接続とパケットについてあれこれと推測を行うためそれらを駆使する必要があるからだ。
ユーザ空間 -- (User space) 僕はこの語句で、カーネルの外で起きるあらゆる事柄を指す。例えば、iptables -h はカーネルの外だが、iptables -A FORWARD -p tcp -j ACCEPT はルールセットに新たなルールを加えるので (部分的にしても) カーネル内の出来事だ。
ユーザランド -- (Userland) ユーザ空間を参照のこと。
VPN - バーチャル・プライベート・ネットワーク (Virtual Private Network) は、非プライベートなネットワーク上 (インターネットなど) で、プライベートなネットワークを仮想的に形成する技術のこと。 VPN を形成するそうした技術のひとつに IPSEC がある。 OpenVPN もそのひとつだ。
このチャプターは、当ドキュメントをなぜ、どうやって書いたのかをざっと見ていただいた。また、このドキュメントのそこここに現れる主な用語についても説明した。
次のチャプターは、TCP/IP に関するいささか長大な事始め とおさらいだ。そこで扱うのは基本的に、iptables と netfilter で利用される IPプロトコルとそのサブプロトコルであり、TCP, UDP, ICMP, SCTP が含まれる。SCTP は、他のものに比べると新参の規格なので、まだ馴染みのない人たちのために、かなりのページと時間を割いた。また、今日使われている基本的なルーティング及び高度ルーティングの技術についても幾らか述べている。
iptables は知識のあるなしで大違いのツールだ。別の言い方をすれば、 iptables をフル活用するにはそれなりの知識が必要だということだ。中でも TCP/IP プロトコルには充分に精通している必要がある。
このチャプターは、とにかく TCP/IP についての「必須知識」を解説するために設けた。先へ進んで iptables と取っ組み合うのはそれからだ。ここでは特に、 IP, TCP, UDP, ICMP プロトコルとそのヘッダについてと、それぞれの用途の違い、それに、各プロトコル同士の関係について、駆け足で見ていく。 iptables はインターネット層とトランスポート層で働く。それ故に、このチャプターではそれらのレイヤー [訳者註: 層] にも焦点を当てる。
iptables はより表層のレイヤーで機能することも可能で、例えばアプリケーション層で作用することもできる。だが、 iptables は本来、そうした用途は意図しておらず、そんな使い方をすべきではない。それについては IPフィルタリングとは のチャプターでもう少し詳しく述べることにしよう。
先にも述べたように、 TCP/IP は多層構造になっている。ということは、或る層でひとつの機能を働かせながら、別の層で別の機能を働かせ、そしてまた別の層で...ということが可能なわけだ。レイヤー構造を持つ意義は、考えてみれば単純だ。
多層構造となっている最も大きな理由は、拡張を容易にするため。例えばアプリケーション層に新たな機能を追加したい時にも、わざわざ TCP/IP スタックのコードを書き直したり、アプリケーション側に TCP/IP スタックの一切合切を装備しなくても済む。同様に、新しいネットワークインターフェースカードを創るたびにプログラムを逐一書き直すなんてことも必要ない。各レイヤーは独立して働けるように、他のレイヤーのことには必要最小限だけ関知していればいいようになっているのだ。
![]() | カーネルの備える TCP/IP プログラミングコードについて語る時、実はそれは TCP/IP スタックのことを言っている場合が多い。 TCP/IP スタックとは、ネットワークアクセス層から表層のアプリケーション層に至るまでの、サブレイヤー一切合切のことである。 |
レイヤーの話をする時、大元となる構造定義がふたつある。ひとつは OSI (Open Systems Interconnect) 参照モデルで、これは TCP/IP を 7 層に分けている。ただし、我々の興味はどちらかというと TCP/IP 階層モデルのほうにあるので、ここではざっと眺める程度にしておこう。しかし、歴史的な意味から、OSI参照モデルにも興味深い点はある。特に、さまざまな種類の異なるネットワークを扱わなければならない場合だ。 レイヤーは OSI参照モデルの の一覧に示した通りだ。
![]() | どちらの参照モデルが広く使われているかについては、意見が分かれている。ただ、どちらかといえば OSI 参照モデルのほうが未だに優勢なように見える。とはいえ、事情は国によっても異なる。US および EU の大多数の国においては、技術者や営業の会話の中で使うなら OSI を基本と考えるほうがいいようだ。 しかしながら当文書では、特に断りのない限り、一貫して TCP/IP 参照モデルのほうで話を進める。 |
アプリケーション層 (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 を相手にしているとすれば、必ずというわけではないがこれは何らかのポートナンバー (例えば 25 など) かもしれない。
ここで、今度は封筒に受取人の住所を書く。こんな具合だ:
V. Andersgardsgatan 2
41715 Gothenburg
これはインターネット層にあたる。インターネット層は、 TCP/IP ネットワーク上のどこへ行けば受取人つまりホストへ行き着けるかを示す情報を格納する。ちょうど、封筒に書く宛名と同じ。つまり、'192.168.0.4' といった 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の特徴 の中でもっと詳しく述べることにする。 |
既に述べたように、 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 はそういったプロトコルの一例だが、 TCP は IP プロトコルよりひとつ上の層だ。 IP 層の段階でコネクション指向にしない理由はいろいろあるが、取り立てて言えるのは、このレベルで敢えて不要なオーバーヘッドを伴うハンドシェイクを実装する意味がないからだ。その重荷は他のプロトコルに背負ってもらえばいい。オーバーヘッドは、返答が得られない場合には伝送経路のどこかでパケットが失われたと判断し、当初のリクエストを送信しなおす、という処理から生じる。お察しの通り、この場合、リクエストを送って一定の時間だけ回答を待つほうが賢い。でなければ、まずコネクション開始の要望を知らせるパケットを送り、次にコネクションが開いたと告げる知らせのパケットを受け取り、続いてコネクションが開いたことを確かに承知しましたという承認を行い、ここでやっとこさ当の目的のリクエストを送り、そうしたらコネクションを切断するためのパケットを更に送りその返事を待つ、という手間になってしまうのだ。
IP はまた、低信頼性 の (unreliable) プロトコルとしても知られる。簡単に言えば、パケットが届いたのか届かなかったのかには無頓着なのだ。 IP プロトコルはただ単純にトランスポート層 からパケットを受け、やるべきことだけやったらネットワークアクセス層 へ渡して、それでおしまい。返答のパケットが来るかもしれないが、それがネットワークアクセス層 から IP プロトコルへとやってくると、 IP はまたやるべきことをやり、上のトランスポート層 へと渡す。しかし、返事のパケットが来るのかパケットが行き先まで届いたのかにはお構いなしだ。 IP のこの低信頼性についても、先ほどの "コネクションレスさ" と同様のことが言える。なぜなら、低信頼性を何とかしようとすれば、パケットを送るたびに追加のパケットが必要となるからだ。 DNS 検索を例に採ってみよう。通常やるように、 servername.com を牽こうと DNS リクエストを送信する。返答が得られなければ、何か手違いがあったと考えてルックアップを再要求する。しかし通常は、リクエストを 1回送り、回答を 1回受けて終わりだ。信頼性を高めようと思ったらどうなるか。リクエストには 2回のパケットを要し (リクエスト 1回と、パケット到着確認 1回)、返答も 2回要る (回答 1回と、回答の領収の通知で 1回)。つまり、送らなくてはならないパケットの数は倍になり、流れるデータの量もほぼ 2倍になってしまう。
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 によれば、この制限を上回るサイズのパケットを送るのは、そのホストが必ず受け取れるという保証のある場合だけにすべきとされている (recommended)。ただし、今日ではほとんどのネットワークがパケットサイズを 1500 バイトにして運用している。ほぼ全てのイーサネット接続がそうだし、ほとんどのインターネットコネクションもそこに含まれる。
識別子 (Identification) - ビット 32-46。このフィールドはパケットフラグメントの再構成を手助けするためにある。
フラグ (Flags) - ビット 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 メッセージを送るべきとされている (should)。
プロトコル (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 で調べるといいだろう。
パディング (Padding) - ビット可変。ここは、ヘッダが 32 ビット境界で終わるように調整するための詰め物。このフィールドは頭から終わりまでゼロばかりが並んでいなければならない。
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 ヘッダにはチェックサム というものもある。チェックサム はパケットの単純なハッシュ値だ。このハッシュを使って、ホスト間の伝送中にパケットに損傷が起きたかどうかが、かなりの精度で検査できる。
TCP ヘッダには、前述の役目を全て達成できる素養が求められる。幾つかのヘッダについては、それがいつ、どのように利用されるか既に説明したが、まだ明らかにしていない領域が他にも沢山ある。下に、 TCP ヘッダを全て網羅した図を示した。見ての通り、1 段を 32 ビットワードとした形になっている。

送信元ポート (Source port) - ビット 0-15。パケットの送信元ポート。元来は、送信元ポートはそれを送信したシステム上のプロセスを直接指し示すものだった。今日では、双方の IP アドレスから求めたハッシュ値と、宛先ポートおよび送信元ポートを併用して、各々のアプリケーション (プログラム) との一意性を確保している。
宛先ポート (Destination port) - ビット 16-31。 TCP パケットの宛先ポート。送信元ポートと同様に、元来は受信側システム上のプロセスを直接指し示していた。今日では、より多くのコネクションを同時に張れるよう、ハッシュ値が用いられる。パケットを受信したら、送信元への返答では宛先ポートと送信元ポートを逆さにする。つまり、元の宛先ポートが送信元ポート、送信元ポートが宛先ポートになるようにするわけだ。
シーケンスナンバー (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 ビット境界から開始することができ、パケット内のデータを隈無く読んでもらえるのだ。詰め物は常に全部ゼロ。
UDP プロトコル (User Datagram Protocol) は、 IP プロトコルのひとつ表層に位置する非常にベーシックでシンプルなプロトコルだ。 UDP はいかなるエラー検出も伴わないシンプルなデータ伝送を目して開発された。とはいえ、問い合わせと回答で成り立つ類のアプリケーションにも適している。 例えば DNS などだが、 DNS では、 DNS サーバから回答が来なければ、それは自ずと問い合わせがどこかで失敗したことを示すからだ。 TCP よりも UDP プロトコルのほうが向いている場面は他にもある。例えば、エラーやロスは検知したいけれどもパケットの順序は気にしなくてもいい場合だ。そうすれば、 TCP プロトコルに付き物のオーバーヘッドを、ほとんど排除できる。また例えば、 UDP のひとつ上に、エラーやロスの検出機能は備えずシーケンス管理だけを行う独自のプロトコルを作るという利用法も考えられる。
UDP プロトコルは RFC 768 - User Datagram Protocol で規定されている。この RFC は極めて簡潔で、いかにもこのシンプルなプロトコルに似つかわしい。
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の補数) の和から、その相補を求めたもので、必要であれば末尾がゼロでパディングされる。
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 も実装する決まりになっている。
前にも述べたが ICMP のヘッダはメッセージのタイプ毎に少しずつ異なる。ほとんどの ICMP タイプはヘッダを基準にして幾つかのグループに分けることができる。そういうわけで、ここからは、まず初めに基本的なヘッダ形態について述べ、それから、特記すべきタイプグループ毎に特性を見ていくことにする。

いずれのタイプのパケットも、基本的な IPヘッダ の値はもれなく持っている。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) 数。
プロトコル (Protocol) - 使用されている ICMP のバージョン (常に 1)。
ヘッダチェックサム (Header Checksum) - IP での説明 を参照のこと。
送信元アドレス (Source Address) - パケットを送ってきた者のアドレス。この表現は正確とは限らない。というのは、パケットに刻まれているアドレスが今問題にしているマシン上のものとは限らず、どこか別の所であることもあり得るからだ。そうした性格を持つ ICMP タイプについては適宜述べる。
宛先アドレス (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 タイプをひとつずつ取り上げ、そのヘッダとコードの違いを簡単に述べていくことにしよう。

僕は、ここで ICMPエコー の応答パケット (reply) と要求パケット (request) をまとめて説明することにした。ふたつは極めて密接な関係にあるからだ。違いのひとつ目はまず、エコー要求 はタイプ 8 であり、エコー応答 はタイプが 0 だという点。或るホストがタイプ 8 を受け取れば、応答はタイプ 0 で行う。
応答パケットを送る際には、送信元アドレスと宛先アドレスも入れ替わる。そうした処理を全て行った後に、チェックサム を再計算してから返答が送信される。ただしそのどちらもコードはひとつに決まっており、常に 0 にセットされる。
識別子 (Identifier) - ここは要求パケットの時にセットされ、それに対する応答エコーの中で繰り返し使用される。複数の ping の中から要求と応答の対を見分けるためである。
シーケンスナンバー (Sequence number) - ホスト毎のシーケンスナンバー。通常 1 から始まり、パケットひとつ毎に 1 ずつ加算される。
パケットにはデータ部もひとつある。デフォルトではデータ部は通常は空だが、任意の量のランダムなデータを収容することもできる。

図に見られる最初の 3つのフィールドは前記と同じ。 Destination Unreachable のタイプでは、以下のリストのように、基本的に 16種類のコードを採る可能性がある。
コード 0 - Network unreachable (ネットワーク到達不能) - 指定されたネットワークへの到達が現在不能であることを知らせる。
コード 1 - Host unreachable (ホスト到達不能) - 指定されたホストへの到達が現在不能であることを知らせる。
コード 2 - Protocol unreachable (プロトコル到達不能) - このコードは指定されたプロトコル (tcp, udp など) への到達が現在できないということを知らせる。
コード 3 - Port unreachable (ポート到達不能) - このメッセージを受けるのは、ポート (ssh, http, ftp-data など) への到達ができない時。
コード 4 - Fragmentation needed and DF set (フラグメント必要だがDFフラグあり) - パケットをフラグメントする必要があるのだが "フラグメント不可" を表すビットがパケットにセットされている時に、ゲートウェイが返すメッセージ。
コード 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 - Network unreachable for TOS (TOS種別によりネットワーク到達不能) - 送信したパケットの "不適切" な TOS のせいでネットワークまで届かなかった時、リターンパケットはこのコードで作られる。
コード 12 - Host unreachable for TOS (TOS種別によりホスト到達不能) - パケットの TOS のせいで送信パケットがホストまで届かなかった時、返事に受け取るのがこのメッセージ。
コード 13 - Communication administratively prohibited by filtering (フィルタリング設定により通信禁止) - 何らかのフィルタリング (例えばファイヤーウォール) によってパケットの着信が禁止されていた場合に、我々送信側はコード 13 を受け取る。
コード 14 - Host precedence violation (ホスト優先区分侵害) - このメッセージは第 1 ホップのルータが送ってくる。パケットの採用している優先区分 (precedence) が当該の宛先/送信元の組み合わせにおいては許容されないということを、接続してきたホストに伝える。
コード 15 - Precedence cutoff in effect (優先区分により遮断発動) - 渡されたデータグラムにセットされていた優先レベル (precedence level) が低すぎたた場合に、第 1 ホップのルータはホストへこのメッセージを送ってもよいことになっている (may)。
これに加えて、小さなデータ部を持つこともでき、その中身はインターネットヘッダ (IPヘッダ) のヘッダ全てと、元の IP データグラムのうちの 64 ビットを写したものとなる。次のレベルのプロトコルがポート定義などを持っているとすれば、この 64 ビットデータ部からそれが読み取れるはずである。

パケットまたはそのストリームの発信元に対して、送信を継続するのならパケットの送信間隔を緩めてくれと伝えるために、ソースクエンチパケットが送られることがある。ただし、パケットが渡り歩いていくそうしたゲートウェイやホストはソースクエンチパケットなど送らずに黙ってパケットを破棄しても構わないことになっている点に注意しなければならない。
このパケットには特別なヘッダはなく、データ部に特徴がある。そのデータ部は、元のデータのインターネットヘッダ と、データグラムのうちの 64 ビット分を格納している。それによって、そのソースクエンチメッセージが、問題のゲートウェイや宛先ホストを通じてデータを送ろうとしているどのプロセス に対するものなのかが、適切に割り出せる。
ソースクエンチパケットは ICMP タイプが必ず 4 である。コードは 0 しかあり得ない。
![]() | ゲートウェイや宛先のホストの過負荷を送信ホストや受信ホストへ知らせるには、今日では 2〜3 種類の手段がある。そのひとつとして ECN (Explicit Congestion Notification = 明示的輻輳通知) という仕組みがある。 |

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 にも絡んでいる場合だ。

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 に達してしまった場合に、このコードが送られる。このコードは最終目的地のホストからしか送られてこない。

パラメータ障害 (parameter problem) の ICMP はタイプ 12 を使用し、 2 種類のコード値も採り得る。パラメータ障害メッセージは、ゲートウェイか受信ホストがエラー等によって IPヘッダ の一部を理解できない場合や、必要とされるオプションが見あたらない場合に、送信元のホストにそれを知らせるために使用される。
パラメータ障害 タイプの ICMP メッセージは或る特殊なヘッダを備える。それは、元のパケットにおけるエラー原因フィールドへのポインタだ (コードが 0 の場合)。有効なコードを以下に述べる:
コード 0 - IP header bad (IPヘッダ異常)(全てのエラーを包括) - これは、上述したような全てのエラーを包括したエラーメッセージだ。このコードをポインタと組み合わせると、エラーが IPヘッダ のどの部位にあるかを伝えることができる。
コード 1 - Required options missing (必須オプション欠如) - 必要なオプションが欠けている時、このコードで知らせる。

タイムスタンプタイプは今日では廃れて使われなくなっているが、ここでは簡単に取り上げておく。応答も要求も同じコード (0) を持つ。要求の場合はタイプ 13、応答はタイプ 14 である。タイムスタンプパケットは UT (Universal Time) 午前 0 時からの時間をミリ秒で表した 32 ビットのタイムスタンプを 3つ格納している。
最初のタイムスタンプはオリジナルタイムスタンプ、つまり送信元が最後にパケットに触れた時を表す。レシーブタイムスタンプ はエコーを返そうとしたホストが初めてパケットに触れた時、トランスミットタイム は、パケットがエコー送信者の手を離れる瞬間のタイプスタンプである。
いずれのタイムスタンプメッセージも、述べたもの以外に、ICMPエコー パケット同様の識別子 とシーケンスナンバー を持つ。

インフォメーション要求とインフォメーション応答というタイプは、今日では、必要であれば IP プロトコルより上に同目的で利用できるプロトコル (DHCP など) があるため、既に廃止されている。インフォメーション要求は、それを受け取ったネットワーク上に存在する応答可能な全ホストから回答を発生させる。
情報を要求したホストは、送信元アドレスを自分の属するネットワーク (例えば 192.168.0.0)、宛先を 0 にしてパケットを送る。応答のパケットには様々な数値情報 (ネットマスクと IPアドレス) が書かれている。
インフォメーション要求は ICMP タイプが 15、かたや応答はタイプ 16 で送られる。
Stream Control Transmission Protocol (SCTP) はネットワーク戦線ではまだ新顔の部類に入るプロトコルだが、利用される場面は日に日に広がりつつあり TCP および UDP プロトコルの弱点を改善するものでもあるため、こうしてセクションを設けて解説することにした。 SCTP は TCP にも勝る高信頼性を備え、なお且つ、プロトコルヘッダの造りから、オーバーヘッドが低く抑えられている。
SCTP には非常に興味深い特長がいくつかある。このプロトコルについてより詳しく知りたい人は、RFC 3286 - An Introduction to the Stream Control Transmission Protocol と RFC 2960 - Stream Control Transmission Protocol を読んでいただくといい。前者は SCTP の紹介文書で、もう少しよく知りたいという人にとって非常に興味深い文書だろう。ふたつ目はプロトコルの仕様書そのもので、このプロトコルを使って何かを開発しようとする人か本気で興味を持った人でもなければ、読んでもあまり役には立たないかもしれない。
SCTP は元々 Telephony over IP や Voice over IP (VoIP) 用に開発されたプロトコルで、このことに由来した非常に興味を惹く特性がいくつかある。商用グレードの VoIP では非常に高次の信頼性が求められる。つまり、様々な障害に対する障害回復機能が幾重にも張り巡らされているのだ。以下に挙げるのが SCTP の主な特徴である。
マルチキャスト 属性によるユニキャスト 。ポイントtoポイント のプロトコルでありながら、末端ホストに対する複数のアドレスをサポートしているということだ。言い換えると、エンドホストまで到達するために複数の経路が利用できる。これに対して TCP では、搬送経路が断ち切られた時、 IP プロトコルによる補正に助けてもらわない限り断絶は避けられない。
高信頼性伝送 (Reliable transmission)。チェックサムと SACK を使用して、データの衝突 (corrupted)、損傷、破棄 (discarded)、重複 (duplicated)、入れ違い (reordered) の検出を行い、そうした場合には必要に応じて再送を行う。この点は TCP も同じだが、特に、順序の入れ違ったデータに関しては SCTP のほうが回復能力に優れ、検知も早い。
メッセージ指向 (Message oriented) メッセージ単位でフレーム化ができるため、データグラムの構造や順序を見失わずにすむ。 TCP はバイト指向であり、受け取れるのは中で順序がばらばらになったバイトの流れ (stream) でしかなく、それを補うために追加のレイヤーを必要とする。
レート対応 (Rate adaptive)。 TCP と協調/共存して帯域幅調整を行うことができるように作られている。 TCP と同様に、ネットワーク負荷に応じて帯域幅の増減ができる。また、スロースタート に相当するアルゴリズムも備える。ECN にも対応している。
マルチホーミング (Multi-homing)。先にも述べたように、複数のエンドノードと遣り取りする機能がプロトコル自体に組み込まれている。そのおかげで、 IP レイヤーに頼らなくても障害回復ができる。
マルチストリーム (Multi-streaming)。ひとつのストリームの中で複数のストリームを同時に流すことができる。これが Stream Control Transmission Protocol という名前の由来だ。例えば、ひとつの Webページをダウンロードするために 1本のストリームを開いておき、その中だけでイメージや html ドキュメントを同時進行的にダウンロードすることができる。これを複数のコントロールストリームの発生可能なデータベースプロトコルで活用すれば、複数のクエリに対する回答を並行して受け取ることができることは言うまでもない。
イニシエーション。コネクションの確立 (initiation) は 4 つのパケットから成るが、3番目と 4番目はデータの送信にも利用できる。syncookies と同等の仕組みも標準で組み込まれており、DoS 攻撃が防げる。SCTP コネクションのバッティングを防ぐことを目的に INIT collision resolution (初期化衝突解決機能) を備えている。
上のリストは、やろうと思えばいくらでも増やせるが、やめておく。関連の情報は RFC 3286 - An Introduction to the Stream Control Transmission Protocol のドキュメントで入手可能なので、必要があればそちらを読んでいただきたい。
![]() | SCTP の話をする時には、これまでのような パケット や ウィンドウ は使わず、チャンク (chunks) という言葉が用いられる。SCTP はメッセージ指向であるため、ひとつの SCTP フレーム が複数のチャンク を含むこともある。チャンク には、コントロールチャンク と データチャンク があり、各チャンク はそのどちらかとなる。コントロールチャンク はセッションの制御を行い、実際のデータは データチャンク によって送られる。 |
コネクションのイニシャライズ (initialize = 初期確立) は、互いに通信を交わしたいホストとホストの間で関係を締結すること (association) によって行われる。関係締結のイニシャライズはユーザからの要請があった時に行われ、それが以後必要に応じて使用される。
イニシャライズは 4つのパケットによって行われる。まず INIT チャンク を送信し、それに対して、クッキーを含んだ INIT ACK が返され、そこからデータの送信が始まる。しかし、イニシャライズの続きがまだあり、さらに 2個のパケットの遣り取りがなされる。クッキーに対して返す COOKIE ECHO チャンク と、その回答でありイニシャライズの締めとなる COOKIE ACK チャンク だ。
SCTP は、ここまで準備が整えばデータの送信ができる。 SCTP においては コントロールチャンク と データチャンク があることは前に述べた。データチャンク は DATA チャンク によって送られ、 DATA チャンク は SACK チャンク の送信によって承認される (acknowledged)。実質的には TCP の SACK と同じだ。この SACK チャンク が コントロールチャンク だ。
コントロールチャンク はこの他にもある。HEARTBEAT と HEARTBEAT ACK チャンクがそのひとつ、もうひとつが ERROR チャンクだ。HEARTBEAT が使われるのはコネクションのキープアライブのため、 ERROR は、ストリーム ID の異常や、必須パラメータの欠如など、コネクション上の様々な問題やエラーを報告するために使われる。
いよいよ SCTP コネクションを閉じようという時には、ABORT チャンク あるいはより行儀正しい SHUTDOWN チャンク を送信する。SCTP には TCP のような半閉鎖 状態は存在しない。つまり、一端が送信ソケットを閉じてしまったら、他端はもう送信を継続することはできない。
ユーザあるいはアプリケーションが礼儀正しく SCTP ソケットを閉じようとする時には、プロトコルに対して SHUTDOWN を要望する。すると、SCTP はバッファ上に残存するデータを送りきった後、 SHUTDOWN チャンク を送信する。他端は、この SHUTDOWN を受け取るとアプリケーションからのデータを受け取るのをやめ、送信すべきデータをすべて完了させようとする。データに対しての SACK がすべて受信できたら、この端末は SHUTDOWN ACK チャンク を送信し、初めにクローズを言い出した側は締めの SHUTDOWN COMPLETE チャンク で返事をする。これでセッションは完全に終了だ。
セッションのクローズの仕方にはもうひとつある。それが ABORT。こちらは SCTP アソシエーション の礼儀正しくない解消法だ。一対の SCTP アソシエーション を即時に解消したい時がこれで、一方が然るべき値を持った ABORT チャンク を送る。バッファ上にあるものも含め、未送信のデータはすべて破棄されて、アソシエーションが解消される。相手方も、 ABORT チャンク の正当性を検査した後に同様の処理を行う。
この節は SCTP ヘッダの非常に手短な紹介だ。SCTP には沢山のタイプのパケットがあるのだが、RFC に示されているものはできるだけすべて網羅するようにし、それらの中で各ヘッダの果たす役割を説明してみようと思う。まずは SCTP パケット全般に共通するヘッダについての概論から始めることにしよう。

これが SCTP パケット全般のレイアウトだ。基本としてまず最初にくるのは、パケット全体についての情報と送信元および宛先のポートなどを伝える一般ヘッダ (common header) だ。一般ヘッダに関しては以降で詳しく述べる。
一般ヘッダの後ろには任意の数の チャンク がくる。 チャンク の最大数は MTU の許す範囲内だ。 チャンク はこのように「束」にできるわけだが、 INIT, INIT ACK, SHUTDOWN COMPLETE は束には入れられない。 DATA チャンクは、パケットの MTU 内に収めるために分割することもできる。

すべての SCTP パケットは上記に見る一般ヘッダを備えている。ヘッダには 4つのフィールドがあり、 SCTP パケット毎にそれぞれの値がセットされる。
送信元ポート (Source port) - ビット 0-15。そのパケットの送り元となったソースポートを伝える。 TCP と UDP のソースポートと同じだ。
宛先ポート (Destination port) - ビット 16-31。パケットの宛先ポート、つまりパケットの行こうとしているポート。TCP と UDP の宛先ポートと同じだ。
検証タグ (Verification Tag) - ビット 32-63。検証タグは、そのパケットがどの相手から送られてきたのかを検証するために使われる。この値は、アソシエーションのイニシャライズの段階で対向のピアから Initiate タグ で受け取った値がずっと使われる。ただし以下のような少数の例外もある:
INIT チャンクを含む SCTP パケットは 検証タグ が 0 と決められている。
T-bit のセットされた SHUTDOWN COMPLETE チャンクでは、 検証タグ は SHUTDOWN-ACK チャンクの 検証タグ の複製でなければならない。
ABORT チャンクを含むパケットでは、 検証タグ は、 ABORT の元となったパケットの 検証タグ と同じものでもよいことに なっている (may)。
チェックサム (Checksum) - ビット 64-95。Adler-32 アルゴリズムによって計算した、その SCTP パケット全体のチェックサム。アルゴリズムについては RFC 2960 - Stream Control Transmission Protocol の appendix B を参照されたし。

すべての SCTP チャンクは、上図に示した決まったレイアウトに則っている。ただし、これらはヘッダそのものではなく、「チャンク はおしなべてこういうフォーマットになっている」という定型を示したものに過ぎない。
タイプ (Type) - ビット 0-7。そのパケットの チャンクタイプを指定する。例えば INIT チャンクなのか SHUTDOWN チャンクなのかそれとも... といった具合。チャンク には、下表に示したようにそれぞれ決まったナンバーが割り当てられている。すべてのチャンク タイプをリストアップしたのが次の表だ:
Table 2-1. SCTPタイプ
| チャンクナンバー | チャンク名 |
|---|---|
| 0 | ペイロードデータ (Payload Data) (DATA) |
| 1 | イニシエーション (Initiation) (INIT) |
| 2 | イニシエーション承認 (Initiation Acknowledgement) (INIT ACK) |
| 3 | 選択的承認 (Selective Acknowledgement) (SACK) |
| 4 | ハートビート要求 (Heartbeat Request) (HEARTBEAT) |
| 5 | ハートビート承認 (Heartbeat Acknowledgement) (HEARTBEAT ACK) |
| 6 | 中止 (Abort) (ABORT) |
| 7 | シャットダウン (Shutdown) (SHUTDOWN) |
| 8 | シャットダウン承認 (Shutdown Acknowledgement) (SHUTDOWN ACK) |
| 9 | オペレーションエラー (Operation Error) (ERROR) |
| 10 | ステートクッキー (State Cookie) (COOKIE ECHO) |
| 11 | クッキー承認 (Cookie Acknowledgement) (COOKIE ACK) |
| 12 | 明示的輻輳通知エコー用の予約域 (Reserved for Explicit Congestion Notification Echo) (ECNE) |
| 13 | 輻輳ウィンドウ縮小用の予約域 (Reserved for Congestion Window Reduced) (CWR) |
| 14 | シャットダウン完了 (Shutdown Complete) (SHUTDOWN COMPLETE) |
| 15-62 | IETFによる予約域 (Reserved for IETF) |
| 63 | IETFによって定義済みのチャンク拡張 (IETF-defined chunk extensions) |
| 64-126 | IETFによる予約域 (reserved to IETF) |
| 127 | IETFによって定義済みのチャンク拡張 (IETF-defined chunk extensions) |
| 128-190 | IETFによる予約域 (reserved to IETF) |
| 191 | IETFによって定義済みのチャンク拡張 (IETF-defined chunk extensions) |
| 192-254 | IETFによる予約域 (reserved to IETF) |
| 255 | IETFによって定義済みのチャンク拡張 (IETF-defined chunk extensions) |
チャンクフラグ (Chunk Flags) - ビット 8-15。 チャンクフラグ はほとんど使われることはないが、他の用途に明け渡す必要が生じない限り、将来の活用に備えて確保されている。フラグ はチャンク 毎に特有のフラグやビットで、対向のピアに必要な情報を格納する。現時点での規格によれば、これらのフラグ は DATA, ABORT, SHUTDOWN COMPLETE パケットでのみ使うことになっている。ただし将来変更になる可能性もある。
![]() | RFC を読んでいると、前にも踏んだはずの危険な轍がそこここに転がっていることに気づかされる。 RFC 2960 - Stream Control Transmission Protocol の文書もそのひとつ。チャンクフラグ は必ず 0 でなくてはならないが、特に用途のない限りは無視される、という規定だ。いたるところに見られるこのような規定は、将来、問題になる危険性をはらんでいる。これには目を光らせておかなくてはならない。というのも、こういったフィールド規定は将来変更となる可能性があり、その時には、道理のいかない理由であなたのファイヤーウォールが爆弾を抱えることになってしまうのだ。同じことは、過去に IP ヘッダ に ECN が取り入れられた時にも起こっている。詳しくはこのチャプターの IPヘッダ セクションを読んでいただきたい。 |
チャンク長 (Chunk Length) - ビット 16-31。バイト単位で数えたチャンク の長さ。チャンクタイプ, チャンクフラグ, チャンク長, チャンク値 を含めたヘッダすべてを含めた長さだ。チャンク値 がひとつもない場合の長さは 4 (バイト) となる。
チャンク値 (Chunk Value) - ビット 32-n。ここはチャンク 毎に固有で、チャンクタイプ に応じた追加のフラグ およびデータを格納できる。空の場合もあり、その時のチャンク長 は 4 に設定される。

ABORT チャンクは、当チャプターの シャットダウンと中止 で述べたようにアソシエーションを断ち切る時に使用する。 ABORT は、データやヘッダの異常など、アソシエーションの中で回復不能の問題が発生した際に発行される。
タイプ (Type) - ビット 0-7。このチャンクタイプ では常に 6。
予約域 (Reserved) - ビット 8-14。将来のチャンクフラグ 拡張用に予約されているが、投稿執筆時点ではまだ使われていない。チャンクフラグ フィールドについては SCTPの一般ヘッダと共通ヘッダ を参照のこと。
T-bit - ビット 15。このビットが 0 だとすれば、このパケットには送信元で TCB が関連付け (associated) されていて、そのパケットがそれを無効化した (destroyed) ということを示している。送信元で TCB を使っていない時には、T-bit は 1 にすることになっている。
全長 (Length) - ビット 16-31。エラーの原因も含めたチャンク の長さのバイト表記。

COOKIE ACK チャンクはコネクションのイニシャライズの際にのみ使用され、コネクション中の他の場面で使われることはない。必ず DATA および SACK チャンクよりも前に送らなければならないが、そうした中の最初のパケットをこれに兼用してもよいことになっている。
タイプ (Type) - ビット 0-7。このタイプでは常に 11。
チャンクフラグ (Chunk flags) - ビット 8-15。今のところ未使用。RFC 2960 - Stream Control Transmission Protocol によれば常に 0 にすることになっている。こういった旨を記した RFC には常に目を光らせておかなければならない。というのは、将来変更される可能性があり、それによってあなたのファイヤーウォールにヒビが入ることになるからだ。 IP の ECN で起きたことの二の舞である。詳しくは SCTPの一般ヘッダと共通ヘッダ のセクションを参照していただきたい。
全長 (Length) - ビット 16-31。このタイプのチャンク では常に 4 (バイト)。

COOKIE ECHO チャンクが使われるのは SCTP コネクションのイニシャライズの過程で、接続の申し出を受けた側が INIT ACK パケットの State クッキー フィールドで送ってきたクッキーに対して、接続を申し出た側が回答する時だ。 COOKIE ECHO チャンクは DATA チャンクを乗せたパケット内で一緒に送ってもよいが、その場合には DATA チャンクよりも前に置かなければならない。
タイプ (Type) - ビット 0-7。このチャンクでは常に 10。
チャンクフラグ (Chunk flags) - ビット 8-15。今日ではまだ使用されていない。 RFC はこのフラグを必ず 0 にせよとしているが、このことは SCTPの一般ヘッダと共通ヘッダ のセクション (特にチャンクフラグの説明部分) で述べたようなトラブルを引き起こす可能性がある。
全長 (Length) - ビット 16-31。チャンク の長さ。タイプ, チャンクフラグ, 全長 および クッキーフィールド を含んだ長さで、バイト表記。
クッキー (Cookie) - ビット 32-n。このフィールドは、これに先立つ INIT ACK チャンクで送信されたクッキーと同じものを持つ。返答をよこした側の送ってきたクッキーと正確に一致していないと、コネクションはオープンできない。 RFC 2960 - Stream Control Transmission Protocol は、互換性を損なわないためにクッキーは可能な限り小さくせよと述べているが、曖昧で、多くを語ろうとしない。

DATA チャンクはストリームの中で実際のデータを送るためのもので、見方によってはかなり込み入っているように思えるかもしれないが、実際のところは TCP ヘッダの一般フォーマットとどっこいどっこいだ。 ひとつのパケットの中に同居していたとしても、各 DATA チャンクは別のストリームに属するものかもしれない。なぜなら、 SCTP はひとつのコネクションで複数のストリームを扱うことができるからだ。
タイプ (Type) - ビット 0-7。DATA チャンクでは常に 0 に設定される。
予約域 (Reserved) - ビット 8-12。今日では未使用。将来の変更のために確保されている。詳しくは SCTPの一般ヘッダと共通ヘッダ を参照。
U-bit - ビット 13。 U-bit は、その DATA チャンクが順不同で構わないものかどうかを表す。順不同であれば、ストリームシーケンスナンバー は無視する決まりになっており、 DATA を並べ直そう (re-order) とはせずにいち早く上位レイヤーに渡さなくてはならない。
B-bit - ビット 14。 B-bit は、フラグメントされた DATA チャンクの存在を示す。このビットが正で E (ending) ビットが正でない場合、このチャンクがいくつかにフラグメントされた DATA チャンクの最初の断片であることを表している。
E-bit - ビット 15。 E-bit は、フラグメントされた DATA チャンクの最終断片を表す。このフラグが正になったものは、 SCTP の受け取り側にとって、フラグメントの再構成を開始し上位レイヤーに渡せという合図になる。また、 B ビット も E ビット も 0 であれば、それはこのチャンクがフラグメントの半ばのチャンクであることを表す。両方とも 1 にセットされるのは、パケットはフラグメントされておらず再構成も必要ない場合などだ。
全長 (Length) - ビット 16-31。チャンクタイプ も含めてチャンク終端まで全部をビット単位で計算した DATA チャンクの長さ。
TSN - ビット 32-63。トランスミッションシーケンスナンバー (Transmission Sequence Number = TSN) は DATA チャンクの中で送られ、受信側ホストはこれを受け取ると、チャンクがきちんと届いたと認め (acknowledge)、引き替えに SACK チャンクを送る。値はその SCTP アソシエーション を通じてカウントされていく。
ストリーム識別子 (Stream Identifier) - ビット 64-79。ストリーム識別子 は DATA に付属して送られ、その DATA チャンクがどのストリームに関連付けられているかを明らかにする。これが存在するのは、 SCTP がひとつのアソシエーションの中で複数のストリームを運ぶ能力を備えているからだ。
ストリームシーケンスナンバー (Stream Sequence Number) - ビット 80-95。 ストリーム識別子 で括られるひとつのストリームの中でのシーケンスナンバー。このシーケンスナンバーは各ストリーム識別子 毎に管理される。チャンクがフラグメントされている場合には、元チャンクが同一ならばすべての断片に同じストリームシーケンスナンバー を付けなければならない。
ペイロードプロトコル識別子 (Payload Protocol Identifier) - ビット 96-127。この値は上位レイヤーまたは、SCTP を使用しているアプリケーションによって入れられ、それらどうしが DATA チャンクの内容を伝え合うために使われる。このフィールドは必ず送信する必要があり、フラグメントしたチャンクもその例外ではない。というのは、フラグメントされたチャンクであっても経路上のルータやファイヤーウォールなどがこの情報を必要とするからだ。値が 0 だった場合、上位レイヤーがセットしなかったと解釈される。
ユーザデータ (User data) - ビット 128-n。そのチャンクの運ぶデータそのもの。可変長だが、偶数オクテットで終わらなければならない。ストリーム S のストリームシーケンスナンバー n 番のデータ。

ERROR チャンクは、特定のストリームの中で起きた問題を対向のピアに知らせるためにある。ひとつの ERROR チャンクは複数の エラー原因 を格納することもでき、そのことについては RFC 2960 - Stream Control Transmission Protocol で詳説されている。僕がここで述べるのは基本だけで、それ以上詳しく述べるのは控えておく。中身が濃すぎるからだ。 ERROR チャンクは、それ自体が致命的 (fatal) な性格のものというよりも、発生したエラーの詳細内容を知らせるものといえる。とはいえ、 ABORT チャンクと組み合わせると、コネクションを断ち切るぞという知らせにもなりうる。
タイプ (Type) - ビット 0-7。 ERROR チャンクでは常に 9。
チャンクフラグ (Chunk flags) - ビット 8-15。今日では未使用。将来の変更を目してのフィールドだ。詳しくは SCTPの一般ヘッダと共通ヘッダ を参照。
全長 (Length) - ビット 16-31。すべての エラー原因 フィールドを含めた長さをバイトで指定する。
エラー原因 (Error causes) - ビット 32-n。ひとつの ERROR チャンクはひとつ以上の エラー原因 フィールドを含むことができ、それぞれのフィールドで対向ピアにコネクションに関する問題を知らせる。ひとつの エラー原因 の中にも決まったフォーマットがあり、それは RFC 2960 - Stream Control Transmission Protocol ドキュメントで述べられているが、ここではそれに踏み込むのはやめ、フィールドが 原因コード と 原因長 (cause length) および、原因毎に特有の情報フィールドから成ると言うだけに留める。指定可能な エラー原因 は下表の通りだ:
Table 2-2. エラー原因コード
| Cause Value | Chunk Code |
|---|---|
| 1 | Invalid Stream Identifier (無効なストリーム識別子) |
| 2 | Missing Mandatory Parameter (必須パラメータの欠如) |
| 3 | Stale Cookie Error (「失効したクッキー」エラー) |
| 4 | Out of Resource (リソースの欠乏) |
| 5 | Unresolvable Address (リゾルブできないアドレス) |
| 6 | Unrecognized Chunk Type (未知のチャンクタイプ) |
| 7 | Invalid Mandatory Parameter (必須パラメータの値が無効) |
| 8 | Unrecognized Parameters (未知のパラメータ) |
| 9 | No User Data (ユーザデータの欠如) |
| 10 | Cookie Received While Shutting Down (シャットダウン中にクッキーを受信) |

HEARTBEAT チャンクは、特定の SCTP 端末アドレスが無効になっていないか走査する目的で、ひとつのピアが送信する。これはアソシエーションのイニシャライズの過程で関係を結んだ (negotiated) 複数のアドレスに対して行われ、それらが生存しているかどうかの確認に使われる。
タイプ (Type) - ビット 0-7。 HEARTBEAT チャンクでは、タイプ は常に 4。
チャンクフラグ (Chunk flags) - ビット 8-15。今日では未使用。将来の変更で使用されるようになる可能性がある。詳しくは SCTPの一般ヘッダと共通ヘッダ を参照。
全長 (Length) - ビット 16-31。Heartbeat Information TLV を含めた、チャンク全体の長さ。
ハートビートインフォメーション TLV (Heartbeat Information TLV) - ビット 32-n。 RFC 2960 - Stream Control Transmission Protocol で定義された可変長のパラメータ。 HEARTBEAT チャンクには必須のパラメータで、infoタイプ=1, info長, 送信元固有の ハートビートインフォメーション パラメータの 3つのフィールドから成る。最後のフィールドは送信元によって内容がかなり異なり、例えば、ハートビートが前回いつ送られたかや送信先の IPアドレスが入ったりする。このパラメータと同じものが HEARTBEAT ACK チャンクで返される。

HEARTBEAT ACK は、HEARTBEAT が受理されコネクションは正常に機能しているという了解 (acknowledge) を示すために使われる。 HEARTBEAT ACK は要求のやってきた IPアドレスに対して返される。
タイプ (Type) - ビット 0-7。 HEARTBEAT ACK チャンクでは常に 5。
チャンクフラグ (Chunk flags) - ビット 8-15。今日では未使用。将来の変更に備えて確保されている。詳しくは SCTPの一般ヘッダと共通ヘッダ を参照。
チャンク長 (Chunk length) - ビット 16-31。 ハートビートインフォメーション TLV を含めた HEARTBEAT ACK チャンクの長さをバイト単位で表したもの。
ハートビートインフォメーション TLV (Heartbeat Information TLV) - ビット 32-n。 ここには必ず、元となった HEARTBEAT チャンクの ハートビートインフォメーション パラメータと同じものを格納する。

INIT チャンクは、宛先ホストとの新たなアソシエーションを開始 (initiate) する時に用いられるもので、コネクションを張ろうとするホストの発する一番最初のチャンクがこれだ。 INIT チャンクはいくつかの固定長パラメータと、省略可能な可変長パラメータから成る。固定長の必須パラメータには、既に他のヘッダで述べたものの他に、 イニシエートタグ (Initiate Tag), 受信ウィンドウ保証量広告 (Advertised Receiver Window Credit), 送出ストリーム数 (Number of Outbound Streams), 流入ストリーム数 (Number of Inbound Streams), 初期TSN (Initial TSN) パラメータがある。その後ろに下の「省略可能なパラエータ」の段落で挙げているいくつかのオプションパラメータが続く。
タイプ (Type) - ビット 0-7。INIT チャンクでは常に 1。
チャンクフラグ (Chunk flags) - ビット 8-15。今日では未使用。将来の変更に備えて確保されている。詳しくは SCTPの一般ヘッダと共通ヘッダ を参照。
チャンク長 (Chunk Length) - ビット 16-31。 チャンク長 は、オプションパラメータも含めたすべてのヘッダを含めたパケットの全長。
イニシエートタグ (Initiate Tag) - ビット 32-63。イニシエートタグ は INIT チャンクの中で決められ、それが 検証タグ (Verification Tag) に利用されて、このアソシエーション中ずっと受信側によるパケット承認に使用される。 イニシエートタグ は 0 以外ならどんな値でも採ることができる。決まりに反して 0 にされた場合には、受信側は ABORT を発生させなければならない。
受信ウィンドウ保証量広告 (Advertised Receiver Window Credit) (a_rwnd) - ビット 64-95。 INIT チャンクの送り主がこのアソシエーション用に割り当てることにしている受信バッファの最小量。 a_rwnd の受け取り側は、この値によって、どれだけの量のデータが SACK で区切らずに送り出せるかを知る。実際の受信ウィンドウをここで広告したサイズよりも縮小することは推奨されないが、 SACK チャンクで改めて a_rwnd を送ることによってサイズを減らす余地はある。
送出ストリーム数 (Number of Outbound Streams) - ビット 96-111。相手に向かって張りたい、こちらから出て行く方向のストリームの最大数を規定する。この値を 0 とすることは許されておらず、もしもそうすれば相手はアソシエーションを即刻 ABORT しなくてはならない。送出ストリームに関しても流入ストリームに関しても最小数についての折衝はなく、単純に、両ホストがヘッダで指定したうちの小さい方が最小値となる。
流入ストリーム数 (Number of Inbound Streams) - ビット 112-127。このセッションで送り側が受信側ホストに生成を許可する、流入方向のコネクションの最大数。0 にすることは許されておらず、そうすれば受信側ホストはコネクションを ABORT しなければならない。送出ストリームに関しても流入ストリームに関しても最小数についての折衝はなく、単純に、両ホストがヘッダで指定したうちの小さい方が最小値となる。
初期 TSN (Initial TSN) - ビット 128-159。この値は、送り手側がデータの送信に使用する 伝送シーケンスナンバー (Transmit Sequence Number = TSN) の初期値を規定する。イニシエートタグ と同じ値を使用してもよいことになっている。
上記に挙げた固定長の必須ヘッダに加えて、設定してもよい オプショナルな数種の可変長パラメータがある。少なくとも IPv4, IPv6, Hostname のいずれかひとつは指定しなければならない。 Hostname はひとつだけしか指定できず、指定してある時には IPv4 と IPv6 パラメータはいずれも指定できない。ひとつの INIT チャンクで IPv4 と IPv6 パラメータを複数指定することは許されている。また、送り手側がひとつのアドレスしか持たずそれをチャンク の送信元として使う場合には、これらのパラメータを 3つとも省略することもできる。これらのパラメータは相手ピアとの接続に使用するアドレスを取り決めるためにある。下記が INIT チャンクで利用可能な全パラメータのリストだ:
Table 2-3. INITの可変長パラメータ
| パラメータ名 | ステータス | タイプ値 |
|---|---|---|
| IPv4 Address | 省略可 | 5 |
| IPv6 Address | 省略可 | 6 |
| Cookie Preservative | 省略可 | 9 |
| Host Name Address | 省略可 | 11 |
| Supported Address Types | 省略可 | 12 |
| Reserved for ECN Capable | 省略可 | 32768 |
以下に、 INIT チャンクで最も一般的な 3つのパラメータについて述べる。

IPv4 パラメータは INIT チャンクで IPv4 アドレスを送るためにある。これによって指定するのは、そのアソシエーションの中でデータ送信に使用していく IPv4 アドレスだ。ひとつの SCTP アソシエーションに対して IPv4 アドレスや IPv6 アドレスを複数指定することもできる。
パラメータタイプ (Parameter Type) - ビット 0-15。IPv4 アドレスパラメータでは常に 5。
全長 (Length) - ビット 16-31。 IPv4 アドレスパラメータでは常に 8 となる。
IPv4 アドレス - ビット 32-63。送り手側端末の、ひとつの IPv4 アドレス。

このパラメータは INIT チャンクで IPv6 アドレスを送るためにある。ここで指定したアドレスはそのアソシエーションの中での送り手側端末との通信に利用することができる。
タイプ (Type) - ビット 0-15。 IPv6 パラメータでは常に 6。
全長 (Length) - ビット 16-31。 IPv6 パラメータでは常に20となる。
IPv6 アドレス - ビット 32-159。送り手側エンドポイントの、ひとつの IPv6 アドレス。受け手側エンドポイントはこのアドレスと通信することになる。

Hostname パラメータは、アドレスとしてのひとつのホストネームを送るためにある。受信側ホストはここで指定されたホストネームをルックアップし、それによって得られた何れかまたはすべてのアドレスを利用する。Hostname パラメータを送るのであれば、 IPv4 や IPv6 も、余計な Hostname パラメータも送ることはできない。
タイプ (Type) - ビット 0-15。 Hostname パラメータでは常に 11。
全長 (Length) - ビット 16-31。 タイプ, 全長 をはじめとする全フィールドを含めたパラメータ全体の長さ。 Hostname フィールドは可変長だ。単位はバイト。
Hostname - ビット 32-n。ホストネームを格納する可変長パラメータ。個々で指定したホストネームは受信側エンドポイントでリゾルブが行われ、そこで得られたアドレスが送り手側エンドポイントとの通信に利用される。

INIT ACK チャンクは INIT チャンクへの返事として送られる。ヘッダは基本的に INIT と同じだが、それらの値はこれを送ってきたピア (つまり元の INIT を受けた側) のものに置き換えられる。また、 INIT にはない追加の可変長パラメータ、 ステートクッキー と 認識不能パラメータ (Unrecognized Parameter) パラメータがある。
タイプ (Type) - ビット 0-7。 INIT ACK チャンクでは常に 2。
チャンクフラグ (Chunk flags) - ビット 8-15。今日では未使用。将来の変更を目して確保されているフィールドだ。詳しくは SCTPの一般ヘッダと共通ヘッダ を参照。
チャンク長 (Chunk Length) - ビット 16-31。チャンク長 は、省略可能なパラメータも入れたすべてのヘッダを含めたパケット全体の長さ。
イニシエートタグ (Initiate Tag) - ビット 32-63。 INIT ACK チャンクの イニシエートタグ を受け取った端末は、その値を記録した上で、INIT ACK チャンクを送りつけてきた端末へ送り返すすべてのパケットの 検査タグ (Verification Tag) フィールドにそれをコピーしなければならない。 イニシエートタグ は 0 であってはならず、もし 0 であれば、その INIT ACK チャンクを受け取ったピアは ABORT によってコネクションをクローズしなければならない。
受信ウィンドウ保証量広告 (Advertised Receiver Window Credit) (a_rwnd) - ビット 64-95。送り手側が受信トラフィック専用に確保したバッファのサイズ。単位はバイト。専用バッファをこれより小さくしてはならない。
送出ストリーム数 (Number of Outbound Streams) - ビット 96-111。出て行く方向のストリームを送り手側ホストがいくつ生成したいか。0 にしてはならず、もしそうすれば INIT ACK を受信した側はそのアソシエーションを ABORT しなくてはならない。送出ストリームに関しても流入ストリームに関しても最小数についての折衝はなく、単純に、両ホストがヘッダで指定したうちの小さい方が最小値となる。
流入ストリーム数 (Number of Inbound Streams) - ビット 112-127。送信側エンドポイントがすすんで受け入れる流入方向ストリームの最大数。0 にしてはならず、もしそうすれば INIT ACK を受信した側はそのアソシエーションを ABORT しなくてはならない。送出ストリームに関しても流入ストリームに関しても最小数についての折衝はなく、単純に、両ホストがヘッダで指定したうちの小さい方が最小値となる。
初期 TSN (Initial TSN) - ビット 128-159。送信側がそのアソシエーションで最初に使う 伝送シーケンスナンバー初期値 (Initial Transmission Sequence Number = I-TSN)。
この後、 INIT ACK チャンクにはオプションの可変長パラメータが続く。パラメータは基本的には INIT チャンクのものと同じだが、 ステートクッキー パラメータと 認識不能パラメータ パラメータが加わり、対応アドレスタイプ (Supported Address Types) パラメータがなくなる。つまり下表のような塩梅だ:
Table 2-4. INIT ACK の可変長パラメータ
| パラメータ名 | ステータス | タイプ値 |
|---|---|---|
| IPv4 Address | 省略可 | 5 |
| IPv6 Address | 省略可 | 6 |
| State Cookie | 必須 | 7 |
| Unrecognized Parameters | 省略可 | 8 |
| Cookie Preservative | 省略可 | 9 |
| Host Name Address | 省略可 | 11 |
| Reserved for ECN Capable | 省略可 | 32768 |

ステートクッキー は INIT ACK チャンクで使用され、対向のホストにクッキーを送るためにある。これを受信したホストが COOKIE ECHO チャンクで回答するまでは、そのアソシエーションが正当なものであることは保証されない。 TCP プロトコルでいうところの SYN アタックを防ぐための仕組みだ。
タイプ (Type) - ビット 0-15。ステートクッキー パラメータでは常に 7。
全長 (Length) - ビット 16-31。タイプ, 全長, ステートクッキー フィールドを含めたパラメータ全体のサイズ。バイト表記。
ステートクッキー (State Cookie) - ビット 31-n。任意の長さのクッキーを格納する。クッキーがどのように生成されるかについては RFC 2960 - Stream Control Transmission Protocol を参照。

SACK チャンクの使用目的は、受信した TSN に照らしてどのチャンク が受信済みでストリームのどこにギャップがあったかを DATA チャンクの送信元に伝えることにある。 SACK チャンクの働きは、ざっくり言えば、特定の時点 (累積 TSN Ack パラメータ) までに受け取ったデータを承認し (acknowledges)、その 累積 TSN Ack ポイントまでに受け取った全データに対する ギャップ Ack ブロック を加えていくというものだ。 SACK チャンクの送信は DATA チャンクの受信 1回に対して 1回だけと決められている。
タイプ (Type) - ビット 0-7。 SACK チャンクでは常に 3。
チャンクフラグ (Chunk flags) - ビット 8-15。今日では未使用。将来の変更を目して確保されているフィールドだ。詳しくは SCTPの一般ヘッダと共通ヘッダ を参照。
チャンク長 (Chunk Length) - ビット 16-31。すべてのヘッダおよびパラメータを含めたチャンク全体の長さ。
累積 TSN Ack (Cumulative TSN Ack) - ビット 32-63。この 累積 TSN Ack パラメータはデータを承認するためにある。その DATA チャンクを受け取ったホストは、アソシエーションにおける現時点までのデータを受信し終えたことをこのフィールドによって送信元ホストに伝える。この時点でまだ明示的に ギャップ Ack ブロック によって承認していないデータは、基本的には喪失扱いになる。
受信ウィンドウ保証量広告 (Advertised Receiver Window Credit) (a_rwnd) - ビット 64-95。この a_rwnd フィールドも基本的には INIT や INIT ACK チャンクの a_rwnd と同じだが、ここでは a_rwnd 値の増減も可能だ。詳しくは RFC 2960 - Stream Control Transmission Protocol を読んでいただきたい。
ギャップ Ack ブロック数 (Number of Gap Ack Blocks) - ビット 96-111。このチャンクに含まれる ギャップ Ack ブロック の数。 ギャップ Ack ブロック ひとつは チャンク の 32 ビットを占有する。
重複 TSN 数 (Number of Duplicate TSNs) - ビット 112-127。重複していた DATA チャンクの数。重複していたすべての TSN はこの チャンク上の ギャップ Ack ブロック の後ろに並べられる。ひとつの TSN を送るには 32 ビットを消費する。
ギャップ Ack ブロック #1 開始点 (Gap Ack Block #1 Start) - ビット 128-143。ここに来るのが、この SACK チャンクで最初の ギャップ Ack ブロック。受け取った DATA チャンクの TSN ナンバーにギャップがひとつもない時には、 ギャップ Ack ブロック はひとつもない。かたや、 DATA の受信順序が乱れたり、伝送中にいくつかの DATA チャンクが失われた場合には、ギャップありと認められ、 ギャップ Ack ブロック を使って報告される。ギャップ Ack ブロック の開始位置は、 累積 TSN の値に ギャップ Ack ブロック開始点 パラメータを足すことによって求められる。そうして求められたのがブロック開始点だ。
ギャップ Ack ブロック #1 終点 (Gap Ack Block #1 End) - ビット 144-159。そのストリームにおける第1 ギャップ Ack ブロック の終点。 ギャップ Ack ブロック開始点 と ギャップ Ack ブロック終止点 の間にある TSN を持つすべての DATA チャンクは受信済み扱いになる。開始点同様に、実際に承認するブロックチャンクの最終 TSN を求めるには、ギャップ Ack ブロック終止点 の値を 累積 TSN と足す。
ギャップ Ack ブロック #N 開始点 (Gap Ack Block #N Start) - ビット可変。 ギャップ Ack ブロック数 パラメータがひとつ増える毎に、 ギャップ Ack ブロック がひとつ追加される (最後の第 N ブロックまで)。例えばつまり、ギャップ Ack ブロック数 = 2 だとすれば、その SACK チャンク内にはふたつの ギャップ Ack ブロック があるはずだ。ここは単純に最後の ギャップ Ack ブロック を意味し、ギャップ Ack ブロック #1 開始点 と同様の値が入ることになる。
ギャップ Ack ブロック #N 終点 (Gap Ack Block #N End) - ビット可変。ギャップ Ack ブロック #N 開始点 と同様。ただし、終点である。
重複 TSN #1 (Duplicate TSN #1) - ビット可変。このフィールドは、重複した TSN のことを通知する。つまり、既に受け取った チャンク と同一の TSN を持つ チャンク を、何度も受信した場合だ。ルーターの悪戯 (送信済みデータの再送) によるもの、エンドポイントの再送によるものなどケースは様々だ。重複した TSN は個々について 1回報告され、例えば、最初の該当データを承認してから 2つの重複 TSN を受信した場合、次回送信する SACK メッセージにふたつの重複 TSN それぞれについての報告が乗る。その SACK 送信後にさらに重複 TSN が観測されたら、そのまた次の回の SACK で知らせ、また... といった具合だ。
重複 TSN #X (Duplicate TSN #X) - ビット可変。ここに、最後の重複 TSN パラメータが来る。パラメータは前のものと同様だ。

SHUTDOWN チャンクが発行されるのは、或るコネクションの一方のエンドポイントが現行のアソシエーションのクローズを希望する時だ。これを発行するホストは前もって送信バッファを空にしておく必要があり、 SHUTDOWN チャンクを送ったらもう DATA を一切送ってはならない。受信した側は、やはり送信バッファを空にして、 SHUTDOWN ACK チャンクで回答することになっている。
タイプ (Type) - ビット 0-7。 SHUTDOWN チャンクでは常に 8。
チャンクフラグ (Chunk flags) - ビット 8-15。今日では未使用。将来の変更を目して確保されているフィールドだ。詳しくは SCTPの一般ヘッダと共通ヘッダ を参照。
チャンク長 (Chunk Length) - ビット 16-31。 累積 TSN Ack パラメータも含めたパケット全体の長さ。 SHUTDOWN チャンクでは常に 8 となる。
累積 TSN Ack (Cumulative TSN Ack) - ビット 32-63。この 累積 TSN Ack フィールドも SACK チャンクのものと同様。累積 TSN Ack は、対向エンドポイントからこれまでにシーケンス上最後に受け取った TSN に承認を与える。このパラメータは、 SHUTDOWN チャンク以降のデータはもちろん、ギャップ Ack ブロック の承認も行わない。以前承認したはずの TSN が SHUTDOWN チャンクの ギャップ Ack ブロック にないからといって、そのブロックが今になって破棄されたと解釈してはならない。

SHUTDOWN ACK チャンクは、受信した SHUTDOWN チャンクを承認するために使用される。 SHUTDOWN ACK を送る前には、送信バッファの中身はすべて絞り出しておかなければならず、アプリケーションから新たなデータを受け取ってもいけない。 SCTP には TCP にあるようなコネクションの半開状態 (half-open) は存在しない。
タイプ (Type) - ビット 0-7。 SHUTDOWN ACK チャンクでは常に 8。
チャンクフラグ (Chunk flags) - ビット 8-15。今日では未使用。将来の変更を目して確保されているフィールドだ。詳しくは SCTPの一般ヘッダと共通ヘッダ を参照。
チャンク長 (Chunk Length) - ビット 16-31。チャンク全体の長さ。 SHUTDOWN ACK では常に 4 となる。

SHUTDOWN COMPLETE チャンクは、SHUTDOWN ACK チャンクへの返答として、 SHUTDOWN を先導したホストによって送信される。アソシエーションのクローズが完了したことを承認するためだ。
タイプ (Type) - ビット 0-7。 SHUTDOWN COMPLETE チャンクでは常に 14。
予約済み (Reserved) - ビット 8-14。今日では未使用。将来の変更を目して確保されているフィールドだ。詳しくは SCTPの一般ヘッダと共通ヘッダ を参照。
T-bit - ビット 15。送信側がこのコネクションに対して 伝送制御ブロック (Transmission Control Block = TCB) を結びつけており、それを無効化したのがその SHUTDOWN チャンクだった場合には、その事実を送信者側に伝えるために T-bit は立てない [訳者註: 0 にする]。 T-bit が立っている [: 1 になっている] とすれば、反故にするような TCB は存在しなかったということになる。
全長 (Length) - ビット 16-31。 SHUTDOWN COMPLETE チャンクでは常に 4。規格が改定でもされない限りは、それより大きくなることはあり得ないからだ。
ルーティングという面では、 TCP/IP はかなり複雑になってきている。当初は誰もが、宛先誘導型ルーティング (destination driven routing) だけで充分だと考えていた。ところがここ数年で、あっという間に複雑さを増してきた。今日では、 Linux でも、基本的には IP ヘッダのフィールドやビットのどれを基準にしてでもルーティングができるし、そればかりか TCP, UDP, ICMP のヘッダでもルーティングが可能になっている。そうした仕組みをポリシールーティング (policy based routing)、あるいは高度ルーティング (advanced routing) と呼ぶ。
この章は、宛先誘導型ルーティングがどのように行われるかの簡略な説明だ。送信元ホストからパケットを送信する時、パケットが生まれる。すると次に、コンピュータはパケットの宛先アドレスを見て、コンピュータ自身の保持しているルーティングテーブルと照合を行う。宛先アドレスがローカルなものならば、パケットは直接、ハードウェア MACアドレス を使って送られる。宛先がゲートウェイの向こう側だった場合には、パケットはゲートウェイの MAC アドレスへと送られる。するとゲートウェイはパケットの IP ヘッダを読み、パケットの宛先アドレスを知る。宛先アドレスはここでまたルーティングテーブルと照合され、パケットは次のゲートウェイへ送られる...という行程が、宛先のアドレスの属する向こう側 のネットワークに到達するまで繰り返される。
お分かりの通り、この仕組みのルーティングは基本原則だけで成り立つ単純なものだ。高度ルーティングやポリシールーティングになると話は別で、仕組みはもっと複雑だ。例えば送信元アドレスや TOS 値などに基づいてパケットのルーティングを変えたりできるのだ。
このチャプターでは、次から始まるチャプターを理解できるようにあなたの知識をアップデートしてきた。要点を簡潔にまとめると:
TCP/IPの構造
IPプロトコルの機能とヘッダ
TCPプロトコルの機能とヘッダ
UDPプロトコルの機能とヘッダ
ICMPプロトコルの機能とヘッダ
TCP/IP宛先誘導型ルーティング
このどれもが、この先、実際にファイヤーウォールのルールセットに取り組む際には非常に役に立つはずだ。これらをパズルの駒のようにぴったりと組み合わせることで、より良いファイヤーウォールを設計することができる。
このチャプターでは IPフィルタ の考え方について詳しく説明する。 IPフィルタとは何なのかや、動作の仕組み、ファイヤーウォールの配置場所、ポリシーなどについて述べる。
このチャプターで取り上げる疑問は、例えば、ファイヤーウォールをどこに配置するかだ。ほとんどの場合は答は簡単だ。しかし、大規模な企業のネットワーク環境となると、そう簡単にはいかない。ポリシーとは何なのか。アクセスはどの程度制限すればいいのか。そもそも IPフィルタとは何なのか。こうした疑問の全てを、当チャプターは解きほぐしていく。
IPフィルタが何であるかをよく理解しておくのは重要なことだ。 iptables は IPフィルタである。このことをよく理解しておかないと、今後自分のファイヤーウォールを設計する時に暗礁に乗り上げることになる。
IP フィルタは TCP/IP 参照スタックのうち、主にレイヤー 2 で働く。現代の IPフィルタのほとんどがそうであるように、 iptables も、レイヤー 3 で作用することもできる。しかし、定義上では、 IPフィルタは第 2 層で働くものとされている。
IPフィルタの実装定義に厳格に従うのならば、 IPフィルタは IP ヘッダ (送信元/宛先アドレス, TOS/DSCP/ECN, TTL, プロトコル など、本当の意味で IP ヘッダ内に存在する情報のみ) に基づいてしかパケットをフィルタリングできないことになる。しかし iptables は IPフィルタの定義に対して 100% 厳格ではないので、パケットの奥深くに埋め込まれているヘッダ (TCP, UDP など) や、もっとローレベルのヘッダ (MACソースアドレス) に基づくフィルタリングも行える。
しかしここでひとつ問題がある。 iptables は最近になって、より厳格になってきているということだ。 iptables はストリームを「追いかけること (follow)」はしないし、データ同士を合体させたりもしない。そういった処理はプロセッサやメモリを喰いすぎるのだ。これが何を意味するかは後でじっくりと説明することにしよう。 iptables は TCP/IP スタックそのものがやるのと全く同じように、パケットそれぞれの身元を調べて記憶し、それらが同一のストリームに属しているかどうかを (シーケンスナンバー やポートナンバー などに基づいて) 判断する。これはコネクション追跡 (connection tracking) と呼ばれ、それがあるからこそ、パケットの宛先/送信元ネットワークアドレス変換 (一般的に DNAT, SNAT と呼ばれる) やステートマッチが行えるのだ。
上で明言を控えた点だが、 iptables は別々のパケットから読み取ったデータ同士を (デフォルトでは) くっつけることはできない。そのため、あなたが目にしているのが常にそのデータの全体とは限らない。僕がわざわざこの点に触れるのは、 netfilter/iptables に関係したメーリングリストの幾つかで、根本的に間違ったアイデアについての「どうやったらできるか」という質問が散見されるからだ。例えば、新たな Windows ベースのウイルスが現れる度に、特定の文字列を含むストリームを破棄する方法を質問する輩が現れる。たちが悪いのは、これが実に簡単にやれるという点だ。例えば:
cmd.exe
のような文字列でマッチを行えば、一応はできてしまう。
しかしここで、もしウイルスなりクラッキングプログラムなりのライターが非常にずる賢くて、パケットサイズを非常に小さくして cmd をひとつのパケットに入れ、次のパケットに .exe を乗せるようにしていたらどうなるか。あるいは、経路途中で、そんな小さなサイズのパケットしか通れないようなネットワークを通らざるを得ないとしたら。その通り。文字列マッチ (string match) はパケットの境界線を越えて作用することはできないので、問題のパケットは擦り抜けてしまう。
「どうして、パケットを跨っても作用するように文字列マッチや何かを作り替えないのさ」という疑問の浮かんだ人もいるだろう。答えは単純。 CPU時間 (processor time) を喰ってしまうからだ。コネクション追跡は既に、 CPU時間 を気にせずにいられるぎりぎりの所まで来ている。そこへ更なるレイヤー、例えば前記の機能を加えれば、おそらく想像を絶する数のファイヤーウォールが使用不能となるだろう。その 1個の機能のメモリ消費量の問題を度外視したとしてもだ。
こうした機能に手をつけないのには他にも理由がある。世の中にはプロキシというテクノロジが存在する。プロキシは IP フィルタよりも上層のレイヤーでトラフィックを扱うために開発されたものであり、この要望を達成するにはそちらのほうが適しているのだ。プロキシの開発目的は、速度の遅いインターネット接続でもダウンロードやページ閲覧をできるだけ効率よく行うことだった。例えば WEBプロキシのひとつに Squid がある。誰かが今、或るページのダウンロード要求を出したとすると、プロキシがその要求を横取り、あるいは受信し、WEB ブラウザとのコネクションを開く。次にプロキシは WEB サーバに接続して当該のファイルをダウンロード。ファイル (あるいはページ) のダウンロードが完了したら、それをクライアントへと送る。今度、別のブラウザがさっきと同じページを要求してきたら、ファイル (あるいはページ) は既にプロキシの懐に存在するため、それを直に送ればいいので、帯域の節約になるというわけだ。
知っている人もいるだろうが、プロキシは、ダウンロードするファイルにもっと踏み込んで、内容を読み取る機能も備えている。だから、ストリームやファイル、ページの中身を検査するのなら、こちらにほうがずっと適しているのだ。
既に iptables 及び netfilter でレイヤー7 フィルタリングを行うことの問題点を警告したところで、実は、その問題の解決に挑む一連のパッチも存在する。これは http://l7-filter.sourceforge.net/ と呼ばれるもので、かなり多くのレイヤー7 プロトコルに対してマッチを行うことができる。ただし、l7-filter は、純粋なフィルタリングに使用できなくもないが、メインの用途は QoS や トラフィック・アカウンティング との併用だ。 l7-filter はまだカーネルや netfilterコアチームとは別のところで試験と開発が進められいるところで、それ故、当文書でこれ以上触れるつもりはない。
これ以降のチャプターをよく理解するためには、 TCP/IP のチャプター で詳説したことも含めて、知っておくべき用語や表現が幾つかある。主要な用語を以下にリストにした。
Drop/Deny (破棄/拒否) - パケットを破棄あるいは拒否すると言う時には、そのパケットは削除され、あとは何のアクションも行わない。パケットが破棄された旨をホストに伝えもしないし、行くはずだったホストにはなおさらだ。ただパケットが消滅するだけだ。 [訳者註: 日本語には deny と reject をうまく区別する語彙がなく、訳すといずれも同じになってしまうので、訳文の中ではなるべく英語彙のまま記述する]
Reject (拒絶) - 基本的には drop や deny ターゲット (ポリシー) と同じだが、 reject の場合にはパケットが破棄されたという事実を送信者に返答する点が異なる。返答の内容は指定することもできるし、自動で決められる場合もある。(残念ながら、執筆時点の iptables には、破棄したパケットの行き先となっていたホストへ報告のパケットを送る機能 (例えば reject ターゲットの意味反転指定) はない。受け取り側ホストには DoS 攻撃をやめさせる術がないという事実に鑑みると、これは利点だともいえる。) [訳者註: 日本語には deny と reject をうまく区別する語彙がなく、訳すといずれも同じになってしまうので、訳文の中ではなるべく英語彙のまま記述する]
State (ステート) - ひとつのストリームの中における、パケットの状態 (state)。具体的に言えば、ファイヤーウォールが初めて観測した (知った) パケットは NEW というステートに識別され (TCPコネクションのSYNパケット)、また、ファイヤーウォールが既に認知している確立済みのコネクションに属しているパケットならば ESTABLISHED と判定される。ステートは、セッションを常に監視しているコネクション追跡機構 によって判断される。
Chain (チェーン) - チェーンは、ルールの集合体 (ルールセット) から成り、パケットがチェーンの中を進んでいく時にそれらのルールが適用される。それぞれのチェーンには特有の役割 (例えばそのチェーンがどのテーブルに属しているかによって、チェーンにできることできないことが決まる) や、適用範囲 (例えば foward されたパケットにのみ適用するとか、自ホスト宛てのパケットだけに適用するなど) がある。後で詳しく述べるが、 iptables には幾つものチェーンがある。
Table (テーブル) - 各テーブルには別個の使用目的があり、 iptables には 4つのテーブルがある。 raw, nat, mangle, filter テーブルだ。例えば filter テーブルはパケットをフィルタリングするためにできており、 nat テーブルはパケットに NAT (Network Address Translation = ネットワークアドレス変換) を施すために設計されている。
Match (マッチ) - IPフィルタリングでは、この語彙にはふた通りの意味がある。ひとつは、或るルールの中の単一の一致条件を指す場合。そのマッチは、ヘッダがこれこれの情報を持っていなければならないということをルールに指示する。例えば --source マッチは送信元アドレスが或る特定のネットワーク範囲やホストアドレスでなければならないということを指示する。もうひとつは、ひとつのルールを丸ごと 1 個のマッチと捉える場合。そのルールに指定してある全ての条件にマッチしたら、ジャンプ (ターゲット) に指定してある動作 (パケットの破棄など) が実行される。
Target (ターゲット) - ルールセットの中の各ルールにはたいてい、ひとつのターゲット・セットがある。ルールの持つ条件が全て満たされたらパケットに何を行うか、を指示するのがターゲットだ。 drop する、 accept する、 NAT を掛けるなどといった処置だ。もうひとつ、ジャンプ (jump) というものもあるが、それは当リストの jump の項目で述べる。最後にひとつ言い添えておくと、ルールには漏れなくひとつのターゲット (あるいはジャンプ) があるとは限らないが、まあ、たいていはある。
Rule (ルール) - ほとんど全ての IPフィルタ一般、そして iptables でも、ルールとは、ひとつのターゲットを従えた 1 項目または数項目の一致条件 (match) の集まりを指す。ひとつのルールが複数のターゲット (アクション) を持てるように実装した IPフィルタもある。
Ruleset (ルールセット) - ルールセットとは、幾つものルールから成るルールの総体のことで、 IPフィルタはこの "ルールセット" をロードして動く。 iptables の場合なら、ルールセットは、 filter, nat, raw, mangle の各テーブルとその下位にある全チェーンに組み込まれた、全てのルールを含有する。通常、ルールセットは設定ファイルの類に書き連ねる。
Jump (ジャンプ) - ジャンプ命令はターゲットと密接な関係にある。 iptables においては、ジャンプ命令はターゲットと全く同じ書き方となるが、唯一異なるのが、ターゲット名ではなく別のチェーンの名前を指定するという点だ。つまり、ルールの条件に一致したら、パケットは指定した副チェーンへと送られ、通常と同じようにそこで処理される。
Connection tracking (コネクション追跡) - ごく簡潔に言うと、コネクション追跡を備えたファイヤーウォールはコネクション/ストリームを追跡することができる。これを行うにはたいてい、プロセッサとメモリに大きな負荷がかかる。残念ながら iptables も例外ではないが、この課題には多くの努力が注ぎ込まれてきた。だが一方、コネクション追跡の良い面は、ファイヤーウォールポリシーの構築者がそれを適切に使えば、この機能を持つファイヤーウォールは持たないものよりも遙かに高いセキュリティを獲得できるということだ。
Accept (許可) - パケットを受け入れ (accept) て、ファイヤーウォール・ルールを通過させること。 drop, deny, reject ターゲットの反対だ。
Policy (ポリシー) - ファイヤーウォールの構築について語る時、ポリシーは大体 2種類の事柄を指す。まず、チェーンのポリシーがある。これは、どのルールにもマッチしなかったパケットを処すデフォルトのアクションをファイヤーウォールに指示する。当書物ではこちらの使い方が大勢を占める。 2種類目のポリシーは、例えば特定の企業や一個のネットワークのためのドキュメントを書く時に、その根底に置く、いわゆるセキュリティ方針だ。セキュリティ方針は、実際にファイヤーウォールの構築に取りかかる前に構想を練ったり問題点を洗い出したりするのに非常に有用なドキュメントだ。
ファイヤーウォールを計画する上でまず最初のステップは、それをどこに置くかだ。ネットワークはたいてい、はっきりとセグメントに分かれているので、これにはそう迷わない。最初に頭に浮かぶのはローカルネットワークとインターネットを隔てるゲートウェイ。ここはかなりタイトなセキュリティを施してしかるべき場所だ。また、大規模なネットワークでは、部署同士をファイヤーウォールで仕切るのも良い考えだ。例えば、開発部が人事部のネットワークへアクセスできる必要があるだろうか。経理部を他のネットワークからガードしない道理があるだろうか。早い話、解雇通知をよこされて頭に来ている従業員に給与データベースを見せて気が紛れるはずもないだろう。
上で言えることは、つまり、そもそもネットワーク自体を組み上げる時によく考え、区画に分けておかなければならないということだ。中/大規模なネットワーク (ワークステーションが 100 台以上の場合。何を基準に大小を論じるかにもよるが) では特にそうだ。小分けにしたネットワーク同士を、通過させたいトラフィックだけを通すようなファイヤーウォールでつなぎ合わせればいい。
また、インターネットからアクセスを受けるサーバがある場合には、ネットワーク内にデミリタライズゾーン (De-Militarized Zone = 非武装地帯, DMZ ) を構築するのも一案だ。 DMZ は極限までアクセスを制限した何台かのサーバから成る物理的に独立した小さな隔離ネットワークのこと。この方法を用いれば、 DMZ 内のマシンに誰かが入り込める可能性は低くなり、潜り込んで外界からトロイの木馬などをダウンロードされる危険性が抑えられる。非武装地帯と呼ばれる理由は (DMZ 一般を一口に言ってしまえば) そこが内側からも外側からもアクセスできる、一種のグレーゾーンだからだ。
ファイヤーウォールにポリシーや既定動作を持たせるには幾つかの方法がある。このセクションでは、実際にファイヤーウォールの実装を始める前に検討すべき実践的なセオリーについて説明する。実装に際して有益な判断材料となるだろう。
まず基本中の基本として、ファイヤーウォールにはほぼ必ず、既定の動作というものがあることを理解しておかなければならない。例えば、或るチェーンの中でそのパケットに合致するルールがひとつも無かった時には、既定動作で drop または accept される。残念ながら各チェーンの持てるデフォルトポリシーはひとつだけだが、例えばネットワークインターフェイス毎に別々のポリシーを与えるなどして、この問題を克服することもできる。
一般によく使うポリシーには 2 つのパターンがある。指定したもの以外は全部 drop するか、反対に、破棄すると指定したもの以外を全て accept するかだ。ほとんどの場合に我々の興味を引くのは専ら drop 型ポリシーのほうで、許可したいものは全て具体的に指定して受け入れる。 drop 型ポリシーは、安全性が高いという事実がある反面、ファイヤーウォールを正常に機能するところまで持っていくだけでも accept 型よりずっと手間がかかる。
あなたが迫られる最初の決断は、どちらのタイプのファイヤーウォールが適切かだ。セキュリティの重大性はどれくらいだろうか。ファイヤーウォール越しにどんなアプリケーションを利用するのだろうか。一部のアプリケーションは、データストリームに使用するポートがコントロールセッション内でのネゴシエーション (話し合い)で決められるという理由から、ファイヤーウォールにとって嫌な相手だ。ファイヤーウォールにとっては、開くポートを知るのが非常に困難なのだ。 iptables は一般的なアプリケーションならばそのほどんどを扱えるが、一般的でないアプリケーションの中には、残念ながらまだ対応していないものもある。
![]() | 対応が不完全なアプリケーションというのもある。そのひとつが ICQ だ。通常の使い方なら完璧に動く。しかし、チャットとファイル送信機能は、プロトコルを扱うために特別なコードを必要とするため、動作させられない。 ICQ プロトコルは標準化されていない (パテント技術であり、いつ何時変更されるか分からない) ので、大多数の IPフィルタは ICQ プロトコルハンドラを蚊帳の外にするか、パッチとしてだけ提供している。 iptables が選択したのはパッチとして別途提供する方向だ。 |
また、多重セキュリティ戦略を用いるのも良いことだ。実は、その一部については既に言及している。どういうことかというと、できるだけ多くの安全策を張り巡らし、単一の政策に頼り切らないようにするのだ。そうしたコンセプトを採ることによって、軽く 10倍はセキュリティを高めることができる。例としてこれを見てもらおう。

ご覧のように、この例では全てのネットワークコネクションの接点となる位置に 1台の Cisco PIX ファイヤーウォールを据えている。このファイヤーウォールは内部の LAN を NAT し、また必要に応じて DMZ に対しても NAT を行ことになるだろう。また、 http, ftp, ssh の戻りのトラフィックを除いた外向きのトラフィックをすべてブロック。 LAN 及びインターネットからやってくる http トラフィックは通し、 LAN からは ftp, ssh トラフィックも許可するとしよう。これに加えて、 WEB サーバはいずれも Linux で動いていると仮定して、それらサーバ自体にも iptables 及び netfilter を投入し、基本的にファイヤーウォールと同様のポリシーを持たせる。このようにしておけば、万が一何者かが Cisco PIX の陥落に成功したとしても、各マシンのローカルの netfilter ファイヤーウォールが我々を守ってくれるし、その逆もまた大丈夫というわけだ。こうした施策を多重セキュリティと呼ぶ。
そこへ上乗せして、各マシンで Snort を動かすという手もある。 Snort は優れたオープンソースのネットワーク侵入検出システム (NIDS = network intrusion detection system) で、接したパケットの中のシグネチャを検査する。そしてもし何らかのアタックや侵入を示すシグネチャを発見したら、システム管理者へ eメールで知らせたり、さらに一歩踏み込んで、アタックの出所だった IP をブロックするなどの積極的対応を行うこともできる。ただし、積極的対応 は安易に使用してはならないということも言い添えておく。というのも、 Snort にはかなりの誤検出をレポートする悪い癖 (本当はアタックではないのにアタックとして報告するなど) があるからだ。
WEB サーバの手前にプロキシを導入して不正なパケットを捕まえるというのも良い考えだ。あるいはまた、ローカルネットワークからの WEB トラフィック用のプロキシを配置するという手もある。 WEBプロキシを使うと、従業員によって消費されるトラフィックが削減でき、さらに、彼らの WEB 使用量を或る程度制限することさえ可能だ。自社 WEB サーバ用 WEBプロキシのほうは、典型的な侵入の企みを防ぐ効果もある。使ってみる価値のある優秀なプロキシといえば Squid だ。
それ以外に考え得る防衛策のひとつに Tripwire のインストールがある。これは、最終防衛ラインとも言うべき優れたアプリケーションで、ホスト侵入検知システム (Host Intrusion Detection System) と呼ばれるもののひとつだ。 Tripwire は、設定ファイルで指定した全てのファイルに対してチェックサムを作成し、 cron で定期的にチェックすることにより、ファイルが前と変わりないか、改変されていないかを確認する。つまり、何者かがシステムに侵入して不正書き換えを行っていないかを、監視できるわけだ。お勧めするのは、全ての WEB サーバでこれを動かしておくことだ。
そして最後に挙げる重要な留意事項は、もう承知のことかもしれないが、標準化されていない技術や規格を使用しないのが最善であるということだ。 ICQ の話でも分かったように、標準化されていないシステムは使わないようにしないと、ほころびがほころびを呼んで酷いことになる。閉じた環境内でなら多少のことは問題ないが、ブロードバンドサービスやモデムプールを運営する場合には、これは大変重要な問題だ。あなたのところと遣り取りする人は全員あなたのところの規格に従わなければならないわけだが、全員に同じオペレーティングシステムを期待するのは無理な話。ある人は Windows、ある人は Linux、さらには VMS を使いたがる人もいるかもしれない、といった具合だ。セキュリティを特殊なパテント技術に負っていると、某かの問題を抱え込むことになる。
その典型例に、スウェーデンで最近始まった一種のブロードバンドサービスがある。このサービスはマイクロソフトネットワークログオンの上に成り立っている。そう聞いただけでは名案だと思えるかもしれないが、オペレーティングシステムが異なる場合を考えると、これはもう名案どころではない。 Linux ユーザはどうしたらログインできるのか? VAX/VMS だったら? HP/UX は? もちろん Linux であれば利用も不可能ではないが、それもネットワーク管理者がそんなブロードバンドサービス通信をブロックしていなかったとしての話だ。何はともあれ、当文書はどれが一番かを論じる宗教論争本ではないので、非標準化技術の利用が間違いであるという例として触れるだけに留めておく。
このチャプターでは IP フィルタリングの概念と、ネットワークやワークステーションやサーバのセキュリティを固めようとした時にどういった戦略を採り得るかについて述べてきた。取り上げた論点は:
IPフィルタリングの適用法
IPフィルタリングにおけるポリシー
ネットワークの設計プラン
ファイヤーウォールの設計プラン
多重セキュリティテクニック
ネットワークのセグメント化
次のチャプターで我々は、ネットワークアドレス変換 (NAT) とは何かについて簡単に触れ、その後 iptables とその働きを詳しく見て回り、いよいよこの魔物との闘いに着手する。
NAT は今日の Linux 及び iptables で最も興味をそそる機能のひとつだと思う。例えば Cisco PIX などといったサードパーティのソリューションにたいまいをはたく代わりに、多くの中小企業や個人ユーザがソリューションとしてこちらを選択している。その大きな理由のひとつが、廉価で且つセキュアだという点だ。型落ちのコンピュータと比較的新しい Linux ディストリビューション (しかもダウンロードで無料で手に入る) と、余り物のネットワークカード 1 枚か 2 枚とケーブル。それくらいあればいいのだから。
このチャプターでは NAT の基本的理屈について少々語り、利用目的や動作の仕組み、それに、 NAT と取っ組み合う前に検討しておくべき事柄について述べていく。
簡単に言えば、 NAT は何台かのホストでひとつの IPアドレスの共用を可能にするひとつの手段だ。例えば今ここに、5 〜 10 台のクライアントから成るローカルネットワークがあるとしよう。クライアント全てのデフォルトゲートウェイが 1 台の NAT サーバを指すようにする。普通ならばパケットはゲートウェイマシンによって単純にフォワードされるだけだが、 NAT サーバの場合は少し事情が異なる。
NAT サーバは (前にも軽く触れたことだが) パケットの送信元アドレスと宛先アドレスを別のアドレスへと変換する。 NAT サーバは、パケットを受け取ると送信元と宛先のいずれかまたは両方を書き換え、パケットの持つチェックサムを計算しなおす。 NAT を使う時、たいていの場合は SNAT (送信元ネットワークアドレス変換) であることが多い。前記の例で SNAT を使うのは、大概、クライアントにひとつ残らず正真正銘のパブリック IP を与える予算がなかったり、どうしたらそんなことが実施できるか思いつかない場合だ。そうした時、我々はプライベート IP 域からひとつの範囲 (192.168.1.0/24 だとしよう) を選び出して使い、そのネットワークに SNAT を施す。すると SNAT は 192.168.1.0 内の全アドレスを NAT サーバ自身の持つパブリック IP (例えば 217.115.95.34) へと通訳 (translate) する。このやり方をすれば、 5 〜 10 台あるクライアント (その気になればもっともっと) で 1 個の IP アドレスを共用することができるわけだ。
もうひとつ DNAT と呼ばれるものも存在する。これはサーバを立てる時などに大活躍する。一番の恩恵は IP スペースの節約に大いに役立つこと、次に、サーバと本物のサーバ群との間に、ほぼ侵入不可能なファイヤーウォールが簡単に実現できること、さらに、物理的に独立した複数台のサーバでひとつの IP を共用できることだ。次のようなわりと小規模な企業のサーバ集合体を考えてみよう。 WEB サーバと FTP サーバは 1 台で運用している。その他に、出先や自宅にいる社員が社内にいる社員といつでも話をできるよう、チャットサービスを提供する物理的に別個のサーバが 1 台ある。 DNAT を介すれば、これら全てのサーバサービスをたったひとつの IP で外部に提供できるのだ。
上記の例は、セパレートポートNAT (PNAT と呼ぶことも多い) のことだという見方もできる。当文書ではセパレートポートNAT にはあまり言及しない。というのも、 netfilter においては、その機能は DNAT と SNAT の中に取り込まれているからだ。
実は Linux ではふたつの違ったタイプの NAT が使えるようになっている。 Fast-NAT と Netfilter-NAT だ。 Fast-NAT は Linux カーネル内の IP ルーティングコードとして実装されており、 Netfilter-NAT は、 Linux カーネル内ではあるがその netfilter コードの中にある。この文書は IP ルーティングコードに深入りはしないのでこの辺でやめておくが、参考にひとつふたつ話をしておこう。 Fast-NAT がその名で呼ばれるのは、 Netfilter-NAT コードよりも格段に速いからだ。 Fast-NAT はコネクション追跡を行わない。そこが Fast-NAT の利点でもあり欠点でもある。コネクション追跡は多くの CPU パワーを必要とし、それが故に処理速度が遅い。これが Fast-NAT が Netfilter-NAT より速い理由だ。先ほど述べたように Fast-NAT の困ったところはコネクション追跡を行わない点で、ネットワーク単位の SNAT がうまく扱えないし、 Netfilter-NAT の得意とする FTP, IRC をはじめとした複雑なプロトコルの NAT もできない。不可能というわけではないのだが、 Netfilter を使ってやるよりも遙かに手間がかかる。
最後に残った単語がひとつある。基本的には SNAT の同義語なのだが、それがマスカレード (Masquerade) という単語だ。 Netfilter においてはマスカレードは SNAT と極めて近い。唯一の違いは、変換後の送信元アドレスを、自動的に、外と接するネットワークインターフェイスの持つデフォルト IP アドレスにするという点だ。
これまでにも軽く触れたが、NAT を使用する際には幾つか細かい注意点がある。まず第一に、全く機能しないアプリケーションがあることだ。ありがたいことに、こうしたアプリケーションはネットワークの世界でマイナーな存在であり、大きな問題につながることは少ない。
二番目は先の問題よりも小さいが、一部不完全にしか動作しないアプリケーションやプロトコルの問題だ。そうしたプロトコルは、全く動作しないプロトコルよりはメジャーなもので、残念ながら、現在のところ、この点に関して我々にできることは少ない。もしこれからも複雑なプロトコルが登場し続けると、我々はこうした問題に延々と付き合わされることになる。特に、標準化されていないプロトコルが問題だ。
残る三番目の大問題は、やや僕の私見になるが、 NAT サーバによってインターネットと隔てられそこを介してしか外へ出て行けないユーザは、自分のサーバを立てることはできないという事実だ。もちろんやれないことではない。しかし設定には多くの時間と労力が要求される。これも、企業においては、インターネットからアクセス可能なサーバが社員達の手で無統制に山ほど立てられるよりは好ましいことだろう。しかし、話をホームユーザに転じると、これは是が非でも避けなければならない事態だ。あなた方がインターネットサービスプロバイダならば、利用者にプライベート IP を割り当てておいてパブリック IP へ NAT するなどということをしてはいけない。そんなことをすれば、あなたは無用のトラブルを抱え込むこととなる。あのプロトコルがちゃんと動くようにしてくれ、このプロトコルも通るようにしてくれと、ユーザからの要求が次から次へと押し寄せることになるだろう。ユーザが変に静かだとすれば、あなたのワラ人形に密かに釘を打ちつけているのかもしれない。
NAT の注意点として最後に添えておきたいのは、元来 NAT は応急処置的に考え出された技術だという点だ。インターネットのあまりにも急速な普及で IPアドレスがじきに枯渇するということが IANA をはじめとした各種団体によって指摘されたのを受け、考え出されたひとつの打開策なのだ。 NAT は、かつて IPv4 (これまで話の中で使ってきた IP とは、実は IPv4 つまり Internet Protocol version 4 の省略形だった) の欠点を補うための急場しのぎとして考案され、そして今でも使われている。 IPv4 アドレス枯渇の根本的解決は IPv6 であり、 IPv6 が普及すれば他にも様々な問題が解決できる。 IPv4 の IPアドレスが 32 ビットであるのに対して、 IPv6 ではアドレスの定義に 128 個のビットを使用する。アドレス空間は飛躍的に増えることになる。しかし IPアドレスの数が充分だからといって地球上の全てのネットワーク最小単位にいちいち IPアドレスを与えるのは馬鹿げているのではないだろうか。かつて、 IPv4 のアドレス空間だって小さすぎるなどとは誰も思ってもみなかったのだ。
ここでは、 NAT の概念を示すため、ネットワークふたつとインターネットコネクションとの真ん中に NAT サーバを配置するという比較的単純な例を紹介する。目標とするのは、ふたつのネットワークをつなぎ、両ネットワークが互いにアクセスでき、且つ、インターネットへもアクセスできるようにすることだ。また、ハードウェアに関して考慮すべき点や、実際に NAT マシンの構築を始める前に考慮しておかなければならないその他のセオリーについても述べる。
本題に入る前にまず、 NAT 用 Linux マシンを仕立てるのにどういった類のハードウェアが必要かを知っておかなくてはならない。小規模なネットワークのほとんどでは取り立てて問題にはならないだろうが、大規模なものを扱うようになるとこれが問題になってくる。最大の問題は、 NAT がかなり急速にリソースを食いつぶすという点だ。 1 から 10 台くらいのユーザしかいない小規模なプライベートネットワークなら、 RAM 32 MB の 486 マシンで充分以上に動く。しかし、もし 100 ユーザ以上を扱うつもりならば、手元にあるハードウェアで大丈夫か、よく考えてみる必要が出てくる。当然、帯域幅や、どれくらいの数のコネクションが同時に発生するかについても考察しておくのが正しい。とはいえ、大概は余り物のコンピュータでもしっかりと仕事をしてくれるし、それが Linux ベースのファイヤーウォールの大きな利点でもある。余っていた古いドンガラが使え、おかげで、他のファイヤーウォール製品に比べてずっと安価にファイヤーウォールが仕立てられるのだ。
ネットワークカードについてもよく検討する必要がある。幾つのネットワークが NAT/フィルタマシンに接続してくるのだろうか。たいていの場合、インターネットコネクションへコネクトさせるのはネットワークひとつだけで充分だ。イーサネットを使ってインターネットへ接続するなら、必要なのは通常 2 枚のイーサネットカードとその他諸々だ。スケーラビリティを考慮すると比較的有名なブランドの 10/100 mbit/s ネットワークカードを選ぶに越したことはないが、 Linux のカーネルがドライバを持っていれば大体どんなカードでも動くことは動く。この点で少々言っておかなければならないことがある: 頒布されている Linux カーネルがドライバを備えていないようなネットワークカードを使ったり、使おうとして手に入れるのは、避けるべきだ。僕には、ドライバが別途ディスクで添付されている型式やブランドのネットワークカードに行き当たって、惨めな結果に終わった経験が何度かある。そうしたドライバは更新がきちんと行われていないことが多く、仮に今標的にしているカーネルで動作に漕ぎ着けたとしても、将来、カーネルのメジャーアップグレードをした際にきちんと動いてくれる可能性は極めて小さい。そうした事柄を考え合わせると、結局、ちょっとばかり高めのネットワークカードを使えというところに落ち着く。長い目で見れば決して損にはならないはずだ。
参考までに付け加えておく。ファイヤーウォールをかなり旧式のハードウェアで作ろうと思っているとしたら、できる限り PCI バスかそれ以上のインターフェイスの使用を検討したほうがいい。第一に、そのカードは将来マシンをアップグレードする時にもおそらく流用できるだろう。それにもうひとつ、 ISA バスは非常に低速で、 CPU の使用率も高いという点だ。 ISA ネットワークカードによる CPU パワーの大量消費は、マシンをダウンさせる危険と隣り合わせなのだ。
最後の一点として、どれくらいのメモリを NAT/ファイヤーウォールマシンに搭載するかを検討しないわけにはいかない。 32 MB でも動かないこともないが、可能であれば、最低でも 64 MB 以上のメモリを積んだほうがいい。 NAT はメモリに関しては極端に大食いというわけではないが、想定した以上のトラフィックが発生した場合に備えて、できるだけたくさん積んでおくのが正しい選択だ。
ご覧のように、ハードウェア面で検討しておく事項はかなりたくさんある。しかし、本当に正直なところを言ってしまえば、大規模ネットワーク用の NAT マシンを作る場合を除けば、これらはほたいてい全く考える必要がない。ほとんどのホームユーザは考える必要はなく、手近にあるハードウェアの中からどれを使おうと構わない。この問題に関する総合的な比較やテストというものは行われたことがない。ただ、常識的な思慮をちょっと働かせれば、事はずっとうまく運ぶはずだ。
この問題は非常に単純に思われるかもしれない。しかし、大きなネットワークでは、思ってもみなかったほどの難問になることがある。一般論としては、 NAT マシンはちょうどフィルタリングマシンと同じようにネットワークの境界点に設置する。当然の帰結として、 NAT とフィルタリングは同一のマシンである場合が多いわけだ。また、非常に大規模なネットワークを扱うのであれば、ネットワーク全体を幾つかのセグメントに分割し、セグメント毎に個別の NAT/フィルタリングマシンを割り当てるという方策にも一考の余地がある。 NAT は多くの CPU パワーを要求するので、このやり方はラウンドトリップ時間 (RTT、ひとつのパケットが宛先へ到達して返答が返ってくるまでの時間) の増加を抑えるのにも効果的だ。
既に示した、ふたつのネットワークとインターネット接続を持つ例のネットワーク構成では、それらふたつのネットワークの規模を勘案する必要がある。比較的小さいと考えれば、クライアントがどんなことを要求するかにもよるが、まともな NAT マシンにとって 200 や 300 のクライアントは問題とならない。とはいえ、パブリック IP を各々与えた NAT マシンを数台置いて負荷を分け合い、各 NAT マシンは担当のセグメントの面倒だけを見るようにして、トラフィックを最終的に 1 台のルーティング専用マシンへ委譲させるように仕立てる手もある。ただし、これをやるには、充分な数のパブリック IP と、パケットをルーティングできるルーティングマシンのことも考えなくてはならない。
不幸なことに、 NAT にとって、プロキシ、特に透過プロキシとの問題は付きものだ。普通のプロキシならばそれほど問題となることはないが、大規模なネットワークでは特に、透過プロキシは強敵となる。第一点として、プロキシは NAT と並んで大量の PCU パワーを必要とする。大規模なネットワークの場合、 NAT とプロキシを 1 台のマシンで賄うのは無理だ。第二に、送信元と宛先の両方を NAT した場合、プロキシには、いったいどのホストへ接続したがっていたのかが分からない。例えば、そのクライアントがどのサーバへコネクトしたがっていたのかが、判定できないのだ。パケットの本来持っていた宛先情報は NAT 変換を施した際に消え、パケットは元の情報を同時には保持できないため、こうした問題が生じる。ただ、ローカル内でだけなら、パケットの備えるしかるべき 内部データ構造にそうした情報を書き加えることによって、プロキシ (例えば Squid) に情報を伝えることは可能だ。
ご覧のように、透過プロキシの問題に突き当たったら対処策の選択肢はほとんど限られてしまっている。もちろん他にも考えられなくはないが、あまりお勧めできる方法ではない。そのひとつとしては、ファイヤーウォールの外側にプロキシを立て、全ての WEB トラフィックをそこへ導くようなルーティングテーブルを作成して、プロキシマシンそれ自体の中で、 WEB パケットをプロキシで決めてある所定のポートへ NAT するというやり方だ。こうすれば、必要な情報はパケットがプロキシマシンに到着するまで保持され、プロキシマシンもその情報を利用できる。
もうひとつの方策は、ファイヤーウォールの外側にプロキシを立て、 WEB トラフィックはプロキシマシン宛てのもの以外全部遮断するという方法だ。こうすると、全ユーザにプロキシの使用を強制することができる。強引なやり方ではあるが、うまくいかなくも ないだろう。
最後のステップとして、これまで述べてきた情報全てを統合し、どう NAT マシンを仕上げるかについて見ていく。まずネットワーク同士の関係を思い描いてみることにしよう。我々は前述の中で、プロキシを NAT/フィルタリングマシンのすぐ外、且つ、ルータよりも内側に設置することに決めた。この領域は DMZ だと考えることができ、 NAT/フィルタリングマシンはこの DMZ とふたつの内部ネットワークとの間に位置する。下の図で、今話している構想のレイアウトが見て取れるだろう。

NAT 処理されるネットワーク [訳者註: つまりふたつの LAN] から来た通常のトラフィックは全て、 DMZ をくぐってルータへと送られ、ルータがインターネットへと送り出す。ただし、お察しの通り、 WEB トラフィックだけは、 NAT マシン上の netfilter がマークを付け、そのマークに基づいてプロキシマシンへとルーティングする。僕が何を言っているのか整理しよう。今、 NAT マシンが http パケットを見つけたとする。 mangle テーブルを使ってパケットに netfilter の mark (nfmark とも呼ばれる) が付けられる。この nfmark はあとでパケットをルータへとルーティングする時になってもルーティングテーブルと照合できるので、この http パケットを選り分けてプロキシサーバへとルーティングすることができる。それが終わると、プロキシサーバはパケットに対して本来の自分の仕事を果たす。ルーティング関連の部分は、幾分、高度ルーティングの領域に属する事柄だが、この話題には当文書の後半でも幾らか詳細に触れることにしている。
NAT マシンにはインターネット上で通用する本物の IPアドレスを与えている。ルータと、インターネットから見えなくてはならない数台のマシンもそうだ。かたや、 NAT されるネットワークの中のマシンは全てプライベートアドレスだ。これによって、予算もインターネットアドレス空間も節約できる。
このチャプターでは NAT とそれに関連する理論について詳しく説明してきた。中でも、採り得るべき幾つかの視点と、 NAT をプロキシと併用する場合に惹起される一般的な問題については詳しく述べた。以下のような項目だ。
NATの使い方
NATの構成要素
NATの歴史
NAT関係の用語
NATを利用する上でのハードウェアについて
NATの問題点
いずれも、 netfilter/iptables に取り組んでいる間中ずっと役に立っていく話題だ。不幸かな予測のつかなかった或る問題への一時的な解決手段に過ぎないにしても、 NAT は、現在のネットワークで広く使われている。 NAT に関しては、追って Linux の netfilter/iptables の実装について具体的な説明を始める辺りでも、当然、もっと詳しく話をする。
このチャプターは、心の準備をするところであり、Netfilter と iptables が今日の Linux で担う役割を理解していただくところでもある。このチャプターを読み終わったら、いざ実験とファイヤーウォール構築に向けて準備完了、となっていることを切に願う。ここはじっくり忍耐で、あなたのやりたいことはもうすぐそこだ。
iptables のユーザ空間パッケージは http://www.netfilter.org/ でダウンロードできる。 iptables パッケージはカーネル空間の機能も利用するが、それらは make configure の際にカーネルに組み込まれる。必要な手順は追って解説する。
iptables の根幹部分を機能させるには、カーネルの make config あるいはそれに類するコマンドの中で、下記のオプションを設定する必要がある:
CONFIG_PACKET -- このオプションによって、様々なネットワークデバイスと直接やりとりしながら働くようなアプリケーションやユーティリティが、動作可能となる。そうしたユーティリティには tcpdump や snort などがある。
![]() | 厳密に言えば、CONFIG_PACKET は iptables を動作させるのに必須ではないのだが、実に様々な使い方ができるので、ここに含めることにした。入れたくないなら入れなくてもいい。 |
CONFIG_NETFILTER -- コンピュータをインターネットへのファイヤーウォールかゲイトウェイとして使うつもりなら、このオプションが必要。言い換えれば、そもそもこのチュートリアルに出てくる何かひとつでも働かせたいなら、何はなくとも必ず要る。このドキュメントを読んでいるということは、必要なはずだ。
それにもちろん、インターフェースをきちんと動作させるための適切なドライバも加えなくてはいけない。イーサネットアダプタ、 PPP、SLIP インターフェースなどだ。上記は iptables の根幹の一部を加えてくれるだけ。正直言って建設的なことは何もしていないわけで、ただカーネルに骨組みを与えているだけだ。 iptables でより高度なオプションを使うには、適切なカーネル設定オプションを加えなければならない。これから、ベーシックな 2.4.9 カーネルで指定できるオプションの紹介と、その簡単な説明をしていこう:
CONFIG_IP_NF_CONNTRACK -- このモジュールはコネクション追跡 (Connection tracking) を行うのに必要。コネクション追跡はいろいろな用途に利用されるが、特に NAT とマスカレード (Masquerading) で重要となる。 LAN のマシンをファイヤーウォールで守りたいのなら、絶対にマークを付けなくてはならない。例えば rc.firewall.txt を動かすためにも必須条件となる。
CONFIG_IP_NF_FTP -- FTP 接続のコネクション追跡を行いたいならこのモジュールが必要。FTP 接続は、通常の状態では追跡が非常に困難なため、conntrack だけでなく、ヘルパー と呼ばれる追加モジュールが必要となる。このオプションがそのヘルパーのコンパイル指定だ。このモジュールを加えないと、ファイヤーウォールやゲイトウェイを越えて FTP することはできない。
CONFIG_IP_NF_IPTABLES -- 何かしらのフィルタやマスカレード や NAT を行いたいなら、このオプションが必要。これはまさに iptables の識別構造の一切合切をカーネルに与える。 iptables で何かやるなら、これがなくては始まらない。
CONFIG_IP_NF_MATCH_LIMIT -- 厳密には必須でないが、rc.firewall.txt で使っている。このオプションは LIMIT マッチを可能にする。つまり、しかるべきルールを書くことで、1 分あたりにいくつのパケットがマッチするかを制御することができる。例を挙げると、-m limit --limit 3/minute には、1 分あたり 3 個のパケットがマッチする。このモジュールはまた、ある種の DoS 攻撃を避けるのにも利用できる。
CONFIG_IP_NF_MATCH_MAC -- これは MAC アドレスによるパケットマッチを可能にする。イーサネットアダプタ は必ず MAC アドレスを持っている。MAC アドレスは滅多に変わらないため、例えば、どういった MAC アドレスを使用しているかによってパケットを遮断すれば、特定のコンピュータを非常に効果的にブロックすることができる。 rc.firewall.txt をはじめ、どの例でも、このオプションは使っていない。
CONFIG_IP_NF_MATCH_MARK -- MARK マッチを可能にする。 MARK ターゲットを使えば、パケットにマークを付けておき、マークがセットされているかどうか調べることで、マークを評価基準としたマッチが行える。実のところ、このオプションは MARK マッチに関するものだが、MARK ターゲット に関するオプションも後で出てくる。
CONFIG_IP_NF_MATCH_MULTIPORT -- 宛先または送信元ポートを複数指定してパケットをマッチさせることができる。こうしたことは通常は不可能だが、これを使えば可能になる。
CONFIG_IP_NF_MATCH_TOS -- この評価を使えば、パケットの TOS フィールドに基づいてパケットをマッチさせられる。 TOS とは Type Of Service のこと。TOS は mangle テーブルのしかるべきルールや ip/tc のコマンドを使用して書き換える こともできる。
CONFIG_IP_NF_MATCH_TCPMSS -- MSS フィールドに基づいて TCP パケットのマッチを行う機能を追加する。
CONFIG_IP_NF_MATCH_STATE -- これは、ipchains との違いの中で最も大きな話題のひとつだ。このモジュールのおかげで、パケットのステートフルマッチが行える。例えば、ある TCP コネクションで既に双方向のトラフィックを検出している時、このパケットは ESTABLISHED と判定される。 rc.firewall.txt の中でも駆使している。
CONFIG_IP_NF_MATCH_UNCLEAN -- このモジュールは、タイプの規定に従わない IP、TCP、UDP、ICMP パケットや、無効なパケットを選別する機能を追加する。例えば、パケットの正当性に無頓着に、これらのパケットを捨て去ることができる。留意すべきは、このマッチはまだ実験段階にあり、たいていの場合完全には機能しないという点だ。
CONFIG_IP_NF_MATCH_OWNER -- このオプションはソケット のオーナーに基づいたマッチ機能を追加する。例えば、root にだけインターネットアクセスを許すといったことができる。このモジュールは元々、新しい iptables の機能を示す例として書かれたもの。このマッチは実験段階にあり、必ずうまく動くとは限らない点に注意。
CONFIG_IP_NF_FILTER -- このモジュールは基本的な filter テーブルを加え、これによって初めて IP フィルタリングが可能となる。 filter テーブル内には INPUT チェーン、FORWARD チェーン、OUTPUT チェーンがあるということは、後々分かるだろう。送受信パケットについて何にせよフィルタリングを行うつもりなら、このモジュールは必要だ。
CONFIG_IP_NF_TARGET_REJECT -- このターゲットを用いれば、入ってくるパケットに対して、黙殺して床に投げ捨てるのではなく ICMP エラーメッセージを返すことができる。 ICMP や UDP とは異なり、 TCP では TCP RST パケットによって接続のリセットや拒否ができるということも記憶に留めておこう。
CONFIG_IP_NF_TARGET_MIRROR -- パケットをその送り主に跳ね返すことが可能となる。例えば、 INPUT チェーンで宛先ポート HTTP に対して MIRROR ターゲットを設定しておく。ある人がこのポートにアクセスしようとすると、パケットは跳ね返されるので、結果として彼には自分のホームページが見えるはずだ。
![]() | MIRROR ターゲットは、軽々しく使っていい類のターゲットではない。 MIRROR ターゲットは元々テスト向けの用例モジュールとして作られたものであり、得てして、設定を行った側に重大な危険をもたらす (例えば極度の DDoS に陥る等)。 |
CONFIG_IP_NF_NAT -- このモジュールは様々な形でのネットワークアドレス変換 つまり NAT を可能とする。テーブル群のうちの nat テーブルへアクセスできるようになる。ポートフォワードやマスカレードなどを行うには、このオプションが必要だ。LAN のファイヤーウォール構築やマスカレードには必須ではないが、ユニークな [訳者補足:インターネットで有効な] IP アドレスをホスト全部に与えられる場合を除き、これは備えておかなくてはならない。そういうわけで、 rc.firewall.txt スクリプトを動作させるのに必要だし、そもそも、上記のような IP アドレスを自由に追加できないネットワークでは、なくてはならないモジュールだ。
CONFIG_IP_NF_TARGET_MASQUERADE -- このモジュールは MASQUERADE ダーゲットを加える。例えば、どんな IP でインターネットにつながっているか分からない時に、 IP を獲得するための手段として、 DNAT や SNAT の代わりに用いることが多い。言い換えると、DHCP、PPP、SLIP などによって IP をもらっているなら、 SNAT でなくこちらを使う必要がある。マスカレードはコンピュータへの負荷が NAT よりも多少大きいが、 IP アドレスをあらかじめ調べておかなくても機能してくれるのが強みだ。
CONFIG_IP_NF_TARGET_REDIRECT -- このターゲットはアプリケーションプロキシを使う場合などに便利。パケットを直に遣り取りする代わりに、パケットをリマップしてローカルマシンに送ることができる。つまり、この手段によって透過プロキシの構築が可能となる。
CONFIG_IP_NF_TARGET_LOG -- LOG ターゲットを加え、 iptables にそれを利用する能力を与える。このモジュールを使えば、特定のパケットを syslogd に記録させることができ、ひいては、パケットに何が起きているのか調べることができる。セキュリティ監査とスクリプトのデバグにはもってこいの機能。
CONFIG_IP_NF_TARGET_TCPMSS -- このオプションは、 ICMP の Fragmentation Needed パケットをブロックしてしまうインターネットサービスプロバイダと対峙するのに使う。 WEBページが通らない、小さなメールはいいが大きなメールが通らない、ssh はいいが scp がだんまりになる、などといった症状だ。その場合、 TCPMSS ターゲットを使用し、 MSS (Maximum Segment Size) を PMTU (Path Maximum Transmit Unit) へ強制的に書き換えることによって障害を乗り越えることができる。カーネルの設定ヘルプで Netfilter の制作者たちがいうところの 「犯罪的にトロいISPやサーバ」 に、これでもって立ち向かえるのだ。
CONFIG_IP_NF_COMPAT_IPCHAINS -- 旧式 ipchains との互換モードを加える。 Linux 2.2 カーネルから Linux 2.4 カーネルへ移行する際の、根本的永続的な解決策だと思ってはいけない。カーネル 2.6 ではうまく面倒を見てくれるだろうから。
CONFIG_IP_NF_COMPAT_IPFWADM -- 旧式 ipfwadm との互換モード。根本的永続的な解決策だと思ってはいけない。
ご覧の通り、オプションは山のようにある。各モジュールでどんな付加機能が得られるのか、ざっと説明してきたわけだが、これらは、無垢な Linux 2.4.9 カーネルで利用できるオプションのみに過ぎない。もっとたくさんのオプションが知りたければ、 Netfilter のユーザ空間にある patch-o-matic (POM) 機構を覗いてみるといいだろう。そうすれば、カーネルに追加できるオプションはまだまだ出てくる。 POM パッチは、行く行くカーネルに加わる予定だがまだカーネルに盛り込まれていない追加オプションたちだ。入り込めていないのにはいろいろと理由がある。パッチの安定性が充分でない、 Linus Tovalds 氏が把握しきれていない、カーネルのメインストリームに乗せるにはまだ実験的すぎる、などなどの理由だ。
rc.firewall.txt スクリプトを動作させるには、下記のオプションがカーネルにコンパイルされている必要がある。他のスクリプトに必要なオプションが知りたい時は、スクリプト例のセクションを見てほしい。
CONFIG_PACKET
CONFIG_NETFILTER
CONFIG_IP_NF_CONNTRACK
CONFIG_IP_NF_FTP
CONFIG_IP_NF_IRC
CONFIG_IP_NF_IPTABLES
CONFIG_IP_NF_FILTER
CONFIG_IP_NF_NAT
CONFIG_IP_NF_MATCH_STATE
CONFIG_IP_NF_TARGET_LOG
CONFIG_IP_NF_MATCH_LIMIT
CONFIG_IP_NF_TARGET_MASQUERADE
rc.firewall.txt スクリプトには、最低でも上記のものが必要だ。各スクリプトが必要とするものは、そのセクションで随時説明する。さしあたり、今精査すべきメインスクリプトに集中することにしよう。
まず最初に iptables パッケージのコンパイルの仕方を見ていこう。大切なのは、iptables のコンフィギュレーションとコンパイルの大部分が カーネル のコンフィギュレーションとコンパイルに密接に結びついているということを意識しておくことだ。いくつかのディストリビューションでは、 iptables パッケージがプレインストールされている。例えば Red Hat がそうだが、古い Red Hat のデフォルトでは iptables が無効になっている。有効にする方法は、その他のディストリビューションも含めて、このチャプターの後のほうで、つまびらかにする。
まずはともかく、 iptables パッケージを解凍する。ここでは、iptables 1.2.6a パッケージと 2.4 の無垢なカーネルを使用している。通常通り、 bzip2 -cd iptables-1.2.6a.tar.bz2 | tar -xvf - (あるいは tar -xjvf iptables-1.2.6a.tar.bz2 でも、前者とまったく同じ結果が得られが、バージョンの古い tar ではこれは動作しない) で解凍する。パッケージが iptables-1.2.6a という名前のディレクトリに正常に解凍されたはずだ。もっと詳しく知りたければ、iptables-1.2.6a/INSTALL ファイルを読めばいい。コンパイルと、プログラムの動かし方について、かなり役立つ情報が書かれている。
それが終わったら、ここで、どの追加モジュールや追加オプションをカーネルにコンフィギュア/インストールするかが選択できる。ここでは、カーネルへの組み込みが保留されている標準的なパッチのみインストールを検討する。もっと実験的なパッチもまだまだ存在するが、それらは、この後の手順を行ってからでないと手が出せない。
![]() | これらのパッチのうちいくつかは極めて実験的であり、インストールするのは得策でない。それでも、この行程でインストールするものの中には、非常に興味深いマッチやターゲットもある。恐れずに、見るだけでも見てみようではないか。 |
実行するには、 iptables パッケージのルートディレクトリで、こういったことを行う:
make pending-patches KERNEL_DIR=/usr/src/linux/
変数 KERNEL_DIR には、カーネルソースのある場所を指定する。通常 /usr/src/linux/ となるが、異なっているかもしれない。おそらく、カーネルソースの所在はあなた自身が一番よく知っているのではなかろうか。
上記のコマンドは、何らかの形でカーネル内に入り込んでゆく一部のパッチをインストールするよう、お伺いを立てる。 Netfilter の開発陣がカーネルに加えようとしているパッチや追加機能はまだまだあるが、それらに辿り着くのはなかなか骨が折れる。そういったものをインストールするひとつの方法は、下記のようにすることだ:
make most-of-pom KERNEL_DIR=/usr/src/linux/
上記コマンドは、 Netfilter 界で patch-o-matic と呼び称せられるパーツをインストールするよう、お伺いを立てる。ただし、カーネルに不具合を起こす危惧のある突飛なパッチはスキップされる。「お伺いを立てる」 と書いている点に注目してほしい。なぜか。それが、まさにこれらのコマンドがすることだからだ。カーネルソースに何か変更を加える場合、実行の前に確認を求めてくるのだ。 patch-o-matic の全部をインストールするには、以下のコマンド:
make patch-o-matic KERNEL_DIR=/usr/src/linux/
実行する前に、各パッチのヘルプをよく読むこと。或るパッチは、他のパッチを壊してしまうこともあるし、 patch-o-matic やそれ以外のパッチとの組み合わせによってはカーネルを破壊してしまうこともある。
![]() | カーネルにパッチを加えるつもりがなければ、上記の手順はまったく無視してもらっても構わない。必須ではないわけだ。それでも、patch-o-matic の中には、一見に値する本当に興味深いものもあるので、コマンドを実行して、そこに何が含まれているか見てみるのも悪くないだろう。 |
ここまでやれば、インストールの pach-o-matic 部分は完了。カーネルソースに追加した新しいパッチの有効となった、新しいカーネルをコンパイルする環境が整った。さっき追加したパッチは今のコンフィギュアオプションにはまだ記述されていないはずなので、カーネルをもういちど configure するのを忘れずに。ただし、できれば、ユーザ空間の iptables プログラムをコンパイルするまで、カーネルのコンパイルは待っていただきたい。
ユーザ空間のアプリケーション iptables を、ここでコンパイルする。 iptables をコンパイルするには、こういうシンプルなコマンドを発行する:
make KERNEL_DIR=/usr/src/linux/
これでユーザ空間アプリケーションはきちんとコンパイルされたはずだ。うまくいかない場合は、自分で解決するか、Netfilter メーリングリスト に投稿してみよう。 Netfilter メーリングリストなら、トラブルの解決を助けてもらえるチャンスがある。 iptables のインストール時に問題が起こることはあまりないので、うまく動かなかったとしてもパニックに陥らないでほしい。理論的に考察してどこに間違いがあるか探すか、誰か助言してくれる人を見つけよう。
すべてうまくいったら、もうバイナリをインストールする準備が整った。インストールを実行するには下記のコマンドを発行:
make install KERNEL_DIR=/usr/src/linux/
うまくいけば、今やすべてはプログラムとして動いてくれるはずだ。ユーザ空間アプリケーション iptables に加えた変更を利用するには、まだやっていないならここでカーネルとカーネルモジュールを再コンパイルしなくてはいけない。ユーザ空間アプリケーションをソースからインストールするためのより詳しい情報が欲しければ、ソースに含まれる INSTALL ファイルを見てみよう。インストールに関する情報が充実している。
Red Hat 7.1 には、Netfilter と iptables を組み込んだ 2.4 カーネルがプレインストールされている。 iptables を動かせるだけの基本的なユーザ空間プログラムと設定ファイルも含まれている。しかし Red Hat 陣は、下位互換の ipchains モジュールを使用することによって、それら一切を無効化してしまっているのだ。うっとうしい話だ。おかげで、なぜ iptables が動かないのか、という質問がそこらじゅうのメーリングリストで飛び交っている。そこで、 ipchains モジュールを殺して iptables をインストールする方法を、ざっとさらっておくことにする。
![]() | Red Hat 7.1 にデフォルトインストールされているユーザ空間アプリケーションは、悲しくなるほどバージョンが古い。 iptables を本気でいじりたいのなら、新しいバージョンのアプリケーションをコンパイルして、カスタムカーネルの再構築・インストールを行うことをお勧めする。 |
まずはともかく、 ipchains モジュールが以後起動しないよう無効にしておかなくてはならない。それには /etc/rc.d/ ディレクトリ下のいくつかのファイル名を変える必要がある。以下のコマンドで行える:
chkconfig --level 0123456 ipchains off
これによって、/etc/rc.d/init.d/ipchains スクリプトを指す全てのソフトリンクの名前が K92ipchains に変わる。ファイル名の最初の文字は、デフォルトでは S で、そのスクリプトを start せよ、と init スクリプトに伝える。それを K に変えることにより、反対に、サービスを kill せよ、または、起動していない場合でも起動しなくていい、と伝えられる。これで、 ipchains サービスが将来再び起動することはなくなった。
しかし、現在既に作動中のサービスを止めるには、コマンドがもうひとつ必要。作動中のサービスに働きかける service コマンドだ。 ipchains サービスを止めるには下記のコマンドを発行する:
service ipchains stop
これでやっと iptables サービスが開始できる。まず、どの runレベル で起動させるか決めなくてはならない。通常は runレベル 2、3、5 だ。これらの runレベルは以下の目的に使われる:
2. NFS なしのマルチユーザ。ネットワークなしの場合は 3 と同じ。
3. 完全なマルチユーザモード。つまり、通常、ブート後に入るのがこの runレベル。
5. X11。自動的に Xwindow でブートする場合に使われる。
以上の runレベルで iptables を有効にするには、下記のコマンド:
chkconfig --level 235 iptables on
上記コマンドは、runレベル 2、3、5 で iptables サービスを起動させることになる。もし他の runレベルで iptables サービスを起動したいなら、その runレベルを指定して同様のコマンドを発行する。しかし、これ以外の runレベルは使ってはいけないので、そうする必要もない。レベル 1 はシングルユーザモードであり、マシンがおかしくなった場合などに使うレベル。レベル 4 は使われていないはず。[訳者による修正: レベル 0 はコンピュータをシャットダウンする時で、レベル 6 はコンピュータを再起動する際にだけ使われる]。
iptablesサービスを活動状態にするには、下のコマンドを走らせればいい:
service iptables start
iptables スクリプトには、ルールはまだひとつも書かれていない。 Red Hat 7.1 のコンピュータでルールを追加するには、ふたつの方法がある。まず、 /etc/rc.d/init.d/iptables スクリプトを直接編集する方法。これには望まざる副作用があって、 RPM で iptables パッケージをアップデートすると、ルールが全部消されてしまう。もうひとつは、ルールセットをロードしてから iptables-save コマンドで保存し、それを rcスクリプトで自動的にロードさせる方法だ。
それではまず最初に、 iptables の init.d スクリプトにカット & ペーストして iptables を設定する方法を解説する。コンピュータがサービスを起動したときに読み込まれるようルールを追加するには、start) セクションか start() 関数にルールを記述する。 start) セクションにルールを記述する場合には、start) セクション内の start() 関数を無効化しておくことを忘れずに。また、コンピュータがシャットダウンする時、あるいは iptables を必要としない他の runレベルに移行する際に何をすべきかを stop) セクションに書いておくのも忘れてはいけない。さらにまた、 restart) セクションと condrestart) セクションの点検も忘れないように。心しておくべきは、こうした変更は、例えば Red Hat Network でパッケージの自動アップデートが行われたりすると、全部帳消しになってしまうということ。 iptables を RPM でアップデートした場合も同じだ。
2 番目の設定方法は、以下の手順を踏む: まず初めに、あなたの望む内容で、シェルスクリプトにルールセットを書くか、 iptables で直接ルールセットを作る。少しはテストもしなくてはいけない。バグがなく、問題なく動作する設定に行き着いたら、 iptables-save コマンドを使う。 iptables-save > /etc/sysconfig/iptables のように普通に使うも良し。このコマンドは /etc/sysconfig/iptables ファイルにルールセットを保存する。それ以後は、rc.d の iptables スクリプトがこれを自動的に読み取って、ルールセットをリストアしてくれる。もうひとつの方法は、service iptables save。これも /etc/sysconfig/iptables にスクリプトを自動的に保存してくれる。次回コンピュータをリブートした際には、 rc.d の iptables スクリプトが、 iptables-restore コマンドを使用して保存ファイル /etc/sysconfig/iptables からルールセットをリストアする。これらふたつのやりかたをごちゃ混ぜにしてはいけない。かち合って、ファイヤーウォール設定を台無しにしてしまうだろう。
ここまでの行程が終われば、インストールされている ipchains と iptables をアンインストールできる。なぜ iptables をアンインストールするかといえば、プレインストールの古い iptables と、新しいユーザ空間 iptables アプリケーションを混在させたくないからだ。この行程が必要なのは、 iptables をソースパッケージからインストールする場合だけだ。新旧のパッケージがごちゃ混ぜにならないケースも珍しくはない。というのは、 RPM ベースのインストールではファイルが通常とは違う場所にインストールされるため、新たに iptables をインストールしても上書きされないからだ。アンインストールを実行するには下記:
rpm -e iptables
もう使うことのない ipchains をそこらに転がしておく道理はないだろう。古い iptables でやったのと同様にして削除できる:
rpm -e ipchains
既にソースインストールの手順に従って iptables はアップデートしてあるだろうから、これにて作業は完了。古いバイナリ、古いライブラリやインクルードファイルは、きれいさっぱりなくなっているはずだ。
このチャプターでは、iptables および Netfilter の入手方法と、主要なプラットフォームでのインストール方法について述べた。最近の Linux ディストリビューションのほとんどでは iptables が標準でインストールされる。ただし一部のディストリビューションではカスタムカーネルと iptables バイナリのコンパイルを行って最新のものにアップデートしてやらなければならない場合がある。当チャプターはそういったことをする上での一助となったのではなかろうか。
次のチャプターでは、テーブルとチェーンをパケットがどのように巡っていくかや、その際の順番などについて述べる。これは、ルールセットを自分で構築して運用するには是非とも理解しておかなければならない非常に重要な事柄だ。また、それぞれのテーブルについてもかなり掘り下げて解説する。各テーブルにはそれぞれ特有の役割があるからだ。
このチャプターでは、パケットが各種チェーンをどういう順番で通過するかを議論する。また、各種テーブルをどのように通過していくかについても考えていく。これがどれほど役に立つかは、あとで自分のルールを書く時に身に浸みるだろう。ここではまた、カーネルにも依存するその他の構成要素がどこで関わってくるのかにも光を当てる。つまり、各段階でのルーティング判断などといった事柄だ。これは、パケットに応じてルーティングパターンや適用ルールを切り替えるような iptables ルールを書こうとする場合に、必要不可欠な知識となる。つまり、パケットがどういう時にどんな風にルーティングされるかといった話で、これが関わってくる代表的なものが DNAT と SNAT だ。もちろん TOS ビットのことも忘れるわけにはいかない。
パケットがファイヤーウォールに入ると、まずハードウェアに行き当たり、次にカーネルの持つしかるべきデバイスドライバ に渡される。それから、目的の (ローカル上の) アプリケーションに送られるとか、別のホストに送られるなど、某かの沙汰を受けるまでに、パケットはカーネル内の一連の行程を通過していく。
まず、我々のローカルホスト に宛てたパケットについて見てみよう。受信すべきアプリケーションに辿り着くまでに、パケットは以下のような道のりを旅する。
Table 6-1. ローカルホスト (我々のマシン) を宛先とするパケット
| 行程 | テーブル | チェーン | コメント |
|---|---|---|---|
| 1 | 通信ケーブル上。 例) インターネット | ||
| 2 | インターフェースに入る。 例) eth0 | ||
| 3 | raw | PREROUTING | このチェーンは、コネクション追跡の発動する手前でパケットを処理する時に使用する。例えば、特定のコネクションにコネクション追跡が働かないようにするといった使い方がある。 |
| 4 | ステート機構 チャプターで述べるコネクション追跡が機能するのがここ。 | ||
| 5 | mangle | PREROUTING | このチェーンは通常、パケットの改変 (mangle) に使う。例えば TOS の変更などだ。 |
| 6 | nat | PREROUTING | 主に DNAT に使用するチェーン。このチェーンはバイパスされる場合もあるので、ここでのフィルタリング処理は御法度。 |
| 7 | ルーティング判断。例) ローカルホスト宛か、フォワードされるならどこへ | ||
| 8 | mangle | INPUT | この時点で、mangle の INPUT チェーンにぶつかる。ルーティング後、且つ、マシン上の実際のプロセスに送られる前の、パケット改変に使用する。 |
| 9 | filter | INPUT | ローカルホスト宛のパケットに対してフィルタリングを行うのは、すべてここ。ローカルホスト宛の進入パケットは、インターフェースや入ってきた方向に関わらず、必ずこのチェーンを通る。 |
| 10 | ローカルプロセス/アプリケーション。 例) サーバ/クライアントプログラム |
今回は FORWARD チェーンでなく INPUT チェーンを通ってくるという点を憶えておいてていただきたい。極めて論理的だ。おそらく最初は、論理的に見えることといったらテーブルとチェーンを巡ってくることくらいしかないかもしれないが、しばらく考察していけば、じきにはっきりしてくるはずだ。
では次に、我々のローカルホスト から出ていくパケットとその通り道を見てみよう。
Table 6-2. ローカルホスト (我々のマシン) を送信元とするパケッ ト
| 行程 | テーブル | チェーン | コメント |
|---|---|---|---|
| 1 | ローカルプロセス/アプリケーション。 例) サーバ/クライアントプログラム | ||
| 2 | ルーティング判断。どういった送信元アドレスを使用するか、出口はどのインターフェースかなど必要な情報の収集。 | ||
| 3 | raw | OUTPUT | ローカルで発生したパケットにコネクション追跡が働く前に処理を施すならここ。例えば、特定のコネクションに追跡が働かないようマークを付けるといったことができる。 |
| 4 | ローカルで発生したパケットにコネクション追跡が働くところ (ステートの変化など)。それについては ステート機構 チャプターで詳しく述べる。 | ||
| 5 | mangle | OUTPUT | パケットを改変するところ。副作用を避けるため、フィルタリング処理はすべきでない。 |
| 6 | nat | OUTPUT | ファイヤーウォールから出ていくパケットに NAT をかけるのはここ。 |
| 7 | ルーティング判断。ここでルーティング判断が必要なのは、パケットに求められるルーティング方針が、前の mangle と nat によって変更された可能性があるからだ。 | ||
| 8 | filter | OUTPUT | ローカルホストから出ていくパケットをフィルタリングするのがここ。 |
| 9 | mangle | POSTROUTING | mangle テーブルの POSTROUTING チェーンは、パケットがホストを離れる前、且つ、ルーティング判断がなされた後で、パケット改変をしたい場合に使う。ファイヤーウォールを通過中のパケットも、ファイヤーウォールで作られたパケットも、ともにこのチェーンにぶつかる。 |
| 10 | nat | POSTROUTING | 前に説明した SNAT が行われるのはここ。副作用を避けるため、ここではフィルタリングを行うべきでない。デフォルトポリシーを DROP にしても、ある種のパケットはここを擦り抜けてしまう。 |
| 11 | いずれかのインターフェースから出ていく。例) eth0 | ||
| 12 | 通信ケーブル上。 例) インターネット |
この例では、パケットは他ネットワーク上の他ホストに宛てられたものだとする。パケットは今までとは異なった下記のような道のりを巡る。
Table 6-3. フォワードパケット
| 行程 | テーブル | チェーン | コメント |
|---|---|---|---|
| 1 | 通信ケーブル上。 例) インターネット | ||
| 2 | インターフェースに入る。 例) eth0 | ||
| 3 | raw | PREROUTING | ここで、特定のコネクションがコネクション追跡処理されないようにしておくことができる。 |
| 4 | ローカル以外で発生したコネクションにコネクション追跡が作用するのがここ。 ステート機構 チャプターで詳しく述べる。 | ||
| 5 | mangle | PREROUTING | このチェーンは通常、パケットの改変に使われる。例えば TOS の変更などだ。 |
| 6 | nat | PREROUTING | 主として DNAT に使うチェーン。SNAT は後で行う。パイパスされる場合もあるのでフィルタリングは避けるべき。 |
| 7 | ルーティング判断。例) ローカルホスト宛か、フォワードならどこへ | ||
| 8 | mangle | FORWARD | 次にパケットは mangle テーブルの FORWARD チェーンに送り込まれる。極めて特殊な用途でのパケット改変に使用する。ここでの改変は、最初のルーティング判断の後であり、且つ、パケットが送出される直前の最終ルーティング判断の前となる。 |
| 9 | filter | FORWARD | パケットは FORWARD チェーンに送られる。ここを通るのはフォワードされたパケットだけで、そのフィルタリングはここで行う。フォワードされたパケットは (進行方向にかかわらず) 必ずここを通るので、ルールセットを書く際には、ここを検討しよう。 |
| 10 | mangle | POSTROUTING | すべてのルーティング判断を経た後だが、まだパケットがマシン上にあるうちに施す、特殊なタイプのパケット改変処理をしたい場合にこのチェーンを使う。 |
| 11 | nat | POSTROUTING | SNAT を行うならここをおいて他にない。ある種のパケットはカスりもせずに擦り抜けてしまうので、フィルタリングに使ってはならない。ここはマスカレード を行う行程でもある。 |
| 12 | 出口インターフェースから出る。 例) eth1 | ||
| 13 | 再び通信ケーブル上へ。 例) LAN |
ご覧の通り、かなりたくさんの行程を通って行く。パケットを止めるのは iptables のどのチェーンでもできるし、壊れたパケットはさらに他のどこでも止めることができる。しかし、何より注目すべきは、iptables 全体を貫くこの流れだ。インターフェースなどの違いによって使い分けるような特定のチェーンやテーブルなど一切ないという点に注目していいただきたい。このファイヤーウォール/ルータを通ってフォワードされるパケットは、もれなく FORWARD を通るのだ。
![]() | フィルタリングを行うのに、前のシナリオに出てきた INPUT チェーンを使ってはいけない! INPUT は、他ならぬローカルホストを行き先とするパケット専用にできているのだ。 |
シナリオが異なればパケットの通ってゆくチェーンも三者三様であることが分かっただろう。それを上手にマップにまとめると、こんな風になる:

図の意味を明確にするため、こんな場合を考えてみよう。ローカルマシン以外に宛てたパケットが、最初のルーティング判断に入ってきたとすると、そのパケットは FORWARD チェーンを通るよう誘導される。一方、ローカルホストが聞き耳を立てている IP アドレスに宛てられたパケットは、 INPUT チェーンを通してローカルマシンに送られる。
もうひとつ注目すべきは、パケットが他でもないローカルマシンに宛てたものだとしても、 PREROUTING チェーンで NAT することにより、宛先を変更できるという事実だ。この変更は最初のルーティング判断より前に行われるため、パケットはそれ以降、変更された宛先を反映した形で扱われる。この方法により、ルーティング判断の前にパケットの誘導方針が変更できるのだ。パケットは、図中のどれかひとつの経路を必ず通ることにも注目してほしい。もし、パケットがやってきたのと同じネットワークへ DNAT してやれば、最終的にネットワークをおさらばするまで、パケットはチェーンの残り部分をさらに旅し続ける。
![]() | もっと詳しく知りたくなったなら、rc.test-iptables.txt を使うといい。このスクリプトを使えば、テーブルとチェーンをパケットがどのように通っていくか試すための実験に必要なルールが出来上がるはずだ。 |
前述したように、このテーブルは主にパケットの改変に使われる。つまり、このテーブル内では、 TOS (Type Of Service) の変更をはじめ、改変用ターゲットが大手を振って使用できる。
![]() | くれぐれも、mangle テーブルをフィルタリングに使わないように。DNAT、SNAT、マスカレード (MASQUERADE) もこのテーブルでは機能しない。 |
下記のターゲットは mangle テーブルでのみ有効なターゲットだ。 mangle テーブル以外で使用することはできない。
TOS
TTL
MARK
SECMARK
CONNSECMARK
TOS ターゲットは、パケットの Type Of Service フィールドを設定または変更 (または両方) するために使う。この仕組みは、パケットをどういった具合にルーティングするかといったポリシーを、ネットワークに与えるのに利用できる。気を付けなくてはならないのは、 TOS は、インターネット上では扱いが不完全かあるいは全く考慮されておらず、ルータの多くは、 TOS フィールドの値を完全に無視したり、時には見当違いの振る舞いをしたりするということだ。つまり、設定しておいた TOS を (iproute2 を使って) ルーティング決定に利用するつもりなら話は別だが、インターネットへと出ていくパケットに TOS を設定するのは御法度だ。
TTL ターゲットは、パケットの TTL (Time To Live) フィールドを変更するのに使う。 TTL を特定の値にセットする場合などだ。活用法のひとつは、詮索好きなインターネットサービスプロバイダーにこちらの情報をさらけ出したくない場合だ。プロバイダの中には、ひとつの接続に対して複数のコンピュータをつないでいるユーザを許さないところがあり、その中にはさらに、発生する TTL の値が変化するようなホストを嗅ぎ回って、複数のコンピュータを接続している証拠のひとつとして取り沙汰するプロバイダも世に知られている。
MARK ターゲットは、パケットに特別なマークを付けるのに使う。マークの有無や、どんなマークが付いているかを iproute2 プログラムを使って調べることによって、パケットのルーティングを切り替えることができる。このマークは帯域制限や Class Based Queuing にも利用できる。
SECMARK ターゲットは、 SELinux をはじめ、セキュリティコンテキストを扱えるセキュリティ機構で使えるセキュリティコンテキストマークを、特定のパケットに付けることができる。そうしておけば、どのパケットにどのサブシステムがタッチできるかといったきめ細かなセキュリティが掛けられる。 SECMARK をコネクション単位で付けるには CONNSECMARK ターゲットが用意されている。
CONNSECMARK を使用すると、単一のパケットのセキュリティコンテキストをコネクション全体にコピーしたり、逆にコネクションからパケットへコピーしたりできる。そうしたことを行えば、 SELinux などのセキュリティ機構を利用してコネクションレベルのきめ細かなセキュリティ対策が施行できる。
このテーブルはパケットの NAT (Network Address Translation) にしか利用できない。つまり、パケットの送信元フィールドまたは宛先フィールドを変更する目的にだけ使用するテーブルだ。前にも述べたことだが、このテーブルに行き当たるのは、ストリームの最初のパケットだけ。後続のパケットは全て、先頭パケットの執った行動に倣う。こうしたことを行う具体的なターゲットは:
DNAT
SNAT
MASQUERADE
REDIRECT
DNAT ターゲットは主に、あなたがパブリック IP を持っている場合に、ファイヤーウォールへのアクセスをその他の (例えば DMZ 上の) ホストへ振り向ける際に使用する。つまり、パケットの宛先アドレスを変更して、特定のホストへルーティングするわけだ。
SNAT は、主としてパケットの送信元アドレスの変更に用いる。通常、こちらのローカルネットワークや DMZ などは外部にさらけ出さないものだ。格好の例が、外部向け IPアドレスの分かっているファイヤーウォールを置いていて、そのファイヤーフォールの持っている外部 IPアドレスを、ローカルネットワーク内のホストに送信元アドレスとして使わせる必要がある場合だ。このターゲットを利用すれば、ファイヤーウォールがパケットの SNAT と逆SNAT 処理を自動的にやってくれるので、LAN からインターネットへの接続が可能となる。あなたのネットワークが例えば 192.168.0.0/netmask というアドレスだった場合、 IANA の規定ではこのアドレス (他にもあるが) はプライベートアドレスであり、閉じた LAN 内でのみ有効なものとされているため、インターネットからは決して返事をしてもらえないのだ。
MASQUERADE ターゲットは SNAT とまったく同じような使い方をする。ただし、 SNAT と比べると処理にかかる負荷は少々大きい。 SNAT ターゲットがひとつの固定した IPアドレスを使えばいいのに対して、 MASQUERADE ターゲットは、パケットが来る度に、使用すべき IPアドレスを調べているからだ。 MASQUERADE ならば、 ISP が 動的に DHCP IPアドレスを割り当ててくる PPP や PPPoE、 SLIP のインターネット接続下でも、きちんと動作してくれる。
raw テーブルの用途はほぼひとつだけ。コネクション追跡で扱われないよう特定のパケットにマークを付けることだ。これはパケットに NOTRACK ターゲットを適用することによって行える。パケットが NOTRACK ターゲットにヒットすると、conntrack は以後、そのコネクションを追跡しなくなる。こういうことは、新たなテーブルなしには不可能なことだった。というのは、 conntrack がパケットに作用したり conntrack テーブルにコネクションが記録されたり既にトラッキング済みのコネクションに照会されたりする以前にパケットが通るテーブルは、ひとつもなかったからだ。これについての詳しいことは、ステート機構 チャプターに書いてある。
このテーブルには PREROUTING と OUTPUT チェーンしかない。コネクション追跡に触れる前にパケットを処理できるのはこれらだけなので、それ以外のチェーンは必要ないのだ。
![]() | このテーブルが機能するには iptable_raw モジュールがロードされていなければならない。ただし、このモジュールが利用可能な環境であれば、iptables が -t raw キーワードとともにコールされるとこのモジュールは自動的にロードされる。 |
![]() | raw テーブルは比較的最近になって iptables およびカーネルに取り入れられたものだ。 2.4 カーネルや初期の 2.6 カーネルでは、パッチを当てないと機能しないだろう。 |
filter テーブルは、主としてパケットのフィルタリングに使用される。お好み次第でパケットを条件選択し、ふるいに掛けることができる。このテーブルこそが、我々がパケットを操作する場所であり、パケットの中身を調べ、その内容に基づいて DROP したり ACCEPT したりといった処置を施す場所だ。もちろん事前処理的なフィルタリングもやらなくはないが、このテーブルはフィルタリングを行うためにこそしつらえられた場所なのだ。ここでは、ほぼすべてのターゲットが使用できる。 filter テーブルについては後でたっぷりとスペースを割くが、とにかく知っておいていただきたいのは、中核的なフィルタリングを施すのは、ここが舞台だということだ。
パケットが例えば filter テーブルの INPUT チェーンにいる時に、そこから同じテーブル内の別のチェーンへ飛ばす jump ルールを指定することができる。ジャンプ先のチェーンはユーザ定義チェーンでなければならず、 INPUT や FORWARD のようなビルトインチェーンでは駄目だ。ルールをチェーンの中で指し示すポインタだと考えると、このポインタは通常、ルールから次のルールへ、上から下へと、然るべきターゲットに行き当たるかメインチェーン (例えば FORWARD や INPUT など) の終点に行き着くまで移動していく。終点に到達した場合には、当のビルトインチェーンのデフォルトポリシーが適用される。

マッチしたルールがジャンプ定義によって他のユーザ定義チェーンを指していた場合には、ポインタは指定されたチェーンに飛び移り、そのチェーンを上から下へと進行していく。例として、上図のルール 3 からチェーン 2 へのジャンプするところを見ていただきたい。パケットがルール 3 にマッチしたとして、そこで指定されていたジャンプ/ターゲットが、パケットをチェーン 2 へ送ってさらなる審査を行えという指示だったとしよう。
![]() | ユーザ定義チェーンには、チェーンの末尾に達した時に適用するデフォルトポリシーを指定することはできない。それが可能なのはビルトインチェーンだけだ。ただし、そのユーザ定義チェーン内のどのルールにもマッチしなかったパケットを捕まえるルールを末尾に書いておき、それをデフォルトポリシーのように作用させれば、この制限に対処することはできる。ユーザ定義チェーンでいずれのルールもマッチしなかった場合、規定の動作では、元のチェーンへ舞い戻ることになっている。上の図で言えば、ルールの執行はチェーン 2 から、チェーン 1 のルール 4 へ、つまり、チェーン 2 の執行のきっかけとなったルールのすぐ下のルールへと戻ることになる。 |
ユーザ定義チェーン内では、順々にひとつずつルールが審査されていき、それが終わるのは、いずれかのルールにマッチした時か (この場合、ターゲットによっては処理がそこで終了する場合とさらに続く場合がある)、ユーザ定義チェーンの終端に達した時だ。ユーザ定義チェーンの終端に達した場合には、パケットは、このジャンプをトリガーしたチェーンへ戻される。トリガーするチェーンは、別のユーザ定義チェーンでもいいしビルトインチェーンでもいい。
このチャプターでは幾種類ものチェーンやテーブルについて述べ、標準のビルトインチェーンとユーザ定義チェーンも含めてそれらがどのように辿られていくかを説明した。ここは是非とも理解しておきたい内容だ。分かってしまえば簡単かもしれないが、理解が不十分だと、致命的なミスを犯すのもまた簡単だ。
次のチャプターでは、netfilter のステート機構について深く掘り下げ、コネクション追跡機構の中でステートがどのようにして生成され、それがパケットにどう設定されるかを見ていく。このチャプターにも引けを取らない重要なチャプターだといえる。
このチャプターでは、ステート機構 (state machine) を取り上げ、その詳細を説明していく。このチャプターを読み終われば、ステート機構の働き全般について、一通りの理解ができているはずだ。また、ここではステート機構の内部でステートがどのように処理されていくかについても、多数の例を挙げながら精査していく。実際の動きを見ることで、物事がくっきりと見えてくるだろう。
ステート機構は iptables の中でも特別な部分であり、本当はステート機構などという名前でさえない。本当はコネクション追跡 (connection tracking) メカニズムのひとつだ。しかし、一般には前者の名で知られている。このチャプターを通して、僕はこれらふたつの名前を同義語のように使っていくが、おそらくひどい混乱を引き起こすことはないだろう。コネクション追跡は、特定の接続における状態 (state) を Netfilter のフレームワークに伝えるためのものだ。こうした機能を備えたファイヤーウォールは、ステートフル (stateful) ファイヤーウォールと呼ばれる。概して、ステートフルファイヤーウォールでは、よりタイトなルールセットが書けるので、ステートフルでないファイヤーウォールよりも高いセキュリティが得られる。
iptables 内部では、パケットは、追跡済みの接続 (tracked connections) との関連性に応じて 4 つの"ステート "なるものに類別される。 NEW, ESTABLISHED, RELATED, INVALID の 4 つだ。各ステートについては追って詳しく述べるが、新たなセッションの開始を誰に、あるいは何に対して許可するかが、 --state マッチを使えばいとも簡単に制御できる。
コネクション追跡は、カーネル内部の conntrack と呼ばれる特別なフレームワークによって行われる。この conntrack はモジュールとしてロードすることもできるし、カーネル自体に組み込んでおくこともできる。たいていは、デフォルトの conntrack エンジン単独でできるよりも、もっと特殊なコネクション追跡を必要とすることのほうが多い。そのために、 TCP、 UDP、 ICMP プロトコルに対して作用するものなど、より特殊なコンポーネントも用意されている。これらのモジュールは、パケットから特有の情報を読み取り、データストリームを追跡の目から逃さないようにする。 conntrack が収集した情報は、ストリームが現在どのステートにあるのかを conntrack が判断するために用いられる。例えば UDP ストリームであれば、あくまでも一般論だが、宛先IPアドレス, 送信元IPアドレス, 宛先ポート, 送信元ポートによってストリームを特定することができる。
先代のカーネルでは、デフラグメンテーション のオン/オフを切り替えることも可能だった。しかし、 iptables と Netfilter、殊にコネクション追跡が提供されるようになってからは、このオプションは無用となった。コネクション追跡はパケットのデフラグメンテーションなしには正常に機能しないため、デフラグメンテーションは conntrack に組み込まれて自動的に行われるようになったからだ。もはや、デフラグメンテーションをオフにするには、コネクション追跡を切るしかない。コネクション追跡を有効にすれば、デフラグメンテーションも常に行われる。
パケットがローカルで発生したものである場合を除き (この場合は OUTPUT チェーンで行われる)、コネクション追跡はすべて PREROUTING チェーンで行われる。つまり、ステートの再検出などすべての処理は PREROUTING チェーン内で行われるのだ。ローカルからストリームの開始となるパケットを送出した場合、 OUTPUT チェーンにおいて、このストリームは NEW ステートだと判定が下される。このパケットに対して返答が返ってくれば、 PREROUTING チェーンでステートが ESTABLISHED に変わる、といった具合だ。最初のパケットがローカルを始祖とするものでなかった場合には、PREROUTING チェーンで下される判断は、無論 NEW ステートとなる。
それでは、conntrack エントリの様子と /proc/net/ip_conntrack の読み方を簡単に見てみよう。 conntrack エントリには、あなたのマシンの現在の conntrack データベースエントリがリストされている。 ip_conntrack モジュールがロードされていれば、 /proc/net/ip_conntrack を cat すると以下のような感じになるだろう:
tcp 6 117 SYN_SENT src=192.168.1.6 dst=192.168.1.9 sport=32775 \
dport=22 [UNREPLIED] src=192.168.1.9 dst=192.168.1.6 sport=22 \
dport=32775 [ASSURED] use=2
この例は、特定のコネクションのステートを判断するために conntrack モジュールが管理する情報をつぶさに示している。まず最初に、プロトコルが読み取れる。この場合 tcp だ。次に、プロトコルの 10 進表記。その次には、この conntrack エントリがどれだけの時間保持されるかが書いてある。現在の値は 117 秒で、新たなトラフィックを検出するまでの間、この値は一定間隔で減ってゆく。その後、しかるべき時に達すると、コネクションの置かれたステートに応じたデフォルト値へとリセットされる。次のフィールドには、現時点で置かれているステートそのものが書いてある。今観察している上記パケットのケースでは、SYN_SENT と読み取れる。内部で使われるコネクションの値は、我々が iptables で使う表層用の記述とは少々異なっている。 SYN_SENT という値は、ここで見ているコネクションでは、今のところ片方向の TCP SYN パケットだけが検出されていることを物語っている。続いては、送信元IPアドレス、宛先IPアドレス、送信元ポート、宛先ポートが並ぶ。次に来るのが、このコネクションに対する返答はまだ受け取っていないということを示す、特別なキーワードだ。そして最後に、返答パケットに期待される事柄が記載されている。上記で見られる情報は、送信元IPアドレス、宛先IPアドレス (ただし、送り返されるパケットを期待しているわけなので、アドレスは入れ替わっている) を具体的に示している。そして送信元ポートと宛先ポートに関しても同様。多少なりとも我々の興味を惹く、こうした値だ。
コネクション追跡エントリの初期値は、数セットの異なったバリエーションがあり、その値は linux/include/netfilter-ipv4/ip_conntrack*.h ファイルの conntrack ヘッダで定義されている。値は、どの IP サブプロトコルを使用するかによって異なる。 TCP, UDP, ICMP プロトコルの場合には linux/include/netfilter-ipv4/ip_conntrack.h で定義された特定のデフォルト値を採る。それぞれを精査するのは、そのプロトコルを解説する下りまでとっておくことにしよう。 conntrack の内部機構以外でそれらを使うことはほとんどないので、これ以上このチャプターで触れるのは止めておく。コネクションが破棄されるまでの時間の値もまた、ステートの推移によって逐次変化していく。
![]() | 最近、 iptables patch-o-matic に、tcp-window-tracking と呼ばれる新しいパッチが上がった。このパッチが面白いのは、特に、前述のタイムアウト値を特定の sysctl 変数で書き換えることができるという点だ。つまり、システムの稼働中でも、随時書き換えが可能になるのだ。タイムアウト値を変えるために、いちいちカーネルをリコンパイルする必要がなくなる。 これらの値の変更は、 /proc/sys/net/ipv4/netfilter ディレクトリにある特別なシステムコールによって行える。 /proc/sys/net/ipv4/netfilter/ip_ct_* は必見と言っていいだろう。 |
コネクションが双方向のトラフィックを検出すると、conntrack エントリから [UNREPLIED] フラグが消え、リセットされる。上記コードの末尾付近の [ASSURED] フラグが、コネクションはまだ双方向のトラフィックを観察していないということを示していたエントリに取って代わるのだ。 [ASSURED] フラグは、このコネクションが確立済みであることを表し、コネクション追跡の数が制限数に達したとしても消去されない。このようにして[ASSURED] にマークされたコネクションは、未確立の ([ASSURED] とマークされていない) コネクションとは異なり、抹消されることがなくなる。コネクション追跡テーブルが接続をいくつ保持できるかは、或る変数に依存しており、それは現行カーネルでは ip-sysctl によって設定できる。デフォルト値はマシンの実装メモリの量と深く関係しており、RAM が 128MB の場合は 8192 エントリが上限、256MB では 16376 エントリとなる。値の確認と設定は /proc/sys/net/ipv4/ip_conntrack_max で行える。
それと同じことをやるには他にも方法があり、こちらのほうがより効率的。 ip_conntrack モジュールのロードの際に hashsize オプションを与える方法だ。常態では、ip_conntrack_max の値はハッシュサイズの 8 倍となる。つまり、 hashsize を 4096 にすれば ip_conntrack_max は 32768 となる。実際に行うとすればどのようになるかを示そう:
work3:/home/blueflux# modprobe ip_conntrack hashsize=4096
work3:/home/blueflux# cat /proc/sys/net/ipv4/ip_conntrack_max
32768
work3:/home/blueflux#
これまで見てきた通り、カーネルの内部では、パケットはそのプロトコルに応じていくつかの異なったステートを採り得る。しかし、カーネルの外に用意されているのは、前述した 4 つのステートだけだ。ステートは、主に state マッチと組み合わせて、コネクション追跡上での現在のステートを調べることを通じて利用する。利用可能なステートは、NEW, ESTABLISHED, RELATED, INVALID, UNTRACKED だ。下の表に、各ステートの採り得る状態を簡潔にまとめた。
Table 7-1. ユーザ空間でのステート
| State | 説明 |
|---|---|
| NEW | NEW ステートは、それが初めて観測したパケットであることを表す。つまり、或る特定のコネクションの中で conntrack モジュールが初めて検出したパケットがこれに合致する。例えば、SYN パケットが検出されたとして、それがあるコネクションで最初のパケットならマッチする。ただし、必ずしも SYN パケットであるとは限らず、 SYN でなくても NEW と判定される。状況によってはこの点が何らかの問題につながる場合もあるが、よそのファイヤーウォールからの接続を見失ってしまったり、コネクションはタイムアウトしているが接続自体はクローズされていないなどといった局面で、非常に有用な面が多い。 |
| ESTABLISHED | ESTABLISHED ステートの場合、既に双方向のトラフィックが検出されている。それからさらにパケットがやりとりされても、マッチの判定は変わらない。 ESTABLISHED なコネクションは比較的理解が楽だ。 ESTABLISHED ステートに判定されるための必要条件は単純。一方のホストがパケットを送信し、別のホストから返答が来れば、即ち ESTABLISHED だ。 NEW ステートにある接続は、直接あるいはファイヤーウォール越しに返答パケットを受け取った途端、 ESTABLISHED ステートになる。こちらから送ったパケットが送信先にお返しの ICMP メッセージを発生させた場合には、 ICMP 応答も ESTABLISHED と判断される。 |
| RELATED | RELATED ステートは他よりかなりややこしい。あるコネクションが、既に ESTABLISHED な別のコネクションに関係している場合、それが RELATED だ。つまり、接続が RELATED になるためには、前提条件として、既に ESTABLISHED として判定済みの、別の接続が必要なのだ。そこで、 ESTABLISHED コネクションが、自分の主接続の他にもコネクションを発生させる。 conntrack モジュールに RELATED と認められれば、この新たに発生した接続が RELATED だ。 RELATED と判定される幾つかの典型を挙げよう。 FTP-data コネクションは FTP コントロール ポートに RELATED している。 IRC で開かれる DCC コネクションもそうだ。このステートは、ファイヤーウォール越しの ICMP 回答や、 FTP 転送、 DCC を成立させるのに利用される。この仕組みを利用すれば、 ICMP エラーメッセージや、 FTP や DCC によるデータ輸送を、ファイヤーウォール越しでもきちんと機能させることができる。気を付けなければならないのは、こうしたメカニズムに依存するほとんどの TCP プロトコルと一部の UDP プロトコルは、非常に複雑で、 TCP または UDP データセグメントのペイロードを使って接続情報をやりとりしているため、正しく解釈するには特別なヘルパーモジュールを必要とするという点だ。 |
| INVALID | INVALID ステートは、パケットが判定できないか、他のどのステートにも当てはまらない場合だ。こうなる要因は幾つか考えられる。例えば、システムがメモリーを使い果たした場合や、どの既知の接続にも関連しない ICMP エラーメッセージを受け取った場合などだ。おしなべて、このステートに当てはまるものは全部 DROP してしまうのが妥当だ。 |
| UNTRACKED | これは UNTRACKED ステート。簡潔に言えば、 raw テーブルで NOTRACK ターゲットを使ってマークされたパケットが、ステート機構上に UNTRACKED なものとして挙がる。これはつまり、 RELATED なコネクションが捉えられないことにもなるので、UNTRACKED なコネクションの取り扱いには注意が必要だ。例えば、関連した ICMP メッセージなどがステート機構で捕捉できなくなるからだ。 |
ステートを利用するには、 --state マッチを使う。それでコネクション追跡ステートに基づいたパケットマッチが行えるようになる。これが、ステート機構がファイヤーウォールにとって恐ろしく強力で効果的たる所以だ。ローカルネットワークへの返答を受け取るために、以前はよく、1024 から上のポートを全部開け放つ必要に迫られたものだ。だが、ステート機構がそれに取って代わった今、そんなことはもう必要ない。雑多なトラフィックを通すことなく、返答のトラフィックに的を絞ってファイヤーウォールを開けられるのだ。
ここからのセクションでは、基本的プロトコルである TCP, UDP, ICMP それぞれについて、ステートと、それがどのように処理されるかをつぶさに見ていくことにする。さらに、デフォルト つまり、これら 3 つのプロトコルに類別されない場合どうなるのかについても掘り下げる。まずは TCP プロトコルから始めることにした。というのも、 TCP はそれ自体ステートフルなプロトコルであるし、 iptables のステート機構に関わる興味深い特性がたくさんあるからだ。
TCP コネクションは常に 3 ウェイハンドシェークによって開始される。このハンドシェークによって、その先実際のデータを乗せることになるコネクションを確立およびネゴシエート (交渉) する。セッションは、まず SYN パケット、次に SYN/ACK パケットが続き、最後に、セッション確立を承認する ACK パケットが送られて始まる。この時点でコネクションは確立し、データが送れる状態となる。問題は、コネクション追跡がこれにどう追従していくかだ。当然の成り行きといえるだろう。
ユーザの目から見る限り、コネクション追跡はどのコネクションタイプでも基本的には同じように動作する。下図は、コネクションの段階を追う毎にストリームのステートがどう変わってゆくかを端的に示している。ご覧の通り、ユーザの目から見た場合、コネクション追跡が示すコードは、 TCP コネクションの手続きを文字通りに反映したものとはなっていない。まずひとつのパケット (SYN パケット) を検出すると、コネクション追跡はこの接続を NEW と判定する。返答パケット (SYN/ACK) を検出すると ESTABLISHED と判定する。一瞬考えれば、理由は分かるだろう。こうした特別な実装方法を採ることによって、 NEW と ESTABLISHED がネットワークから出ていくのを許可しつつ、戻ってくることができるのは ESTABLISHED コネクションだけに絞ったままで、コネクションは完璧に成り立つのだ。では逆に考えてみよう。もしも、コネクション追跡機構がコネクションの確立過程をひとまとめに NEW と判断してしまったとしたら、返ってくる NEW ステートのパケットもすべて許可しない限り、我々のローカルネットワークへ向けた外からの接続を阻止することは事実上不可能だ。さらに物事をややこしくしているのは、カーネルの内部では、 TCP コネクションに特有のいくつものステートが存在するという点だ。ただし、それらのステートはユーザ空間では利用できない。内部ステートは概ね、 RFC 793 - Transmission Control Protocol の 21-23 ページで規定されたステートに倣っている。これについては、このセクションの後半で考察することにしよう。

見て分かるように、ユーザの視点から見た場合、ことは非常に単純だ。しかし、カーネル側に視点を移して全体の構造を見ると、やや難解さを増す。例を見てみよう。 /proc/net/ip_conntrack テーブルでコネクションのステートが実際にどう変わってゆくかを考察してみる。最初のステートは、コネクションで最初の SYN パケットを受け取った時点だ。
tcp 6 117 SYN_SENT src=192.168.1.5 dst=192.168.1.35 sport=1031 \
dport=23 [UNREPLIED] src=192.168.1.35 dst=192.168.1.5 sport=23 \
dport=1031 use=1
上記エントリから読み取れる通り、 SYN パケットは送信済み (SYN_SENT フラグがセットされている) で、それに対する返答はまだ受け取っていない ([UNREPLIED] フラグが裏付けている) という、文字通りそのままのステートになっている。次のステートは反対方向のパケットを検出した時に訪れる。
tcp 6 57 SYN_RECV src=192.168.1.5 dst=192.168.1.35 sport=1031 \
dport=23 src=192.168.1.35 dst=192.168.1.5 sport=23 dport=1031 \
use=1
今、こちらからの送信に呼応した SYN/ACK を受け取ったところだ。このパケットを受信した瞬間に、今度はステートが SYN_RECV に変わる。SYN_RECV が示すのは、冒頭の SYN の配信が正常に完了し、返答の SYN/ACK パケットもファイヤーウォールを無事に通った、という意味だ。これに加えて、このコネクション追跡エントリは既に双方向のトラフィックを検出済みなので、即ち返答受け取り済みとなる。ただしこれは明示されない。前述の [UNREPLIED] とは違って暗黙的だ。最後のステップは、3 ウェイハンドシェークの締めとなる ACK を検出した時点でやってくる。
tcp 6 431999 ESTABLISHED src=192.168.1.5 dst=192.168.1.35 \
sport=1031 dport=23 src=192.168.1.35 dst=192.168.1.5 \
sport=23 dport=1031 [ASSURED] use=1
最後の例の時点において、我々は 3 ウェイハンドシェークの最後の ACK を受け取り、コネクションは、少なくとも iptables 内部メカニズムの範疇では ESTABLISHED ステートに移行した。通常、ストリームはこの時すでに ASSURED になっている。
コネクションが [ASSURED] にならずに ESTABLISHED ステートに移行することもある。コネクションピックアップ (pickup) をオンにした場合だ (tcp-window-tracking パッチを適用して ip_conntrack_tcp_loose の値を 1 以上に設定する必要あり)。デフォルトの状態つまり tcp-window-tracking を適用していない場合には、前記のような振る舞いをするはずであり調整は不能だ。
TCP コネクションが閉じられる際には、以下のような流れを採り、ステートは以下のようになる。

ご覧の通り、本当にコネクションが閉じるのは、最後の ACK が送られてからだ。ただしこの図は、あくまでも、接続が正常にクローズされる場合に限った図式だ。例えば、接続が拒否された場合には RST (リセット) の送信によって閉じられたりする。その場合、接続は即座にクローズする。
TCP コネクションが閉じると、その接続は TIME_WAIT ステートに移行する。デフォルトの待ち時間は 2 分間だ。これは、何らかの障害に遭遇したパケットが、コネクションが既にクローズした後でもルールセットに入って来られるようにするための仕組みだ。これは一種の時間的緩衝域 (buffer time) として働き、パケットがどこかの中継ルータで渋滞に捕まってしまった場合でも、こちらのファイヤーウォールや相手へ到達できるようになっている。
RST パケットによってコネクションがリセットされた場合には、ステートは CLOSE に移行する。この意味するところは、接続が完全にクローズされるまで猶予 (デフォルトは 10 秒) を与えるというものだ。 RST パケットは、いかなる場合にも承認される (acknowledged) ことはない。ただ一方的にコネクションを切断するだけだ。ステートには、解説してきたもの以外にもいくつかの種類がある。 TCP ストリームが採り得るステートを、タイムアウトの値とともに紹介しておこう。
Table 7-2. 内部ステート
| State | タイムアウトの値 |
|---|---|
| NONE | 30 分 |
| ESTABLISHED | 5 日 |
| SYN_SENT | 2 分 |
| SYN_RECV | 60 秒 |
| FIN_WAIT | 2 分 |
| TIME_WAIT | 2 分 |
| CLOSE | 10 秒 |
| CLOSE_WAIT | 12 時間 |
| LAST_ACK | 30 秒 |
| LISTEN | 2 分 |
これらの値は、まったくもって普遍的とは言い難い。カーネルのリビジョンによっても変わるし、proc ファイルシステムの変数 /proc/sys/net/ipv4/netfilter/ip_ct_tcp_* の操作によって変更することもできる。とはいえ、デフォルトの値はかなり熟考されており、理にかなった値になっている。値は秒単位で記述される。初期のバージョンのパッチでは (バグにより) ジッフィ [:jiffy: 1/100 秒] が用いられていた。
![]() | もうひとつ心に留めておかなくてはならないことがある。ユーザ空間でのステート機構は TCP パケットに記載された TCP フラグ (RST, ACK, SYN といったもの) を見ないという点だ。これは大抵の場合、悪い結果を招く。というのも、NEW ステートのパケットをファイヤーウォールを通過させたい場面があるだろうが、その際、 NEW フラグを指定しておいて SYN パケットのことを指しているつもりになっているケースが多々見受けられるのからだ。 これは現在のステート機構の実装にはできない相談だ。実際には、たとえどんな TCP フラグも立っていないパケットだとしても、あるいは ACK フラグの立ったパケットだとしても、 NEW の判定が下されてしまう。この仕組みは冗長化 (redundant) ファイヤーウォールなどに用いられるが、一般的に言って、ファイヤーウォールがひとつしかないようなホームネットワークにおいては非常にまずい。こうした挙動を手当てするには、付録 "よくある問題と質問" の "NEWステートでありながらSYNビットの立っていないパケット" で説明しているコマンドを使う。また別のやり方としては、patch-o-matic の tcp-window-tracking 拡張機能をインストールした上で /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_loose にゼロを設定する方法がある。こうすれば、ファイヤーウォールは NEW パケットのうち SYN フラグのセットされているもの以外は全てドロップするようになる。 |
UDP コネクションそれ自体は、ステートフルなコネクションではない。どちらかといえばステートレスなコネクションだ。理由としてはいくつかあるが、まず、接続の確立もクローズも備えていないという点。そもそも UDP 接続にはシーケンス [訳者補足: sequence = 順序、手順] がない。ある順序で UDP データグラムがやりとりされる場合でも、どういう順序で送るかといった情報のやりとりは何もないのだ。それにもかかわらず、カーネル内部で接続のステートを判定することは可能だ。では、コネクションがどのように追跡されるか、 conntrack の中身はどんな風に見えるかを見ていこう。

ご覧の通り、 TCP 接続とまったく同じ要領でコネクションが確立する。これはユーザ空間から見た様子だ。内部的に見ると conntrack 情報は少し様子が違う。とはいえ、細部は本質的には変わらない。ではまず、最初の UDP パケットが送られた時のエントリを見てみよう。
udp 17 20 src=192.168.1.2 dst=192.168.1.5 sport=137 dport=1025 \
[UNREPLIED] src=192.168.1.5 dst=192.168.1.2 sport=1025 \
dport=137 use=1
第一と第二の値から、これが UDP パケットであることが分かる。第一はプロトコル名であり、第二はプロトコルナンバーだ。 TCP コネクションと同様である。第三の値は、このステートエントリがどれだけの時間保持されるべきかを示す。そこから先は、検出したパケットの情報と、このコネクションで次に送信主から来るパケットに期待される情報だ。つまり、送信元、送信先、送信元ポート、宛先ポートだ。この時点では、 [UNREPLIED] フラグが記され、このパケットに対する返答はまだ一度も行われていないことを示している。そして最後に、返答パケットに期待される事柄の簡潔なリストがある。後者のグループでは、エントリが前者のものとは入れ違いになっていることも見逃さないでいただきたい。この時点でのタイムアウトは、デフォルトでは 30 秒だ。
udp 17 170 src=192.168.1.2 dst=192.168.1.5 sport=137 \
dport=1025 src=192.168.1.5 dst=192.168.1.2 sport=1025 \
dport=137 [ASSURED] use=1
この時点で、最初の送出パケットへの応答がなされ、コネクションは ESTABLISHED の判定に移行した。見ての通り、その事実はコネクション追跡には明示されない。主な違いは [UNREPLIED] フラグが消え去ったことだ。それに加えて、デフォルトタイムアウトは 180 秒に変わった。ただし、この例では既に 170 秒に減少している。さらに 10 秒後には 160 秒となるだろう。しかし、まだ何かひとつ足りない。そう、前にも述べた [ASSURED] フラグだ。追跡中のコネクションに [ASSURED] フラグが付くには、 NEW 状態のパケットに対して正規な回答パケットが返ってきている必要がある。
udp 17 175 src=192.168.1.5 dst=195.22.79.2 sport=1025 \
dport=53 src=195.22.79.2 dst=192.168.1.5 sport=53 \
dport=1025 [ASSURED] use=1
この時点をもって、コネクションは確立 (assured) した。コネクションの様子は先ほどの例と全く同じだ。もしも、180 秒の間この接続が使用されなければ、コネクションはタイムアウトする。180 秒というとかなり短いように思えるが、ほとんどの場合にはこの数字で充分だ。エントリにマッチするパケットがファイヤーウォールに入ってくる度に、この値はデフォルト値へとリセットされる。他の内部ステートについてもこれと同じことが言える。
ICMP パケットは、ステートフルとは対極に位置するものだ。というのも、 ICMP は制御に使用するだけであり、いかなるコネクションも張らないからだ。さりながら、 ICMP のうち 4つのタイプだけは、回答パケットを発生させ、これらはふたつのステートを採る可能性がある。 ICMP メッセージが採り得るステートは NEW と ESTABLISHED。これに該当する ICMP タイプが、 Echo の request と reply 、 Timestamp の request と reply 、 Information の request と reply 、そして Address mask の request と reply だ。この中で、 Timestamp の request と reply と、 Information の request と reply は廃れて使われなくなっているため、たいていは捨ててしまって構わない。しかし Echo メッセージはホストに対して ping を打つ時など、各種設定の最中に利用することがある。 Address mask 要求は、それほど使う機会は多くないとはいえ、時々役に立つし、通行を許すだけの価値はある。どんな具合に見えるか、下の図を見ていただこう。

上の図から見て取れるように、ホストが echo request を送ると、ファイヤーウォールでは NEW と判定される。相手が echo reply で返答すれば、それはファイヤーウォールで ESTABLISHED と判定される。最初の echo request を検出した時、ip_conntrack には以下のステートエントリが書き込まれる。
icmp 1 25 src=192.168.1.6 dst=192.168.1.10 type=8 code=0 \
id=33029 [UNREPLIED] src=192.168.1.10 dst=192.168.1.6 \
type=0 code=0 id=33029 use=1
見ての通り、 TCP と UDP で見てきた標準的なステートとは、少し様子が違う。プロトコルもある。タイムアウトもある。送信元と宛先のアドレスもある。しかし問題はその先。今までに見覚えのない、 type, code, id という 3 つのフィールドがあるのだ。だが特殊という程のものでもない。 type フィールドには ICMP タイプが、 code フィールドには ICMP コードが書いてあるだけのことだ。これらは付録 ICMPタイプ で見られる。最後の id フィールドは、 ICMP ID が入っている。 ICMP パケットは、送出される際に固有の ID を与えられ、 ICMP メッセージの受け手は新たな ICMP メッセージにこれと同じ ID をセットして送り出す。こうすることよって、送り主はどれが目的の回答か識別でき、的確な ICMP request と結びつけることができるのだ。
次のフィールドには、どこかで見覚えのある [UNREPLIED] フラグがまたまた読み取れる。以前と同じく、今我々が見ているのが、まだ片方向のトラフィックしか検出していないコネクション追跡エントリだと告げている。最後に、 ICMP 回答パケットに期待される内容が、オリジナルの送信元と宛先が入れ替わった形で書かれている。type と code に関しては、回答パケットに求められる値に書き換わっており、 echo request だったら echo reply に換わっているという具合だ。 ICMP ID は元のままだ。
前に説明したように、返答パケットは ESTABLISHED と判定される。だが、ICMP の回答の後には、もう同じコネクションで正規のトラフィックが交わされることはあり得ない。この理由により、回答が一度 Netfilter 構造を通り抜ければ、そのコネクション追跡エントリは破棄される。
上記のような場面が繰り返される度に、要求は NEW、応答は ESTABLISHED という判定が行われる。もう少し踏み込んでみよう。ファイヤーウォールは、 request パケットを検知すると NEW と判定する。そのホストが要求に対する応答パケットを返すと、ファイヤーウォールは ESTABLISHED と判断するわけだ。
![]() | つまり、応答パケットは、コネクション追跡エントリに基づいて ESTABLISHED を判定するような判定基準に必ずマッチすることになる。その点は他のトラフィックタイプと同様だ。 |
ICMP request は 30 秒のデフォルトタイムアウトを備えており、 /proc/sys/net/ipv4/netfilter/ip_ct_icmp_timeout エントリで変更もできる。タイムアウトとして、これは概ね最適な値だ。これだけあれば、まず間違いなく回答が拾えるだろう。
ICMP に関してもうひとつ極めて重要な点は、 ICMP が、特定の UDP や TCP コネクションあるいはその試みに起きた事柄を、ホストに伝える役割を持っているという点だ。自明な帰結として、 ICMP 要求は多くの場合、その元となったコネクションやコネクションの試みに対して RELATED だと判定される。分かりやすい例が Host unreachable や Network unreachable の ICMP メッセージだ。これらのメッセージは、他のホストへの接続を試み、それが失敗した場合に発生し、こちらへ返信される。目的のネットワークやホストがダウンしていると、問題のサイトにアクセスしようとした最後のルータが、その旨を報告するための ICMP メッセージを返してくる仕組みだ。この時の ICMP 応答は RELATED なパケットだと判定される。下図にその様子を示した。

上記の例では、あるアドレスへ向けて SYN パケットを送っている。このパケットはファイヤーウォールで NEW と判定される。だが、パケットがアクセスしようとしているネットワークは到達不能 (unreachable) である。よって、ルータが network unreachable の ICMP パケットを送り返してくる。接続はこの時すでに追跡エントリに追加されているため、コネクション追跡プログラムはこのパケットを RELATED と判定することができる。このようにして ICMP 応答は無事にクライアントまで届き、クライアントは (願わくば) 要求をあきらめる。
UDP 接続においても、上記と同様な障害に突き当たった場合には、前記と同じことが起こる。 UDP コネクションに対して送り返された ICMP メッセージも、すべて RELATED と判定される。下の図を見てほしい。

今回ホストに向かって送り出すのは UDP パケットだ。この UDP コネクションは NEW となる。しかし、経路途中のファイヤーウォールかルータによって、目的のネットワークへの接続は禁止 (prohibit) されていたとする。その結果、我々のファイヤーウォールは Network Prohibited の ICMP メッセージを受け取る。ファイヤーウォールはこの ICMP エラーメッセージが既に開かれた UDP コネクションに関連していることを知っているので、これを RELATED なパケットとしてクライアントに渡す。ファイヤーウォールはこの段階でそのコネクション追跡エントリを破棄し、クライアントは ICMP メッセージを受け取って (願わくば) 接続を取り止める。
時として、プロトコルの扱い方が conntrack 機構に分からないことがある。コネクション追跡機構にとって未知のプロトコルだったり、そのプロトコルの動作仕様が分からない場合だ。こうした際には、コネクション追跡はデフォルト動作に立ち返る。デフォルト動作を採るプロトコルの例が、 NETBLT、 MUX、 EGP だ。デフォルト動作は UDP 接続の追跡に似通ったものとなる。初めのパケットは NEW と判断され、回答以降のトラフィックは ESTABLISHED と判断される。
デフォルト動作が選択された場合、これらのパケットはどれも、初期値として同じタイムアウトの値を採る。この値は /proc/sys/net/ipv4/netfilter/ip_ct_generic_timeout の変数で設定可能だ。デフォルト値は 600 秒つまり 10 分となっている。デフォルトコネクション追跡を使用するリンクにどういったトラフィックを乗せたいかによって、この値は調整する必要がある。特に、衛星を経由してトラフィックを送り返す場合などには、長い時間を要する可能性があるため、値を調整しなければならない。
Linux のコネクション追跡における UNTRACKED はかなり特殊なキーワードだ。ごく平たく言えば、 raw テーブルで追跡を除外するようマークされたパケットを、選別するためのものだ。
まさにこの目的のために作られたのが raw テーブルであり、そこでは、netfilter で追跡してほしくないパケットに対して NOTRACK マークを付けることができる。
![]() | 僕が コネクション ではなく パケット と言っていることにお気づきだろうか。ここで言うマークは、進入してきた各パケットに対して付けられるのだ。もしそうでなかったら、追跡すべきでないパケットかどうかを知るために何らかの追跡が必要、というおかしなことになってしまう。 |
既に当チャプターで述べてきたように、 conntrack およびステート機構はかなりのリソース食いだ。そのため、コネクション追跡とステート機構が働かないようにしたほうがいい時もある。
ひとつの例が、大量のトラフィックを扱うルータで、ルータ自体への入出トラフィックはファイヤウォールしたいが、他へルーティングするトラフィックはファイヤーウォールしたくない場合だ。この時、 raw テーブルで自分宛のパケットは ACCEPT し、それ以外にはすべて NOTRACK マークを付ければいい。そうすれば、ルータ自身へのトラフィックに対してはステートフルマッチが行え、なお且つ、中継するだけのトラフィックに対する CPU パワーがセーブできるのだ。
NOTRACK が活用できる別の例としては、トラフィックの多い Web サーバで、ステートフルな追跡はしたいが Web トラフィック自体の追跡には CPU パワーを消費したくない場合がある。この時には、ローカルな IPアドレスおよび Webサービスを提供している IPアドレスそのもののポート 80 に対しては追跡をオフにすればよい。その結果、既に明らかにオーバーロード状態になっている Web トラフィックに関してはプロセッシングパワーを節約しながら、それ以外のサービスに対しては依然としてステートフルな追跡が行える。
ただし、 NOTRACK には留意しておかなくてはならない問題点もある。コネクションが NOTRACK されるということは、RELATED なコネクションが察知できないということでもあり、conntrack や nat ヘルパーは軒並み機能しなくなるし、関連のある (related) ICMP エラーも拾えなくなるのだ。つまり、そうしたパケットに対しては個別的に門を開いてやらなければならない。特に FTP や SCTP などといった複雑なプロトコルとなると、手動で面倒を見るのは難行だ。だが、このことに注意しておけば、 NOTRACK を操ることは難しくはない。
プロトコルの中にはかなり複雑なものもある。つまり、コネクション追跡の面で、こういったプロトコルは正しく追跡するのが難しい。その典型が ICQ、 IRC、 FTP だ。これらはいずれも、データのペイロード自体で情報をやりとりするので、追跡が正しく機能するためにはコネクション追跡にヘルパー が必要となる。
下表に、Linux カーネルでサポートされている「複雑なプロトコル」のリストと、サポートの始まったカーネルバージョンを示す。
FTP
IRC
TFTP
最初に、 FTP を例として取り上げよう。 FTP プロトコルは、まず最初に FTPコントロール セッションと呼ばれる 1 本のコネクションを張る。このセッション上でこちらからコマンド送ると、他に幾つかのポートが開き、コマンドに応じた後続データをそこに乗せて輸送する。こうした接続を行うにはふたつの方式がある。アクティブ とパッシブ だ。 アクティブコネクションでは、 FTP クライアントが、接続に使用するポートと IPアドレスをサーバに伝える。それが終わると、 FTP クライアントが解放したポートに向かって、サーバが特権ポート以外 (>1024) で接続を張り、そこにデータを乗せて送ってくる。
問題は、こうしたネゴシエーションがプロトコルデータのペイロードそのものの中で行われるため、ファイヤーウォールにとっては前記の追加コネクションを知る術がないという点だ。その結果、ファイヤーウォールには、それら特定のポートを通じてサーバからクライアントへ行われる接続を許可すべきだということが分からないのだ。
問題を解決するには、コネクション追跡モジュールに特別なヘルパー を付加して、コントロールコネクション内のデータから特有の書式と情報をスキャンできるようにする。正しい情報に行き当たったら、モジュールはその特別な情報を RELATED として加える。この RELATED エントリを利用すれば、サーバはコネクションを追跡することが可能となるのだ。 FTP サーバがクライアントへコネクションを張り返した時に、ステートがどのようになるか、下の図をとくとご覧いただきたい。

パッシブFTP は、これとちょうど逆の動作をする。 FTP クライアントが特定のデータをサーバに要求する。するとサーバは、これこれの IPアドレス及びポートで接続せよと回答する。このデータを受け取ると、クライアントは自分の 20 番 (FTP-data) ポートで、サーバから言われたポートへ接続を行い、求めていたデータを実際に受け取る。つまり、もしファイヤーウォールの内側に FTP サーバを置いているのなら、インターネット上のクライアントからこのサーバへ正常に接続させるためには、標準の iptables モジュールに加えて、このモジュールが必要となる。また、ローカルネットワーク上のユーザをきつく縛り、インターネット上の HTTP サーバと FTP サーバへだけ接続を許し、その他のポートは塞いでおきたい場合にも、やはり必要だ。下の図で、パッシブFTP における動作を検証してみよう。

conntrack ヘルパーの幾つかは、カーネルの一部として初めから利用可能になっている。中でも FTP と IRC プロトコル用の conntrack ヘルパーは、このドキュメントを執筆している時点で、もうカーネルに備わっている。見つからない場合は、 iptables ユーザ空間の patch-o-matic ツリーを覗いてみるといいだろう。 patch-o-matic ツリーには、 ntalk や H.323 プロトコル用の conntrack ヘルパーをはじめとして、たくさんのものが見つかるはずだ。必要なヘルパーが patch-o-matic でも見つからない時には、いくつかの選択肢がある。ひとつは、 iptables の CVS ソースツリーに最近仲間入りしていないか調べてみること。もうひとつは、求めるようなモジュールがないか、Netfilter-devel メーリングリストで尋ねてみることだ。まだ存在せず、追加される予定もないと判明したら、残るはただひとつ、自分でそうしたデバイスを書くしかない。その際には、付録 その他の資料とリンク から辿れる Rusty Russell's Unreliable Netfilter Hacking HOW-TO が参考になること請け合いだ。
conntrack ヘルパーは、コンパイル時にカーネルへスタティックに組み込むこともできるし、モジュールとしてコンパイルすることもできる。モジュールとしてコンパイルしてある場合には、下記のコマンドでロードできる。
modprobe ip_conntrack_ftp modprobe ip_conntrack_irc modprobe ip_conntrack_tftp modprobe ip_conntrack_amanda
注意してほしいのは、 NAT とコネクション追跡がまったくの別物だという点だ。よって、コネクションの NAT も同時に行いたいのなら、他にもモジュールが必要となる。例えば、NAT と、 FTP コネクションの追跡がしたいなら、 NAT モジュールも要る。 NAT ヘルパーはすべて、 ip_nat_ で始まる一定の命名法を採っている。例えば、FTP NAT ヘルパーは ip_nat_ftp となるし、 IRC モジュールなら ip_nat_irc だ。 conntrack ヘルパーも同様の命名法に則っているので、 IRC conntrack ヘルパーなら ip_conntrack_irc、 FTP conntrack ヘルパーなら ip_conntrack_ftp で御明算だ。
このチャプターでは、netfilter のステート機構の働きと、それが各種のコネクションをどうやって追跡するかを解説した。また、エンドユーザであるあなた方にとってどんな意義があるか述べ、ステート機構の挙動を変更する方法や、コネクション追跡の面で複雑な処理の必要となる様々なプロトコルと、そこで conntrack ヘルパーがどう関わってくるかについても説明した。
次のチャプターでは、 iptables アプリケーションに付属する iptables-save と iptables-restore を使ってルールセットを保存/リストアする方法について述べる。これらふたつのツールには良い点もあれば悪い点もあるが、そのことについても次章で述べる。
iptables パッケージには、大きなルールセットを扱う時に重宝するふたつのツールが付属している。iptabeles-save と iptables-restore と呼ばれるツールで、ルールセットを、以降のドキュメントで扱うスタンダードなシェルスクリプトとはやや異なったフォーマットで、指定のファイルに保存し、復元 (restore) してくれる。
![]() | iptables-restore をスクリプト言語から使うことも可能だ。その際の大きなポイントは、出力を iptables-restore の STDIN に渡すことだ。これだとルールの全てを一挙に素早く挿入できるため、非常に大きな (数千行の) ルールセットを登録する場合にも効率がいい。例えば make_rules.sh | iptables-restore とやればいい。 |
iptables-save と iptables-restore コマンドを利用する最も大きな要因は、大きなルールセットのロードおよび保存が、これらによってかなり高速化できることだ。 iptables ルールを収めたシェルスクリプトを走らせる場合に一番の問題となるのは、スクリプト中で iptables コマンドを発行する度に、 iptables は、まずカーネル領域からルールセット全体を抽出してから、当該のコマンドが要求するルールの挿入なり、追加なり、変更なりを行うという点だ。そうしてから、自メモリ領域上にできた更新版のルールセットを、カーネルスペースに送り込む。シェルスクリプトを使う場合、ルールの挿入を伴うルールひとつひとつでこうした動作が行われ [訳者註: その度毎にルールセットはより大きくなってゆくので]、抽出と挿入にかかる時間はどんどん長くなっていく。
この難点を解決するために、 iptables-save および restore コマンドがある。 iptables-save コマンドは特別なフォーマットを持ったテキストファイルにルールセットを保存し、 iptables-restore コマンドは、このテキストファイルからルールセットを再びカーネルにロードするためにある。このふたつのコマンドの強みは、ルールセットのロードと保存が、たったひとつのリクエストで片づくという点だ。 iptables-save は、ひと息の動作だけで、カーネルからルールセット全体を掴み取りファイルに保存する。 iptables-restore は、テーブルをひとつの動作単位として、当該のルールセットをカーネルにアップロードする。つまり、極めて巨大なルールセットがここにあるとして、カーネルからの削除を 3,000 回行い、再び同じ数だけカーネルにアップロードする代わりに、丸ごと一挙にファイルへ保存し、いくつのテーブルがあるかにもよるが、 3つやそこらの動作でそれをアップロードできるのだ。
お分かりのことと思うが、 iptables-save と iptables-restore は、もしあなたが膨大なルールを挿入しなければならない環境で作業するのなら、まさにもってこいのツールだ。しかし、短所が存在するのも確か。それについて、以下のセクションで検討していくことにしよう。
既に脳裏にこんな疑問が浮かんだ読者もいるかもしれない。 iptables-restore でスクリプトの類は扱えるのか? 答は、今のところノーだ。おそらくは将来もできないだろう。これが iptables-restore の不備な点であり、設定ファイルから広範な事柄を行うことはできない。例えばあなたが IPアドレスを動的に割り当ててもらっているとしよう。コンピュータが起動する毎にこのダイナミック IP を読み取って、スクリプトの中で利用したいとしたら? iptables-restore では事実上不可能だ。
ひとつの打開策として可能性があるのは、スクリプト中で利用したい値を把握するために小さなスクリプトを用意して、特定のキーワードで iptables-restore ファイルを sed し、小スクリプトによって把握しておいた値に置き換える方法だ。この時点で置き換え結果をテンポラリファイルに保存し、それを iptables-restore でロードする。だがこのやり方にも問題は多い。あなたがリストア用スクリプトに書いておいた置き換え対象のキーワードは、 iptables-save を使用したらすべて削除されてしまう。だから、あなたはもう iptables-save コマンドをろくに使えなくなってしまうのだ。解決策としてはあまりにも不格好だ。
次に考えられるのは、既に述べた例の方法だ。つまり、ルールを iptables-restore のフォーマットで吐き出すようなスクリプトを作成し、その出力を iptables-restore の標準入力に渡すというやり方である。極めて大きなルールセットを扱う場合には、iptables コマンドを直接使うよりもこちらのほうが適切といえるだろう。iptables コマンドには、当チャプターの冒頭でも述べたように、非常に大きなルールセットを登録する時に多くの CPU パワーを消費する悪い癖があるからだ。
他に、先に iptables-restore スクリプトをロードしておいてから、動的ルールを必要箇所に追加で挿入するシェルスクリプトを走らせるという方法も考えられる。だが、こちらもたいして格好が良くなったとはいえない。 iptables-restore は、 IPアドレスが動的割り当ての場合や、設定オプションに応じて振る舞いを変えたいようなケースには、元来、不向きにできているのだ。
iptables-restore および iptables-save のもうひとつの欠点は、このドキュメントの執筆時点でだが、完璧には機能しないケースがあるということだ。これらのツールをまだあまりたくさんの人たちが使っていないために、それだけバグを発見する機会も少ない。そのせいで、一部のマッチやターゲットが正常に挿入されず、予想外の奇妙な挙動を引き起こす場合がある。しかし、こうした問題があってもなお、僕は iptables-restore と iptables-save の利用を強くお勧めする。ツールに扱い方が分からないような極端なターゲットやマッチでも使っていない限り、ほとんどのルールセットでは絶大な効果を発揮してくれるはずだ。
これまで説明した通り、 iptables-save コマンドは、現在のルールセットを iptables-restore で利用できるファイルへ保存するツールだ。このコマンドは極めてシンプルで、たったふたつの引数しか採らない。コマンド書式を理解するために、以下のコマンドを見ていただこう。
iptables-save [-c] [-t table]
-c 引数は、バイトおよびパケットのカウンタ値を保持するよう iptables-save に指示する。これは例えば、メインファイヤーウォールを再起動したいものの、統計に役立つバイトおよびパケットカウンタは喪失したくない場合に使用する。そうした際に -c 引数付きの iptables-save コマンドを使用すれば、統計および集計のルーティーンをリセットすることなくファイヤーウォールをリブートできる。デフォルトの挙動では、当然、カウンタは無傷では済まない。
-t 引数は、保存対象とするテーブルを iptables-save に指示する。-t を指定しない場合、 iptables-save コマンドは自動的に、有効なすべてのテーブルをファイルに保存する。下記の例は、いかなるルールセットもロードしていない時に iptables-save コマンドが出力するはずの内容を示している。
# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:17 2002 *filter :INPUT ACCEPT [404:19766] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [530:43376] COMMIT # Completed on Wed Apr 24 10:19:17 2002 # Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:17 2002 *mangle :PREROUTING ACCEPT [451:22060] :INPUT ACCEPT [451:22060] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [594:47151] :POSTROUTING ACCEPT [594:47151] COMMIT # Completed on Wed Apr 24 10:19:17 2002 # Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:17 2002 *nat :PREROUTING ACCEPT [0:0] :POSTROUTING ACCEPT [3:450] :OUTPUT ACCEPT [3:450] COMMIT # Completed on Wed Apr 24 10:19:17 2002
コメントも何行か書かれており、それらは # で始まっている。テーブルは *<table-name> のような書き方になっており、例えば *mangle がそれだ。そして各テーブル毎に、チェーンの定義とルールが記されている。チェーンの定義は :<チェーン名> <チェーンポリシー> [<パケットカウンタ>:<バイトカウンタ>] のような形式だ。チェーン名は例えば PREROUTING などで、それにポリシーが ACCEPT のように続く。最後にパケットカウンタとバイトカウンタが来るが、これは iptables -L -v が出力するものと同じだ。最後に COMMIT キーワードでテーブルの宣言が締めくくられる。 COMMIT キーワードは、取り敢えずこの時点まででカーネルへのパイプライン上にあるすべてのルールを登録実行せよ、という指示をする。
上記は例はちょっと単純すぎる。今度は、ごく小さな iptables-save ルールセット を含む簡単な例を示すのが何よりもの説明になると思う。その状態で iptables-save を掛けたとすると、下記のような出力が得られるはずだ:
# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002 *filter :INPUT DROP [1:229] :FORWARD DROP [0:0] :OUTPUT DROP [0:0] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A FORWARD -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT -A FORWARD -i eth1 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT -A OUTPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT COMMIT # Completed on Wed Apr 24 10:19:55 2002 # Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002 *mangle :PREROUTING ACCEPT [658:32445] :INPUT ACCEPT [658:32445] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [891:68234] :POSTROUTING ACCEPT [891:68234] COMMIT # Completed on Wed Apr 24 10:19:55 2002 # Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002 *nat :PREROUTING ACCEPT [1:229] :POSTROUTING ACCEPT [3:450] :OUTPUT ACCEPT [3:450] -A POSTROUTING -o eth0 -j SNAT --to-source 195.233.192.1 COMMIT # Completed on Wed Apr 24 10:19:55 2002
見ての通り、ここでは -c 引数を用いたので、各コマンドの先頭にはバイトおよびパケットカウンタが付いている。その点を除いては、各コマンドラインはほぼスクリプトの生き写しとなっている。今や課題は、出力をいかにしてファイルに書き出すかだけだ。答は簡単、 Linux をちょっと触ったことのある読者なら、やり方はもう分かっているはずだ。渡してやりたいファイルへコマンド出力をリダイレクトするだけである。こんな要領だ:
iptables-save -c > /etc/iptables-save
つまり、このコマンドは、バイトおよびパケットカウンタを保持したまま、ルールセットを丸ごと /etc/iptables-save という名前のファイルに保存する。
iptables-restore コマンドは、 iptables-save コマンドで保存した iptables ルールセットを、復元 (restore) するのに使用する。入力は標準入力から読み取られ、このドキュメントを執筆している時点では、残念ながらファイルからロードすることはできない。 iptables-restore のコマンド書式はこうだ:
iptables-restore [-c] [-n]
-c 引数はバイトおよびパケットカウンタをリストアする。 iptables-save で保存しておいたカウンタ値もリストアしたいなら、必ず指定しなくてはならない。長いオプションで --counters と書くこともできる。
-n 引数は、これから書き込もうとする特定のテーブルあるいはすべてのテーブルの内容を上書きしないよう iptables-restore に伝える。デフォルト動作では、iptables-restore は既存のルールをフラッシングし抹消する。短いオプションでは -n だが、長いオプションで --noflush と書くこともできる。
iptables-restore コマンドでルールセットをロードするには幾つかの方法があるが、代表として、最もシンプルで一般的なやり方を紹介しておこう。
cat /etc/iptables-save | iptables-restore -c
下記のコマンドでもできる:
iptables-restore -c < /etc/iptables-save
このコマンドは、 /etc/iptables-save ファイルの保持するルールセットを cat して iptables-restore にパイプ渡しし、 iptables-restore がそれを標準入力を通じてバイトとパケットのカウンタとともに受け取ってリストアする。こんな具合で、そもそもが込み入ったことではない。ただし、やり方は気が遠くなるほどのバリエーションがあり、違った手法はいくらでも挙げられる。しかし、それはこのチャプターの守備範囲から外れるので省略し、各読者諸氏の実地検証に委ねることにしよう。
これにて、ルールセットはカーネルに正しくロードされ動作しているはずだ。もしだめなら、ことによるとコマンドのバグにぶつかってしまったのかもしれない。
このチャプターでは iptables-save プログラムと iptables-restore についてかなり詳しく述べ、その使い方を解説した。これらふたつは iptables パッケージに付属しているプログラムで、長大なルールセットを素早く保存したりそれをまたカーネルに投入したりできる。
次のチャプターでは iptables のルールの書式と、その規則に則ったルールセットを正しく記述する方法について述べる。また、必要に応じて、見本にしてもらえる基本的なコーディングスタイルも紹介していく。
当チャプターと後続の 3 つのチャプターでは、自分のルールを構築する際のやり方を深く掘り下げる。ルールとは、特定のチェーンにおけるコネクションとパケットのブロックや許可に際して、ファイヤーウォールが従うべき指示だと言うことができる。チェーン内の一行一行が、ルールとして解釈される。ここではまた、基本的なマッチとその使い方、ターゲットのいろいろ、独自ターゲット (つまりサブチェーン) の作り方についても検討していく。
このチャプターは、ルール作成のいろはを説明する。ユーザ空間の iptables プログラムに受け入れてもらうにはルールをどう書き、どのようにして組み込めばいいのかや、テーブルの種類、それに、 iptables に与えるコマンドについて取り上げていく。それが終わったら、次のチャプターで、 iptables で利用可能なあらゆるマッチに目を通し、各種ターゲットの詳細に踏み込んでいくことにする。
説明してきたように、ルールとは、カーネルがパケットの処置について指示を仰ぐ、行単位の記述だ。そのパケットに完全に一致する評価基準 (つまりマッチ) が見つかると、ターゲット (ジャンプ) に書かれた指示が実行される。我々が記述するルールは、通常、下記のような書式を採る:
iptables [-t テーブル] コマンド [マッチ] [ターゲット/ジャンプ]
ターゲット指示は行末に置かなければならないという根拠はどこにもない。しかし、読みやすさの面から、この文法に付き従うことが多い。ともかく、あなたが目にするほとんどのルールはこの書式を採っているだろう。そのおかげで、誰の書いたスクリプトを読んでも、文法に惑わされることなくルールがすぐに理解できる。
標準以外のテーブルを使いたい場合には [テーブル] のところでテーブルを指定してもいい。しかし、どのコマンドでも iptables はデフォルトで filter テーブルを対象にするので、敢えて書く必要はない。また、指定する位置もここしか駄目というわけではない。行の中のどこに書いても構わないのだが、一番頭に書くのが、いわば常識となっている。
ただし、留意すべき点はある。コマンドは必ず先頭またはテーブル指定の直後になくてはならない。コマンドとは、 iptables に何をさせるかということ。例えば、ルールを挿入する、チェーンの末尾にルールを加える、ルールを削除する、といった事柄だ。これについては後で詳しく述べよう。
マッチとは、ルールの一部であり、それによって、目的とするパケットを他のパケットから見分けるための、パケットの素性をカーネルに教える。つまり、これで指定するのは、パケットがどんな IPアドレスから来るか、どのネットワークインターフェースから入ってくるか、宛先はどんな IPアドレスやポートやプロトコルか、など。利用できるマッチは、このチャプターの後のほうで解説するが、膨大な数に上る。
行の最後にはパケットのターゲットが来る。パケットがすべての条件に合致したら、カーネルはこのターゲットを執行する。一例を挙げれば、カーネルに対して、我々が独自に作成したチェーンへの送致を指令することであり、その場合、独自チェーンは元のチェーンの一部と見なされる。あるいはまた、カーネルに、パケットを黙殺してそれ以上頓着しないようにさせたり、特定の返答を送り主へ返すなどの指示を与えることもできる。この続きは、このチャプターで追い追い詳述してゆくことにしよう。
-t オプションは操作の対象テーブルを指定する。デフォルトでは filter テーブルが使用される。 -t オプションで指定できるテーブルは下記。これはチャプター テーブルとチェーンの道のり の内容を手短に要約したものだ。
Table 9-1. テーブル
| テーブル | 説明 |
|---|---|
| nat | nat テーブルは主としてネットワークアドレス変換 (Network Address Translation) に使用する。"NAT" されたパケットは、ルールに従って IPアドレスが変更される。どのパケットも、ストリーム中で一度だけこのテーブルを通る。つまり、そのストリームの先頭パケットだけがこのテーブルの通行を許されるのだということを心しておかなければならない。ストリームの残りのパケットは、自動的に "NAT" あるいはマスカレード (Masquerade) され、先頭パケットの行動に追従する。言い換えれば、後続パケットは二度とこのテーブルは通らないし、先頭パケットと同じ処方さえ受けないのだ。後でくどいほど説明するが、このテーブルでは一切のフィルタリングを行うべきでないという理由がここにある。ファイヤーウォールに入ってすぐにパケットを変換するには PREROUTING チェーンを使用する。ローカル (つまりファイヤーウォール) で発生したパケットをルーティング判断前に変換するには、OUTPUT チェーンを使用する。そして、パケットがファイヤーウォールを離れる直前に変換を行うためには POSTROUTING チェーンが用意されている。 |
| mangle | このテーブルは主にパケットの改変 (mangle) に利用する。主な利用法は、様々なパケットの内容やヘッダを書き換えること。例えば TTL、TOS、MARK の変更だ。 MARK は厳密にはパケットの変更ではないが、カーネル領域でパケットへのマークが設定される。このマークは、ファイヤーウォール上での以降のフィルタリングにおいて、他のルールやプログラムから利用できるばかりでなく、例えば tc などの高度ルーティングにも利用可能だ。 mangle テーブルは PREROUTING、 POSTROUTING、 OUTPUT、 INPUT、 FORWARD という 5 つの組み込み済みチェーンを持つ。 PREROUTING は、パケットを、ファイヤーウォールへの進入直後、且つルーティング判断前の時点で改変するのに用いる。 POSTROUTING は、すべてのルーティング判断が行われた後に改変を行う。ローカル (つまりファイヤーウォール) で発生したパケットをルーティング判断の後で変換するには OUTPUT チェーンを使用する。 INPUT は、パケットがローカルコンピュータ自体へ誘導された後で、且つデータがユーザ空間アプリケーションに相まみえる前の改変に使う。 FORWARD は、ルーティングの第一判断の後、なお且つ最終ルーティング判断の前にパケットを改変するのに使用する。 mangle は、いかなるネットワークアドレス変換にもマスカレードにも利用できないという点に注意。そうした処理を意図して作られているのは nat テーブルだ。 |
| filter | filter テーブルこそ、まさにパケットフィルタリングを精力的に行う場所だ。例えば、他のテーブルでも可能な DROP、 LOG、 ACCEPT、 REJECT のいずれの処理も、ここでは何の問題なく行える。 filter テーブルは 3 つのチェーンを内蔵している。最初のチェーンは FORWARD で、発生源がローカルでなく、宛先もローカルホスト (つまりファイヤーウォール) 宛でないパケットに対して使用する。 INPUT はローカルホスト (ファイヤーウォール) を宛先とするすべてのパケットに、そして OUTPUT はローカルで発生したパケットすべてに使用する。 |
| raw | raw テーブルとその中のチェーンは、 netfilter にあるどのテーブルよりも先に適用される。これは NOTRACK ターゲットを利用するために考案されたテーブルだ。登場してまだ日が浅く、2.6 後期カーネル以降で、なお且つ、それを使うようにコンパイルされていなければ使用できない。raw テーブルは 2つのチェーンを内包している。PREROUTING と OUTPUT チェーンがそれで、この 2つのチェーンはパケットがどの netfilter サブシステムに触れるよりも前にパケットを処理する。 PREROUTING チェーンは当マシンへ入ってきたパケットとフォワードされたパケットに対して使用することができ、もう一方の OUTPUT チェーンは、ローカルで発生したパケットに他のどの netfilter サブシステムよりも早い段階で変更を加えたい時に使うことができる。 |
上の説明で、利用可能な 4 つのテーブルの概念が分かったことと思う。テーブルはそれぞれ、まったく別々の目的に利用するもので、各々の内蔵チェーンの使い分けを知っておかなければならない。使い方を誤れば、あなたはファイヤーウォールに墓穴を穿ち、ほどなく誰かがそれを発見して何かを潜り込ませ、あなたは自ら掘った穴に落ち込むことになるだろう。前提となるテーブルとチェーンについては、前のチャプター テーブルとチェーンの道のりで説明した。もし理解が完全でないなら、ぜひ、そのチャプターに戻りもう一度通読することをお勧めする。
このセクションでは、いろいろなコマンドとその使用目的を取り扱う。コマンドは、パーサに読み込まれたルールの後続部分をどう処すべきかを、 iptables に伝える。通常、テーブルなどに対して何かを追加あるいは削除する操作を指令する。 iptables では以下のコマンドが利用可能だ:
Table 9-2. コマンド
| コマンド | -A, --append |
| 例 | iptables -A INPUT ... |
| 説明 | チェーンの最後尾にルールを追加する。つまり、このコマンドでは必ずルールセットの最後尾にそのルールが追加され、さらにルールを追加しない限り、テストの順位も最後となる。 |
| コマンド | -D, --delete |
| 例 | iptables -D INPUT --dport 80 -j DROP, iptables -D INPUT 1 |
| 説明 | ひとつのルールをチェーンから削除する。これには書き方が 2種類ある。合致すべきルールの全文を示す (最初の例) か、ルールナンバーで指定するかだ。前者を使用する場合、記述する定義と、チェーンにおけるエントリは、正確に一致していなくてはならない。後者では、削除したいルールのナンバーを一致させればよい。ルールのナンバーはチェーン毎に頭から振られ、1 から始まる。 |
| コマンド | -R, --replace |
| 例 | iptables -R INPUT 1 -s 192.168.0.1 -j DROP |
| 説明 | 指定した行にある既存のルールを置き換える。 --delete コマンドと同じ動作を行うが、ただ単にそのエントリを削除するだけでなく、それを新たなエントリで置き換える。 iptables の組み立てを思考錯誤している最中に利用する機会が多いだろう。 |
| コマンド | -I, --insert |
| 例 | iptables -I INPUT 1 --dport 80 -j ACCEPT |
| 説明 | チェーンの中途にルールを挿入する。ルールはナンバーで指定した箇所に割り込む。つまり、上記の例は INPUT チェーンの 1 番目にルールを挿入し、結果としてそのルールがチェーンの最初のルールとなる。 |
| コマンド | -L, --list |
| 例 | iptables -L INPUT |
| 説明 | このコマンドは指定したチェーンのすべてのエントリをリストアップする。上記の場合 INPUT チェーンのエントリすべてを列挙する。チェーンをまったく指定しないやり方も反則ではなく、この場合には、所定のテーブル内にあるすべてのチェーンの内容をリストアップする (テーブルの指定方法は テーブル セクションを参照)。出力の詳細はパーサに与える -n や -v などといったオプションによって変わる。 |
| コマンド | -F, --flush |
| 例 | iptables -F INPUT |
| 説明 | このコマンドは指定したチェーンからすべてのルールをフラッシングする。ルールをひとつずつ削除していくのと意味は同じだが、こちらのほうがてきめんに迅速だ。このコマンドにオプションは必要なく、所定のテーブル内に存在するチェーンの全ルールがを削除される。 |
| コマンド | -Z, --zero |
| 例 | iptables -Z INPUT |
| 説明 | このコマンドは指定したチェーンまたは全チェーンにおける、全カウンタをゼロに戻すよう iptables に命令する。 -L コマンドで -v オプションを使うと各フィールドの頭にパケットカウンタがつくのを憶えているだろう。このパケットカウンタをゼロリセットしたいときに使うのが -Z だ。 -Z はルールのリストアップこそしないが、 -L と同様の動作を行う。 -L と -Z を同時に用いた場合 (これも反則ではない)、まずチェーンがリストアップされ、その後パケットカウンタがゼロリセットされる。 |
| コマンド | -N, --new-chain |
| 例 | iptables -N allowed |
| 説明 | このコマンドは、指定したテーブルに指定した名称の新しいチェーンを作成するよう、カーネルに指令する。上の例では allowed という名のチェーンを作成している。既に同名のチェーンやターゲットが存在してはならないという点に注意しなければならない。 |
| コマンド | -X, --delete-chain |
| 例 | iptables -X allowed |
| 説明 | 指定したチェーンをテーブルから削除する。このコマンドが成功するには、削除対象になるチェーンを参照しているルールが存在してはならない。つまり、チェーンを削除するには、前もって、そのチェーンを参照しているルールすべてを置換または削除しておく必要がある。このコマンドをオプションなしで使用した時には、対象のテーブルにあるビルトイン以外のチェーンがすべて削除される。 |
| コマンド | -P, --policy |
| 例 | iptables -P INPUT DROP |
| 説明 | このコマンドは、或るチェーンに対してのデフォルトターゲットつまりポリシーを設定するよう、カーネルに指示する。どのルールにもマッチしなかったパケットは、チェーンに設定されたポリシーに従わせられる。指定可能なターゲットは DROP と ACCEPT だ (他にもあるかもしれない -- 見つけたらメールしてほしい)。 |
| コマンド | -E, --rename-chain |
| 例 | iptables -E allowed disallowed |
| 説明 | -E コマンドは、第1引数の名前を持つチェーンを、第2引数の名前に改名する。つまり上の例では、チェーンの名前を allowed から disallowed に変えていることになる。これは当該テーブルの振舞いを実質的に変ているわけではない。つまり、テーブルに対する見た目上の変更に過ぎないということに気を付けてほしい。 |
コマンドラインは必ず、最後まで 1 行で入力しなければならない。でなければ、 iptables のビルトインヘルプかコマンドのバージョンを見せられることになるだろう。バージョンを表示させたいのなら -v オプションがあるし、ヘルプメッセージを出すには -h オプションがある。つまり、とりわけ iptables に限ったオプションではない。次に、コマンド毎に異なるいくつかのオプションを挙げておこう。下記では、どのオプションがどのコマンドで有効で、どう作用するかを解説している。なお、ここで挙げているのは、ルールやマッチに影響しないオプションだけだ。マッチとターゲットについては、このセクションの後半に預けることにしよう。
Table 9-3. オプション
| オプション | -v, --verbose |
| 利用対象コマンド | --list, --append, --insert, --delete, --replace |
| 説明 | このコマンドは出力を冗長 (verbose) にするために使用され、よく --list コマンドと併用する。 --list コマンドと一緒に使用した場合、インターフェースアドレス、ルールオプション、 TOSマスクも表示される。 --verbose オプション付きの --list コマンドはまた、ルール毎のバイトおよびパケットカウンタも表示する。カウンタには、 K (x 1,000), M (x 1,000,000), G (x 1,000,000,000) の単位記号が適宜使用される。これを避けるには、下記に述べる -x オプションを使う。 --verbose オプションを --append, --insert, --delete, --replace コマンドに使用すると、指示したルールがどう解釈されたかの内容と、挿入などが正常に行われたかどうかといった事柄が表示される。 |
| オプション | -x, --exact |
| 利用対象コマンド | --list |
| 説明 | このオプションは数字を展開して見せる。つまり、 --list の出力は K, M, G の単位記号を含まなくなる。問題とするルールにマッチした累計パケット数とバイト数のカウンタ値が、文字通りの数字で表されるのだ。このコマンドは --list コマンドでのみ利用可能で、それ以外どのコマンドにもまったく効き目がないことに注意してほしい。 |
| オプション | -n, --numeric |
| 利用対象コマンド | --list |
| 説明 | このオプションは、値を数字で表示するよう iptables に指示する。 IPアドレスとポートナンバーは、ホスト名、ネットワーク名、アプリケーション名などでなく、数字で表現される。これは --list コマンドでのみ有効。デフォルトでは数字とホストは可能な限りリゾルブされるが、それを抑止するのがこのオプションだ。 |
| オプション | --line-numbers |
| 利用対象コマンド | --list |
| 説明 | --list コマンドとともに使用し、出力に行番号を表示させる。このオプションを使うと、各ルールが行番号付きで出力される。ルールの挿入時には、ルールのナンバーが分かるとありがたいものだ。このオプションは --list コマンドでしか機能しない。 |
| オプション | -c, --set-counters |
| 利用対象コマンド | --insert, --append, --replace |
| 説明 | このオプションは、何らかの形でルールを作成や変更する場面で用いる。その際に、パケットとバイトのカウンタ値を指定の値へと変更できる。書式は --set-counters 20 4000 のようになり、この場合、パケットカウンタを 20 に、バイトカウンタを 4000 にセットするよう、カーネルに指示していることになる。 |
| オプション | --modprobe |
| 利用対象コマンド | すべて |
| 説明 | --modprobe オプションは、カーネルに対して、モジュールの探索 (probe) が必要になった時にどのモジュールを使うべきか伝えるのに利用する。これは、 modprobe コマンドがサーチパス外にある場合に活用するとよい。そうしたケースでは、このオプションを使って、必要なモジュールがロードされていない場合にどうすべきかをプログラムに教えてやる必要があるからだ。このオプションはすべてのコマンドで利用可能だ。 |
このチャプターでは iptables の基本的なコマンドと、netfilter で利用可能なテーブルについて手短に説明してきた。ご覧いただいたように、これらのコマンドを使えば、カーネルにロードされた netfilter パッケージに対して多岐にわたる様々な操作を行うことができる。
次のチャプターでは iptables 及び netfilter で利用可能な全てのマッチを取り上げる。非常にヘビーで長い章だ。しかし、ひとつのご進言として申し上げるとするなら、使いたいものだけ見ればいいのであって、全部のマッチを熟読していく必要はない。どんなことをするマッチがあるのか、ざっと目を通しておいて、必要な時が来たら必要なマッチだけしっかり読み込む、というのもひとつの手だ。
このチャプターでは、マッチについて掘り下げる。僕はマッチを 5 つのサブカテゴリーに分類することにした。最初に扱うのが 汎用的なマッチ (generic matches) で、これはあらゆるルールに使用できる。次は、 TCP パケットにだけ用いる TCPマッチ。 UDP パケットにだけ用いる UDPマッチ 、それに、 ICMP パケットにだけ使用できる ICMPマッチ。そして最後に、 state, owner, limit マッチなどの特殊なマッチを扱う。最後のグループに含まれるマッチは、必ずしも互いに区別されるべきものではないのだが、さらに数個のサブカテゴリーに細分化することにした。これが理にかなった分類であり、皆さんの理解の助けになることを願うばかりだ。
前のチャプターを読んだ人ならもうお分かりだと思うが、マッチとは、パケット内に見られる真 (あるいは偽) であるべき某かの条件を指定するものだ。ひとつのルールは複数のマッチを持つことができる。例を示そう。実際にありそうなケースとして、身内のローカルエリアネットワーク内の特定のホストの出したパケットで、なお且つ、そのホスト上の特定のポートから発したパケットをマッチさせたい場合を考える。この場合、特定の送信元アドレスを持ち、 LAN とつながっているインターフェイスを通じて届き、なお且つ、指定したポート群のうちのいずれかに一致するパケットだった時にだけ、ターゲット (つまりジャンプ定義) を適用せよ、という指示を行うマッチを使用する。もしも、このルールの中のマッチがひとつでも一致し損なえば (例えば、他の全ての条件は満たしているが送信元アドレスだけが違っていた場合)、このルールそのものが不一致ということになり、パケットは次のルールの試験へ引き渡される。かたや、マッチが全て満たさされる場合には、ルール中で指定したターゲットが適用される。
このセクションでは汎用的なマッチ を取り扱う。汎用的なマッチは、扱うプロトコルが何であろうと、どんなマッチ拡張をロードしていようと、常に利用可能なマッチだ。マッチの利用に際して特別なパラメータも一切必要としない。僕は、この仲間に、プロトコルのマッチに関わるやや特別なものではあるが --protocol マッチも含めることにした。理由はこうだ。 TCPマッチ を使おうとする場合には、 --protocol マッチを用い、さらにそのオプションとして TCP を指定しなければならない。だが、 --protocol は、特定のプロトコルをマッチさせるという単独のマッチとしても存在しているのだ。常に利用可能なマッチには、以下のものがある。
Table 10-1. 汎用的なマッチ
| マッチ | -p, --protocol |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp |
| 説明 | このマッチは、特定のプロトコルを検出するために用いられる。プロトコルとは TCP, UDP, ICMP といったものだが、内部的に定義済みのプロトコルである TCP, UDP, ICMP のみを指す場合もある。 /etc/protocols ファイルで定義されたプロトコルも引数に採れるが、指定したプロトコルがファイルに見つからない場合にはエラーを返す。また、プロトコルは数字で指定することもできる。例えば ICMP プロトコルの値は 1、 TCP は 6、 UDP は 17 だ。更に、ALL を採ることもできる。ALL は TCP, UDP, ICMP の 3つだけをマッチさせることを意味する。コマンド引数にカンマ区切りのプロトコルリストを採ることもでき、例えば udp,tcp は UDP および TCP のすべてパケットにマッチする。引数に数字のゼロ (0) を与えると ALL の意味となり、それはつまり --protocol マッチをはなから使用しなかった時のデフォルトの動作でもある。マッチは ! 記号で否定することも可能で、つまり --protocol ! tcp は UDP と ICMP を見つけることを意味する。 |
| マッチ | -s, --src, --source |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -s 192.168.1.1 |
| 説明 | これはパケットを送信元の IPアドレスに基づいて判定する送信元 (source) マッチだ。基本形は 192.168.1.1 のように IPアドレスをひとつだけ合致させるのに使う。 CIDR のビット形式 (ネットマスクビットにおいて左側に連続する 1 の個数) によって、ネットマスクも指定することもできる。つまり、例えば 255.255.255.0 のネットマスクを使用するには /24 を付け足せばよい。この方法で、ファイヤーウォールの内側のローカルネットワークやネットワークセグメントなどといった IP領域を根こそぎマッチさせることが可能だ。その際の表現は 192.168.0.0/24 のようになり、これは 192.168.0.x で括られるすべてのパケットにマッチする。ネットマスクを指定するもうひとつ方法は、通常の 255.255.255.255 形式 (例えば 192.168.0.0/255.255.255.0) だ。前の TCPマッチと同様に、 ! でマッチを否定することもできる。つまり、 --source ! 192.168.0.0/24 と指定すれば、 192.168.0.x の領域を送信元アドレスとしない すべてのパケットにマッチさせることができる。デフォルトでは、すべての IPアドレスにマッチする。 |
| マッチ | -d, --dst, --destination |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -d 192.168.1.1 |
| 説明 | --destination マッチは、複数または単一の宛先アドレスに基づいてパケットをマッチさせるのに用いる。 --source マッチとほとんど同様の動作を行い、書式も同じで、ただ、評価基準がパケットの行き先である点が異なるだけだ。 IPを範囲でマッチさせるには、ネットマスクを、文字通りのネットマスク方式か、ネットマスクビットの 1 を左側から数えた個数の、いずれかで指定する。例を挙げるとそれぞれ、 192.168.0.0/255.255.255.0 と 192.168.0.0/24 となる。ふたつは同義だ。前のマッチと同じく、マッチ全体を ! で反転させることもできる。つまり、 --destination ! 192.168.0.1 とすれば、 IPアドレス 192.168.0.1 以外 に宛てられたすべてのパケットにマッチする結果となる。 |
| マッチ | -i, --in-interface |
| カーネル | 2.3, 2.4, 2.5 and 2.6 |
| 例 | iptables -A INPUT -i eth0 |
| 説明 | これはパケットの入ってくるインターフェースに基づいてマッチを行う。このマッチは INPUT, FORWARD, PREROUTING チェーンでのみ有効であり、その他で使用するとエラーメッセージが返ってくるという点に注意しよう。どのインターフェースも指定しない時のこのマッチのデフォルト動作は、文字としての値 + を指定したものとして扱われる。 + は、文字または数字から成る任意の文字列を表す。つまり単独の + は、カーネルに対して、どのインターフェースから入って来るかにかかわらず、すべてのパケットを合致させよと指示することになる。 + 記号はインターフェースのタイプに付加して使用することも可能で、例えば eth+ ならすべてのイーサネットデバイスを表す。 ! 記号の助けを借りて、意味を逆転することも可能だ。その場合の記述は -i ! eth0 のような書き方となり、これは eth0 を除いたすべてのインターフェースにマッチする。 |
| マッチ | -o, --out-interface |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A FORWARD -o eth0 |
| 説明 | --out-interface マッチはパケットが出て行こうとするインターフェイスに基づいてパケットをマッチさせるのに用いる。注意は、 --in-interface とは逆に OUTPUT、FORWARD、POSTROUTING チェーンにしか使えないということだ。この点を除いては、 --in-interface マッチとまったく同様に働く。 + による拡張は、同一タイプのインターフェースすべてに合致すると解釈され、 eth+ ならすべての eth デバイスにマッチするといった具合だ。マッチの意味合いを反転するには、 --in-interface とまったく同様に ! 記号が使用できる。 --out-interface を指定しなかった場合のこのマッチのデフォルト動作は、パケットがどこから出て行くかにかかわらずすべてのデバイスにマッチする。 |
| マッチ | -f, --fragment |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -f |
| 説明 | このマッチは、フラグメンテーションされたパケットの 2番目や 3番目のパケットにマッチさせる時に使用する。これが必要になるのは、パケットがフラグメンテーションされていた場合、後続フラグメントでは、送信元ポートや宛先ポート、 ICMPタイプなど諸々を判定する術がないからだ。フラグメンテーションしたパケットは、非常に希にだが、他のコンピュータへの攻撃に悪用されることもある。しかしパケットフラグメントは他のいかなるルールでも検出不可能。それ故にこのマッチが考案された。このオプションも ! と併用することができる。ただし今回は、 ! -f のように、 ! はマッチの前に付けなければならない。 --fragment マッチが反転された場合には、ヘッダフラグメントか、フラグメンテーションしていないパケットを検出する。つまり、フラグメンテーションしたパケットの先頭パケットにはマッチし、2番目、3番目のパケットなどにはマッチしないということになる。また、搬送経路上でフラグメンテーションされなかったすべてのパケットにもマッチする。ただし、カーネル自体にも、これの代わりに利用可能な非常に優れたフラグメンテーションオプションが用意されていることを覚えておこう。もうひとつの註は、コネクション追跡を使用している場合には、フラグメンテーションはパケットが iptables のテーブルやチェーンに到達する前に処置されるため、フラグメンテーションパケットに出会う可能性は皆無だということだ。 |
このセクションでは、暗黙的にロードされるマッチを取り扱う。暗黙的なマッチ (implicit match) は、暗示された、存在が前提的自動的に認められたマッチだ。例えば、 --protocol tcp によるマッチをその他の評価基準なしで行う場合などが当てはまる。現在のところ、 iptables は 3つのプロトコルに対応する 3種類の暗黙的マッチを備えている。 TCPマッチ、 UDPマッチ、 ICMPマッチ だ。 TCP ベースのマッチには、 TCP パケットでのみ有効な特有の評価基準がある。 UDP ベースのマッチでは、 UDP パケットにのみ利用可能な、また別の評価基準がある。 ICMP パケットにもまた同じことが言える。これに対して、明示的にロードしなくてはならない、明示的なマッチがあってもおかしくない。明示的なマッチ (explicit match) は、暗黙的でない、自動的でないマッチで、使用したければ明確に指定する必要がある。その際に使うのが -m または --match オプションだが、それについては次のセクションで詳述することにしよう。
これらのマッチはプロトコル固有で、 TCP パケットおよび TCP ストリームを取り扱う場合にのみ有効だ。このマッチを使用するには、コマンドライン上の、これらを使用する前で --protocol tcp を指定しなければならない。念を押しておこう。 --protocol tcp は必ず、特定のプロトコルマッチよりも左側に置く必要がある。これらのマッチは、 UDPマッチ と ICMPマッチ がそうであるように、ある意味、暗黙的にロードされる。 UDP および ICMPマッチについては、 TCPマッチ の後に続くセクションで目を通す。
Table 10-2. TCPマッチ
| マッチ | --sport, --source-port |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp --sport 22 |
| 説明 | --source-port マッチは、その送信元ポートに基づいてパケットを一致させるのに使われる。このマッチを指定しない時には、すべての送信元ポートを暗に合致させていることになる。後ろにはサービス名かポートナンバーを採る。サービス名を指定した場合、そのサービス名は /etc/services ファイルに載っていなければならない。 iptables はこのファイルを検索するからだ。ポートをポートナンバーで指定したほうが、 iptables はサービス名をチェックする必要がなくなるため、ルールのロードはわずかながら速い。しかし、サービス名を使用するのに比べてルールセットはやや読みにくくなる。 200個以上のルールから成るルールセットを書くなら、速度の違いは顕著になるので、迷うことなくポートナンバーのほうを使うべきだ。 (遅いサーバで 1,000 以上のルールを含むような巨大なルールセットを組んだ場合、 10秒単位の差が出ることも珍しくない)。 --source-port 22:80 といった具合に、 --source-port マッチにポートの範囲を使うことも可能だ。この例は 22 から 80 までのポートにマッチする。最初のポートを省略すると、(暗黙的に) ポート 0 として解釈される。 --source-port :80 はポート 0 から 80 にマッチする。また、最後のポートが省略されると、ポート 65535 として解釈される。ポート範囲をひっくり返した場合には、 iptables が自動的に再逆転して元に戻す。もし --source-port 80:22 と書けば、単純に --source-port 22:80 として解釈される。また、 ! 記号を加えることによってマッチを反転させることもできる。例えば --source-port ! 22 は、ポート 22 以外のすべてのポートに一致する。反転はポート範囲との併用も可能で、即ち --source-port ! 22:80 のような記述となり、この例は 22 から 80 までを除く全ポートにマッチさせたいということを表している。注意としては、このマッチは離ればなれのポートとポートや、複数の範囲を扱うことができない。そうした利用について知りたいなら、マルチポート(multiport) マッチ拡張を見てみてほしい。 |
| マッチ | --dport, --destination-port |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp --dport 22 |
| 説明 | このマッチは TCP パケットを宛先ポートによってマッチさせる。文法は --source-port マッチとまったく同じだ。ポートやポート範囲の指定はもちろん、反転も通用する。また同様に、ポート範囲指定における高低の入れ替えも働く。早い話が --source-port の文法と瓜二つだ。注意としては、このマッチは離ればなれのポートとポートや、複数の範囲を扱うことができない。そうした利用について知りたいなら、マルチポート(multiport) マッチ拡張を参照してほしい。 |
| マッチ | --tcp-flags |
| カーネ | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -p tcp --tcp-flags SYN,FIN,ACK SYN |
| 説明 | このマッチは、TCP のフラグでパケットをマッチさせるのに利用する。これはふたつの引数を取り、まず最初の引数では検討するフラグのリスト (マスク)、そして 2番目で、"1" にセットされているべき (ON になっている) フラグのリストを指定する。ふたつのリストはともにカンマ区切りだ。このマッチに理解できるのは SYN, ACK, FIN, RST, URG, PSH のフラグであり、加えて ALL と NONE という語彙も理解する。 ALL と NONE は読んで字の如しだが、 ALL はこのオプションに対してすべてのフラグを指定し、 NONE はどのフラグも指定しないことに通じる。つまり、 --tcp-flags ALL NONE は、 TCP フラグすべてを確認して、そのどれひとつとしてセットされていなければマッチするということだ。このオプションは ! 記号で反転することも可能で、例えば ! SYN,FIN,ACK SYN と指定すれば、ACK ビットと FIN ビットは立っているが SYN ビットの立っていないパケットに合致するマッチを作ることができる。カンマ区切りのリストの中にホワイトスペースを入れてはいけないことに注意しよう。正しい文法は上記の用例で分かるだろう。 |
| マッチ | --syn |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -p tcp --syn |
| 説明 | --syn マッチは ipchains 時代からの遺物の類で、下位互換性と、 ipchains から、あるいは ipchains への移行を容易にするために未だ残されている。もし SYN ビットが立ち ACK ビットと RST ビットが立っていなければ、そのパケットにマッチする。つまり、 --tcp-flags SYN,RST,ACK SYN というマッチと同義だ。このようなパケットは、主として、新しい TCP コネクションをサーバに要求する時に使われる。これらのパケットをブロックすれば、外から接続して来ようとする企てを見事にブロックする効果が得られる。とはいうものの、あなたはまだ外へ出ていくコネクションはブロックしていないだろう。昨今、数々の不正侵入によく使われるのが外へのコネクションなのだ (例えば、正規のサービスをハッキングして何らかのプログラムに類するものをインストールしてから、新たなポートを開けるのではなく既存のコネクションを利用して、あなたのホストへの接続を導き入れるなど)。このマッチは ! --syn のように ! 記号による反転も可能で、これは RST か ACK ビットの立ったパケット、つまり、確立済み (established) のコネクションにマッチする。 |
| マッチ | --tcp-option |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -p tcp --tcp-option 16 |
| 説明 | このマッチはパケットの TCPオプション によるマッチに利用される。 TCPオプションとは、ヘッダの中の特別な部分だ。このヘッダ部位は、意味合いの異なる 3つのフィールドで構成されている。最初のフィールドは 8ビット長で、そのストリームでどんなオプションを使用しているかを告げる。 2番目のフィールドは、やはり 8ビット長だが、オプションフィールドの長さを知らせている。長さを示すフィールドがなぜ必要なのかといえば、 TCPオプションはそもそも存在自体が任意だからだ。すべてのオプションをサポートしていなくても、規格違反というわけではない。だから、パケットに使用されているのがどのオプションかを調べて、それがこちらのサポート外だったら、そのデータを読み飛ばしても構わないのだ。このマッチは、オプションを数字で表した値を使って、様々な TCPオプションをマッチさせる。 ! 記号で反転することも可能で、その場合のマッチは、指定した以外の TCPオプションに合致する。オプションの完全なリストを見たければ Internet Engineering Task Force を参照のこと。 IETF はインターネットで使用される数字規格を管理している団体だ。 |
このセクションでは、UDP パケットとの組み合わせでのみ機能するマッチについて述べる。これらのマッチは --protocol UDP を指定することによって暗黙的にロードされ、利用が可能となる。忘れてはならないのは、 UDP パケットがコネクション指向のプロトコルではないという点だ。よって UDP パケットは、コネクションを開きたいのか閉じたいのか、あるいは単にデータを送ろうとしているのかといったデータグラムの目的に応じて、パケットに立てるフラグを区別したりはしない。そればかりか、 UDP パケットはいかなる種類の了解 (acknowledgement) も行わない。パケットが消失したら、ただ消失されっぱなしだ (ICMPエラーメッセージなどは考慮に入れないとしての話)。以上のことから、 UDP パケットで機能するマッチは TCP パケットに比べてはるかに少なくなる。ただし、ステート機構は、 UDP や ICMP といった非コネクション指向のプロトコルであっても、すべての種類のパケットに対して機能する。ステート機構は UDP パケットに対しても TCP パケットとまったく同様に機能するのだ。
Table 10-3. UDPマッチ
| マッチ | --sport, --source-port |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p udp --sport 53 |
| 説明 | このマッチは TCP版 とまったく同様に働く。送信元 UDP ポートに基づいてパケットのマッチを行うのに使用する。ポートの範囲、単一のポート、意味の反転も、同様の書式でサポートされている。 UDP ポートの範囲を指定するには、22:80 とすればよく、これは UDP の 22 から 80 番ポートにマッチする。初めの値を省略すると、ポート 0 と解釈、後ろの値を省略すれば 65535 と解釈される。数字の大きいポートが若いポートより前に指定されていた場合には、前後のポートは自動的に入れ替わる。単一UDP ポートによるマッチは上記の例に見る通りだ。ポートマッチを逆転するには ! 記号を使い、 --source-port ! 53 のように書く。これはポート 53 以外のすべてのポートにマッチする。このマッチは /etc/services に記載があるものに限っては、サービス名も理解する。なお、このマッチは離ればなれのポートとポートや、複数の範囲を扱うことができない。そうした利用について知りたいなら、マルチポート(multiport) マッチ拡張を見てほしい。 |
| マッチ | --dport, --destination-port |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p udp --dport 53 |
| 説明 | 前記の --source-port と同じことがこのマッチにも当てはまる。 UDP パケットに用いられるということ以外では、 TCP における同一機能のマッチと完全に同じだ。このマッチは UDP の宛先に基づいてパケットマッチを行う。ポートの範囲、単一ポート、意味反転を扱うことができる。単一のポートに使うには --destination-port 53 のように用い、反転するには --destination-port ! 53 などとすればよい。ひとつ目のマッチはポート 53 に宛てられたすべてのパケットにマッチし、2番目の例はポート 53 へ向かおうとするもの以外のパケットにマッチする。ポートの範囲を指定するには --destination-port 9:19 のように用いる。この例は UDP の 9 から 19 までを宛先とするすべてのパケットに合致する。初めのポートが省略されれば、ポート 0 と了解され、2番目が省略されればポート 65535 と解釈される。数字の大きいポートが若いポートより前に指定されていた場合には、自動的に入れ替えられ、若いほうのポートが数字の大きいポートの前に繰り込まれる。なお、このマッチは離ればなれのポートとポートや、複数の範囲を扱うことができない。そうした利用について知りたいなら、マルチポート(multiport) マッチ拡張を見ていただきたい。 |
ICMP にもいくつかのマッチがある。 ICMP パケットは、コネクションレスであるという面で UDP にも増して生存時間が短い、つまり短命だ。 ICMP プロトコルは主に、エラー通知やコネクションコントロールに類する事柄に使われる。 ICMP は、 IPプロトコルの構成要素というよりも、どちらかといえば IP プロトコルを補強する プロトコルとしての性格が強く、エラー担当という役目で IPプロトコルを補佐している。 ICMP パケットのヘッダは IPヘッダ と非常に似通っているものの、いくつかの点において違いがある。その最も特徴的なものが、タイプヘッダだ。これはパケットの目的を表す。ひとつの例を挙げると、アクセス不能の IPアドレスにアクセスしようとした場合、我々は通常、入れ違いに ICMP host unreachable を受け取る。 ICMP タイプの完全なリストが知りたければ、付録 ICMPタイプ を見ていただきたい。 ICMP パケットで利用可能な特有のマッチは 1種類しかないが、たいていはそれだけで事足りる。このマッチは --protocol ICMP を使用した際に暗黙的にロードされ、自動的に利用可能となる。ただし、汎用的なマッチ は ICMP にも使用でき、それによって送信元アドレスや宛先アドレスなどによるマッチも行うことができる。
Table 10-4. ICMPマッチ
| マッチ | --icmp-type | ||
| カーネル | 2.3, 2.4, 2.5, 2.6 | ||
| 例 | iptables -A INPUT -p icmp --icmp-type 8 | ||
| 説明 | このマッチは、合致すべき ICMPタイプ を指定して利用する。 ICMPタイプ は数値でも名称でも指定可能だ。数値は RFC 792 に規定されている。名称での ICMP 値の完全なリストを得るには、 iptables --protocol icmp --help するか、付録 ICMPタイプ を参照のこと。マッチは --icmp-type ! 8 という具合に ! 記号で反転することも可能だ。いくつかの ICMPタイプ は時代遅れで使われなくなっており、他の幾つかのタイプはまた、パケットをお門違いの場所へリダイレクトするなどといった作用も起こしかねないので、きちんと防御していないホストにとって "脅威" とも成り得るという点に注意が必要だ。なお、タイプとコードは、タイプ名称、タイプ番号で書く以外に、 type/code の形で指定することもできる。例えば --icmp-type network-redirect や, --icmp-type 8 や, --icmp-type 8/0 といった具合だ。指定できる名称の全リストは iptables -p icmp --help で得られる。
|
SCTP つまり Stream Control Transmission Protocol は、 TCP や UDP プロトコルに比べると登場して日の浅いネットワークプロトコルだ。SCTP については SCTPの特徴 のチャプターでもっと詳しく述べる。暗黙的な SCTP マッチは iptables コマンドラインに -p sctp を加えた時にロードされる。
SCTP は大手の通信系及びスイッチ/ネットワーク機器メーカー数社によって開発されたプロトコルで、高スループットと信頼性を確保しながら相当数の同時並行トランザクションを成り立たせなければならないような環境に向いている。
Table 10-5. SCTPマッチ
| マッチ | --source-port, --sport |
| カーネル | 2.6 |
| 例 | iptables -A INPUT -p sctp --source-port 80 |
| 説明 | --source-port マッチは SCTP パケットヘッダの送信元ポートに基づいて SCTP パケットをマッチさせるのに使う。ポートには、例に示したように単一のポートを指定することも、 --source-port 20:100 のように範囲で指定したり、 ! マークによって意味を反転させることもできる。最後の形式は例えば --source-port ! 25 といった具合になる。送信元ポートに指定できるのは符号なしの 16ビット数値、つまり最大値は 65535、最小値は 0 ということになる。 |
| マッチ | --destination-port, --dport |
| カーネル | 2.6 |
| 例 | iptables -A INPUT -p sctp --destination-port 80 |
| 説明 | このマッチは SCTP パケットの宛先に対してマッチを行う。 SCTP パケットはもれなく、そのヘッダに、送信元ポートと並んで宛先のポート情報も持っている。ポートは、例のように指定したり、ポート範囲で --destination-port 6660:6670 のように指定することができる。また、--destination-port ! 80 のように ! マークで反転することもで、この例ならポート 80 以外にマッチすることになる。ポートの値に関しては送信元の場合と同様で、最大値 65535、最小値は 0。 |
| マッチ | --chunk-types |
| カーネル | 2.6 |
| 例 | iptables -A INPUT -p sctp --chunk-types any INIT,INIT_ACK |
| 説明 | SCTP パケットのチャンク (chunk) タイプによるマッチを行う。現在のところ、チャンクタイプにはかなりの種類がある。詳しくは下記を参照いただきたい。マッチは --chunk-types キーワードで始まり、続いて、どうマッチさせるかを表す all, any または none のフラグ。その後ろに マッチさせたい SCTPチャンクタイプ を指定する。チャンクタイプの種類は下記の表に別記した。 フラグには、追加パラメータとして チャンクフラグ を付加することもできる。例えば --chunk-types any DATA:Be という指定になる。フラグは SCTPチャンクタイプ 毎に異なり、当該のチャンクに対して有効なものでなければならない (別表に示す)。 フラグを大文字で書いた場合には、それがセットされたパケットに対してマッチは真となり、小文字の場合はセットされていない時に真となる。--chunk-types キーワードの手前に ! 記号を置くとマッチ全体の意味を反転させることができる。例えば --chunk-types ! any DATA:Be は、そのパターンを除いた全てのものにマッチする。 |
下記に示したのが --chunk-types マッチの認識する全ての チャンクタイプ のリストだ。見ての通り膨大な数だが、通常用いられるのは概ね DATA パケットと SACK パケット程度である。その他のものは主にアソシエーションの制御に用いられる。
--chunk-typesで使用できるSCTPチャンクタイプ
ABORT
ASCONF
ASCONF_ACK
COOKIE_ACK
COOKIE_ECHO
DATA
ECN_CWR
ECN_ECNE
ERROR
HEARTBEAT
HE ARTBEAT_ACK
INIT
INIT_ACK
SACK
SHUTDOWN
SHUTDOWN_ACK
SHUTDOWN_COMPLETE
前述した、--chunk-types マッチで使用できるフラグの種類が下記。 RFC 2960 - Stream Control Transmission Protocol の規定によると、これ以外のフラグは予約済みか使用されていないかのどちらかとなっており、必ず 0 でなければならない。ありがたいことに、今のところ iptables はそれを強要する何らの仕組みも持っていない。かつて IP プロトコルに ECN が実装された時の二の舞となりかねないからだ。
--chunk-typesで使用できるチャンクフラグ
DATA - U または u :Unordered bit, B または b :Beginning fragment bit, E または e :Ending fragment bit
ABORT - T または t :TCB destroy flag
SHUTDOWN_COMPLETE - T または t :TCB destroyed flag
明示的なマッチ は、-m あるいは --match オプションではっきりとロードする必要のあるマッチだ。例えばステートマッチは、実際に使用したいマッチに先立って -m state ディレクティブ (命令句) を必要とする。この仲間には、プロトコル特有のマッチも幾つかある。また或るものは、プロトコルの種別とは一切無関係 -- 例えばコネクションステートがそうだ。コネクションステートとは NEW (まだ確立していない接続の最初のパケット)、 ESTABLISHED (既にカーネルに記録されている接続)、 RELATED (既存の確立済み接続に関連した接続) などといったものだ。さらに、ごく一部のマッチには、開発途上か実験的だったり、ただ単に iptables の可能性をデモンストレーションする目的で書かれたものもある。そうしたことから、一見しただけではいったい何に使ったらいいのか分からないマッチも存在するのだが、もしかすると、あなた自身がその有効な活用法を発見するかもしれない。さらには、 iptables の新しいリリース毎に、次々と新しいマッチが登場してくる。活用法が見いだせるかどうかは、あなたの創造力とニーズ次第だ。暗黙的なマッチ と 明示的なマッチ との違いは、暗黙的なマッチ 、例えば TCP パケットの性状に基づいてマッチを行うような場合には、そのマッチは自動的にロードされるが、明示的にロードする必要のあるマッチでは、自動的なロードは行われないという点だ。明示的マッチ の発見と活用は、あなたの腕に掛かっている。
addrtype モジュールはパケットのアドレスタイプに基づいたマッチを行う。このアドレスタイプを基準にしてカーネルがパケットの分類を行う。このマッチを使えば、カーネルの保持しているアドレスタイプ情報に基づいてパケットをマッチさせることができるわけだ。注意しなければならないのは、アドレスタイプの意味がレイヤー3 のプロトコル毎に異なるという点だ。ここでも概要は述べるが、 Linux Advanced Routing and Traffic Control HOW-TO や Policy Routing using Linux でもっと詳しく読んでおいたほうがいい。利用できるタイプには以下のものがある:
Table 10-6. アドレスタイプ
| Type | 意味 |
|---|---|
| ANYCAST | これは 1対多の関係にあるコネクションであり、実際にデータを受信するのはその多数の受信ホストのうちひとつだけだ。この実装のひとつに DNS が挙げられる。ルートサーバは通常ひとつのアドレスで示すが、実際のルートサーバは複数のロケーションを指しており、パケットは直近のサーバひとつへ誘導されることになる。Linux の IPv4 には実装されていない。 |
| BLACKHOLE | ブラックホールアドレスはパケットをただひらすら破棄し、返答も返さない。おおよそ宇宙のブラックホールと同じだ。これは Linux のルーティングテーブルで設定される。 |
| BROADCAST | ブロードキャストパケットは、ひとつのパケットが 1対多の関係でネットワーク内の全員に送られるタイプのもの。 ARP 解決 (ARP resolution) がその一例で、或る IP への到達路を問い合わせるひとつのパケットが送信されると、我こそはというひとつのホストが然るべき MAC アドレスを回答する。 |
| LOCAL | 我々の今操作しているホスト自体のローカルなアドレス。例えば 127.0.0.1。 |
| MULTICAST | マルチキャストパケットは、最短の距離経路 (distance) を使って複数のホストへ向けて送信される。それぞれの中継地点 (waypoint) へは単一のパケットだけが送られ、そこで特定のマルチキャストアドレスに加入しているホストやルータの分だけ複製して送られる。ビデオやサウンドなど、一方通行のストリーミングメディアに使用されることが多い。 |
| NAT | カーネルによって NAT されたアドレス。 |
| PROHIBIT | blackhole と同じだが、「禁止されている」という返答を発生させる点が異なる。 IPv4 において言うならば、この返答は ICMP communication prohibited (タイプ 3, コード 13) となる。 |
| THROW | Linux カーネルの持つ特殊なルート。ルーティングテーブルでパケットが throw されると、そのパケットは、ルートが見つからなかった時と同じ挙動をする。通常のルーティングでは、ルートがなかったかのように振る舞うことになる。ポリシールーティングにおいては、他のルーティングテーブルを検索して別のルートが割り出される場合もある。 |
| UNICAST | ひとつのアドレスだけを指す正真正銘のルーティング可能なアドレス。最も一般的なルート。 |
| UNREACHABLE | 到達するまでの経路が分からず到達不能なことを知らせる。パケットは破棄され ICMP Host unreachable (タイプ 3, コード 1) が発生する。 |
| UNSPEC | 定義になく正当な意味を持たないアドレス。 |
| XRESOLVE | カーネルの行うルート検索の代わりにユーザ空間のアプリケーションにルート検索指示を送る時に使われるアドレスタイプ。あり得るケースとしては、込み入った汚いルックアップをカーネル外でやらせたり、何らかのアプリケーションにやらせたい場合だ。Linux では実装されていない。 |
addrtype マッチは -m addrtype キーワードを使用した時にロードされる。ロードされると、下表に示すマッチオプションが使えるようになる。
Table 10-7. Addrtypeマッチオプション
| マッチ | --src-type |
| カーネル | 2.6 |
| 例 | iptables -A INPUT -m addrtype --src-type UNICAST |
| 説明 | --src-type マッチオプションは、パケットの送信元のアドレスタイプに対してマッチを行う。アドレスタイプは単一でもよいし、カンマ区切りで複数指定してもよく、例としては --src-type BROADCAST,MULTICAST のようになる。このマッチオプションは手前にエクスクラメーションマークを置いて ! --src-type BROADCAST,MULTICAST のように意味反転を行うことも可能。 |
| マッチ | --dst-type |
| カーネル | 2.6 |
| 例 | iptables -A INPUT -m addrtype --dst-type UNICAST |
| 説明 | --dst-type も --src-type と同様に働き、文法も同じ。違うのは、宛先のアドレスタイプに対してパケットマッチを行うという点だけだ。 |
このふたつのマッチは IPSEC の AH と ESP プロトコルを対象にしている。 IPSEC はセキュリティに欠けるインターネット接続上にセキュアなトネリングを構築するためのもの。 AH と ESP プロトコルは、そうしたセキュアなコネクションを構築する際に IPSEC によって利用される。本当のところ、 AH マッチと ESP マッチは全く別個のマッチなのだが、非常によく似たところがあるし用途も共通しているので、ここで一緒に述べることにする。
IPSEC についてここで詳しく述べるのは差し控える。より詳しい情報は以下のページやドキュメントを見て頂きたい:
IPSEC に関するドキュメントはインターネット上に五万とあるが、必要なら思い思いに検索してみてほしい。
AH/ESP マッチを使用するには、 AH マッチなら -m ah、 ESP ならば -m esp を使ってマッチをロードする必要がある。
![]() | カーネル 2.2 と 2.4 においては、Linux は IPSEC を実装するために FreeS/WAN と呼ばれるものを利用していた。しかし Linux カーネル 2.5.47 以降では、カーネル自体が IPSEC を実装するようになり、カーネルへのパッチを必要としなくなった。 Linux の IPSEC 実装部分がまるっきり書きなおされたのだ。 |
Table 10-8. AHマッチオプション
| マッチ | --ahspi |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p 51 -m ah --ahspi 500 |
| 説明 | このマッチは AH パケットの持つ AH Security Parameter Index (SPI) ナンバーを照合する。注意が必要なのは、このマッチを使用するならプロトコルも指定しなければならないという点。 AH は標準的な TCP, UDP, ICMP とは違ったプロトコルの上で働くからだ。 SPI ナンバーは送信元/宛先アドレスと秘密キーとともに、セキュリティアソシエーション (SA = security association) の生成に使われる。 SA はホスト毎の IPSEC トンネルそれぞれに一意性を与える。 SPI は一対のピア-ピア間の個々の IPSEC トンネルを識別するのに利用される。 --ahspi を使うと、パケットの SPI に基づいたマッチングを行うことができる。このマッチでは : を使って SPI 値の範囲をまとめてマッチさせることも可能だ。例えば 500:520 のような使い方で、これは範囲内の SPI に合致する。 |
comment マッチを使うと iptables ルールセット及びカーネル内にコメントを記述することができる。ルールセットが理解しやすくなりデバグに役立つだろう。例えば、或る一連のルールがどの bash ファンクションによってどんな理由で差し込まれたかを述べたコメントを加える、といった使い方が考えられる。このマッチは実際のところ本当のマッチではないことに注意していただきたい。comment マッチは -m comment キーワードによってロードされる。現在のところ、以下のオプションが利用できる。
connmark マッチは、 MARK ターゲットと mark マッチの組み合わせと全く同じような使い方をする。 CONNMARK ターゲットでコネクション単位に付けられたマークに対して、マッチを行うのが connmark だ。オプションはひとつのみ。
![]() | そのコネクションに mark を付けるに至ったまさにその最初のパケットをマッチさせるには、先に CONNMARK ターゲットに最初のパケットへマークを付けさせてから、 connmark マッチを使用しなければならない。 |
Table 10-11. Connmarkマッチオプション
| マッチ | --mark |
| カーネル | 2.6 |
| 例 | iptables -A INPUT -m connmark --mark 12 -j ACCEPT |
| 説明 | mark オプションは、コネクション単位で関連付けされたマーク (mark) に対してマッチを行う時に使用する。マークは完全に一致する必要がある。実際のマッチングに先立って不要なコネクションマークを排除しておきたい場合には、マスクを指定してマークとの論理積 (logical AND) を行わせることもできる。例を示す。或るコネクションにマーク 33 (バイナリ表現 10001) がセットされており、最初のビットだけをマッチさせたいとしよう。この時、 --mark 1/1 のようにコマンドすることができる。マスク (00001) は 10001 に対してマスク適用され、その結果 10001 && 00001 = 1 となり、 1 にマッチすることになる。 |
conntrack マッチはステートマッチの拡張版であり、ステートマッチよりも格段に詳しくパケットを照合することが可能だ。これは、コネクション追跡機構から得られる情報を、ステートマッチのような "フロントエンド" の助けを借りることなしに直接調べることを可能にしてくれる。コネクション追跡機構についての詳細は ステート機構 のチャプターを参照して頂きたい。
conntrack マッチには、コネクション追跡機構の様々なフィールドに呼応する、幾種類ものマッチが収められている。そうしたマッチを集めたのが、下のリストだ。これらのマッチをロードするには -m conntrack を指定する必要がある。
Table 10-12. Conntrackマッチオプション
| マッチ | --ctstate |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m conntrack --ctstate RELATED |
| 説明 | このマッチは conntrack の状態を基にしてパケットのステートをマッチさせる場合に使用する。本来の state マッチでのステートとほぼ同じものを対象にしたい場合に用いる。このマッチで有効なステートエントリは:
ステートエントリをカンマで区切って複数指定することもできる。例えば -m conntrack --ctstate ESTABLISHED,RELATED といった具合だ。また、 -m conntrack ! --ctstate ESTABLISHED,RELATED の例ならば ESTABLISHED と RELATED 以外にマッチする。 |
| マッチ | --ctproto |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m conntrack --ctproto TCP |
| 説明 | これは --protocol が行うのと同様にプロトコルでマッチを行う。タイプとして指定できる値も同じで、 ! 記号を使っての反転もできる。例えば -m conntrack ! --ctproto TCP は TCP プロトコルを除く全てのプロトコルにマッチする。 |
| マッチ | --ctorigsrc |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m conntrack --ctorigsrc 192.168.0.0/24 |
| 説明 | --ctorigsrc マッチは、特定のパケットに関する conntrack エントリに記録された送信元IP の諸元に基づいてマッチを行う。 --ctorigsrc ! 192.168.0.1 のように --ctorigsrc と IP 指定との間に ! を挟むことにより意味を反転させることもできる。また --ctorigsrc 192.168.0.0/24 といった風に CIDR 形式のネットマスクを採ることも可能だ。 |
| マッチ | --ctorigdst |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m conntrack --ctorigdst 192.168.0.0/24 |
| 説明 | このマッチは、照合対象が conntrack エントリの宛先フィールドである点を除けば --ctorigsrc と全く同じ。書式もあらゆる点において共通だ。 |
| マッチ | --ctreplsrc |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m conntrack --ctreplsrc 192.168.0.0/24 |
| 説明 | --ctreplsrc マッチは、 conntrack に記載されている回答パケットの送信元に基づいてマッチを行いたい時に使う。基本的には --ctorigsrc と同様なのだが、こちらの場合には、次に期待される返答の送信元と照合する。もちろん、このクラスの前述のターゲットと同様に、反転や、アドレスの範囲指定も可能だ。 |
| マッチ | --ctrepldst |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m conntrack --ctrepldst 192.168.0.0/24 |
| 説明 | --ctrepldst マッチは --ctreplsrc と同様で、パケットがマッチしたことにより記録された conntrack エントリの中の、回答の宛先を照合対象にする点が異なる。これも --ctreplsrc 同様に反転や範囲指定ができる。 |
| マッチ | --ctstatus |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m conntrack --ctstatus RELATED |
| 説明 | このマッチは ステート機構 のチャプターで述べたコネクションの状態 (status) でマッチを行う。照合できるステータスには以下のものがある:
このマッチもまた ! 記号で反転することができる。例えば -m conntrack ! --ctstatus ASSURED で、これは ASSURED 以外のステータスに合致する。 |
| マッチ | --ctexpire |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m conntrack --ctexpire 100:150 |
| 説明 | このマッチは conntrack エントリの有効期限タイマー (expiration timer) に残っている時間 (秒単位) に基づいたパケットマッチに使用する。単一の値を与えて照合することもできるし、上記の例のように範囲を指定することもできる。また、 -m conntrack ! --ctexpire 100 のように ! 記号を使用しての反転も可能で、これは、ぴったり 100 秒残っているもの以外の有効期限をマッチさせる。 |
このマッチはパケットをその DSCP (Differentiated Services Code Point) フィールドに基づいてマッチさせる。 DSCP については RFC 2638 - A Two-bit Differentiated Services Architecture for the Internet に網羅されている。このマッチは明示的に -m dscp を指定するとロードされる。下記に挙げる、排他関係にあるふたつのオプションが採れる。[訳者補足: DSCP自体については IPヘッダ セクションで幾分か詳しく述べられている]
Table 10-13. DSCPマッチオプション
| マッチ | --dscp |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m dscp --dscp 32 |
| 説明 | このオプションは引数として 10進または 16進表記の DSCP 値を採る。オプション値を 10進で書くとすれば 32 や 16 などといった具合。 16進表記の際には頭に 0x を添えて 0x20 のように書く。 ! の字を使って -m dscp ! --dscp 32 のように意味を反転することも可能。 |
| マッチ | --dscp-class |
| カーネル | 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m dscp --dscp-class BE |
| 説明 | --dscp-class マッチはパケットの DiffServ クラスによってマッチを行う際に使用する。その値は、幾つかの RFC で定義されているように BE, EF, AFxx, CSx のいずれかのクラスとなる。 --dscp と同じく反転もできる。 |
![]() | --dscp オプションと --dscp-class オプションは排他の関係にあり、同時指定はできないことに注意して頂きたい。 |
ECN マッチは TCP と IPv4 ヘッダにある ECN フィールド類を照合するのに使う。 ECN の詳細は RFC 3168 - The Addition of Explicit Congestion Notification (ECN) to IP の中で述べられている。このマッチはコマンドラインで -m ecn を指定して明示的にロードしなければならない。 ECN マッチが採れるオプションには以下のようなものがある。[訳者補足: ECN自体については IPヘッダ のセクションで幾分か詳しく述べられている]
Table 10-14. ECNマッチオプション
| マッチ | --ecn |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m ecn --ecn-tcp-cwr |
| 説明 | このマッチは、パケットに CWR (Congestion Window Received) ビットがセットされているかどうかを照合する。 CWR フラグを設定するのは、 ECE を受け取ったかどうかと ECE に対して反応したかを、コネクションの相手方に知らせる時。デフォルトでは、このマッチは CWR ビットがセットされていれば一致するが、エクスクラメーションマークを使って逆の意味にすることもできる。 |
| マッチ | --ecn-tcp-ece |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m ecn --ecn-tcp-ece |
| 説明 | このマッチは ECE (ECN-Echo) ビットでマッチを行うことができる。 ECE ビットは、どちらか一方のエンドポイントが、ルータによって CE ビットの立てられたパケットを受け取った場合にセットされる。すると、受信者は応答の ACK パケットに ECE ビットを立てることによって、相手方に、ペースを遅くする必要があることを知らせる。知らせを受け取った相手は --ecn-tcp-cwr で述べた CWR パケットを送出する。デフォルトではこのマッチは ECE ビットが立っているとマッチするが、エクスクラメーションマークを使って意味を逆転することもできる。 |
| マッチ | --ecn-ip-ect |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m ecn --ecn-ip-ect 1 |
| 説明 | --ecn-ip-ect マッチは ECT (ECN Capable Transport) コードポイントによってマッチを行いたい時に利用する。 ECT コードポイントには幾つかの利用法がある。まず主には、2 ビットあるうちの片方を 1 にセットすることにより、そのコネクションが ECN に対応しているかどうかを折衝する、という使い方。また、ルータにおいては、 ECT コードポイントを 2 ビットとも 1 にして、輻輳の発生を知らせるという使い方もある。 ECT の値は下表 IP内のECNフィールド にまとめておいた。 マッチはエクスクラメーションマークで ! --ecn-ip-ect 2 のように反転することができる。この例の場合、コードポイント ECT(0) を除いた全ての ECN値にマッチする。 iptables で使用できる値は 0 から 3 までだ。それらの値に関しては下表を参照のこと。 |
これは Limitマッチ の変形バージョンだ。単にひとつのトークンバケツ (token backet) ではなく、宛先IP, 送信元IP, 宛先ポート, 送信元ポートの組み合わせから成る幾つものトークンバケツを指す、ひとつのハッシュテーブルが構築される。例えば、各 IPアドレス毎に最大 1000個/秒のパケットを受け取れるようにしたり、待ち受け IPアドレスはひとつであっても異なったサービス毎に最大 200パケット/秒まで受け入れるようにするなどといった設定ができる。 hashlimit マッチは -m hashlimit を指定するとロードされる。
hashlimit マッチの使用された各々のルールに対して別個のハッシュテーブルが生成され、ひいては、バケツのサイズ及び数の上限値は各ハッシュテーブル毎に定義できる。このハッシュテーブルはひとつの値だけを保持することもできるし、複数を保持することもできる。その値とは、宛先IP, 送信元IP, 宛先ポート, 送信元ポートのいずれか、またはそれら全てだ。これが形成されれば、それぞれのルールエントリが自分のトークンバケツを limit マッチ同様に参照できるようになる。
Table 10-16. Hashlimitマッチオプション
| マッチ | --hashlimit | ||
| カーネル | 2.6 | ||
| 例 | iptables -A INPUT -p tcp --dst 192.168.0.3 -m hashlimit --hashlimit 1000/sec --hashlimit-mode dstip,dstport --hashlimit-name hosts | ||
| 説明 | --hashlimit はトークンバケツのリミットを設定する。上記の例では、ハッシュリミットを 1000 に設定している。その中でハッシュリミット-モードを dstip,dstport、宛先を 192.168.0.3 にしている。これによって、送信先ホストのポートまたはサービス毎に、1秒あたり 1000個のパケットを受信することができる。これは limit マッチの limit オプションと同じ設定だ。リミットの値には /sec, /minute, /hour, /day のいずれかの接尾語を付けることもできる。接尾語を付けない場合のデフォルトは毎秒となる。
| ||
| マッチ | --hashlimit-mode | ||
| カーネル | 2.6 | ||
| 例 | iptables -A INPUT -p tcp --dst 192.168.0.0/16 -m hashlimit --hashlimit 1000/sec --hashlimit-mode dstip --hashlimit-name hosts | ||
| 説明 | --hashlimit-mode オプションは、ハッシュの値としてどの値を使用するかを定義する。上記の例では、ハッシュ値に dstip (destination IP) だけを使用している。これにより、当例の場合 192.168.0.0/16 のネットワークからの受信は、各相手ホストあたり最大 1000パケット/毎秒に制限される。 --hashlimit-mode に設定可能な値には dstip (宛先IP), srcip (送信元IP), dstport (宛先ポート), srcport (送信元ポート) がある。複数のハッシュ値を含めたい場合にはカンマで区切ることによってどれでも指定することができる。例えば --hashlimit-mode dstip,dstport といった具合だ。
| ||
| マッチ | --hashlimit-name | ||
| カーネル | 2.6 | ||
| 例 | iptables -A INPUT -p tcp --dst 192.168.0.3 -m hashlimit --hashlimit 1000 --hashlimit-mode dstip,dstport --hashlimit-name hosts | ||
| 説明 | このオプションは、そのハッシュを利用する際の名称を指定する。ここで指定したものは /proc/net/ipt_hashlimit ディレクトリで見ることができる。上記のものは /proc/net/ipt_hashlimit/hosts ファイルとして見られる。指定できるのはファイル名だけだ。
| ||
| マッチ | --hashlimit-burst | ||
| カーネル | 2.6 | ||
| 例 | iptables -A INPUT -p tcp --dst 192.168.0.3 -m hashlimit --hashlimit 1000 --hashlimit-mode dstip,dstport --hashlimit-name hosts --hashlimit-burst 2000 | ||
| 説明 | このマッチは、バケツのサイズを設定するという意味で --limit-burst と同じ。バケツには必ずバーストリミットがある。バーストリミットは、単位時間内にマッチさせることのできる最大パケット数だ。トークンバケツの動作については、 Limitマッチ に示した例を見ていただくといいだろう。 | ||
| マッチ | --hashlimit-htable-size | ||
| カーネル | 2.6 | ||
| 例 | iptables -A INPUT -p tcp --dst 192.168.0.3 -m hashlimit --hashlimit 1000 --hashlimit-mode dstip,dstport --hashlimit-name hosts --hashlimit-htable-size 500 | ||
| 説明 | このオプションは、利用可能なバケツの最大数を規定する。上記の例は 500個までのポートが同時にオープンでき並行してアクティブになれることを意味する。 | ||
| マッチ | --hashlimit-htable-max | ||
| カーネル | 2.6 | ||
| 例 | iptables -A INPUT -p tcp --dst 192.168.0.3 -m hashlimit --hashlimit 1000 --hashlimit-mode dstip,dstport --hashlimit-name hosts --hashlimit-htable-max 500 | ||
| 説明 | --hashlimit-htable-max はハッシュテーブル・エントリの最大数を指定する。これによって表されるのはコネクションの総数であり、その時トークンバケツを一切使用していない非活性なコネクションも含まれる。 | ||
| マッチ | --hashlimit-htable-gcinterval | ||
| カーネル | 2.6 | ||
| 例 | iptables -A INPUT -p tcp --dst 192.168.0.3 -m hashlimit --hashlimit 1000 --hashlimit-mode dstip,dstport --hashlimit-name hosts --hashlimit-htable-gcinterval 1000 | ||
| 説明 | ガーベッジ・コレクション (garbage collection=ゴミ収集) 機能の実行頻度。一般論的には、expire の値よりも低くすべきである。単位はミリ秒。小さくしすぎるとシステムリソースや CPUパワーを無用に消費する一方、大きくしすぎると、使いもしないトークンバケツが長期間ほったらかしになり、他のコネクションが受け入れ不能となる。上記の例では、ガーベッジ・コレクションは 1秒毎に実行される。 | ||
| マッチ | --hashlimit-htable-expire | ||
| カーネル | 2.6 | ||
| 例 | iptables -A INPUT -p tcp --dst 192.168.0.3 -m hashlimit --hashlimit 1000 --hashlimit-mode dstip,dstport --hashlimit-name hosts --hashlimit-htable-expire 10000 | ||
| 説明 | この値は、ハッシュテーブルの特定エントリがどれくらいの期間アイドルを続けたら失効となるかを規定する。バケツの使用されない期間がこの値を超えるとそのエントリは失効し、次回のガーベッジ・コレクションの際に、関連する情報もろともハッシュテーブルから削除される。 |
これは、書式の特殊性という意味において、マッチの中でも異端に数えられるマッチと言えるかもしれない。このマッチは、そのパケットがどの conntrack ヘルパーに関係しているかに基づいてパケットを選定する。 FTP セッションを例に採ろう。コントロールセッションが開き、そのコントロールセッションを通じてデータセッション用のポート/コネクションがネゴシエートされる。その情報を見つけるのがヘルパー ip_conntrack_ftp で、それが conntrack テーブルに RELATED エントリを記録する。よって我々は、次のパケットが来た時、それがどのプロトコルに関連したパケットなのかを知ることができ、利用されたヘルパーの種類に基づいたパケットマッチをルールセットの中で行えるのだ。このマッチは、キーワード -m helper を使用することによってロードされる。
Table 10-17. Helperマッチオプション
| マッチ | --helper |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m helper --helper ftp-21 |
| 説明 | --helper オプションの利用法は、文字列値を指定して、合致させたい conntrack ヘルパーを指示することだ。基本的な書式としては --helper irc のようになる。しかしここからがちょっと込み入ってくる。特定のポートで初期ネゴシエーションが行われたパケットだけをマッチさせることも可能なのだ。例えば FTP では、コントロールセッションには通常なら 21番ポートを使うが、 954など他のポートを使用することもある。そういった場合には、どのポートでネゴシエーションが行われたものかを指定すればいい。例えば --helper ftp-954 のようにだ。 |
IP range マッチは IP の範囲に対してマッチを行う。これは --source マッチや --destination マッチでもできることだが、 IP range マッチでは --source や --destination では不可能だった 始点IP - 終点IP という形での照合が可能となる。特異な設定のネットワークではこうしたやり方が必要で、しかもこちらのほうがやや柔軟性に富む。IP range はキーワード -m iprange を使うことによってロードされる。
Table 10-18. IP rangeマッチオプション
| マッチ | --src-range |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m iprange --src-range 192.168.1.13-192.168.2.19 |
| 説明 | このマッチは送信元 IP の範囲を照合する。指定した範囲は始点から終点までの全 IP を含み、上の例なら 192.168.1.13 から 192.168.2.19 までが入る。 ! を加えることによって意味を反転することもできる。それを使うとすれば上記の例は -m iprange ! --src-range 192.168.1.13-192.168.2.19 のようになり、指定した範囲を除く全 IPアドレスがマッチすることになる。 |
| マッチ | --dst-range |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m iprange --dst-range 192.168.1.13-192.168.2.19 |
| 説明 | --dst-range の動作は、対象が送信元でなく宛先である点を除けば --src-range マッチと全く同様だ。 |
Length マッチを使うと、パケットの長さに基づいたマッチが行える。ことは至って単純だ。もしも何か特別な理由でパケットの長さを制限したかったり、 ping-of-death 的な挙動をブロックしたいのであれば、 length マッチを使うといい。
Table 10-19. Lengthマッチオプション
| マッチ | --length |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m length --length 1400:1500 |
| 説明 | 例に示した --length は、長さが 1400バイトから 1500バイトである全パケットに合致する。また、このマッチは ! 記号を使って -m length ! --length 1400:1500 といった具合に意味を反転させることもできる。更に、 : 記号以下を取り去って特定の長さのパケットだけをマッチさせることも可能だ。言うまでもないが length マッチの対象は「以上」や「以下」であり、指定した数値も含めた中間のあらゆる長さのパケットを含む。 |
limit マッチ拡張機能は -m limit オプションによって明示的にロードする必要がある。このマッチは例えば、特定のルールのログを限定的に間引きながら取るなどの活用法がある。或る値が指定数値以下であるパケットにマッチさせ、その数値を超えたら、対象としているイベントのロギングを抑制 (limit) する、などといった場合だ。時間的な limit について考えてみよう。あるルールが一定の単位時間内に何回マッチするかを制限することによって、例えば DoS の synパケット氾濫 (syn flood) 攻撃の影響を低減することができる。これが limit マッチの主な使い道ではあるが、もちろん利用法は他にもある。limit マッチは、マッチの前に ! 記号を用いることによって反転することも可能だ。これを使うと、-m limit ! --limit 5/s と書ける。これは、リミットを突破した後のすべてのパケットにマッチする。[訳者補足: 以下の説明に出てくる "トークン (token)" は地下鉄などの切符を意味する英語彙、"バースト (burst)" は直訳すると "爆発" や "突発"]
より詳しく説明すると、 limit マッチは、理屈としては一種のトークンバケツフィルタ (token bucket filter) だと言える。ここに、水漏れバケツがあるとしよう。このバケツは、単位時間あたり X 個のパケットが漏れる。 X は、条件に合致するパケットがいくつ入ってきたかによって規定が変化する。もし 3パケット入ってくれば、バケツからは単位時間あたり 3 個のパケットが漏れる。 --limit オプションは単位時間あたりいくつのパケットをバケツに補充できるかを規定し、 --limit-burst オプションは、そもそものバケツの大きさを表す。よって、 --limit 3/minute --limit-burst 5 と指定してあるところに 5個のパケットがマッチすると、バケツはカラになる。 20秒 [訳者補足: 1/3 分] 経つと、バケツのトークンは 1 つ増え...が、再び --limit-burst に達するかトークンが使用されるまで繰り返される。
これがどう作動するかの説明に代えて、以下の例を考察していただこう。
-m limit --limit 5/second --limit-burst 10/second というルールを設定する。 limit-burst トークンバケツの初期値は 10 にセットされる。ルールに合致する各パケットはトークンをひとつ消費する。
マッチするパケット 1-2-3-4-5-6-7-8-9-10 が来る。パケットは 1/1000 秒以内で入ってくるとする。
トークンバケツはカラになった。トークンバケツが一旦カラになると、ルールに沿ったパケットであってももうルールにマッチしないので、もし次のルールがあればその評価を受け、なければチェーンのポリシーが適用される。
パケットがマッチしない時間が 1/5 秒続くと、そのたびにトークンカウントが 1 ずつ上がり、最大では 10 まで上がる。先ほどの 10パケットを受け取ってから 1秒経過すると、トークンの残り数は 5 まで復活している。
そして当然、パケットを 1つ受け取る度に、バケツのトークンも 1 ずつ減っていく。
Table 10-20. Limitマッチオプション
| マッチ | --limit |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -m limit --limit 3/hour |
| 説明 | limit マッチのための、平均マッチ頻度の最大値を設定する。指定には、数と単位時間 (省略可能) が使用できる。現行では、次の様な単位時間が理解可能だ: /second /minute /hour /day 。デフォルト値は毎時 3回、 つまり 3/hour。この指定は limit マッチに対して、単位時間 (例えば分) あたりに許容するマッチの回数を指示する。 |
| マッチ | --limit-burst |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -m limit --limit-burst 5 |
| 説明 | limit マッチにおける burst limit [訳者補足: 短い時間内で爆発的ににヒットする数の上限] を設定する。これは iptables に対して、与えられた単位時間内にマッチするパケットの最大数を伝える。この数は、1 単位時間 (--limit オプションで指定されたもの) がイベントの発生なしに経過する度に、この値の採れる最小値である 1 へ向かって 1 ずつ減ってゆく。イベントがまた発生すると、カウントがバーストリミットに達するまで、カウンタの値はまた増えていく。この繰り返しだ。デフォルトの --limit-burst 値は 5。その動作を確認する簡単な方法は、たったひとつのルールから成るスクリプト例 Limit-match.txt を使ってみることだ。このスクリプトを利用すれば、間隔とバースト数 [訳者補足: 短い時間内で爆発的ににヒットする数] を変えながら ping を送るだけで、 limit ルールがどう働くのかをその目で見ることができる。カウントがバーストリミットのしきい値まで回復するまで、 echo応答 はブロックされて返ってこない。 |
MAC (Ethernet Media Access Control) マッチは送信元 MACアドレス に基づいたパケットマッチに使用する。このドキュメントを書いている時点では、このマッチには幾らかの制限事項があるが、将来的にはもっと開発が進み便利になるだろう。既に述べたように、送信元の MACアドレス によるマッチしかできない。
![]() | このモジュールを使用するには明示的に -m mac オプションを指定しなければならない。わざわざこんなことを言うのは、 -m mac-source でいいのではないかと勘違いする人が多いからだ。 |
Table 10-21. MACマッチオプション
| マッチ | --mac-source |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -m mac --mac-source 00:00:00:00:00:01 |
| 説明 | 送信元 MACアドレス に基づいたパケットマッチに使用する。指定する MACアドレス は XX:XX:XX:XX:XX:XX の形式でなくてはならなず、それ以外は通用しない。マッチは ! 記号で反転でき、 --mac-source ! 00:00:00:00:00:01 のように書ける。これはマッチの意味を逆転するので、その MACアドレス 以外から来るパケットにマッチする。留意点として、 MACアドレス はイーサネットタイプのネットワークでだけ用いられており、このマッチもイーサネットインターフェースにしか使用できない。 MAC マッチは PREROUTING, FORWARD, INPUT チェーンでのみ有効で、他で使うことはできない。 |
mark マッチは、パケットに付けておいたマーク (mark) に基づいてパケットをマッチさせるのに使用する。マークはカーネルの中でのみ維持管理できる特別なフィールドで、パケットがコンピュータの中を通過している間だけ識別に利用できる。マークはトラフィックシェーピングやフィルタリングなどといった様々なカーネル関連ルーティンに利用可能だ。今のところ、マークをセットする Linux で唯一の方法は、iptables の、その名も MARK ターゲットのみだ。同じことが、以前は ipchains の FWMARK ターゲットで行われていた。高度ルーティングのコミュニティで未だに FWMARK が言及されるのは、これが理由だ。現行では、mark フィールドには符号無しの整数が指定でき、32ビットシステムでは 4,294,967,296通りの値を採ることができる。おそらく、この制限にひっかかることは滅多にないだろう。
Table 10-22. Markマッチオプション
| マッチ | --mark |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -t mangle -A INPUT -m mark --mark 1 |
| 説明 | あらかじめマークを付けられたパケットにマッチさせるために用いる。マークは、次のセクションで述べる MARK ターゲットによって付与することができる。 Netfilter を通過している間、パケットにはもれなく特殊なマークフィールドが関連づけされる。このマークフィールドは、パケットの内側にも外側にも、実際に余分なフィールドをくっつけるわけではないという点に注意してほしい。マークは、マークを生成したコンピュータの中に保持されるのだ。マークフィールドが指定したマークに合致すれば、即ちマッチとなる。マークフィールドは符号無しの整数で、 4,294,967,296 通りの値を採ることができる。また、マークにマスクを設定することも可能だ。これを使用した場合、マークの指定は、 --mark 1/1 といった具合になる。マスクが指定されていた場合には、実際の比較に先立って、マスクとマークの論理積が行われる。 |
multiport マッチ拡張によって、複数の宛先ポートや、複数の宛先ポート範囲が指定できるようになる。このマッチの能力を利用しないと、ポートが異なるだけで、同じタイプのルールをいくつも書かなければならない。
![]() | 通常のポートマッチングと multiport マッチを同時に使用することはできない。つまり、このように書くことはできない: --sport 1024:63353 -m multiport --dport 21,23,80。実際にやったらどうなるかといえば、 iptables はルールの最初の要素だけを尊重し、 multiport 指示は無視される。 |
Table 10-23. Multiportマッチオプション
| マッチ | --source-port |
| カーネル | 2.3, 2.4, 2.5 2.6 |
| 例 | iptables -A INPUT -p tcp -m multiport --source-port 22,53,80,110 |
| 説明 | このマッチは複数の送信元ポートにマッチする。不連続なポート 15個まで指定できる。上記の例のように、ポートはカンマ区切りで指定する。このマッチは -p tcp または -p udp との併用でしか使用できない。基本的には、通常の --source-port マッチの拡張版だ。 |
| マッチ | --destination-port |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m multiport --destination-port 22,53,80,110 |
| 説明 | このマッチは複数の宛先ポートを合致させるのに使用する。ポートが宛先である点を除いては、上記の送信元ポートマッチとまったく同様に働く。このマッチも制限は 15個で、 -p tcp または -p udp と組み合わせなければ使えない。 |
| マッチ | --port |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -p tcp -m multiport --port 22,53,80,110 |
| 説明 | このマッチ拡張は、宛先と送信元のポートに基づいてパケットをマッチさせるのに用いる。上記の --source-port と --destination-port と同じように作用する。最大 15個のポートを採ることができ、 -p tcp か -p udp との組み合わせでしか機能しない点も同じだ。ただし、 --port マッチは、送り手のポートと受け側ポートが同じであるパケットのみにマッチする。例えば、ポート 80 からポート 80 へ宛てたパケット、ポート 110 からポート 110 へ宛てたパケット、といった具合だ。 |
owner マッチ拡張は、パケットを作り出したプロセスの身元に基づいてパケットをマッチさせるのに用いる。 owner にはプロセスの ID を指定する。 ID は、当該のコマンドを発行したユーザ、グループ、プロセス、セッションの ID、あるいはコマンドそのもの ID のいずれか。この拡張は元々 iptables の活用方法のひとつを示す例として書かれたものだ。 owner マッチは OUTPUT チェーンでしか使えない。その理由はいくつかある: パケットを送り出したプロセスの身元に関する情報を、着信した先で探し出しすのはほぼ不可能に近い。また、最終的な目的地に辿り着くまでに中継ホップを経ている場合にも、やはり難しい。ある種のパケットはオーナーを欠いているため、たとえ OUTPUT チェーン内に限ったとしても、このマッチはあまり信頼できない。これに類する悪名高いパケットには、(他にもあるが) 様々な ICMP 応答が挙げられる。 ICMP応答 は決してこれにマッチすることはない。
Table 10-24. Ownerマッチオプション
| マッチ | --cmd-owner |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A OUTPUT -m owner --cmd-owner httpd |
| 説明 | コマンドのオーナーによるマッチであり、パケットを送り出したプロセスの名前に基づいてマッチを行う。例に示したものには httpd が合致する。エクスクラメーションマークを使って -m owner ! --cmd-owner ssh のように意味を反転させることもできる。 |
| マッチ | --uid-owner |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A OUTPUT -m owner --uid-owner 500 |
| 説明 | このマッチは、パケットを作り出したユーザ の ID が、指定された ID (UID) と一致していればマッチする。これは、誰がパケットを発生させたかに基づいて、ローカルから出ていくパケットを選択するのに用いられる。想定される用途としては、root 以外のユーザがファイヤーウォール外への新規コネクションを開始するのを阻止する場合。あるいは、 http ユーザ以外は HTTP ポートからパケットを送出できなくする、という使い方が考えられる。 |
| マッチ | --gid-owner |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A OUTPUT -m owner --gid-owner 0 |
| 説明 | このマッチは、パケットのグループID (GID) に基づいてパケットをマッチさせる。つまり、パケットを発生させたユーザがどのグループに属するかによってパケットをマッチさせるわけだ。 network グループに属するユーザを除いてはインターネットに出て行けなくする、あるいは、前のマッチのように HTTP ポートから出ようとするパケットの発行を http グループのメンバーにのみ許す、という使い方ができる。 |
| マッチ | --pid-owner |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A OUTPUT -m owner --pid-owner 78 |
| 説明 | このマッチは、パケットの元となったプロセス の ID (PID) に基づいてパケットマッチを行うのに使用する。このマッチは使い方がやや難しいが、例えば、PID 94 にだけ HTTP ポートからのパケット送出を許す場合がある (言うまでもなく、HTTPD プロセスがスレッド化されていない場合に限る)。或いは、 ps の出力を利用して探知した特定デーモンの PID に応じてルールを追加するような小さなスクリプトを書いておく、という手法も考えられる。やるとすれば、スクリプト例 Pid-owner.txt で示しているようなルールが書ける。 |
| マッチ | --sid-owner |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A OUTPUT -m owner --sid-owner 100 |
| 説明 | 当該プログラムのセッションID に基づいてパケットをマッチする。SID つまりセッションID の値は、プロセス自体の ID であると同時に、そのプロセスから派生した全プロセスの SID でもある。後者には、元のプロセスのスレッドや子プロセスも含まれる。つまり、例えば HTTPD がスレッド化されていたとしても (ほとんどの HTTPD はそうだ。 Apache も Roxen も)、すべての HTTPD プロセスは親プロセス (大元の HTTPD プロセス) と同じ SID を持っているはずである。この実例として Sid-owner.txt を作っておいた。この小さなスクリプトの使い道は、 HTTPD が起動しているかどうか調べて必要ならば起動させ、必要に応じて OUTPUT チェーンをフラッシュしてから OUTPUT チェーンをロードし直すような、追加のスクリプトと組み合わせて、例えば 1時間に 1回走らせることだ。 |
![]() | pid, sid マッチは SMPカーネルでは動作が破綻している。これらのマッチはプロセスリストをプロセッサ別に管理するからだ。とはいえ、将来は修正されるだろう。 |
packet type マッチはパケットのタイプに基づいてマッチを行う。言い換えると、特定のひとりに宛てたものなのか、全員なのか、あるいは或る限られたマシンの一団やユーザの一団宛てなのか、ということだ。これら 3つの括りはそれぞれ、ユニキャスト、ブロードキャスト、マルチキャスト と呼ばれる。そのことは TCP/IPのおさらい のチャプターで述べている。このマッチは -m pkttype によってロードする。
Table 10-25. Packet typeマッチオプション
| マッチ | --pkt-type |
| カーネル | 2.3, 2.4, 2.5, 2.6 |
| 例 | iptables -A OUTPUT -m pkttype --pkt-type unicast |
| 説明 | --pkt-type マッチは、どのようなパケットタイプに合致させるかを指示する。例にあるように、その引数には unicast, broadcast, multicast のいずれが指定できる。 ! を使って -m pkttype --pkt-type ! broadcast のように意味を反転することも可能で、この場合、ブロードキャスト以外のパケットタイプにマッチすることになる。 |
realm は、そのパケットがどのルーティング・レルム (Routing realm) に属するかによってマッチを行う。ルーティング・レルムは Linux で複雑なルーティングを行う必要のある場面で、例えば、どんな場合に BGP を使用するかといった制御をするのに使われる。 realm マッチはコマンドラインに -m realm キーワードを加えるとロードされる。[訳者註: 英語彙 realm の元々の意味は「領域」「〜界」で、感覚的には domain という語と近い]
Linux においては、ルーティング・レルムというものは複数のルートを幾つかの論理上のグループにまとめるのに使われる。現今の専用ルータでは、RIB (Routing Information Base) と転送エンジン (forwarding engine) は非常に近くにある。例えばだが、どちらもカーネルの中で扱われていたりする。しかし Linux は専用ルータではないので、RIB と FIB (Forwarding Information Base) を別々に扱わざるを得ない。 RIB はユーザ空間、 FIB はカーネル空間にあるわけだ。これが足かせとなり、 RIB の高速検索はかなりリソースに厳しい処理となってしまう。ルーティング・レルムは Linux におけるその解決策であり、柔軟な対応と機能の充実をシステムにもたらしている。
Linux でのレルムは、BGP など、大量のルート情報を遣り取りするプロトコルと組み合わせての使用が想定される。そうすれば、ルーティングデーモンはプレフィクスや AS PATH、送信元などに基づいてそれらをソートし、別々のレルムに仕分けすることができる。 realm は数値だが、 /etc/iproute2/rt_realms ファイルで定義された名前を使うことも可能だ。
Table 10-26. Realmマッチオプション
| マッチ | --realm |
| カーネル | 2.6 |
| 例 | iptables -A OUTPUT -m realm --realm 4 |
| 説明 | このオプションはレルムナンバーと、場合によってはマスクによるマッチを行う。数値以外で指定された時には、ファイル /etc/iproute2/rt_realms を照会して数値への解決も行おうとする。名前によるレルム指定時にはマスクは指定できない。エクスクラメーションマークを使って意味を反転することも可能で、その場合の例は --realm ! cosmos のようになる。[訳者註: マスクの指定方法が原文に抜けているので補足。書式は REALM/MASK] |
recent マッチは、かなり幅広い使い方のある複雑なマッチング機構で、前に行われたマッチを手掛かりにしてパケットを選定することができる。例えば、今こちらから IRC コネクションを張ったとしよう。その IPアドレスをホストのリストに記録しておけば、別のルールで、先ほどのパケットから数えて 15秒間だけその IRC サーバからの ident 要求を許可する、といったことができるのだ。
このマッチをつぶさに見ていく前に、その動作の仕組みを分かってもらえるかやってみることにする。まず前提として、 recent マッチを活用するには複数のルールが要る。 recent マッチは最近発生したイベントについてのリストを何種類か使う。デフォルトで使用されるリストは DEFAULT リストだ。リストには set オプションを使ってエントリを追加するのだが、或るルールが完全にマッチする度に (set オプションではマッチは常に真となる)、当該の「最近 (recent) リスト」に set オプションでエントリをさらに追加していく。リスト上の各エントリには、タイムスタンプと、その set オプションのトリガーとなったパケットの送信元 IPアドレスが記録されている。ここまで準備が整えば、それらの情報を頼りに recent マッチの様々なオプションを使ってマッチを行ったり、エントリのタイムスタンプを更新するといったことができる。
最後に付け加えておくと、もし何かの理由で或るエントリをリストから削除したい場合には recent モジュールの提供する remove マッチで削除ができる。他のモジュールと同様、 recent マッチを使用するには recent モジュールをロード (-m recent) しなければならない。では、使用例を云々する前にまず、オプションの全てを見てみよう。
Table 10-27. Recentマッチオプション
| マッチ | --name |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A OUTPUT -m recent --name examplelist |
| 説明 | name オプションは使用するリストの名前を指定する。デフォルトでは DEFAULT リストが使用されるが、 DEFAULT リストは、複数のリストを使用している時にはおそらく用のないリストだ。 |
| マッチ | --set |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A OUTPUT -m recent --set |
| 説明 | このオプションを使うと name 済みのリストへエントリを追加できる。リストエントリには、そのルールのトリガーとなったホストの送信元 IPアドレスとタイムスタンプが格納される。このマッチは ! 記号を前置きすれば偽を返すが、そうしない限り常に真を返す。 |
| マッチ | --rcheck |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A OUTPUT -m recent --name examplelist --rcheck |
| 説明 | --rcheck オプションは指定のリストにそのパケットの送信元 IPアドレスが登録済みかどうかをチェックする。あれば真を返すし、なければ偽を返す。 ! 記号を使えばオプションの意味は反転する。その場合、送信元 IPアドレスがリストになければ真を返し、あれば偽を返す。 |
| マッチ | --update |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A OUTPUT -m recent --name examplelist --update |
| 説明 | このマッチは、その送信元情報の組み合わせが指定したリストにあれば真となるとともに、そのリストの最終観測時間を更新する。このマッチも前置きの ! 記号で反転でき、例えば ! --update のようにも指定できる。 |
| マッチ | --remove |
| カーネル | 2.4, 2.5, 2.6 |
| 例 | iptables -A INPUT -m recent --name example --remove |
| 説明 | このマッチを発すると、リストからそのパケットの送信元アドレスを探し、見つかれば |