基本的なトラフィック制御の実現

以下のテキストは、執筆時当時の情報を元に書いたものであり、 現在の情勢にそぐわないことを含む場合があるので注意されたい。 また、テキストは最終提出原稿で校正を経る前のものなので、実際にUNIXUSER 本誌に記載されたものとは異なる。誤字脱字等そのままである。

致命的な誤り以外は加筆修正等は行なわないので情報の鮮度に気をつけつつ 利用して欲しい。

目次


===========================================
Part2 基本的なトラフィック制御の実現
===========================================

Part1で解説したトラフィック制御機構を実際に管理場面に適用する例を挙げて
行こう。ここでは ipfw+dummynet(FreeBSD) と iproute2+tc(Linux)、
そして altq(NetBSD) による実際の制御手順を追って行く。

■
■トラフィック制御の設計
■

実際の設定に入る前に、どのようなプランでトラフィック制御を構築するかを考
えよう。トラフィック制御をを行なう目的を考えると分かりやすい。多くの場合
トラフィック制御は、特定のネットワークサービスの通信量が肥大したときにも、
その他のサービス供給の質を落さないために導入を検討するものであろう。そう
した場合、利用者からの観点ではスループットが低下したときに、

	* 対話的・同期的なサービスは敏感にストレスを感じやすい
	  (リモートログイン等)
	* 非同期的なサービスはあまり気付かない
	  (電子メイルの配送等)

ということが言える。ここでは、上記の判断を現実の設定として反映させていく
方法を考える。これを適用する仮想事例として、以下のようなケースを考える。

	* 対外回線の物理帯域幅は1Mbpsである
	* 制御対象とするサービスは SSH(22), HTTP(80), SMTP(25)
	  (いずれも外向き)
	* 非同期、非対話的なSMTPは全体の1/4程度の速さに思い切って絞る
	* 同期的だが遅くても「重いなぁ」程度であまりイライラしない
	  HTTPは「SSHの妨げにならない程度」の帯域幅利用にする
	* SSHは残りを悠々使用する

■
■制御の概要
■

上記のようなトラフィック制御の方針を、Part1で紹介した制御方式に適用する
には、以下のような設定に噛み砕いておくと実際の設定に反映させやすい。

	* SMTPは上限を決めた Traffic Shaping で256Kbpsに絞る
	* 残りの768KbpsをHTTPとSSHに割り当て、それぞれが 3:7 で
	  帯域を使用するように指定する


後者は少し複雑だが、Part1の内容と照らし合わせれば、もうピンと来るだろう。
Weighted Fair Queue と Traffic Shaping を組み合わせれば実現可能だ。
イメージとしては【図 は】のようになるだろう。

---[図 は]------------------------------------------------------------
%%% これも丸枠でお願いします。

       通信回線全体
         (1Mbps)
   +------------------------------------------------------+
   |                                                      |
   |  +----256Kbps-----------+                            |
   |  |                      |                            |
   |  |     [SMTP]	     |                            |
   |  | [SMTP]        [SMTP] |                            |
   |  |         [SMTP]	     |                            |
   |  +----------------------+                            |
   |  +------------------- 768Kbps --------------------+  |
   |  |			        		       |  |
   |  | +---------+  +-------------------------------+ |  |
   |  | | [HTTP]  |  |   [SSH]                       | |  |
   |  | |         |  |                 [SSH]         | |  |
   |  | | [HTTP]  |  |             [SSH]             | |  |
   |  | |                              [SSH]         | |  |
   |  | | [HTTP]  |  |        [SSH]                  | |  |
   |  | |         |  |                    [SSH]      | |  |
   |  | |         |  |    [SSH]                      | |  |
   |  | +---------+  +-------------------------------+ |  |
   |  |       3    :    7                              |  |
   |  +------------------------------------------------+  |
   +------------------------------------------------------+

----------------------------------------------------------------------


以下、FreeBSDとLinuxにおけるトラフィック制御設定例を順に示す。いずれの場
合もいきなり完成形を示すのではなく、

	1. 固定的 Traffic Shaping の設定と検証
	2. 3:7の比率でのトラフィック割り振りの設定と検証
	3. 1と2両者の統合(完成形)

とステップを踏む形で進める。

●注意点

トラフィック制御で速度を律する場合、たとえば「256Kbps」と指定したとして
も実際のデータ転送時に正確に256Kbpsになるわけではない。現実には回線負荷
の変動、システムの負荷の変動など様々な要因により、256Kbpsをつねに保ちつ
づけることはまれといって良い。したがって、指定した値はあくまでも「上限値」
でありそれを下回ることを確認できれば良い。

逆に、「下限値」を指定する「帯域保証」もPRIQを組み合わせることである程度
実現可能だが、これも物理的に回線を占有できなければ現実的ではなく、別の次
元の問題となるので今回は触れない。

■
■ipfwを用いた設定(FreeBSD)
■

ipfwはパケットフィルタリングも行なえるが、今回それについては触れない。お
そらく、トラフィック制御を考えている管理者であれば既にipfwによるパケット
フィルタリングの設定はしている可能性が高い。その場合、トラフィック制御を
行なうルールが、同時にパケット通過の許可を意味するという点だけ注意して欲
しい【註 り】。たとえば、
---[註 り]------------------------------------------------------------
sysctl変数 net.inet.ip.fw.one_pass がセットされていないときは
後続するルール評価に戻される。
----------------------------------------------------------------------

	ipfw pipe 512 config bw 512K
	ipfw add pipe 512 tcp from me 22 to any

のように先に設定した場合、自ホストへのSSH接続が全て512Kbpsに制限されたう
えで許可される。したがってそれ以後のルールで

	ipfw add reset tcp from me 22 to 10.10.10.1

などとしてもこのルール評価にマッチするようなパケットはここに到達しない。


●pipeとqueue

ipfwでは選別したパケットをdummynet(4)の持つ2つのトラフィック制御機構に渡
すことができる。

	* pipe	- 帯域幅、遅延、キューサイズ、パケットロス率 を
	  	  指定した値に設定できるパイプ。
	* queue	- WF2Q+によるキュー。各キューの行先を
	  	  pipeに繋ぐ。queueには「重み」を与えることができ、
		  同じpipeに繋がれたqueue同士は、各自の持つ「重み」
		  の比率にしたがって処理される。

例とともに pipe と queue の作成方法を示そう。

・作成例

  pipeとqueueは、

	ipfw pipe <パイプ番号> config <パイプパラメータ>
	ipfw queue <キュー番号> config <キューパラメータ>

  として作成する。番号の部分は1〜65535の任意整数でそれぞれの識別子と
  なる。パラメータの部分に指定できるものを【表 に】に示す。

---[表 に]------------------------------------------------------------

 ----------------------------------------------------------
  【pipeとqueue両方に指定できるもの】
 ----------------------------------------------------------
  buckets <ハッシュ表サイズ>
	キューに使われるハッシュのサイズ
  mask <マスク指定>
	pipeまたはqueueに送られて来たパケットのsrc/dstアドレス、ポート、
  	プロトコル番号に応じて、各パケットごとに独立した(同じパラメータ
  	の)pipe/queueに入れることができる。<マスク指定>は
	  dst-ip <マスク値>
	  src-ip <マスク値>
	  dst-port <マスク値>
	  src-port <マスク値>
	  proto <マスク値>
	  all
	のいずれかで行ない、<マスク値>でビットマスクした値が同じパケッ
  	トは同じpipe/queueに入れられる。同じサービス用のpipe/queueを全ク
  	ライアントで共有するのではなく、クライアントごとに分けたい場合に
  	有用。
  noerror
	パケットを落とした場合にエラーを返さない。
  plr <ロス率>
	指定したロス率(0〜1)でパケットロスを(意図的に)発生させる。
  queue (<スロット数> | <KB>)
	queueのサイズ指定。
  red | gred  <w_q/min_th/max_th/max_p>
	RED(Random Early Detection)またはGRED(Glant RED)の指定。
 ----------------------------------------------------------
  【pipeに指定できるもの】
 ----------------------------------------------------------
  bw <帯域幅>
	帯域幅の指定。単位として bit/s または Byte/s を指定する。
	単位の前に K あるいは M も指定できる。
  delay <ミリ秒値>
	遅延の指定。
 ----------------------------------------------------------
  【queueに指定できるもの】
 ----------------------------------------------------------
  pipe <パイプ番号>
	指定した番号のパイプに接続する。
  weight <重み値>
	キューの持つ重み値(1〜100)を指定する。同じパイプに接続されたキュー
  	同士の重みの比率によって処理される割合が計算される。
 ----------------------------------------------------------

----------------------------------------------------------------------

  まず単純な例として、上限が256Kbpsに制限されたpipeを作ってみよう。pipe
  の作成には、識別子となる整数値が必要だが、これは他と重複しなければ何で
  も良い。分かりやすいように制限値と同じ256番としよう。

	ipfw pipe 256 config bw 256K

  これだけだ。作成した256番のパイプを通るように、フィルタリングルールを
  追加する。ルール追加には ipfw の add コマンドを使用する。

	ipfw add [ルール番号] pipe <パイプ番号> <パケットの選別指定>

  <パケットの選別指定>にはパケットフィルタリングのときと同様のルールを
  書く。ここでは【表 ほ】の関係があるものとし、特定のホストからのSSH接続
  を256Kbpsのpipeに通す例を示す。以下の実行例にあるプロンプトは、コマン
  ド入力するホスト名を示している。

---[表 ほ]------------------------------------------------------------
  表: ファイル転送速度を測定する2台のマシンの仮定

					ホスト名	IPアドレス
   トラフィック制御をかけるホスト	server		10.1.1.1
   測定を行なう別のホスト		client		10.1.1.100
----------------------------------------------------------------------



	server# ipfw add pipe 256 tcp from me 22 to 10.1.1.100
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  別のマシン client(10.1.1.100)からsshでserverからのファイルをコピーして
  速度を計ってみよう【註 へ】。

	client# ssh -o Compression=no server cat /bin/csh | dd of=/dev/null
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	1273+1 records in
	1273+1 records out
	651840 bytes transferred in 21.670 secs (30080 bytes/sec)

  ただしSSHによるデータ転送では暗号化処理が入るためCPU処理速度の影響を受
  けやすい。そのため正味の転送速度の測定には不向きだが、特別な設定を追加
  せずに測定が行なえるので、測定数値が目安程度であることを考慮しつつ利用
  する分には都合が良い。今回の場合は設定速度が遅いのでSSHプロトコルによ
  るオーバーヘッドの影響はすくないが、より高速の設定速度の確認をする場合
  はオーバーヘッドの小さいftpを利用すると良い【註 へ】。
---[註 へ]------------------------------------------------------------
このようなときの実験目的で普段起動していないデーモンを臨時に有効化したり、
パケットフィルタリングを解除したりし、そのまま戻し忘れてセキュリティホー
ルにつながることが多い。実験のあとは後始末を忘れないよう十分に注意して欲
しい。
----------------------------------------------------------------------

 上記の例で得られた速度(30080 bytes/sec)をKbpsに換算すると

	% echo '30080*8/1024' | bc -l
	  ~~~~~~~~~~~~~~~~~~~~~~~~~~~
	235.00000000000000000000

  235Kbpsとなり、ほぼ256Kbps程度に抑えられていることが分かる。

●queueの実験

続いてはHTTPとSSHを 3:7 の比率で共有する設定をかけてみよう。まず先ほどの
pipeに導くルール設定を消しておく。

	server# ipfw sh | grep 'pipe 256'
		~~~~~~~
	00100     1858      869804 pipe 256 tcp from me 22 to 10.1.1.100
	(ルール番号が100の場合)
	server# ipfw del 100
		~~~~~~~~~~~~

256Kbpsのpipeを共有するqueueを二つ作成する。それぞれの持つ重みを3:7の比
率にし、256番のpipeに接続する。

	server# ipfw queue 1 config weight 30 pipe 256
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	server# ipfw queue 2 config weight 70 pipe 256
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

パケットフィルタリングルールにより、HTTPとSSHをそれぞれ二つ作成したqueue
に向かわせる。

	server# ipfw add queue 1 tcp from me 80 to 10.1.1.100
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	server# ipfw add queue 2 tcp from me 22 to 10.1.1.100
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

先程と同様、別マシン(10.1.1.100)からHTTPとSSH経由でファイルコピーを行ない、
転送速度の比較をしてみる。以下に、筆者の環境で測定した例を示す。

	(SSH単独でコピーしたとき)
	server# ssh -o Compression=no server cat /bin/csh|dd of=/dev/null
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	1273+1 records in
	1273+1 records out
	651840 bytes transferred in 21.605 secs (30170 bytes/sec)
        → 235.7Kbps

	(HTTPで大きなファイルを同時コピーしながらの場合)
	server# ssh -o Compression=no server cat /bin/csh|dd of=/dev/null
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	1273+1 records in
	1273+1 records out
	651840 bytes transferred in 31.079 secs (20973 bytes/sec)
        → 163.9Kbps

後者の転送速度が前者の約70%になっていることから、残りの30%をHTTPが利用し
ていることが伺えた。



●ipfwによるSMTP/HTTP/SSHの制御

ここまでの設定を組み合わせて、【図 は】のような共有関係を持つトラフィッ
ク制限を構築しよう。まず、これまで実験で作成したルールを削除しておく。

	server# ipfw sh |grep queue
		~~~~~~~~~~~~~~~~~~~
	00100     2591     3224774 queue 1 tcp from me 80 to 10.1.1.100
	00200     1409     2031788 queue 2 tcp from me 22 to 10.1.1.100
	(ルール番号が100, 200だと仮定すると)
	server# ipfw del 100
		~~~~~~~~~~~~
	server# ipfw del 200
		~~~~~~~~~~~~

【図 は】の関係を作成するには、

	* SMTP用に独立した256Kbpsのpipe
	* 768Kbpsのpipeを、3:7で分けあうqueueを2本作成

すれば良い。ここまでの例を踏まえて作成したものが、
【リスト ぬ】のシェルスクリプトである。

---[リスト ぬ]------------------------------------------------------------
#!/bin/sh
ipfw pipe 256 config bw 256K
ipfw pipe 768 config bw 768K
ipfw queue 1 config weight 30 pipe 768
ipfw queue 2 config weight 70 pipe 768
ipfw add pipe 256 tcp from me to any 25 via ppp0
ipfw add queue 1 tcp from me 80 to any via ppp0
ipfw add queue 2 tcp from me 22 to any via ppp0
----------------------------------------------------------------------


ここで、via ppp0 は、出力インタフェースの指定で、この例ではppp0から
出て行くパケットのみが選択対象となる。各自の環境に合わせ、WANに継ってい
るインタフェース名を指定すると良いだろう。

 
■
■iproute2+tcを用いた設定
■

iproute2+tc では、複数のキューに対する重み付けを用いた優先制御機構として
HTB(Hierarchical Token Bucket) が用意されている。より一般的
なCBQも利用可能だが、iproute2+tcの場合設定のために指定しなければならない
パラメータが異常に多く、設定構文自体も複雑さをきわめるため慣れていない人
にはあまりお勧めできない。

本稿では、ipfwでの事例で利用したWF2Q+とほぼ同等の制御が可能なHTBを利用す
るものとして解説を進める。HTBは、Traffice Shaping 可能な Token Bucket を
階層的に配置し、なおかつそれぞれのキューの優先割合を指定できる。

●カーネルオプションの設定

本稿ではカーネルとして Linux kernel 2.4.28 を利用した。
http://www.kernel.org/pub/linux/kernel/v2.4/linux-2.4.28.tar.bz2
を /usr/src に展開し、QoSのためのオプションをセットする【註 ろ】。

---[註 ろ]------------------------------------------------------------
カーネル構築のためには bison, ncurses-devel を追加導入する必要があるディ
ストリビューションも多いようだ。apt-get で管理している場合は、
apt-get update
apt-get install kernel-source
apt-get install bison
apt-get install ncurses-devel
として必要なパッケージを事前に導入して欲しい。
----------------------------------------------------------------------

今回利用するのは、HTB と U32 クラスフィルタなので、最低限そのオプショ
ンを組み込む。参考までに /usr/src/linux で make menuconfig し、

	Networking options  --->
	  QoS and/or fair queueing  --->

のメニューに進んだときの画面を【図 い】に示す。
---[図 い]------------------------------------------------------------
 image menuconfig.png
 %%もし、↑の画像が白黒ページで読みづらいようなら以下のテキストダンプを
 %%利用してください。

 Linux Kernel v2.4.28 Configuration
 ──────────────────────────────────────
┌─────────────QoS  and/or fair queueing ───────────┐
│  Arrow keys navigate the menu.   selects submenus --->.           │
│  Highlighted letters are hotkeys.  Pressing  includes,  excludes,  │
│   modularizes features.  Press  to exit,  for Help.      │
│  Legend: [*] built-in  [ ] excluded   module  < > module capable      │
│ ┌─────────────────────────────────┐   │
│ │              [*] QoS and/or fair queueing                        │   │
│ │              < >   CBQ packet scheduler                          │   │
│ │              <*>   HTB packet scheduler                          │   │
│ │              < >   CSZ packet scheduler                          │   │
│ │              < >   H-FSC packet scheduler                        │   │
│ │              < >   The simplest PRIO pseudoscheduler             │   │
│ │              < >   RED queue                                     │   │
│ │              <*>   SFQ queue                                     │   │
│ │              < >   TEQL queue                                    │   │
│ │              < >   TBF queue                                     │   │
│ │              < >   GRED queue                                    │   │
│ │              < >   Network emulator                              │   │
│ │              < >   Diffserv field marker                         │   │
│ │              [ ]   QoS support                                   │   │
│ │              [*]   Packet classifier API                         │   │
│ │              < >     TC index classifier                         │   │
│ │              < >     Routing table based classifier              │   │
│ │              < >     Firewall based classifier                   │   │
│ │              <*>     U32 classifier                              │   │
│ └─────────────────────────────────┘   │
└─────────────────────────────────────┘

 %%%もしくはさらに簡略版の↓コレ

 [*] QoS and/or fair queueing
   <*>   CBQ packet scheduler
   <*>   HTB packet scheduler
   < >   H-FSC packet scheduler
   < >   The simplest PRIO pseudoscheduler
   < >   RED queue
   <*>   SFQ queue
   < >   TEQL queue
   <*>   TBF queue
   < >   GRED queue
   < >   Network emulator
   < >   Diffserv field marker
   [ ]   QoS support
   [*]   Packet classifier API
   < >     TC index classifier
   < >     Routing table based classifier
   < >     Firewall based classifier
   <*>     U32 classifier
----------------------------------------------------------------------


オプションの設定が終わったらカーネルをコンパイル&インストールする。

	(LILOを使用している例)
	# make dep
	# make bzImage
	# make bzlilo
	(必要なら /etc/lilo.conf も修正)

●HTB対応tcのインストール

ディストリビューションによってはtcコマンド(/sbin/tc)がHTB対応になってい
ないものもある。その場合は
http://luxik.cdi.cz/~devik/qos/htb/
よりHTB3のパッケージを入手しインストールする必要がある。
本稿執筆時点の最新版は
http://luxik.cdi.cz/~devik/qos/htb/v3/htb3.6-020525.tgz
で、これを展開したディレクトリにある tc コマンドを /sbin にコピーすれば
良い(念のため既存のtcコマンドはバックアップを取っておくと良い)。

●qdiscとclass

設定前に理解しておくべき概念としてqdiscとclassがある。これは非常に重要で
あるにもかかわらず複雑で、iproute2でのトラフィック制御の登龍門と言えよう。

実際のキューを処理するqdisc(queue discipline)には様々なものがあるが、その
中で内部に複数のグループ、つまりクラス(class)を持つことができるものを「ク
ラスフルなqdisc」という。クラスフルなqdiscを用いることで、複数のサービス
を一定の帯域幅を持ったひとつのqdiscに導き、その内部にあるクラスに各サー
ビスを分けてキュー入れしていくという形を取ることで【図 は】のような階層
的な帯域制限がかけられる。

抽象的な話になってしまったが、階層的制御の目的に適うクラスフルなqdiscとし
て CBQ と HTB(Hierarchical Token Bucket) が挙げられる。CBQはPart1で示した
ものだが、残念ながらiproute2におけるCBQは詳細なパラメータを管理者が逐一指
定せねばならず複雑さをきわめる。その点HTBは作成したクラスの識別子【註 と】
とclassごとの親子関係、それぞれに与える帯域幅だけを指定すれば良いので、よ
り手軽に利用できる。今回はHTBを利用した制御例を示そう。

---[註 と]------------------------------------------------------------
iproute2では生成した qdisc や class の識別子のことをhandleと呼ぶ。
----------------------------------------------------------------------

●handle

qdiscはいずれかのネットワークインタフェースに対して割り当てていく。割り
当て時にはかならず識別子となるhandleをつけなければならない。classはいず
れかのqdisc内部に生成していき、これにもやはりhandleをつける。handleは、

	<メジャー番号>:<マイナー番号>

という形式でつけていく。ひとつの qdisc は他の qdisc とは重複しないメジャー
番号を持ち、そのマイナー番号は必ず0である。qdiscの内部に生成したclassのメ
ジャー番号は親となるqdiscのメジャー番号と同一のもので、マイナー番号は0以
外の番号で、他のclassと重複しないものとする。

	<メジャー番号>:

のようにマイナー番号を省略したものは <メジャー番号>:0 と同じ意味で、つ
まり常にqdiscを指すことになる。

インタフェースの出口となるroot qdiscを示す handle は慣習的に 1: とする。
また、handleはインタフェースごとに独立した名前空間を持つのでtcコマンドに
よるhandle操作には必ずインタフェース名を併記しなければ操作対象が特定でき
ないことに注意する。

以上を踏まえてHTBによるqdiscとclassを作成して行こう。

●HTBによる帯域制限

ipfwの例と同様まずは単純にSSHを、指定した速度256Kbpsに絞ってみよう。
ここでは、帯域制限をかけるインタフェースが eth0 であるものとする。
まず root qdisc (1:) をHTBのqdiscとして作成する。

	# tc qdisc add dev eth0 root handle 1: htb

続いて、htb qdisc の内部に256Kbpsに絞られた htb classを作成する。

	# tc class add dev eth0 parent 1: classid 1:1 htb rate 256kbit

これにより、親として 1:0 のqdiscをもつ新規の htb class が 1:1 という
handleで作成される。rate パラメータでclassに与えられる帯域幅を指定する。

ここで定義したclassにSSHパケットが向けられるようにフィルタを定義する。
【表 ほ】のクライアントの 10.1.1.100 からのSSHポート接続を htb class 1:1
に導く例は以下のようになる。

	# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 \
	   match ip sport 22 0xffff \
	   match ip dst 10.1.1.100  flowid 1:1



●HTBによるSMTP/HTTP/SSHの制御

続いて【図 は】相当の階層的なトラフィック制御を考える。

【図 は】をqdisc とclassを用いて書き直した【図 ち】を参考に qdisc と
class を定義する。

---[図 ち]------------------------------------------------------------
%%% これも丸枠でお願いします。

       通信回線全体
         (1Mbps)
   +---------------- htb qdisc 1: ------------------------+
   |                                                      |
   |  +-htb class 1:10 256K--+                            |
   |  |                      |                            |
   |  |     [SMTP]	     |                            |
   |  |                      |                            |
   |  |                	     |                            |
   |  +----------------------+                            |
   |  +------------ htb class 1:20 768Kbps ------------+  |
   |  |			        		       |  |
   |  | +----- htb class 1:80 230Kbps -------+         |  |
   |  | |                                    |         |  |
   |  | |       [HTTP]                       |   3     |  |
   |  | |                                    |         |  |
   |  | +--------------------------------  --+         |  |
   |  |			        		 :     |  |
   |  | +----- htb class 1:22 538Kbps ---  --+         |  |
   |  | |                                    |         |  |
   |  | |                                    |         |  |
   |  | |                                    |   7     |  |
   |  | |        [SSH]                       |         |  |
   |  | |                                    |         |  |
   |  | |                                    |         |  |
   |  | |                                    |         |  |
   |  | +------------------------------------+         |  |
   |  |                                                |  |
   |  +------------------------------------------------+  |
   +------------------------------------------------------+
----------------------------------------------------------------------

シェルスクリプトとしてまとめると以下のようになる。

----------------------------------------------------------------------------
#!/bin/sh
# 既存のセットをクリアしておく。
tc qdisc del dev eth0 root 2> /dev/null

# root qdiscの作成
tc qdisc add dev eth0 root handle 1: htb

# SMTP用class作成
tc class add dev eth0 parent 1: classid 1:10 htb rate 256Kbit

# HTTP+SSH用class作成
tc class add dev eth0 parent 1: classid 1:20 htb rate 768Kbit

# 1:20の子としてHTTP用とSSH用classをそれぞれ作成
tc class add dev eth0 parent 1:20 classid 1:80 htb rate 230Kbit ceil 768Kbit
tc class add dev eth0 parent 1:20 classid 1:22 htb rate 538Kbit ceil 768Kbit

# フィルタ起動を簡略化するための関数
u32() {
  tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 "$@"
}
# SMTP, HTTP, SSHへのアクセスをそれぞれ 1:10, 1:80, 1:22 へ。
u32 match ip dport 25 0xffff flowid 1:10
u32 match ip sport 80 0xffff flowid 1:80
u32 match ip sport 22 0xffff flowid 1:22
----------------------------------------------------------------------------

HTBのclassでは rate パラメータで、兄弟関係にあるclassと帯域を分けあう場合
の速度を指定し、ceilパラメータで競合パケットがない場合の上限速度を指定す
る。1:80 と 1:22 は1:20の子なのでそれぞれのceilは自動的に1:20のrateになっ
て欲しいところだがそうはなっていない。

%%%編集註
%%%       ↑の意味難しいですよねえ。でも実際難しいんですけどどうしましょ。
%%% あと、ipfwの文法説明がちゃんとしていて、tcのほうの説明がないのは、
%%% ipfwの pipe/queue のオプションは表に示したものが全てなのに対し、
%%% tcのほうは自然言語並の複雑さとパラメータの数なので一部をとりあげる
%%% ことが極めて難しいからです。tcのほうで指定しているパラメータ以外は
%%% どれも実装上重要なパラメータというものが多くて、実装の中味を知らない
%%% 管理者にとっては意味不明のものが殆んどです。私も良く分からないし。
%%% そういう点が、iproute2のtcの体系的な文書がない理由じゃないかと
%%% 思います。書けないっす。

■
■ALTQによるCBQの設定例(NetBSD)
■

CBQを用いた設定例としてALTQによるものを紹介しよう。PF+ALTQをとりあげたい
ところだが、筆者の環境では、IPfilterと単体のALTQを組み合わせて使っている
ためALTQ単体での利用方法を紹介する。パケットフィルタツールとして
IPfilter を好んで利用している場合は、トラフィック制御機構としては単体の
ALTQを利用することになるだろう。

ここではNetBSD/i386で【表 は】の設定をALTQ単体で行なうものとする。


●ALTQ対応カーネルの作成

NetBSDでALTQを利用するためにはALTQ対応カーネルをビルドする必要がある。カー
ネルソースが既に /usr/src/sys 展開されているものとしてその後の手順を説明
する。ALTQ関連ソースは揃っているのだが、一部の配布ファイルを手直しする必
要がある。

・カーネルコンフィグファイルの修正

  /usr/src/sys/arch/i386/conf に移動し、GENERICファイルを適当な名前のファ
  イルにコピーし、それを編集する。

	# cd /usr/src/sys/arch/i386/conf
	# cp GENERIC ALTQ
	# vi ALTQ

  ALTQに関するリスト3の記述を追加する。

---[リスト 3]---------------------------------------------------------
options         ALTQ
options         ALTQ_CBQ
options         ALTQ_WFQ
options         ALTQ_FIFOQ
options         ALTQ_RED
options         ALTQ_RIO
options         ALTQ_HFSC
options         ALTQ_CDNR
options         ALTQ_PRIQ
pseudo-device   altq
----------------------------------------------------------------------

  今回利用するのは ALTQ_CBQ なので、それ以外のオプションは適宜省略して構
  わない。修正後、configコマンドによりコンパイルディレクトリを作成する。

	# config ALTQ

・ソースファイルの修正

  標準配布のソースには1つ足りない関数(altqattach)があるのでこれを追加する。
  /usr/src/sys/altq/altq_conf.c を開き、altqattach という文字列を探す。
  【リスト 4】のような修正を施す。
---[リスト 4]---------------------------------------------------------
【修正前】
#ifdef __FreeBSD__
static void altq_drvinit __P((void *));
#else
void    altqattach __P((int));
#endif

   ↓

【修正後】
#ifdef __FreeBSD__
static void altq_drvinit __P((void *));
#elif defined(__NetBSD__)
void    altqattach __P((int));
void 
altqattach(int unused) 
{ /* XXX Dummy function */ 
} 
#else
void    altqattach __P((int));
#endif
----------------------------------------------------------------------

  ダミー関数を追加しているだけだがこれで良い。
  また、/usr/src/sys/conf/files に以下の行がなければ追加する。

	defpseudo altq:         ifnet

・カーネルのコンパイルとインストール

  ファイルの修正が終わったら、以下の手順でコンパイルとインストールを行な
  う。

	# cd /usr/src/sys/arch/i386/compile/ALTQ
	# make depend
	# make
	# make install

  リブート後、ALTQによるトラフィック制御が利用可能となる。

●ALTQによる制御設定手順

ALTQではaltqd(8)デーモンが実際の制御を司る。altqdの動作定義ファイルはデフォ
ルトで /etc/altq.conf であり、ここに制御するインタフェースと想定する帯域、
利用する制御方式とその詳細定義を書いて行く。altq.conf(5)の書き方の詳細は
オンラインマニュアルに譲り、ここでは最低限必要なものを解説する。
altq.conf には以下の3つのコマンドを記述して行く。

	* interfaceコマンド

	  制御下に置くネットワークインタフェースとそこに適用するキューイ
	  ング方式を記述する。bandwidthオプションでインタフェースの帯域を
	  指定できる。

	   interface <IF名> bandwidth <BPS> <キューイング方式>

	* classコマンド

	  指定したインタフェースと方式に属するクラスを定義する(実際は1行で)。

	  class <キューイング方式> <IF名> 
		<クラス名> <親クラス名> <その他のオプション>

	* filterコマンド

	  パケットを指定したクラスにふるい分けるためのルールを記述する
	  (実際は1行で)。

	  filter <IF名> <クラス名> <destアドレス> <destポート>
			<srcアドレス> <srcポート> <プロトコル番号>

以上の文法に従い /etc/altq.conf を作成し、 altqd を起動する。設定ファイル
を別の場所に置きたい場合は -f オプションでそのファイルを指定する。
altq.conf の実際の記述例については後述する。

●ALTQ_CBQによる帯域制限

ALTQ_CBQで通信全体の帯域、あるいは特定のパケットの帯域を制限するには、
CBQで利用するルートクラスの帯域を宣言し、子クラスでそれを配分するようにす
ればよい。Part1の図8をもう一度見て欲しい。ALTQ_CBQでも、回線自身の帯域を
宣言し、図にある「ルートクラス」を作成する。さらに、パケットのデフォルト
の行先となる「デフォルトクラス」も作成する。
	
------------------------------------------------------------
# pppoe0 を 1Mbps として宣言。CBQを利用。
interface pppoe0 bandwidth 1M cbq

# rootクラスを作成する. 親がない場合はNULLを指定
# pbandwidth はインタフェースの帯域のパーセンテージを指定
class cbq pppoe0 root_class NULL priority 0 pbandwidth 100

# デフォルトクラスの定義。末尾の default キーワードは、
# フィルタにもれたときにデフォルトでここに分類されるべきクラスである
# ことを指定する。borrowキーワードは帯域が余っていれば借りられることを
# 意味する。
class cbq pppoe0 default_class root_class borrow pbandwidth 100 default
------------------------------------------------------------

ここまでが、CBQを利用する場合に必ず必要となるクラスの定義となる。

さらに続けて、256Kbpsに制限されたクラスを作成してみよう。default_class を
親に持ち、親の25%を利用する子クラス「b256_class」を作る。

------------------------------------------------------------
class cbq pppoe0 b256_class default_class pbandwidth 25
------------------------------------------------------------

FreeBSD/Linuxの例と同様SSHプロトコルを256Kbpsに制限してみよう。
作成した b256_class にSSHパケットが分類されるようにフィルタを記述する。

------------------------------------------------------------
filter pppoe0 b256_class 0 0 0 22 6
------------------------------------------------------------

以上全ての記述を altq.conf に書き込み altqd を起動することで
外部へのSSHパケットが約256Kbpsに制限される。


●ALTQ_CBQによるSMTP/HTTP/SSHの制御

Linuxで作成した【図 5】と同様のクラス定義をALTQ_CBQにて行なってみよう。
ALTQ_CBQで留意すべきなのは、ctl_class という名前のクラスが内部的に利用さ
れる点で、そのために2%を未使用で残しておかなければならない。これを考慮し
て作成した altq.conf を【リスト 4】に示す。


リスト 4 ---------------------------------------------------------------
# インタフェースと利用する方式の定義
interface pppoe0 bandwidth 1M cbq

# rootクラスを作成する. 第4引数は親クラス指定。rootには親がないのでNULL
class cbq pppoe0 root_class NULL priority 0 pbandwidth 100

# デフォルトクラスの定義。
class cbq pppoe0 default_class root_class borrow pbandwidth 100 default

# default_classの子として、smtp_class, misc_class をそれぞれ
# 28%, 70% の帯域幅で作成。smtp_class は borrow 指定していないので
# 28%を超過することがない。ALTQでは2%を制御用として利用するので
# 2%を残す。
class cbq pppoe0 smtp_class default_class pbandwidth 28
        filter pppoe0 smtp_class 0 0 0 25 6
class cbq pppoe0 misc_class default_class borrow pbandwidth 70

# filter の引数は、destアドレス destポート srcアドレス srcポート protocol
# protocolは /etc/protocolsにあるプロトコル番号で、TCPは6
# 0はワイルドカードを意味する。
class cbq pppoe0 http_class misc_class borrow pbandwidth 21
        filter pppoe0 http_class 0 0 0 80 6
class cbq pppoe0 ssh_class misc_class borrow pbandwidth 49
        filter pppoe0 smtp_class 0 0 0 22 6
----------------------------------------------------------------------------


■
■まとめ
■

以上で解説した方法を組み合わせれば、品質を落としたくないサービスの安定利
用が実現できる。検索ロボットやアンテナなどの度を過ぎたアクセスが負荷を上
げている場合、Apacheの機能で全面的に禁止してしまうのも手だが、それらを禁
止すると別の人に自分のサイトを見てもらう機会も減ってしまう。このようなと
きにロボットとおぼしきIPアドレスへのデータ送出速度を落とすのも有効な解決
策といえよう。

さて、トラフィック制御は一から設計するのに十分な考察を必要としたり、一時
的な制限をかけたりなどの変更を気軽に試せないなどの難しい側面がある。とく
にiproute2のtcコマンドでは、指定すべきパラメータが多いため全貌を知るのは
困難をきわめる。たとえば、「OSのバージョンアップをするときに最新版の大量
の配布ファイルを、他のユーザに影響を与えずに緩やかに取って来たい」、そん
なときに臨時で設定を追加するにもかなりの熟練を要するのではないだろうか。
また、トラフィック制御機構ごとに文法が全く異なり、別のOSを使うと
きにはまた一から覚え直す必要があるのも辛い点である。
Part3ではそのような場合の決定的な解決案を紹介する。

---[註 る]------------------------------------------------------------
----------------------------------------------------------------------
---[註 を]------------------------------------------------------------
----------------------------------------------------------------------


yuuji@gentei.org
Fingerprint16 = FF F9 FF CC E0 FE 5C F7 19 97 28 24 EC 5D 39 BA
HIROSE Yuuji - ASTROLOGY / BIKE / EPO / GUEST BOOK / YaTeX [Tweet]