Shammer's Philosophy

My private adversaria

tcとiptablesの組み合わせ方法

以下の記事で、それぞれtcとiptablesについて書いたが、今回は両方を組み合わせて、特定のパケットのみtcで遅延させてみる。

これを本気で理解しようとしたら、Linux内部のパケットの動きについて相当詳しくないとできなそうなので、とりあえず、おまじないのような感じでメモ。おまじないにしては結構な分量だが。

tcの設定

条件として500msの遅延、10%のパケットロスを定義する例。

tc qdisc add dev eth1 root handle 1: htb default 15
tc class add dev eth1 parent 1: classid 1:1 htb rate 1000mbit
tc class add dev eth1 parent 1:1 classid 1:10 htb rate 1000mbit prio 0
tc class add dev eth1 parent 1:1 classid 1:11 htb rate 1000mbit prio 1
tc filter add dev eth1 parent 1:0 protocol ip prio 1 handle 1 fw classid 1:10
tc filter add dev eth1 parent 1:0 protocol ip prio 2 handle 2 fw classid 1:11
tc qdisc add dev eth1 parent 1:10 handle 10: netem delay 500ms
tc qdisc add dev eth1 parent 1:11 handle 20: netem loss 10%

一つずつ実行したときの様子。少しずつ定義が変わっていくが、、、詳細はよくわからない。

~# tc qdisc show dev eth1
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
~# 
~# 
~# tc qdis add dev eth1 root handle 1: htb default 15
~#                       
~# 
~# tc qdisc show dev eth1
qdisc htb 1: root refcnt 2 r2q 10 default 15 direct_packets_stat 8
~# 
~# 
~# tc class show dev eth1
~# 
~# 
~# tc class add dev eth1 parent 1: classid 1:1 htb rate 1000mbit
~# 
~# 
~# tc qdisc show dev eth1
qdisc htb 1: root refcnt 2 r2q 10 default 15 direct_packets_stat 79
~# 
~# 
~# tc class show dev eth1
class htb 1:1 root prio 0 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
~# 
~# 
~# tc class add dev eth1 parent 1:1 classid 1:10 htb rate 1000mbit prio 0
~# 
~# 
~# tc qdisc show dev eth1
qdisc htb 1: root refcnt 2 r2q 10 default 15 direct_packets_stat 81
~# 
~# 
~# tc class show dev eth1
class htb 1:10 parent 1:1 prio 0 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:1 root rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
~# 
~# 
~# tc class add dev eth1 parent 1:1 classid 1:11 htb rate 1000mbit prio 1
~# 
~# 
~# tc qdisc show dev eth1
qdisc htb 1: root refcnt 2 r2q 10 default 15 direct_packets_stat 83
~# 
~# 
~# tc class show dev eth1
class htb 1:11 parent 1:1 prio 1 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:10 parent 1:1 prio 0 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:1 root rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
~# 
~# 
~# tc filter show dev eth1
~# 
~# 
~# tc filter add dev eth1 parent 1:0 protocol ip prio 1 handle 1 fw classid 1:10
~# 
~# 
~# tc qdisc show dev eth1
qdisc htb 1: root refcnt 2 r2q 10 default 15 direct_packets_stat 83
~# 
~# 
~# tc class show dev eth1
class htb 1:11 parent 1:1 prio 1 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:10 parent 1:1 prio 0 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:1 root rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
~# 
~# 
~# tc filter show dev eth1
filter parent 1: protocol ip pref 1 fw 
filter parent 1: protocol ip pref 1 fw handle 0x1 classid 1:10 
~# 
~# 
~# tc filter add dev eth1 parent 1:0 protocol ip prio 2 handle 2 fw classid 1:11
~# 
~# 
~# tc qdisc show dev eth1
qdisc htb 1: root refcnt 2 r2q 10 default 15 direct_packets_stat 85
~# 
~# 
~# tc class show dev eth1
class htb 1:11 parent 1:1 prio 1 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:10 parent 1:1 prio 0 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:1 root rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
~# 
~# 
~# tc filter show dev eth1
filter parent 1: protocol ip pref 1 fw 
filter parent 1: protocol ip pref 1 fw handle 0x1 classid 1:10 
filter parent 1: protocol ip pref 2 fw 
filter parent 1: protocol ip pref 2 fw handle 0x2 classid 1:11 
~# 
~# 
~# tc qdisc add dev eth1 parent 1:10 handle 10: netem delay 500ms
~# 
~# 
~# tc qdisc show dev eth1
qdisc htb 1: root refcnt 2 r2q 10 default 15 direct_packets_stat 85
qdisc netem 10: parent 1:10 limit 1000 delay 500.0ms
~# 
~# 
~# tc class show dev eth1
class htb 1:11 parent 1:1 prio 1 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:10 parent 1:1 leaf 10: prio 0 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:1 root rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class netem 10:1 parent 10: 
~# 
~# 
~# tc filter show dev eth1
filter parent 1: protocol ip pref 1 fw 
filter parent 1: protocol ip pref 1 fw handle 0x1 classid 1:10 
filter parent 1: protocol ip pref 2 fw 
filter parent 1: protocol ip pref 2 fw handle 0x2 classid 1:11 
~# 
~# 
~# tc qdisc add dev eth1 parent 1:11 handle 20: netem loss 10%   
~# 
~# 
~# tc qdisc show dev eth1
qdisc htb 1: root refcnt 2 r2q 10 default 15 direct_packets_stat 85
qdisc netem 10: parent 1:10 limit 1000 delay 500.0ms
qdisc netem 20: parent 1:11 limit 1000 loss 10%
~# 
~# 
~# tc class show dev eth1
class htb 1:11 parent 1:1 leaf 20: prio 1 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:10 parent 1:1 leaf 10: prio 0 rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class htb 1:1 root rate 1000Mbit ceil 1000Mbit burst 1375b cburst 1375b 
class netem 10:1 parent 10: 
class netem 20:1 parent 20: 
~# 
~# 
~# tc filter show dev eth1
filter parent 1: protocol ip pref 1 fw 
filter parent 1: protocol ip pref 1 fw handle 0x1 classid 1:10 
filter parent 1: protocol ip pref 2 fw 
filter parent 1: protocol ip pref 2 fw handle 0x2 classid 1:11 
~# 

iptablesの設定

上記tcを適用させるパケットの条件をiptablesで定義する。mangle table を使用する。

iptables -F -t mangle
iptables -t mangle -A POSTROUTING -p tcp --dport 80 -j MARK --set-mark 0x1
iptables -t mangle -A POSTROUTING -p tcp -j MARK --set-mark 0x2 -m string --algo kmp --string "BADREQUEST"

iptables -F -t mangle はmangleテーブルの初期化。
iptables -F -t mangle -A POSTROUTING -p tcp --dport 80 -j MARK --set-mark 0x1で、ポート80宛のリクエストに0x1のマーキングをする。次に、tcのフィルタでhandleが0x1のものを探し、該当するtcのフィルタからclassidを取得、このclassidで定義されたアクションを実行、ということだろうか。0x1を例にすると、

  1. dport 80 のパケットに0x1をマーク
  2. tc filter で handle が 0x1 のものを見つける
  3. tc filter で handle が 0x1 のフィルタのclassidは1:10
  4. tc qdisc で parent が 1:10 のものを探す(netem delay 500msのqdisc)
  5. 見つかったqdicsのアクションを実行

という流れになるのではないだろうか。これは、とりあえずこうすればできる、という人のメモからの引用で処理の順序はそのメモに基いた自分の予想。この予想通りか、そうでないのかは改めて確認する必要がある。ちなみに、--set-mark 0x2の方はTCPペイロードにBADREQUESTを含むパケットを対象にする設定らしい。