[前に戻る: アンカーと名前付き (サブ) ルールセット] [目次] [次に進む: アドレスプールと負荷分散 (Load Balancing)]
何かをキューに入れるということは、それが処理を待つ間、順序に従って、 それを蓄積するということです。コンピュータネットワークでは、データパケットが ホストから送出される際、それらはオペレーティングシステムによって処理されるのを待つ間、 キューに入れられます。そして、オペレーティングシステムは、どのキューに入れられている、 どのパケットを処理すべきであるのかを決定します。オペレーティングシステムが選択した、 処理すべきパケットの順序は、ネットワークの性能に影響を与えます。 たとえば、SSH と FTP という、ふたつのネットワークアプリケーションを実行する ユーザのことを想像してみてください。理想的には、時間に敏感な SSH の性質のため、 SSH のパケットは FTP のパケットより先に処理されるべきでしょう。 たとえば、SSH クライアントで鍵がタイプされた場合には、直ちに反応することが 期待されますが、数秒の余計な遅延を発生させる FTP 転送が、何らかのメッセージの 原因となることはほとんどありません。しかし、このような接続のハンドリングを行うルータが、 SSH 接続の処理の前に FTP 接続からの大量のパケットの塊を処理する場合、 どのようなことが起きるでしょうか ? SSH 接続のパケットはキューに取り残されるので (あるいは、ルータがすべてのパケットを保持するのに十分大きなキューがない場合には、 このようなパケットは廃棄されてしまうかも知れません)、 SSH の接続は遅延し、動きが悪くなったように見えるかも知れません。そこで、 使用されているキューイングの戦略を変更することによって、異なるアプリケーション、ユーザ、 そしてコンピュータの間で、ネットワークの帯域幅を適正に共有することができるようになります。
キューイングは、送出方向のパケットに対してのみ役に立つということに 注意してください。いったん、着信方向のパケットがインターフェイスに到着した時点で、 このパケットのキューイングするのはもう既に遅過ぎるのです。なぜなら、このパケットは、 これを受信するインターフェイスに到着するまでに、既にネットワーク帯域を 消費してしまっているからなのです。これに対する解決策は、近隣のルータの キューイングを有効化するか、パケットをルータに送出する内部インターフェイスの キューイングを有効化することだけです。
OpenBSD では FIFO 以外に以下のふたつのスケジューラをサポートしています。
CBQ のキューは階層的な方法で配置されます。階層の最上位には、 利用可能な帯域幅の総量を定義するルート (root) キューが 位置しています。子キューがルートキューの下に生成され、その それぞれに、ルートキューの帯域幅の一部が割り当てられています。 たとえば、キューが以下のように定義されているとしましょう。
この場合、利用可能な総帯域幅は 2Mbps に設定されています。 そして、この帯域幅は 3 個の子キューの間で分割されています。
この階層は、キューの中にキューを定義することで、さらに拡張することができます。 異なるユーザの間で帯域幅を均等に分割し、そして、あるプロトコルが他のトラフィックの 帯域幅を枯渇させることがないように彼らのトラフィックをクラス化することで、 たとえば、以下のようなキューイング構造を定義することができます。
それぞれのキューに割り当てられた帯域幅の合計が、親キューに 割り当てられた帯域幅以上にはならないことに注意してください。
他の子キューに利用されていない利用可能な帯域幅を、親キューが過剰に 持っている場合に、その親キューから帯域幅を借りるようにキューの設定を 行うことができます。たとえば、以下のようなキューの構成を考えてみます。
ここで、ftp 用のキューのトラフィックが 900Kbps を超え、ユーザ A のキューのトラフィックが (ssh は、それ用のキューに割り当てられた 100Kbps 未満の帯域しか使用していないので) 1Mbps 未満なら、ftp 用のキューは超過した帯域幅をユーザ A から借用することができます。 このように、ftp 用のキューは、それが過負荷に直面した場合に、 それ用に割り当てられた帯域幅以上を使用することができるのです。もし、ssh の負荷が増加した場合には、借用した帯域幅は返却されることになります。
CBQ はそれぞれのキューに優先度を割り当てます。より高い優先度を持つキューは、 より低い優先度を持つキューと同じ親を共有する限りにおいて (換言すれば、 両方のキューが階層構造の中の同じ枝に位置している限りにおいては)、 輻輳の際により低い優先度を持つキューに優先します。また、同一の優先度を持つ キューは、ラウンドロビン方式で処理されて行きます。たとえば、以下において、
CBQ は、ユーザ A とユーザ B 用のキューをラウンドロビン方式で 処理します。つまり、どちらのキューも他方に優先することはありません。 ユーザ A 用のキューが処理されている間、CBQ はその子キューの処理も行います。この場合、ssh 用のキューはより高い優先度を持ち、ネットワークが輻輳している場合に、 ftp 用のキューより優先的な待遇が与えられることになります。 ユーザ A 用とユーザ B 用のキューと比較して、ssh 用と ftp 用のキューは、どのような優先度も持っていないことに注意してください。 なぜなら、これらは、階層構造の中ですべてが同じ枝に位置しているわけではないからです。
より詳細な CBQ 関連の理論を知りたい場合には、 CBQ に関するリファレンスを参照してください。
PRIQ のキューイング構造は均一です。つまり、キューの中に キューを定義することはできません。利用可能な帯域幅の 総量を設定したルートキューが定義され、その下にサブキューが 定義されます。以下の例について考えてみましょう。
ルートキューは、2Mbps の利用可能な帯域幅を持つものとして定義され、 その下に 3 個のサブキューが定義されています。最も高い優先度のキュー (優先順位の番号が最高のもの) は最初に処理されます。いったん、キューの 中のすべてのパケットが処理されるか、あるいはキューが空になると、PRIQ は次に高い優先度を持つキューに処理を移します。与えられたキューの中では、 パケットは FIFO (First In First Out) 方式で処理が行われます。
PRIQ を使用する場合、キューイングの計画を非常に慎重に行わなければならないことに 注意することが非常に重要です。これは、PRIQ が、常により高い優先度のキューを より低い優先度のものより先に処理するからであり、より高い優先度のキューが定常的な ストリームのパケットを受信している場合には、このより高い優先度のキューが、より低い 優先度のキューを遅延させたり、あるいはパケットが廃棄される原因になったりすることが あるからなのです。
RED は、global synchronization 問題として知られる状況を回避することができますし、 また、爆発的なトラフィックに対応することもできますので、非常に役に立つものです。 global synchronization は、いくつかの接続から同時にパケットを廃棄してしまうため、 全体的なスループットの低下を招いてしまうものでず。たとえば、10 個の FTP 接続を 中継するルータで輻輳が発生し、(FIFO キューイングの場合のように) すべての (あるいはほとんどの) これらの接続が廃棄された場合には、全体的なスループットが 著しく低下してしまうことになります。 もちろん、これは、すべての FTP 接続がそのスループットを低下させてしまうことに なりますし、また、ネットワークはもはやその能力を十分に活用できない状態になって しまいますので、とても理想的な状態であるとは言えなくなってしまいます。 RED は、すべての接続を選択する代わりに、パケットを廃棄する接続をランダムに 選択することによって、このような状況を回避します。より大きな帯域幅の総量を 使用している接続は、そのパケットを廃棄される可能性がより高くなります。 このようにして、大きな帯域幅を消費する接続が減速されることで輻輳が回避され、 全体的なスループットの著しい低下は発生しなくなるのです。さらに、RED は、 キューが一杯になる前にパケットの廃棄を開始するため、爆発的な トラフィックにも対応することができます。爆発的なトラフィックがやって来た場合にも、 キューには新しいパケットを受信するのに十分なスペースが残されているはずです。
RED は、ネットワークからの輻輳通知に対して、トランスポートプロトコルが応答できる 場合にのみ使用することができます。ほとんどの場合において、これは UDP や ICMP のトラフィックではなく、TCP トラフィックをキューイングする場合に RED を使用すべきであるということを意味します。
より詳細な RED 関連の理論を知りたい場合には、 RED に関するリファレンスを参照してください。
ECN についてのより詳しい情報は、 RFC 3168 を参照してください。
ALTQ は PF にマージされたので、キューイングを動作させるためには PF を 有効化しなければなりません。PF をどのようにして有効化させるのかに関しては、 はじめに を参照してください。
キューイングは、 pf.conf で設定されます。キューイングの設定には、以下のような ふたつのタイプのディレクティブが使用されます。
altq on ディレクティブのシンタクスは以下のとおりとなっています。
altq on interface scheduler bandwidth bw qlimit qlim \
tbrsize size queue { queue_list }
たとえば、
altq on fxp0 cbq bandwidth 2Mb queue { std, ssh, ftp }の場合は、インターフェイス fxp0 上で CBQ を有効化しています。また、利用可能な全帯域幅は 2Mbps に設定されています。そして、std、ssh および ftp という 3 個の子キューが定義されています。
queue ディレクティブのシンタクスは以下のとおりとなっています。
queue name [on interface] bandwidth bw [priority pri] [qlimit qlim] \
scheduler ( sched_options ) { queue_list }
上記の例の続きを以下に示します。
queue std bandwidth 50% cbq(default)
queue ssh { ssh_login, ssh_bulk }
queue ssh_login priority 4 cbq(ecn)
queue ssh_bulk cbq(ecn)
queue ftp bandwidth 500Kb priority 3 cbq(borrow red)
ここでは、以前に定義された子キューのパラメータを設定しています。 std キューは、ルートキューの帯域幅 (または 1Mbps) の 50% の帯域幅が割り当てられ、デフォルトキューとして設定されています。 ssh キューには、ssh_login と ssh_bulk という、ふたつの子キューが定義されています。ssh_login には ssh_bulk よりも高い優先度が与えられており、どちらも ECN が有効化されています。また、ftp キューには 500Kbps の帯域幅が割り当てられ、優先度 3 が与えられています。そしてこれは、 余力がある場合には帯域幅の借用も可能となっていて、RED も有効化されています。
キューにトラフィックを割り当てるためには、PF の フィルタルール とともに queue キーワードを使用します。たとえば、 以下のような行を含むフィルタルールの設定を考えてみましょう。
pass out on fxp0 from any to any port 22
パケットは、queue キーワードを使用して、 特定のキューに割り当てられたルールにマッチします。
pass out on fxp0 from any to any port 22 queue ssh
queue キーワードが block ディレクティブとともに使用された場合、 その結果として、TCP RST や ICMP Unreachable パケットが指定されたキューに 割り当てられます。
altq on ディレクティブで定義されたインターフェイス以外の、 インターフェイス上でキューの指定が行われる可能性があることに注意してください。
altq on fxp0 cbq bandwidth 2Mb queue { std, ftp }
queue std cbq(default)
queue ftp bandwidth 1.5Mb
pass in on dc0 from any to any port 21 queue ftp
キューイングは fxp0 上で有効化されていますが、タグ付けは dc0 上で行われます。もし、インターフェイス fxp0 から出てきた、pass ルールにパケットがマッチする場合、これらのパケットは、ftp キューにキューイングされるはずです。この方式のキューイングは、 ルータに取っては非常に役に立つものです。
通常は、ひとつのキュー名だけが queue キーワードに与えられますが、 もし、ふたつ目の名前が指定されている場合には、そのキューは、低遅延のための ToS (Type of Service) パケットか、データのペイロードのない TCP ACK パケットのために使用されます。 この良い例が、SSH を使用している場合になります。SSH のログインセッションは、 低遅延のために ToS を設定していますが、SCP や SFTP のセッションには これが設定されていません。PF は、非ログイン接続以外の異なるキューの中のログイン 接続に属するパケットをキューイングするために、この情報を使用することができます。 これは、ファイル転送のパケットよりログイン接続のパケットを優先させるのに役立ちます。
pass out on fxp0 from any to any port 22 queue(ssh_bulk, ssh_login)
これは、SSH のログイン接続に属するパケットを ssh_login キューに割り当て、SCP や SFTP 接続に属するパケットを ssh_bulk キューに割り当てています。ssh_login キューは、 より高い優先度を持っているので、SSH ログイン接続のパケットは、 SCP や SFTP 接続に優先して処理されるようになります。
TCP ACK パケットをより高い優先度を持つキューに割り当てることは、たとえば ADSL 回線のように、アップロードとダウンロードの帯域幅が異なっているような、 非対称の接続において便利なものです。ADSL 回線では、アップロードのチャネルが 非常に混雑している場合にダウンロードが始まると、アップロードチャネルを通して TCP ACK パケットを送信しようとする際に、輻輳している中に それを送信しなければならないため、 ダウンロードも影響を受けることになってしまいます。 最良の結果を得るためにテストを行って、アップロードのキューの帯域幅を、 その能力より小さな値に設定すべきです。実際には、アップロードの最大値が 640Kb の ADSL 回線の場合ですと、ルートキューの帯域幅を 600Kb のような値に設定することで、より良い性能を得られるようになります。 そして試行錯誤を行うことで、最良の帯域幅の設定を得られるはずです。
以下のように、keep state が指定されたルールに対して queue キーワードを使用する場合、
pass in on fxp0 proto tcp from any to any port 22 flags S/SA \
keep state queue ssh
PF は、状態を持つ接続にマッチした、fxp0 から出て行くパケットが、 最後に ssh キューに入るよう、その状態テーブルのエントリにキューを登録します。 queue キーワードが着信トラフィックをフィルタリングするルールに対して 使用されているにも関わらず、その目的が関係する送出トラフィックのためのキューを 指定するためであるということに注意してください。上記のルールは、 着信パケットをキューイングするためのものではないのです。
[ Alice ] [ Charlie ] | | ADSL ---+-----+-------+------ dc0 [ OpenBSD ] fxp0 -------- ( Internet ) | [ Bob ]
この例では、OpenBSD は 3 台のワークステーションが接続された、小規模な家庭内 ネットワークのインターネットゲートウェイとして使用されています。このゲートウェイは、 パケットフィルタリングと NAT の仕事を行っています。ADSL 回線を使用した インターネット接続は、下りが 2Mbps で上りが 640Kbps となっています。
このネットワークのキューイングのポリシーは以下のとおりです。
以下は、このネットワークポリシーに適合するルールセットです。上記のポリシーに 直接的に適用される pf.conf のディレクティブだけが存在していることに 注意してください。nat、 rdr と オプションなどは、 これには含まれていません。
# enable queueing on the external interface to control traffic going to # the Internet. use the priq scheduler to control only priorities. set # the bandwidth to 610Kbps to get the best performance out of the TCP # ACK queue. altq on fxp0 priq bandwidth 610Kb queue { std_out, ssh_im_out, dns_out, \ tcp_ack_out } # define the parameters for the child queues. # std_out - the standard queue. any filter rule below that does not # explicitly specify a queue will have its traffic added # to this queue. # ssh_im_out - interactive SSH and various instant message traffic. # dns_out - DNS queries. # tcp_ack_out - TCP ACK packets with no data payload. queue std_out priq(default) queue ssh_im_out priority 4 priq(red) queue dns_out priority 5 queue tcp_ack_out priority 6 # enable queueing on the internal interface to control traffic coming in # from the Internet. use the cbq scheduler to control bandwidth. max # bandwidth is 2Mbps. altq on dc0 cbq bandwidth 2Mb queue { std_in, ssh_im_in, dns_in, bob_in } # define the parameters for the child queues. # std_in - the standard queue. any filter rule below that does not # explicitly specify a queue will have its traffic added # to this queue. # ssh_im_in - interactive SSH and various instant message traffic. # dns_in - DNS replies. # bob_in - bandwidth reserved for Bob's workstation. allow him to # borrow. queue std_in cbq(default) queue ssh_im_in priority 4 queue dns_in priority 5 queue bob_in bandwidth 80Kb cbq(borrow) # ... in the filtering section of pf.conf ... alice = "192.168.0.2" bob = "192.168.0.3" charlie = "192.168.0.4" local_net = "192.168.0.0/24" ssh_ports = "{ 22 2022 }" im_ports = "{ 1863 5190 5222 }" # filter rules for fxp0 inbound block in on fxp0 all # filter rules for fxp0 outbound block out on fxp0 all pass out on fxp0 inet proto tcp from (fxp0) to any flags S/SA \ keep state queue(std_out, tcp_ack_out) pass out on fxp0 inet proto { udp icmp } from (fxp0) to any keep state pass out on fxp0 inet proto { tcp udp } from (fxp0) to any port domain \ keep state queue dns_out pass out on fxp0 inet proto tcp from (fxp0) to any port $ssh_ports \ flags S/SA keep state queue(std_out, ssh_im_out) pass out on fxp0 inet proto tcp from (fxp0) to any port $im_ports \ flags S/SA keep state queue(ssh_im_out, tcp_ack_out) # filter rules for dc0 inbound block in on dc0 all pass in on dc0 from $local_net # filter rules for dc0 outbound block out on dc0 all pass out on dc0 from any to $local_net pass out on dc0 proto { tcp udp } from any port domain to $local_net \ queue dns_in pass out on dc0 proto tcp from any port $ssh_ports to $local_net \ queue(std_in, ssh_im_in) pass out on dc0 proto tcp from any port $im_ports to $local_net \ queue ssh_im_in pass out on dc0 from any to $bob queue bob_in |
( IT Dept ) [ Boss's PC ] | | T1 --+----+-----+---------- dc0 [ OpenBSD ] fxp0 -------- ( Internet ) | fxp1 [ COMP1 ] [ WWW ] / | / --+----------'
この例では、OpenBSD のホストを、企業ネットワークのファイアウォールとして稼働させています。 この会社では、その顧客が FTP で顧客の web サイトをアップロードするため、 この会社のネットワークの DMZ で WWW サーバを運用しています。IT 部門では、 自身のネットワークを基幹ネットワークに接続して使用しています。また、社長は、 電子メールや web サーフィンに使用するための PC を、自分の机に持っています。 インターネットへは、上下方向ともに 1.5Mbps の T1 回線を使用して接続しています。 その他のすべてのネットワークセグメントは、Fast Ethernet (100Mbps) を使用しています。
ネットワーク管理者は以下のようなポリシーを決定しました。
以下は、このネットワークポリシーに適合するルールセットです。上記のポリシーに 直接的に適用される pf.conf のディレクティブだけが存在していることに 注意してください。nat、 rdr と オプションなどは、 これには含まれていません。
# enable queueing on the external interface to queue packets going out # to the Internet. use the cbq scheduler so that the bandwidth use of # each queue can be controlled. the max outgoing bandwidth is 1.5Mbps. altq on fxp0 cbq bandwidth 1.5Mb queue { std_ext, www_ext, boss_ext } # define the parameters for the child queues. # std_ext - the standard queue. also the default queue for # outgoing traffic on fxp0. # www_ext - container queue for WWW server queues. limit to # 500Kbps. # www_ext_http - http traffic from the WWW server # www_ext_misc - all non-http traffic from the WWW server # boss_ext - traffic coming from the boss's computer queue std_ext cbq(default) queue www_ext bandwidth 500Kb { www_ext_http, www_ext_misc } queue www_ext_http priority 3 cbq(red) queue www_ext_misc priority 1 queue boss_ext priority 3 # enable queueing on the internal interface to control traffic coming # from the Internet or the DMZ. use the cbq scheduler to control the # bandwidth of each queue. bandwidth on this interface is set to the # maximum. traffic coming from the DMZ will be able to use all of this # bandwidth while traffic coming from the Internet will be limited to # 1.0Mbps (because 0.5Mbps (500Kbps) is being allocated to fxp1). altq on dc0 cbq bandwidth 100% queue { net_int, www_int } # define the parameters for the child queues. # net_int - container queue for traffic from the Internet. bandwidth # is 1.0Mbps. # std_int - the standard queue. also the default queue for outgoing # traffic on dc0. # it_int - traffic to the IT Dept network. # boss_int - traffic to the boss's PC. # www_int - traffic from the WWW server in the DMZ. queue net_int bandwidth 1.0Mb { std_int, it_int, boss_int } queue std_int cbq(default) queue it_int bandwidth 500Kb cbq(borrow) queue boss_int priority 3 queue www_int cbq(red) # enable queueing on the DMZ interface to control traffic destined for # the WWW server. cbq will be used on this interface since detailed # control of bandwidth is necessary. bandwidth on this interface is set # to the maximum. traffic from the internal network will be able to use # all of this bandwidth while traffic from the Internet will be limited # to 500Kbps. altq on fxp1 cbq bandwidth 100% queue { internal_dmz, net_dmz } # define the parameters for the child queues. # internal_dmz - traffic from the internal network. # net_dmz - container queue for traffic from the Internet. # net_dmz_http - http traffic. # net_dmz_misc - all non-http traffic. this is also the default queue. queue internal_dmz # no special settings needed queue net_dmz bandwidth 500Kb { net_dmz_http, net_dmz_misc } queue net_dmz_http priority 3 cbq(red) queue net_dmz_misc priority 1 cbq(default) # ... in the filtering section of pf.conf ... main_net = "192.168.0.0/24" it_net = "192.168.1.0/24" int_nets = "{ 192.168.0.0/24, 192.168.1.0/24 }" dmz_net = "10.0.0.0/24" boss = "192.168.0.200" wwwserv = "10.0.0.100" # default deny block on { fxp0, fxp1, dc0 } all # filter rules for fxp0 inbound pass in on fxp0 proto tcp from any to $wwwserv port { 21, \ > 49151 } flags S/SA keep state queue www_ext_misc pass in on fxp0 proto tcp from any to $wwwserv port 80 \ flags S/SA keep state queue www_ext_http # filter rules for fxp0 outbound pass out on fxp0 from $int_nets to any keep state pass out on fxp0 from $boss to any keep state queue boss_ext # filter rules for dc0 inbound pass in on dc0 from $int_nets to any keep state pass in on dc0 from $it_net to any queue it_int pass in on dc0 from $boss to any queue boss_int pass in on dc0 proto tcp from $int_nets to $wwwserv port { 21, 80, \ > 49151 } flags S/SA keep state queue www_int # filter rules for dc0 outbound pass out on dc0 from dc0 to $int_nets # filter rules for fxp1 inbound pass in on fxp1 proto { tcp, udp } from $wwwserv to any port 53 \ keep state # filter rules for fxp1 outbound pass out on fxp1 proto tcp from any to $wwwserv port { 21, \ > 49151 } flags S/SA keep state queue net_dmz_misc pass out on fxp1 proto tcp from any to $wwwserv port 80 \ flags S/SA keep state queue net_dmz_http pass out on fxp1 proto tcp from $int_nets to $wwwserv port { 80, \ 21, > 49151 } flags S/SA keep state queue internal_dmz |
[前に戻る: アンカーと名前付き (サブ) ルールセット] [目次] [次に進む: アドレスプールと負荷分散 (Load Balancing)]