<!doctype linuxdoc system>

<article>

<title>traffic control Mini-Howto
<author>井之上　和弘, <tt/pierre@astrum.co.jp/
<date>1999/11/09 v0.13
<abstract>
この Mini-HOWTO では、Linux を稼働中のホストから送出されるパケットの
流量を QoS 機能を用いてコントロールする方法について簡単に説明します。
</abstract>

<toc>

<sect>はじめに
<p>
<sect1>本ドキュメントの有用性
<p>

下記に該当する方は、本ドキュメントを参照することで何らかのメリットがあるかもしれません。
<p>
<itemize>
<item>ftp や http で linux カーネルなどの大量のデータを受信中に
暇つぶしを兼ねてメールの送受信その他を実行してみたものの、
レスポンスが悪く、いらいらしたことがある方
<item>ネットワークに対する負荷の高い機材を Linux で運用していて、
なんとかしたいと考えている方
<item>ファイヤウォールを通過するパケットの帯域を制限したい方
<item>telnet や ssh で遠隔地の機材のメンテナンスを行う際のレスポンスが 
ftp や http などのトラフィックで低下することを避けたい方
</itemize>
<p>
逆に下記に該当する方には、本ドキュメントは不要かもしれません。
<p>
<itemize>
<item>現在の回線品質に不満を感じていない方
<item>ネットワークに対する負荷の高い機材を運用していない方
</itemize>

<sect1>免責
<p>
このドキュメントの内容について作成者は一切の責任を負いません。
<p>
<sect1>著作権表記
<p>
Copyright (C) 1999 Kazuhiro Inoue (Astrum, Inc)
<p>
このドキュメントは、フリーソフトウエアです。
GNU General Public License 第二版 (もしくはそれ以降の版) に従う限り、
この文書を配布または変更できます。 

<sect1>その他
<p>
本ドキュメントにおける各種用語の用法は間違っている可能性があります。
このドキュメントは「とりあえず動く」ことを最大の目的としていますので
正しい理解を深めるには他の適切な書籍等をあわせてお読みください。
<p>
日本語で読めるドキュメントとしては 
<url
	url="http://www.nic.ad.jp/iw98/presentations/C12.pdf"
	name="Internet Week 98 チュートリアル C12「QoS技術：Intservとdiffserv」">
などが参考になります。
<p>
また、帯域制御が可能なルータのドキュメントを読むのもよいでしょう。


<sect>一般的な基礎知識
<p>
<sect1>帯域制御と優先制御
<p>
ネットワークに接続されている機器がパケットを送出する場合、
パケットの衝突が発生しない限りは無条件に送出が行われます。

このため同一セグメントに大量のパケットを送出する機器が存在すると
他の機器のデータの送受信のレスポンスが大幅に悪化する原因となります。

また 128Kbps 以下の低速な回線では、 ftp や http で大量のデータの
送受信が行われると、メールの送受信や telnet, ssh のような接続で
レスポンスが著しく悪化します。
内部側ネットワークに複数の端末を接続している場合には、レスポンスの悪化が
より顕著に出てきます。

しかしパケットの送信先アドレスやポート番号に応じて帯域の上限値や
優先度を調整することができれば、ftp で大量のデータを受信中でも pop3 や 
smtp などのパケットを優先的に送出でき、レスポンスの向上につながります。

Linux カーネルではこのような制御を帯域制御と優先制御を組み合わせて
設定できます。

<sect2>帯域制御
<p>
帯域制御では基本的に次のルールでパケットの送出を行っています。

<itemize>
<item>単位時間あたりの転送量が設定値を下回っている間は
パケットの送出を行う。
<item>単位時間あたりの転送量が設定値を超えた場合は
パケットの送出を停止する。
</itemize>


たとえば 128Kbps の回線において ftp の帯域を 64Kbps と設定した場合、
その他のパケットについては少なくとも 64Kbps の帯域が残っているため、
外部ネットワークの pop3 サーバからメールを受信するような場合でも
レスポンスが大きく低下することはありません。


<sect2>優先制御
<p>
優先制御では同時に複数のパケットの送信が発生した場合に優先度の高い
パケットから処理を行います。
優先度の高いパケットが存在する間は基本的に優先度の低いパケットの
送出は行われません。
<p>
たとえば telnet や ssh のようなパケットを ftp や http のパケットよりも
優先するように設定すれば、遠隔地のホストに接続する場合でも十分な
レスポンスを確保することも不可能ではありません。
しかし優先度の設定を逆にすると、ftp や http で大量のデータを受信中に
telnet や ssh の接続を行っても全然接続できないという困った状況に
なります。
<p>
そのため、優先制御の設定を行う際は十分に注意する必要があります。


<sect1>QoS
<p>
QoS (Quality of Service) はネットワークの帯域をより効果的に利用する
ための技術で、Linux kernel では開発版カーネルの 2.1 で実装され、
現在の安定版カーネル 2.2.x で利用できます。
<p>
Linux kernel の QoS では前述の帯域制御と優先制御の両方が可能です。

<sect>ソフトウェアの入手・設定
<p>
<sect1>必要なソフトウェア
<p>
QoS 機能を利用するには下記のソフトウェアが必要です。
<p>
<enum>
<item>Linux kernel 2.2<p>
本ドキュメント執筆時点 (1999/10/29) での安定版カーネルのバージョンは 
2.2.13 です。
QoS は開発版カーネル (2.1.x) でもサポートされていますが、
特別な理由がなければ安定版カーネルを利用するのがよいでしょう。
<p>
<item>iproute+tc<p>
QoS の設定には iproute+tc が必要です。
<url url="ftp://ftp.infoscience.co.jp/pub/linux/ip-routing/iproute2-current.tar.gz"> 
や 
<url url="ftp://ftp.inr.ac.ru/ip-routing/iproute2-current.tar.gz"> 
から入手できます。
tarball ではなく rpm を入手したい場合は 
<url url="ftp://ftp.kddlabs.co.jp/.9/Linux/ipv6-rpm/"> 
や
<url url="ftp://omni.rk.tusur.ru/Tango/"> 
から入手できるようですが、筆者は主に Slackware を使用しているため動作の
確認はしておりません。
<p>
<item>cbq.init<p>
設定内容がシンプルな場合は cbq.init スクリプトを用いると便利です。
<url url="ftp://ftp.equinox.gu.net/pub/linux/cbq/cbq.init"> から
入手できます。
<p>
</enum>

<sect1>インストール先の決定
<p>
iproute2+tc も cbq.init も適切なパスに自分でコピーする必要がありますので、
どのディレクトリにコピーするかを予め決めておきましょう。
<p>
cbq.init をデフォルト設定のままで利用する場合は下記のとおりとなります。
<p>
<itemize>
<item>iproute2+tc<newline>
/sbin
<p>
<item>cbq.init<newline>
/etc/rc.d
<p>
<item>cbq.init で使用する設定ファイル<newline>
/etc/sysconfig/cbq
<p>
</itemize>

<sect1>QoS サポートを組み込んだカーネルのコンパイル
<p>
ここではカーネルをコンパイルする方法をすでに理解しているという前提で話を
進めます。
カーネルのコンパイルについての知識が不十分な場合は Linux Kernel HOWTO 
などの適切なドキュメントを先に参照してください。
<p>
QoS を利用するには少なくとも以下のオプションを組み込んでコンパイルする
必要があります。
モジュールとしてコンパイルしても動作するようですが、確認はしていません。

<itemize>
<item>Prompt for development and/or incomplete code/drivers
<item>QoS and/or fair queueing
<item>CBQ packet scheduler
<item>TBF queue
<item>QoS support
<item>Rate estimator
<item>Packet classifier API
<item>Routing table based classifier
<item>Firewall based classifier
<item>U32 classifier
<item>Ingres traffic policing
</itemize>

<sect1>iproute+tc のインストール
<p>
iproute2-current.tar.gz を任意のディレクトリで展開してください。
なお、libc5 の環境で BIND 4 の libresolv シェアードライブラリが存在
しない場合は Makefile の修正が必要です。glibc6 では特別な修正は不要です。

コンパイルの準備ができたら make を実行します。コンパイルが正常に終了
したら生成されたバイナリのうち、ip/ip と tc/tc を適切なディレクトリに
コピーします。
<p>
cbq.init では /sbin にインストールされていることを前提としていますから
よくわからない方はここにコピーしてください。

<sect1>cbq.init スクリプトのインストール
<p>
cbq.init スクリプトを適切なディレクトリにコピーし、chmod 500 cbq.init 
などの適切なパーミッションを設定しておきます。
Slackware の場合は /etc/rc.d あたりにコピーしておくとよいでしょう。
<p>
さらに環境に合わせて cbq.init の修正を行います。変更が必要な設定を
挙げておきます。
<p>
<itemize>
<item>PATH<newline>
ip, tc のインストールパスが含まれるようにしてください。
/sbin にインストールしている場合は変更不要です。<p>
<item>CBQ_PATH<newline>
cbq.init が使用する設定ファイルを格納するパスで、
デフォルトは /etc/sysconfig/cbq です。
必要に応じて修正してください。
<p>
<item>CBQ_INIT<newline>
cbq.init ではスクリプト自身ののインストールパスを /etc/rc.d と
しているため、異なるパスにインストールした場合は正しいパスを指定します。
ただし現在のところこの変数は未使用のようなので特に変更しなくても
問題はないと思われます。
<p>
<item>モジュールの組み込み設定<newline>
QoS 関連のモジュールをカーネルに直接組み込んでいる場合は、モジュールの
組み込みを行っている個所をコメントにします。
<p>
<item>その他<newline>
tc, ip のインストールパスが /sbin 以外の場合は、cbq.init 内で /sbin/tc,
/sbin/ip が存在しているかどうかを確認している個所が 1 個所ありますので
適切に修正してください。
</itemize>


<sect>トラフィックコントロールの設計
<p>
<sect1>注意事項
<p>
設計を行う前に一つだけ注意すべき点があります。
QoS の設定はデバイスから送出するパケットについてのみ有効であり、
受信するパケットについて設定することはできません。
<p>
ファイヤウォールを経由する送受信の場合は、内部側デバイスで設定を行うことで
外部から受信するパケットについての流量を調整することになります。

<sect1>トラフィックコントロールの設計
<p>
タイトルは堅苦しい表現を使っていますが、別に難しい話をするつもりは
ありません。
どのプロトコル（ポート番号）にどの程度の帯域を割り当てるかを決めましょう、
ということです。
<p>
ここでは例として下記のような設定を行うものとします。

<sect2>すべてのパケットの帯域を制限する
<p>
このケースでは設定は非常にシンプルなので、cbq.init を使用しなくても
設定できます。
しかし負荷の高い機材でこれを行うと telnet 等でのメンテナンスも困難と
なりますのでこの設定を行うケースは少ないかもしれません。
<p>
ここでは eth0 の帯域を 512Kbps に制限する設定を行ってみます。

<sect2>http, ftp のみ制限する
<p>
このケースでは cbq.init スクリプトを利用したほうが設定が簡単です。
ここでは http に 64Kbps, ftp に 32Kbps の帯域を割り当てるケースを考えます。

<sect2>ファイヤウォールを通過するパケットについて制限する
<p>
ここでは次のようなネットワークについて設定を行うものとします。
<p>
192.168.0.0/24 はプライベートネットワークで、local 側から
ファイヤウォールを通過するすべてのパケットは IP マスカレード
で変換されるものとします。
<p>
<quote><verb>
  (any)
    |
 router
    |
---------------------------- 172.16.0.0/24 (global)
       |
       | eth0, 10baseT, 172.16.0.1
      F/W
       | eth1, 10BaseT, 192.168.0.1
       |
---------------------------- 192.168.0.0/24 (local)

</verb></quote>

設定方針:<p>
<itemize>
<item>telnet, ssh は制限なし。
<item>local -&gt; global は制限なし。
<item>any &lt;-&gt; local の http を 96Kbps に制限
<item>any &lt;-&gt; local の ftp は 64kbps に制限
<item>any &lt;-&gt; local へのその他のパケットは制限なし。
<item>その他のパケットは制限しない。
<item>優先順位は ftp < http < その他の順で ftp が最も低い
</itemize>

<sect>設定手順
<p>
<sect1>すべてのパケットを制限する
<p>
次のコマンドを実行してください。

<quote>
tc qdisc add dev eth0 root tbf limit 15Kb buffer 10Kb/8 rate 512Kbps
</quote>

次に設定が有効となっていることを適切なツールを用いて確認します。
ncftp や wget などのように転送レートが確認できるツールを用いて
ファイルの転送を行ってみてください。
<p>

設定を削除する場合は次のコマンドを実行します。

<quote>
tc qdisc del dev eth0 root
</quote>

帯域の上限値は rate の部分を任意の数値に変更することで自由に設定できます。
単位は Kbps, Mbps が使用できます。

動作が確認できたら、設定コマンドが起動時スクリプトの適切な個所で実行される
ようにしてください。


<sect1>http, ftp のみ制限する
<p>
cbq.init 用の設定ファイルを下記のように作成します。

<quote><verb>
/etc/sysconfig/cbq/cbq-64.http
DEVICE=eth0,10Mbit,1Mbit
RATE=64Kbit
WEIGHT=6Kbit
PRIO=5
RULE=:80

/etc/sysconfig/cbq/cbq-32.ftp
DEVICE=eth0,10Mbit,1Mbit
RATE=32Kbit
WEIGHT=3Kbit
PRIO=5
RULE=:20
RULE=:21
</verb></quote>

この例はネットワークデバイスの帯域速度が 10Mbps を前提としていますので、
100BaseTX のカードを利用している場合は DEVICE=eth0,100Mbit,10Mbit と
設定します。

ファイルが作成できたら 'cbq.init start' を実行し、wget や ncftp などで
動作を確認します。

動作が確認できたら cbq.init スクリプトが起動時スクリプトの適切な個所で
実行されるようにします。

cbq.init の設定ファイルの細かい説明については、cbq.init スクリプトの
コメント部分に解説がありますのでそちらを参照してください。

<sect1>ファイヤウォールを通過するパケットについて制限する
<p>
ファイヤウォールを通過するパケットについても、基本的には cbq.init 
スクリプトを使用して設定することができます。しかし設定が複雑になると 
cbq.init スクリプトではエラーとなる
ケースがありますので、ここでは tc コマンドを直接実行するスクリプトを
作成してみます。<p>
iproute+tc に添付のドキュメントは英語なので、英語が苦手な方は
これを読むのも一苦労だと思いますが日本語で細かい説明をはじめると
ドキュメントの量が増えてしまいます。
<p>
そのため動確認済みのサンプルスクリプトを引用しておきますので、
これを適当に修正して使うなり、参照しながら iproute+tc のドキュメントを
読むなりして理解してください。
<p>

サンプルスクリプトでは cbq.init と同様に CBQ (Class Based Queue) 
スケジューラを用いて設定を行っています。
CBQ の大きな特徴はその名前のとおり、クラス構造による帯域制御を行うことが
可能であるということです。
<p>
そのため CBQ での基本的な設定手順は次のようになります。
<p>
<enum>
<item>ルートクラスの設定<newline>
ネットワークデバイスに cbq パケットスケジューラを設定します。<p>
<item>親クラスの帯域設定<newline>
すべてのクラスのベースとなる親クラスを設定します。<p>
<item>子クラスの帯域設定<newline>
親クラスから派生する子クラスを設定します。<p>
<item>子クラスに孫クラスを設定するか、またはパケットスケジューラを設定<newline>
子クラスに対して必要に応じて孫クラスを設定します。孫クラスが不要な場合はここで
tbf パケットスケジューラを設定します。<p>
<item>設定したクラスを適用するフィルタの設定<newline>
設定した各クラスを適用するフィルタを設定します。<p>
</enum>

なお IP マスカレードを使用している環境でも、
これらの設定を行う際には内部側、外部側のネットワークを
そのまま指定するだけで設定を行うことができます。
<p>
以下は設定スクリプトの例です。

<quote><verb>
#!/bin/sh
########################################################################
#
# rc.cbq
#
#	1999/10/10 Kazuhiro Inoue (Astrum, Inc)
#
########################################################################

PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin"

GLOBAL=172.16.0.0/16
LOCAL=192.168.0.0/24

################
# ルールの初期化

tc qdisc del dev eth0 root 1> /dev/null 2> /dev/null
tc qdisc del dev eth1 root 1> /dev/null 2> /dev/null


##############################
# ルートクラス・親クラスの作成

## /dev/eth0 のルートクラスに cbq をセットし、ハンドルを 10 とする ##
tc qdisc add dev eth0 root handle 10: cbq \
        bandwidth 10Mbit avpkt 1000 cell 8

## /dev/eth1 のルートクラスに cbq をセットし、ハンドルを 11 とする ##
tc qdisc add dev eth1 root handle 11: cbq \
        bandwidth 10Mbit avpkt 1000 cell 8

## 10Mbit/sec の帯域クラスを priority 8 で作成 (classid 10:1)
##
## 以後、handle 10:1 を parent とするクラスは最大で 10Mbit/sec の
## 帯域が利用可能となる。
tc class add dev eth0 parent 10:0 classid 10:1 cbq \
        bandwidth 10Mbit rate 10Mbit allot 1514 cell 8 \
        weight 1Mbit prio 8 maxburst 20 avpkt 1000

## 10Mbit/sec の帯域クラスを priority 8 で作成 (classid 10:1)
##
## 以後、handle 11:1 を parent とするクラスは最大で 10Mbit/sec の
## 帯域が利用可能となる。
tc class add dev eth1 parent 11:0 classid 11:1 cbq \
        bandwidth 10Mbit rate 10Mbit allot 1514 cell 8 \
        weight 1Mbit prio 8 maxburst 20 avpkt 1000


##################################
# /dev/eth0 の帯域制御クラスの作成

## 64Kbit/sec の帯域クラスを priority 5, classid 10:64, parent 10:1 で ##
## 作成し tbf スケジューラを設定 ##
tc class add dev eth0 parent 10:1 classid 10:64 cbq \
        bandwidth 10Mbit rate 64Kbit allot 1514 cell 8 \
        weight 6Kbit prio 5 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth0 parent 10:64 tbf \
        rate 64Kbit buffer 10Kb/8 limit 15Kb

## 96Kbit/sec の帯域クラスを priority 4, classid 10:65, parent 10:1 で ##
## 作成し tbf スケジューラを設定 ##
tc class add dev eth0 parent 10:1 classid 10:65 cbq \
        bandwidth 10Mbit rate 96Kbit allot 1514 cell 8 \
        weight 9Kbit prio 4 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth0 parent 10:65 tbf \
        rate 96Kbit buffer 10Kb/8 limit 15Kb

## 10Mbit/sec の帯域クラスを priority 3, classid 10:66, parent 10:1 で ##
## 作成し tbf スケジューラを設定 ##
tc class add dev eth0 parent 10:1 classid 10:66 cbq \
        bandwidth 10Mbit rate 10Mbit allot 1514 cell 8 \
        weight 1Mbit prio 3 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth0 parent 10:66 tbf \
        rate 10Mbit buffer 10Kb/8 limit 15Kb


##########################################
# 各帯域クラスを適用するネットワークを定義

## GLOBAL と LOCAL の間にクラス 10:66 を適用 ##
tc filter add \
        dev eth0 parent 10:0 protocol ip prio 100 u32 \
        match ip src $GLOBAL \
        match ip dst $LOCAL flowid 10:66

## LOCAL から任意のホストへの ssh にクラス 10:66 を適用 ##
tc filter add \
        dev eth0 parent 10:0 protocol ip prio 100 u32 \
        match ip sport 22 0xffff \
        match ip dst $LOCAL flowid 10:66

## LOCAL から任意のホストへの telnet にクラス 10:66 を適用 ##
tc filter add \
        dev eth0 parent 10:0 protocol ip prio 100 u32 \
        match ip sport 23 0xffff \
        match ip dst $LOCAL flowid 10:66

## LOCAL から任意のホストへの http にクラス 10:65 を適用 ##
tc filter add \
        dev eth0 parent 10:0 protocol ip prio 100 u32 \
        match ip sport 80 0xffff \
        match ip dst $LOCAL flowid 10:65

## LOCAL から任意のホストへの ftp にクラス 10:64 を適用 ##
tc filter add \
        dev eth0 parent 10:0 protocol ip prio 100 u32 \
        match ip sport 21 0xffff \
        match ip dst $LOCAL flowid 10:64
tc filter add \
        dev eth0 parent 10:0 protocol ip prio 100 u32 \
        match ip sport 20 0xffff \
        match ip dst $LOCAL flowid 10:64

## LOCAL から任意のホストへの任意の接続にクラス 10:66 を適用 ##
tc filter add \
        dev eth0 parent 10:0 protocol ip prio 100 u32 \
        match ip dst $LOCAL flowid 10:66


##################################
# /dev/eth1 の帯域制御クラスの作成

## 64Kbit/sec の帯域クラスを priority 4, classid 11:64, parent 11:1 で ##
## 作成し tbf スケジューラを設定 ##
tc class add dev eth1 parent 11:1 classid 11:64 cbq \
        bandwidth 10Mbit rate 64Kbit allot 1514 cell 8 \
        weight 6Kbit prio 5 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth1 parent 11:64 tbf \
        rate 64Kbit buffer 10Kb/8 limit 15Kb

## 96Kbit/sec の帯域クラスを priority 4, classid 11:65, parent 11:1 で ##
## 作成し tbf スケジューラを設定 ##
tc class add dev eth1 parent 11:1 classid 11:65 cbq \
        bandwidth 10Mbit rate 96Kbit allot 1514 cell 8 \
        weight 9Kbit prio 4 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth1 parent 11:65 tbf \
        rate 96Kbit buffer 10Kb/8 limit 15Kb

## 10Mbit/sec の帯域クラスを priority 3, classid 11:66, parent 11:1 で ##
## 作成し tbf スケジューラを設定 ##
tc class add dev eth1 parent 11:1 classid 11:66 cbq \
        bandwidth 10Mbit rate 10Mbit allot 1514 cell 8 \
        weight 1Mbit prio 3 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth1 parent 11:66 tbf \
        rate 10Mbit buffer 10Kb/8 limit 15Kb


##########################################
# 各帯域クラスを適用するネットワークを定義

## GLOBAL と LOCAL の間にクラス 11:66 を適用 ##
tc filter add \
        dev eth1 parent 11:0 protocol ip prio 100 u32 \
        match ip src $LOCAL \
        match ip dst $GLOBAL flowid 11:66

## LOCAL から任意のホストへの ssh にクラス 11:66 を適用 ##
tc filter add \
        dev eth1 parent 11:0 protocol ip prio 100 u32 \
        match ip sport 22 0xffff \
        match ip dst $LOCAL flowid 11:66

## LOCAL から任意のホストへの telnet にクラス 11:66 を適用 ##
tc filter add \
        dev eth1 parent 11:0 protocol ip prio 100 u32 \
        match ip sport 23 0xffff \
        match ip dst $LOCAL flowid 11:66

## LOCAL から任意のホストへの http にクラス 11:65 を適用 ##
tc filter add \
        dev eth1 parent 11:0 protocol ip prio 100 u32 \
        match ip sport 80 0xffff \
        match ip dst $LOCAL flowid 11:65

## LOCAL から任意のホストへの ftp にクラス 11:64 を適用 ##
tc filter add \
        dev eth1 parent 11:0 protocol ip prio 100 u32 \
        match ip sport 20 0xffff \
        match ip dst $LOCAL flowid 11:64
tc filter add \
        dev eth1 parent 11:0 protocol ip prio 100 u32 \
        match ip sport 21 0xffff \
        match ip dst $LOCAL flowid 11:64

## LOCAL から任意のホストへの任意の接続にクラス 11:66 を適用 ##
tc filter add \
        dev eth1 parent 11:0 protocol ip prio 100 u32 \
        match ip dst $LOCAL flowid 11:66

## end of rc.cbq ##
</verb></quote>


<sect>さいごに
<p>
このドキュメントは内容が不十分なため、これだけで 100% を理解することは
難しいかもしれません。
しかし Linux kernel の QoS 機能については日本語はおろか英語のドキュメント
でさえもあまり存在しないようです。
<p>
そういう意味では、このドキュメントでも QoS 機能を利用してみたいという方に
は何らかの役に立つことでしょう。
<p>
</article>
