1 tcコマンドとは?
カーネル(kernel)とドライバ(network device driver)の間に、送信パケットの送信順序の変更、遅延、廃棄等の機能を提供するqdiscというコンポーネントがあります。tcコマンドは、qdisc(Queueing Discipline)に対する設定、削除、参照等の操作をするコマンドです。
-*- +----------------------------------------------------+ | | | | | kernel | | | | | +----------------------------------------------------+ soft +------------------------+ +------------------------+ | | qdisc | | qdisc | <== tcコマンドは、ここ(qdisc)に対する操作をする | +------------------------+ +------------------------+ | +------------------------+ +------------------------+ | | network device driver | | network device driver | -*- +------------------------+ +------------------------+ -*- +------------------------+ +------------------------+ | | | | | hard | eth0 | | eth1 | | | | | | -*- +------------------------+ +------------------------+
2 検証環境
2.1 ネットワーク構成
サーバとクライアントの2台構成です。図中のeth0はNICの名前です。サーバでtcコマンドを実行します。
192.168.2.0/24 client(eth0) -------------------------------------------- (eth0) server .105 .100
3 qdiscの種類
分類 | qdiscの名前 | カーネルソースファイルの場所 | 概要 |
---|---|---|---|
クラスレス qdisc | pfifo_fast | net/sched/sch_generic.c | デフォルトのqdiscです。FIFOのキューが3つあります |
pfifo | net/sched/sch_fifo.c | FIFOのキューが1つあります。キュー長の最大値はパケット数で表します | |
bfifo | 同上 | FIFOのキューが1つあります。 キュー長の最大値はバイト数で表します | |
sfq(Stochastic Fairness Queueing) | net/sched/sch_sfq.c | フローにもとづいてパケットをスケジューリングします。フローとは送信元/送信先のIPアドレス、ポート番号の組です | |
tbf(Token Bucket Filter) | net/sched/sch_tbf.c | パケットシェーピング機能を提供します。制限レートを超えたパケットはキューに保留されます | |
netem(Network Emulator) | net/sched/sch_netem.c | 送信パケットに対して、パケット廃棄、遅延等の操作をするためのqdiscです | |
fq(Fair Queue Packet Schedule) | net/sched/sch_fq.c | Fair Queue Packet Scheduler (per flow pacing) | |
クラスフル qdisc | prio | net/sched/sch_prio.c | ルールにしたがってパケットを分類(class)します。デフォルトでは3つのクラスがあります。それぞれのクラスにqdisc(sfq,tbf等)を設定することができます。 |
htb(Hierarchy Token Bucket) | net/sched/sch_htb.c | 1つのリンクを複数のサービス(www,ftp等)で共有する場合、各サービスに最低限必要な帯域を割り当てることができます |
4 クラスレスqdiscの設定、削除
4.1 設定、削除の書式
設定は下記書式になります。
# tc qdisc add dev DEV root QDISC QDISC-PARAMETERS
- DEVはデバイス名(eth0,eth1等)を指定
- QDISCはqdiscの名前(pfifo,sfq,tbf,netem等)を指定
- QDISC-PARAMETERSは、QDISC毎のパラメータを指定
削除は下記書式になります。
# tc qdisc del dev DEV root
4.2 pfifoの設定、削除の方法
qdiscを確認します。デフォルトのpfifo_fast(★印)が設定されていることがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdiscをpfifo_fastからpfifoに変更します。limit 100は、FIFOキューが最大100パケットをキューイングできることを意味しています。
[root@server ~]# tc qdisc add dev eth0 root pfifo limit 100
qdiscを確認します。qdiscがpfifo(★印)に変更されたことがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo 8001: root refcnt 2 limit 100p
変更したpfifoの統計情報を確認してみます。
[root@server ~]# tc -s qdisc ls dev eth0 qdisc pfifo 8001: root refcnt 2 limit 100p Sent 7202 bytes ★77 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0
少し時間をおいてから、再度統計情報を確認してみます。送信パケット数が増加(77->83)していることがわかります。
[root@server ~]# tc -s qdisc ls dev eth0 qdisc pfifo 8001: root refcnt 2 limit 100p Sent 7938 bytes ★83 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0
pfifoを削除します。
[root@server ~]# tc qdisc del dev eth0 root
qdiscを確認します。qdiscがデフォルトのpfifo_fast(★印)に戻ったことがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
4.3 sfqの設定、削除の方法
sfqはフローにもとづいてパケットをスケジューリングするqdiscです。フローとは、下記4つの情報で識別されるパケットです。たとえば、TCPパケットAとBがあるとします。下記4つの情報が全て同じであれば、AとBは同一フローになります。1つでも異なれば、別フローになります。
・送信元IPアドレス
・送信先IPアドレス
・送信元ポート番号
・送信先ポート番号
4.4 tbfの設定、削除の方法
- (1)トークンと「同じ」割合で、TBF にデータが到着する場合
この場合各受信パケットは、それぞれ対応するトークンがあるので、 遅延することなしにキューを通過する。
- (2)トークンの速度よりも「遅い」割合で、TBF にデータが到着する場合
キューに入った受信データパケットの出力に応じて 削除されるトークンは一部分のみなので、トークンはバケツサイズ一杯にまで溜まっていく。
使われなかったトークンは、 突発的なデータのバーストが起こったような場合に利用でき、 トークンの標準流入速度を越えたデータが送信できる。
- (3)トークンの速度よりも「大きい」割合で、TBF にデータが到着する場合
この場合、バケツのトークンはすぐに空になってしまい、 TBF はしばらくの間入力を絞る。
これは「過負荷状態 (overlimit situation)」と呼ばれる。 パケットがそのまま入り続けてくる場合には、 パケットは破棄され始める。
qdiscを確認します。qdiscがデフォルトのpfifo_fast(★印)であることがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdiscをpfifo_fastからtbfに変更してみます。
- burst:トークンを入れるバケツのサイズ(byte単位)を表します。
- latency:バケツのトークンが空の場合、送信パケットはtbfのキューにバッファリングされます。バッファリングする最大時間を表します。
[root@server ~]# tc qdisc add dev eth0 root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540
qdiscを確認します。qdiscがtbf(★印)に変更されたことがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★tbf 8002: root refcnt 2 rate 500Kbit burst 5Kb peakrate 1Mbit minburst 1540b lat 70.0ms
tbfを削除します。
[root@server ~]# tc qdisc del dev eth0 root
qdiscを確認します。qdiscがデフォルトのpfifo_fast(★印)に変更されたことがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
4.5 netemの設定、削除の方法
4.5.1 一定の遅延を設定する方法(delay)
qdiscを確認します。デフォルトのpfifo_fast(★印)が設定されていることがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdiscに遅延を設定します。送信パケットをqdiscで10ミリ秒保留してからドライバに送信パケットを渡すようにしてみます。
[root@server ~]# tc qdisc add dev eth0 root netem delay 10ms
qdiscを確認します。qdiscがnetem(★印)に変更されたことがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★netem 8003: root refcnt 2 limit 1000 delay 10.0ms
netemを削除します。
[root@server ~]# tc qdisc del dev eth0 root
qdiscを確認します。qdiscがデフォルトのpfifo_fast(★印)に変更されたことがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
4.5.2 遅延にばらつきを設定する方法(delay)
遅延にばらつきを設定してみます。
qdiscを確認します。qdiscがデフォルトのpfifo_fast(★印)であることがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
遅延のばらつきを100ミリ秒±10ミリ秒に設定してみます。この設定をすると、送信パケットがqdiscで90ミリ秒~110ミリ秒のランダムな時間保留されてからドライバに渡されます。
[root@server ~]# tc qdisc add dev eth0 root netem delay 100ms 10ms
qdiscを確認します。遅延のばらつきが100ミリ秒±10ミリ秒に設定されていることがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc netem 8001: root refcnt 2 limit 1000 delay 100.0ms 10.0ms
netemを削除します。
[root@server ~]# tc qdisc del dev eth0 root
qdiscを確認します。qdiscがデフォルトのpfifo_fast(★印)に変更されたことがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
4.5.3 廃棄の設定方法(loss)
qdiscを確認します。qdiscがデフォルトのpfifo_fast(★印)であることがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
パケット廃棄率を0.1%に設定してみます。
[root@server ~]# tc qdisc add dev eth0 root netem loss 0.1%
パケット廃棄率を確認すると0.1%に設定(★印)されたことがわかります。なお、0.1%程度のパケット廃棄率だと、コマンドの実行に支障は感じられませんでした。
[root@server ~]# tc qdisc show dev eth0 qdisc netem 8002: root refcnt 2 limit 1000 loss ★0.1%
パケット廃棄率を0.1%から20%に変更します。変更はchangeコマンドを使います。
[root@server ~]# tc qdisc change dev eth0 root netem loss 20%
パケット廃棄率を確認します。パケット廃棄率が20%に設定(★印)されたことがわかります。パケット廃棄率が20%になると、たとえばlsコマンドを実行しても、コマンドの実行完了までに時間がかかったりしました。
[root@server ~]# tc qdisc show dev eth0 qdisc netem 8002: root refcnt 2 limit 1000 loss ★20%
統計情報を確認します。
[root@server ~]# tc -s qdisc ls dev eth0 qdisc netem 8002: root refcnt 2 limit 1000 loss 20% Sent 13632 bytes 138 pkt (dropped 20, overlimits 0 requeues 0) backlog 0b 0p requeues 0
netemを削除します。
[root@server ~]# tc qdisc del dev eth0 root
qdiscを確認します。qdiscがデフォルトのpfifo_fast(★印)に戻ったことがわかる。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
5 クラスフルqdiscの設定、削除
5.1 prioの設定、削除の方法
5.1.1 設定方法
qdiscを確認します。デフォルトのpfifo_fast(★印)が設定されていることがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdiscをpfifo_fastからprioに変更します。
[root@server ~]# tc qdisc add dev eth0 root handle 1: prio
qdiscを確認します。qdiscがprio(★印)に変更されたことがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc ★prio 1: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
classを確認します。classが自動的に3つ作成されていることがわかります。
[root@server ~]# tc class show dev eth0 class prio 1:1 parent 1: class prio 1:2 parent 1: class prio 1:3 parent 1:
作成したclassにsfqを設定します。
[root@server ~]# tc qdisc add dev eth0 parent 1:1 handle 10: sfq
作成したclassにtbfを設定します。
[root@server ~]# tc qdisc add dev eth0 parent 1:2 handle 20: tbf rate 20kbit buffer 1600 limit 3000
作成したclassにsfqを設定します。
[root@server ~]# tc qdisc add dev eth0 parent 1:3 handle 30: sfq
qdiscを確認します。
[root@server ~]# tc qdisc show dev eth0 qdisc prio 1: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 qdisc sfq 30: parent 1:3 limit 127p quantum 1514b depth 127 divisor 1024 qdisc tbf 20: parent 1:2 rate 20Kbit burst 1600b lat 560.0ms qdisc sfq 10: parent 1:1 limit 127p quantum 1514b depth 127 divisor 1024
設定したclassとqdiscの関係は以下のようになります。
1: root qdisc / | \ / | \ / | \ 1:1 1:2 1:3 classes | | | 10: 20: 30: qdisc sfq tbf sfq band 0 1 2
統計情報を確認してみます。
[root@server ~]# tc -s qdisc ls dev eth0 qdisc prio 1: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 Sent 25082 bytes 233 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 qdisc sfq 30: parent 1:3 limit 127p quantum 1514b depth 127 divisor 1024 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 qdisc tbf 20: parent 1:2 rate 20Kbit burst 1600b lat 560.0ms Sent 1122 bytes 15 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 qdisc sfq 10: parent 1:1 limit 127p quantum 1514b depth 127 divisor 1024 Sent 14122 bytes 119 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0
統計情報を再度確認してみます。
[root@server ~]# tc -s qdisc ls dev eth0 qdisc prio 1: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 Sent 26236 bytes 238 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 qdisc sfq 30: parent 1:3 limit 127p quantum 1514b depth 127 divisor 1024 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 qdisc tbf 20: parent 1:2 rate 20Kbit burst 1600b lat 560.0ms Sent 1164 bytes 16 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 qdisc sfq 10: parent 1:1 limit 127p quantum 1514b depth 127 divisor 1024 Sent 15234 bytes 123 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0
5.2.2 削除方法
netemを削除します。
[root@server ~]# tc qdisc del dev eth0 root
eth0のqdiscを確認する。qdiscがデフォルトのpfifo_fast(★印)に戻ったことがわかる。
[root@server ~]# tc qdisc show dev eth0 qdisc pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
classを確認する。何も登録されていないことがわかる。
[root@server ~]# tc class show dev eth0 [root@server ~]#
5.2 htbの設定、削除の方法
5.2.1 設定方法
[root@server ~]# tc qdisc show dev eth0 qdisc pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
[root@server ~]# tc qdisc add dev eth0 root handle 1: htb default 30 [root@server ~]# tc qdisc show dev eth0 qdisc htb 1: root refcnt 2 r2q 10 default 30 direct_packets_stat 11 direct_qlen 1000
[root@server ~]# tc class add dev eth0 parent 1: classid 1:1 htb rate 6mbit burst 15k [root@server ~]# tc qdisc show dev eth0 qdisc htb 1: root refcnt 2 r2q 10 default 30 direct_packets_stat 85 direct_qlen 1000
[root@server ~]# tc class add dev eth0 parent 1:1 classid 1:10 htb rate 5mbit burst 15k [root@server ~]# tc class add dev eth0 parent 1:1 classid 1:20 htb rate 3mbit ceil 6mbit burst 15k [root@server ~]# tc class add dev eth0 parent 1:1 classid 1:30 htb rate 1kbit ceil 6mbit burst 15k
[root@server ~]# tc class show
[root@server ~]# tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10 [root@server ~]# tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10 [root@server ~]# tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10
[root@server ~]# tc class show
[root@server ~]# tc qdisc show dev eth0 qdisc htb 1: root refcnt 2 r2q 10 default 30 direct_packets_stat 186 direct_qlen 1000 qdisc sfq 30: parent 1:30 limit 127p quantum 1514b depth 127 divisor 1024 perturb 10sec qdisc sfq 20: parent 1:20 limit 127p quantum 1514b depth 127 divisor 1024 perturb 10sec qdisc sfq 10: parent 1:10 limit 127p quantum 1514b depth 127 divisor 1024 perturb 10sec
[root@server ~]# U32="tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32" [root@server ~]# $U32 match ip dport 80 0xffff flowid 1:10 [root@server ~]# $U32 match ip sport 25 0xffff flowid 1:20 [root@server ~]# tc filter show dev eth0 filter parent 1: protocol ip pref 1 u32 chain 0 filter parent 1: protocol ip pref 1 u32 chain 0 fh 800: ht divisor 1 filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:10 not_in_hw match 00000050/0000ffff at 20 filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:20 not_in_hw match 00190000/ffff0000 at 20
[root@server ~]# tc class show dev eth0 class htb 1:1 root rate 6Mbit ceil 6Mbit burst 15Kb cburst 1599b class htb 1:10 parent 1:1 leaf 10: prio 0 rate 5Mbit ceil 5Mbit burst 15Kb cburst 1600b class htb 1:20 parent 1:1 leaf 20: prio 0 rate 3Mbit ceil 6Mbit burst 15Kb cburst 1599b class htb 1:30 parent 1:1 leaf 30: prio 0 rate 1Kbit ceil 6Mbit burst 15Kb cburst 1599b
5.2.2 削除方法
htbを削除します。
[root@server ~]# tc qdisc del dev eth0 root [root@server ~]# tc qdisc show dev eth0 qdisc pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 [root@server ~]# tc class show dev eth0 [root@server ~]# tc filter show dev eth0
6 統計情報を確認する方法
eth0に対してtcコマンドを実行します。eth0にはpfifo_fastが使われていることがわかります。
[root@server ~]# tc -s qdisc show dev eth0 qdisc pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 Sent 104161 bytes 1109 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0
7 実験
7.1 遅延
クライアントでpingを実行します。このとき、サーバのqdiscで10ミリ秒の遅延を設定して、pingのrttがどのように変化するのか確認してみます。なお、pingの使い方は、pingコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
まず、遅延を設定していない状態でpingのrttを計測してみます。rttの平均時間は0.648ミリ秒であることがわかります。
[root@client ~]# ping -q -c 3 192.168.2.100 PING 192.168.2.100 (192.168.2.100) 56(84) bytes of data. --- 192.168.2.100 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2010ms rtt min/avg/max/mdev = 0.575/0.648/0.721/0.066 ms
サーバのqdiscに10ミリ秒の遅延を設定してみます。
[root@server ~]# tc qdisc add dev eth0 root netem delay 10ms
クライアントでpingを実行します。rttの平均時間が14.875ミリ秒になったことがわかります。おおよ14.2ミリ秒程度遅延が増大したことがわかります。
[root@client ~]# ping -q -c 3 192.168.2.100 PING 192.168.2.100 (192.168.2.100) 56(84) bytes of data. --- 192.168.2.100 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2019ms rtt min/avg/max/mdev = 12.864/14.875/16.016/1.429 ms
7.2 廃棄
サーバのqdiscに送信パケットの廃棄率として50%を設定してみます。
[root@server ~]# tc qdisc change dev eth0 root netem loss 50%
qdiscを確認します。qdiscに送信パケットの廃棄率が50%に設定されたことがわかります。
[root@server ~]# tc qdisc show dev eth0 qdisc netem 8006: root refcnt 2 limit 1000 loss 50%
pingを1回実行します。pingの応答がクライアントに返る場合もありますが、以下のようにクライアントに返ってこない場合もあります。
[root@client ~]# ping -q -c 1 192.168.2.100 PING 192.168.2.100 (192.168.2.100) 56(84) bytes of data. --- 192.168.2.100 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms
8 その他
tcコマンドに関係ありません。たんなるメモです。pfifo_fastのキュー長を変更してみます。キュー長を確認すると、キュー長が1000(★印)であることがわかります。
[root@server ~]# ip l show dev eth0 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen ★1000 link/ether 00:0c:29:66:4c:a4 brd ff:ff:ff:ff:ff:ff
キュー長を2000に変更します。
[root@server ~]# ip l set eth0 txqueuelen 2000
pfifo_fastのキュー長を確認します。キュー長が、2000(★印)に変更されたことがわかります。
[root@server ~]# ip l show dev eth0 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen ★2000 link/ether 00:0c:29:66:4c:a4 brd ff:ff:ff:ff:ff:ff
Z 参考情報
私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ