3. 動作内容

上りの帯域を最適化するには、基本的な段階が二つあります。 ADSL モデムがキューを扱う方法を制御することはできないのですから、 まずは、パケットが ADSL モデムにキューイングされないようにする必要があります。 そのためには、ルーターが eth0 に送出するデータ量を、 ADSL モデムの上り方向の帯域全体よりも若干低めに抑えます。 この結果、許容されている送出速度よりも速い速度でローカルネットワークから到達するパケットを、 ルーターでキューイングする必要が出てきます。

二番目の段階は、ルーターに優先順位付きのキューイング方式を設定することです。 これについては、telnet やマルチプレーヤゲームといった、 対話的なトラフィックに優先順位をつけるように設定できるキューを調査します。

そして最後に、 fwmark を使ってパケットに優先順位を与えるように、 ファイアウォールを設定します。

3.1. Linux HTB を使って、外向きのトラフィックを抑える

ルーター・モデム間は 10Mbit/s で接続されていますが、 モデムは 128Kbit/s でしか送出できません。 この速度以上に送出されるデータは、いずれもモデムでキューに溜ります。 したがって、ルーターから出た ping パケットはすぐモデムに届くかもしれません。 でもそのモデムのキューになんらかのパケットがあれば、 その ping パケットが実際にインターネットに出ていくには数秒かかるかもしれません。 あいにく大半の ADSL モデムには、 パケットの取り出し方法やキューの大きさを指定する仕組みがありません。 ですから、ここでの最初の目標は、キューをもっと管理できるような場所に移して、 そこに外向きのパケットをキューイングするようにすることです。

この場合は、HTB キューを使って、 ADSL モデムにパケットを送出する速度を制限します。 上りの帯域は 128Kbit/s だとしても、 パケットの送出速度はそれより若干低めに制限しなければなりません。 遅延を抑えたければ、 モデムのキューには、一個たりともパケットが溜らないようにする必要があります。 実験を通じて分かったことですが、 外向きのトラフィックを約 90kbit/s に制限すれば、 HTB の速度制御を使わなくても、 帯域の 95% が使えるようになります。 この速度で HTB を有効にすれば、 ADSL モデムがパケットをキューイングしないようになりました。

3.2. HTB を使った優先順位付きキューイング

しかしこの時点では、性能の変化はまだ分かりません。 FIFO キューを ADSL モデムからルーターに移しただけですから。 実際、キューサイズをディフォルトの 100 パケットに設定した Linux では、 この時点で問題がたぶん悪化しています。 でももう少しです。

HTB キューの各隣接クラスには優先順位を割り当てることができます。 異なる種類のトラフィックを別々のクラスに置き、 それからこのクラスに別々の優先順位を割り当てれば、 パケットを取り出して送出する順序が管理できます。 HTB キューを使うと、このようなことも可能になるし、 どのクラスも沈み込まずに済みます。 というのは、各クラスに対して、最低保証速度を明記できるからです。 この他に、HTB を使えば、 ある上限までなら他のクラスの未使用帯域を使ってもいいと、 クラスに指示できるようにもなります。

まずクラスを設定し、 次にクラスにトラフィックを配置するためのフィルターを設定します。 これにはいくつか方法がありますが、本文書で解説する方法は、 fwmark でパケットに印をつけるのに、 慣れ親しんだ iptables/ipchains を使用します。 このフィルターは fwmark に基づいて、 HTB キューのクラスにトラフィックを配置します。 こうすれば、iptables にマッチングルールを設定し、 種類に応じてトラフィックを一定のクラスに送ることができます。

3.3. ibtables を使った外向きパケットの分類

ルーターを設定して、 対話的なトラフィックに優先順位を付ける最後の段階は、 ファイアウォールを設定して、 どうやってトラフィックを分類すればいいのかを定義することです。 これには、パケットの fwmark フィールドを設定します。

あまり詳細に立ち入らず、 ここでは優先順位のもっとも高いクラスが 0x00 になっている四つのクラスに、 外向きのパケットがどう分類されるのかを簡単に説明します。

  1. 全パケットに 0x03 という印をつけます。 これで、パケットはすべて、 ディフォルトで一番優先順位が低いキューに置かれます。

  2. ICMP パケットに 0x00 という印をつけます。 ping には遅延を示して欲しいので、 このパケットの優先順位は一番高くなります。

  3. 宛先のポートが 1024 以下になっているすべてのパケットに 0x01 という印をつけます。 これは Telnet や SSH といったシステムサービスに優先順位をつけているんです。 FTP の制御ポートもこの範囲に入りますが、 FTP のデータ転送は高位のポートで行なうので、 0x03 のバンドのままになります。

  4. 宛先のポートが 25 (SMTP) になっているすべてのパケットに 0x03 という印をつけます。 誰かが大きな添付ファイルをつけて電子メールを出しても、 それで対話的なトラフィックを埋め尽くしてほしくはないですからね。

  5. マルチプレーヤゲームサーバーに向かうパケットには、 すべて 0x02 という印をつけます。 これでゲーマーに対する遅延は低くなりますが、 同時にゲーマーのパケットが、低い遅延が必要なシステムアプリケーションを 埋め尽くさないようにもしています。

    他の「細かい」パケットには 0x02 という印をつけます。 内向きのダウンロードに対する外向きの ACK パケットはすぐに送出して、 効率的にダウンロードできるように保証したほうがいいですからね。 これは、iptables の length モジュールを使えば可能です。

もちろん、必要に応じてカスタマイズしてもかまいません。

3.4. もう少しチューニング

遅延改善のためにできることがあと二つあります。 まず、最大伝送ユニット (mtu) がディフォルトの 1500 バイト未満になるように設定できます。 この値を小さくすれば、既にフルサイズで優先順位の低い送出パケットがある場合に、 優先順位の高いパケットの送出を待つ平均時間が短くなります。 でも、この値を小さくすると、スループットが若干低下することにもなります。 なぜなら、各パケットには IP ヘッダーと TCP ヘッダーの情報が、 最低でも 40 バイト分含まれているからです。

優先順位の低いトラフィックでも、 遅延改善のためにできることがもう一つあります。それは、 キューの長さをディフォルトの 100 よりも小さくすることです。 ADSL ラインでは、キューの長さがディフォルトのままだと、 mtu が 1500 バイトのパケットを送出してしまうのに、 10 秒もかかってしまうことがあります。

3.5. 内向きのトラフィックを抑えてみる

Intermediate Queuing Device (IMQ) を使えば、 外向きのパケットをキューイングするのと同じ方法で、 内向きのパケットをすべてキューに入れるができます。 この場合、パケットの優先順位ははるかに単純です。 できる(やろうとしている)のは、 内向きの TCP のトラフィックを制御することだけですから、 TCP 以外のトラフィックは、すべて 0x00 クラスに配置し、 TCP トラフィックは、すべて 0x01 クラスに配置します。 他の「細かい」TCP パケットは、 おそらく送出済みの外向きデータに対する ACK パケットですから、 それらも 0x00 クラスに配置します。 0x00 クラスには標準的な FIFO キューを設定し、 0x01 クラスには Random Early Drop (RED) キューを設定します。 RED は TCP を制御する上で、 FIFO (tail-drop) キューよりも優れたキューです。 というのは、制御できなくなりそうな伝送をスピードダウンさせようとして、 キューがオーバーフローする前にパケットを落とすからです。 さらに、この 2 つのクラスの内向きの最大速度を制限します。 この速度は ADSL モデム上の実際の内向きの速度よりも低くします。

3.5.1. 内向きのトラフィックを制限しても、さほど効果がない理由

ISP 側でキューが飽和しないように、内向きのトラフィックを制限したいわけです。 でもこうすると、5 秒間分ものデータがバッファされることがあります。 問題は、内向きの TCP のトラフィックを制限する唯一の方法によって、 全く問題ないパケットまで落ちてしまうことです。 こういったパケットは既に ADSL モデム上の帯域をある程度占有していたものですが、 結果としてそれ以後のパケットをスピードダウンさせようとして、 Linux 機が落としてしまうのです。 これらの落ちたパケットは結局は再送され、 もっと帯域を消費してしまいます。 トラフィックを制限すると、 自分たちのネットワークで受け入れるパケット速度を制限することになります。 実際の内向きのデータ速度は、 パケットを落とした分だけ増加しますから、 実際は、確実に遅延を低くするには、ADSL モデムの実際の速度よりも、 下りの速度をずっと低く抑えなければならないんです。 実際には、 同時に五つのダウンロードをしながら遅延を許容範囲にしておくには、 ADSL の下り 1.5Mbit/s を 700kbit/s に落とす必要がありました。 TCP セッションが増えればもっとパケットが落ちるので、 帯域の無駄がさらに大きくなります。 だから速度の上限はもっと低く設定する必要があるでしょうね。

内向きの TCP のトラフィックを制御するもっと優れた方法は、 TCP ウィンドウサイズを操作することでしょうが、 本文書の作成時点では、 Linux 用にこれを実装した(フリーな)ものはありません (筆者は知りません)。