昔からパソコン通信をやってた人は実感している*14 と思いますが, データ通信にはエラーがつきものです。 IP(Internet Protocol)においても, 通信経路上の通信エラー, 輻輳(ふくそう)などの原因によってパケットは壊れたり捨てられたりします。
UDP(User Datagram Protocol)*15 のように, 失われたパケット対策*16 をアプリケーションが面倒を見るプロトコルもあります。 しかし, 多くのアプリケーションは, パケットが失われたか否かを監視するのが面倒なので, データが失われずに届き, かつ送信ホストが送り出した順番で受信ホストに届くことを保証したプロトコルである TCP(Transmission Control Protocol)を利用しています。
TCP についてより詳しく知りたい場合は, RFC761 を参照してください。 ここではファイアウオールに関係してくる部分だけを簡単に解説します。
TCP のパケットは, ヘッダーとデータからなり, ヘッダーには図 3 のような情報が含まれます。
| |||||||||||||||||||||||||||||||||
図 3 TCP パケットのヘッダーの内容 |
ただし図 3 の(1), および(3)は TCP に限らず, すべての IP パケットに共通して含まれます。 インターネットにおいてホストを識別するためのアドレス, すなわち IP アドレスです。 (2)と(4)は UDP にもありますが, その他は TCP に特有で, TCP の信頼性を高めたデータ通信を支えるためのものです。
TCP では送信するデータの 1 バイトずつにシーケンス番号が割り振られます。 最初の 1 バイトの番号が 101 ならば次のバイトは 102, 1000 バイトのデータを送った後ではその次のデータは 1101 といった具合です。 図 3 の(5)のシーケンス番号は, これから相手ホストへ送る最初のデータのシーケンス番号(以下,SEQ 番号) を表わします。
また(6)確認応答番号(以下,ACK 番号)は, 相手ホストから正常に受け取ったデータのシーケンス番号の次の番号を表わします。 相手ホストが送信した最初の 1 バイトの番号が 301 で, 1000 バイトを正常に受け取ったのならば, ACK 番号は 1301 になります。 つまりシーケンス番号が「ACK 番号」であるデータを送ってほしい, という要求であると考えると理解しやすいかも知れません。
ホストA からホスト B へ TCP/IP で接続する場合を例に, TCP の仕掛けを説明しましょう。 図 4 に接続が完了する(ESTABLISHED 状態) までのハンドシェイクの様子を示します。
図 4 3-ウエイ・ハンドシェイク |
シーケンス番号を同期させるために,3 つのパケットをやりとりする。 |
最初, ホスト A の状態は CLOSED, つまり接続されていない状態, ホスト B は LISTEN, つまり接続受付可能状態です。 ホスト A は, ホスト B に対して接続要求パケットを送信します。 「SEQ 番号」はホスト A が適当に決める数値です。 また「SYN フラグ」は, TCP 接続において最初に送られるパケットにのみ*17 フラグが立ちます (すなわち値が 1 )。
この最初の接続要求パケットのことを, SYN パケットと呼びます。 送信後, ホスト A の状態は「SYN-SENT 」, つまり「SYN パケットを送信した」状態へ移行します。
接続要求パケットがホスト B に届くと, ホスト B の状態は「SYN-RECEIVED 」, つまり「SYN パケットを受信した」状態に移行します。
そしてホスト B は, SYN パケットを受け取ったことをホスト A に伝えるべく, 「ACK 番号」に SYN パケットの SEQ 番号に 1 を加えた値, つまり 101 を設定した確認応答パケットを送信します。 この 101 が, ホスト A が送るデータの最初の 1 バイトのシーケンス番号となります。
確認応答パケットの場合, 「ACK フラグ」が立ちます。 TCP 接続の場合, SYN パケット以外のパケットはすべて直前に接続相手から送られたパケットの 確認応答という性格を帯びますので, すべて ACK フラグが立っています。 「SEQ 番号」はホスト B が適当に決める数値です。 このパケットはホスト B からホスト A に送られる「最初の」パケットですから 「SYN フラグ」が立ちます。 この最初の確認応答パケットのことを SYNACK パケットと呼びます。
SYNACK パケットがホスト A に届くと, ホスト A とホスト B の双方が, お互いのデータの最初の 1 バイトのシーケンス番号を知ったことを, ホスト A は知ります。
なお, この段階ではまだホスト B は, ホスト B のデータの最初の 1 バイトのシーケンス番号がホスト A に伝わったか どうかを知ることができないことに注意してください。
「双方がお互いのシーケンス番号を知ったこと」 を知る状態を「ESTABLISHED」, つまり「接続完了」状態と呼びます。 ホスト A は「ESTABLISHED」状態に移行しますが, この段階では, まだホスト B は「ESTABLISHED」状態に移行できません。
ホスト A は, ホスト B が送った SYNACK パケットを受け取ったことをホスト B に伝えるべく, 「ACK 番号」に SYNACK パケットの「SEQ 番号」に 1 を加えた値, 301 を設定した確認応答パケット(以下,ACK パケットと略記)を送信します。 この 301 が, ホスト B が送るデータの最初の 1 バイトのシーケンス番号となります。
このパケットを受け取って初めて, ホスト B は, ホスト B のデータの最初の 1 バイトのシーケンス番号が ホスト A に伝わったことを確認できます。 つまりホスト B も「ESTABLISHED」状態に移行します。
以上の 3 つのパケットのやりとりは, 「3-ウエイ・ハンドシェイク」と呼ばれます。 この 3-ウエイ・ハンドシェイクによって, 双方のホストがお互いのシーケンス番号を同期させると, 後はどちらのホストからでも, データを送信することができます。
データを受信した側は, 適当なタイミングで正しく受信できた最後のデータのシーケンス番号の次の番号を 「ACK 番号」にセットして, ACK フラグを立てた ACK パケットを送ります。 もしデータが通信エラーなどで受信側に届かなかった場合, 送信側が送るパケットの SEQ 番号より, 受信側が送り返す ACK パケットの ACK 番号の方が小さくなります。 その場合,送信側はシーケンス番号が「ACK 番号」であるデータから送信し直します。
例えば, n0 バイト, n1 バイト, n2 バイトの 3 つのパケット(図 5)を図 6 のように送ったとします。 もし 3 つのパケットとも正常に受信側で受信できたとすると, 受信側は「ACK 番号」に x + n0 + n1 + n2 をセットして, ACK パケットを返します。
| |||||||||
図 5 送信した 3 つのパケット |
図 6 データの送信 |
確認応答番号によって, 何バイト目のデータまで正しく受信側に届いたかを, 送信側は認識できる。 |
もし最後のパケットが何らかの原因で受信側で受信できなかったとすると, 正常に受信できた最後のデータのシーケンス番号は x + n0 + n1 − 1 ですから, 受信側は「ACK 番号」に x + n0 + n1 をセットして, ACK パケットを返します。 すると, 送信側はシーケンス番号 x + n0 + n1 のデータから再送しなければなりません。 つまり,図 7 のパケットを再送します。
| |||
図 7 再送されるパケット |
本稿は日経Linux 2000 年 10 月号に掲載された、 実践で学ぶ、一歩進んだサーバ構築・運用術, 第 7 回「ファイアウォール (前編)」を日経BP 社の許可を得て転載したものです。
Copyright(C)2000 by 仙石浩明 <sengoku@gcd.org>
無断転載を禁じます