高度なトラフィック制御
Linux カーネルのネットワークスタックにはネットワークのトラフィック制御・トラフィックシェーピング機能が備わっています。{pkg|iproute2}} パッケージでインストールされる tc
コマンドを使うことでコマンドラインで制御できます。
この記事ではキューイング規則によってトラフィックをシェーピングする方法を説明します。例えば、ユーザーが帯域を占有しないように、管理しているネットワークのダウンロードや torrent を制限したい場合、キューイング規則を使ってトラフィック自体は許可しつつもネットワーク全体に負担をかけるような操作を制限できます。
上級者向けの記事としてネットワークデバイスや iptables の知識が必要です。
目次
キューイング
キューイングはデータの送信を制御します。データの受信は受動的なものでありネットワークで制御できる部分は限られます。ただし、TCP/IP パケットは遅延実行を使って送信されるため、パケットが転送される前にルーターに到達した時点でパケットを破棄することで LAN で受信されるトラフィックの量を制限することは可能です。ただし、キューイングロジックには直接には関係ありません。
トラフィックシェーピングを完全に制御するには、チェインの一番遅いリンクが必要です。接続の最大ダウンロード速度が 500k の場合、出力を 450k 以下に制限しないとモデムがトラフィックシェーピングを代わりに行ってしまいます。
各ネットワークデバイスには qdisc が設定できるルート (root) が存在します。デフォルトではルートには fq_codel qdisc が使われます (下を参照)。
キューイング規則にはクラスフルとクラスレスの2種類あります。
クラスフル qdisc はクラスを作成することができ、ツリーのブランチとして機能します。各クラスに対してルールを設定したりパケットをフィルタリングすることができます。各クラスには他のクラスフルあるいはクラスレス qdisc を設定することも可能です。
クラスレス qdisc では他の qdisc を追加することはできません。
qdisc の設定を始める前に、既存の qdisc をルートから削除する必要があります。以下のコマンドは eth0 デバイスから qdisc を削除します:
# tc qdisc del root dev eth0
クラスレス Qdisc
パケットの順序を変えたり遅延・破棄することでトラフィックを管理するキューです。クラスを作成することはできません。
fifo_fast
systemd 217 まではデフォルトの qdisc でした。qdisc のカスタム設定がないネットワークでは、fifo_fast がルートに設定される qdisc です。fifo は First In First Out を意味しており、最初にキューに入ったパケットが最初に送信されます。つまり特別な扱いは何もしません。
Token Bucket Filter (TBF)
特定のレート制限に達するまでパケットの送信を許可します。
仮想バケットを作成してバケットが満杯になったときに特定の速度でトークンを破棄することで機能します。各パッケージはバケットから仮想トークンを取得して、トークンを使って送信許可を得ます。大量のパケットが来た場合、バケットはトークンを発行することができず新しいトークンができるまで待機することになります。トークンが十分高速に到達しない場合、パケットが破棄されます。逆の場合 (送信されるパケットが少なすぎる場合)、トークンを使うことで一時的に通信速度を爆発させることができます。
上記より、インターフェイスを遅くしたい場合に役立つ qdisc です。例:
アップロードによってモデムのキューが満杯になってしまうことがあるため、巨大なファイルをアップロードしようとすると通信が破壊されます。
# tc qdisc add dev ppp0 root tbf rate 220kbit latency 50ms burst 1540
上記のアップロード速度はチェインの一番遅いリンクとなるように数パーセント減じた値に変更してください。ppp0
デバイスに TBF を設定して、アップロード速度が 220k に制限されパッケージが破棄されるまでの遅延が 50ms に、バーストが 1540 に設定されます。モデムの代わりに Linux マシンのキューイングを使うことで動作します。
Stochastic Fairness Queueing (SFQ)
ラウンドロビンの qdisc です。会話は fifo キューにセットされ、各ラウンドで、会話はデータを送信できる可能性があります。これが "Fairness" と呼ばれる所以です。"Stochastic" とも呼ばれる理由は、実際には会話に対してキューを作成するのではなくハッシュアルゴリズムを使っているためです。ハッシュは同一のバケットで複数のセッションが作成される可能性があります。この問題を解決するために、SFQ はハッシュアルゴリズムを頻繁に変更して問題が発現しにくいようにしています。例:
以下のコマンドは eth0 デバイスのルートに SFQ を設定して、10秒ごとにハッシュアルゴリズムを変更します:
# tc qdisc add dev eth0 root sfq perturb 10
CoDel と Fair Queueing CoDel
systemd 217 から fq_codel がデフォルトとなっています。CoDel (Controlled Delay) は遅くて邪魔な悪いクエリとすぐに空になる良いクエリを区別することで飽和状態にあるネットワークリンクにおけるバッファブロートや遅延を抑えます。Fair Queueing Codel は公平なキューを利用して Codel のフローに帯域を分配します。動的なネットワークでも使えるようにアルゴリズムが設計されているため、設定できるオプションは意図的に少なくなっています。稀なケースとして、バッファブロート wiki で議論されているような大規模なスイッチとメガビット未満の接続での問題などがあります。
詳しい情報は man tc-codel
や man tc-fq_codel
を読んでください。
クラスフル Qdisc
扱いを変える必要がある様々な種類のトラフィックが混在している場合はクラスフル qdisc が非常に有用です。クラスフル qdisc ではブランチを作成することができます。ブランチはクラスと呼ばれます。
クラスフル qdisc を設定するには各クラスに名前を付ける必要があります。クラスに名前を付けるときは classid
パラメータを使います。parent
パラメータはクラスの親を指定します。
名前は全て x:y
という形式で設定する必要があり、x
はルートの名前、y
はクラスの名前になります。通常、ルートは 1:
として子クラスは 1:10
などとなります。
Hierarchical Token Bucket (HTB)
帯域幅が固定されていて、様々な用途にあわせて使用できる帯域を保証して帯域幅を分割したい場合に HTB は有用です。設定例:
# This line sets a HTB qdisc on the root of eth0, and it specifies that the class 1:30 is used by default. It sets the name of the root as 1:, for future references. tc qdisc add dev eth0 root handle 1: htb default 30 # This creates a class called 1:1, which is direct descendant of root (the parent is 1:), this class gets assigned also an HTB qdisc, and then it sets a max rate of 6mbits, with a burst of 15k tc class add dev eth0 parent 1: classid 1:1 htb rate 6mbit burst 15k # The previous class has this branches: # Class 1:10, which has a rate of 5mbit tc class add dev eth0 parent 1:1 classid 1:10 htb rate 5mbit burst 15k # Class 1:20, which has a rate of 3mbit tc class add dev eth0 parent 1:1 classid 1:20 htb rate 3mbit ceil 6mbit burst 15k # Class 1:30, which has a rate of 1kbit. This one is the default class. tc class add dev eth0 parent 1:1 classid 1:30 htb rate 1kbit ceil 6mbit burst 15k # Martin Devera, author of HTB, then recommends SFQ for beneath these classes: tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10 tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10 tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10
フィルター
クラスフル qdisc をルートに設定した場合、フィルターを使ってどのクラスによってどのパッケージを処理するのか指定する必要があります。
クラスレスしか使わない環境では、フィルターは不要です。
tc あるいは tc + iptables を使ってパケットをフィルタリングできます。
tc だけを使う
フィルターの例:
# This command adds a filter to the qdisc 1: of dev eth0, set the # priority of the filter to 1, matches packets with a # destination port 22, and make the class 1:10 process the # packets that match. tc filter add dev eth0 protocol ip parent 1: prio 1 u32 match ip dport 22 0xffff flowid 1:10 # This filter is attached to the qdisc 1: of dev eth0, has a # priority of 2, and matches the ip address 4.3.2.1 exactly, and # matches packets with a source port of 80, then makes class # 1:11 process the packets that match tc filter add dev eth0 parent 1: protocol ip prio 2 u32 match ip src 4.3.2.1/32 match ip sport 80 0xffff flowid 1:11
tc + iptables を使う
iptables には fwmark と呼ばれる機能がありインターフェイスを跨いでパケットに印を付けることができます。
まず、以下のコマンドで 6 という番号が付けられたパケットが 1:30 クラスで処理されるようにします:
# tc filter add dev eth0 protocol ip parent 1: prio 1 handle 6 fw flowid 1:30
そして iptables を使って 6 という番号を付けます:
# iptables -A PREROUTING -t mangle -i eth0 -j MARK --set-mark 6
iptables を使ってパケットをマッチさせて fwmark で印を付けてください。