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 メッセージを受け取って (願わくば) 接続を取り止める。