Tc:入力ポリシングおよびifbミラーリング


20

ここに書かれているように、Linuxゲートウェイでトラフィックシェーピングを設定しようとしています。複数のLANインターフェイスがあるため、スクリプトをカスタマイズする必要があります。LAN側を形成するために、次のようなifb疑似デバイスを作成する予定です。

     modprobe ifb
     ip link set dev ifb0 up
    /sbin/tc qdisc add dev $WAN_INTERFACE ingress
    /sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0

上記の要点リポジトリのスクリプトには、次の行があります。

 /sbin/tc qdisc add dev $WAN_INTERFACE handle ffff: ingress
    /sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip prio 1 u32 match ip sport $INTERACTIVE_PORT 0xffff flowid :1
    /sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip prio 1 u32 match ip dport $INTERACTIVE_PORT 0xffff flowid :1
    /sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip prio 5 0 u32 match ip src 0.0.0.0/0 police rate $MAX_DOWNRATE_INGRESS burst 20k drop flowid :2

このコードとifbインターフェース作成コードはうまく連携しません。カスタマイズされたスクリプトが実行されますが、ifb0デバイスはトラフィックの統計情報を表示しません。入力gistリポジトリコード(上記の引用)をコメントアウトすると、ifb0デバイスは転送されたパケットの数を表示します。また、これらの行は一緒に実行できません。

/sbin/tc qdisc add dev $WAN_INTERFACE ingress
/sbin/tc qdisc add dev $WAN_INTERFACE handle ffff: ingress

ファイルが存在するというエラーが表示されます。それで、WAN_INTERFACEでイングレスを形成し、同時にifb0デバイス経由でLANに向かうトラフィックを形成するにはどうすればよいですか?

回答:


41

IFBは、入力トラフィックを仮想インターフェイスにリダイレクトすることにより、入力トラフィックを処理するためのtcフィルターの代替であり、そこでは出力トラフィックとして扱われます。に。

ifbモジュールを挿入するときに、必要な仮想インターフェイスの数を伝えます。デフォルトは2です。

modprobe ifb numifbs=1

ここで、すべてのifbインターフェイスを有効にします。

ip link set dev ifb0 up # repeat for ifb1, ifb2, ...

そして、入力トラフィックを物理インターフェースから対応するifbインターフェースにリダイレクトします。eth0-> ifb0の場合:

tc qdisc add dev eth0 handle ffff: ingress
tc filter add dev eth0 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0

繰り返しますが、eth1-> ifb1、eth2-> ifb2などについて、整形したいすべてのインターフェースがカバーされるまで繰り返します。

これで、必要なすべてのルールを適用できます。eth0の出力ルールは、eth0で通常どおりに実行されます。たとえば、帯域幅を制限しましょう。

tc qdisc add dev eth0 root handle 1: htb default 10
tc class add dev eth0 parent 1: classid 1:1 htb rate 1mbit
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 1mbit

言うまでもなく、eth1、eth2、...についても繰り返します。

eth0の入力ルールは、ifb0の出力ルールとして使用されるようになりました(ifb0に入力されるものはすべて出力され、eth0の入力トラフィックのみがifb0に入力されます)。再び、帯域幅制限の例:

tc qdisc add dev ifb0 root handle 1: htb default 10
tc class add dev ifb0 parent 1: classid 1:1 htb rate 1mbit
tc class add dev ifb0 parent 1:1 classid 1:10 htb rate 1mbit

このアプローチの利点は、出力ルールが入力フィルターよりもはるかに柔軟であることです。フィルタを使用すると、パケットのドロップのみが許可され、待機時間は発生しません。入力トラフィックを出力として処理することにより、トラフィッククラスおよび必要に応じてフィルターを使用して、キューの規則を設定できます。単純なフィルターだけでなく、tcツリー全体にアクセスできます。


よくできました。ロックスターの最初の答えでプロがポップアップするのを見るのは常に良いことです。
マゼラン

これは素朴な質問かもしれませんが、特定の情報を見つけることができません。この答えに基づいて(これは素晴らしいです)、ifb0cgroup classidの統計情報を取得することは可能ですか?つまり、classidフィルターを使用してcgroupの出力統計を正常に取得できます。しかし、着信トラフィックもcgroupごとに計算できますか?
jdi

パケットをマークするためにiptableを使用してからフィルタリングする場合、すべての入力トラフィックがマーキングの前に転送されるため、ifbを使用できないことに注意してください。そのため、クラスは0のままで、すべてデフォルトに転送されます。IMQは、iptablesユーザーにとって適切なソリューションのようです。
ornoone

@SérgioCarvalhoこれをcgroupsのnet_clsコントローラーと連携して動作させることはできません。net_clsとtcを併用して通常の送信ネットワークトラフィック(出力)を制限できるため、少し混乱しています。私の最善の推測は、何らかの理由でifbをこの方法で使用している場合、ifbから出てくる出力パケットはイングレスとして開始されてから適切にタグ付けされていないということですか?誰でもこれを確認したり、私ができる方法を提案できますか?
オンドリ14年

3

SérgioCarvalhoの回答に基づいて、帯域幅を制限する小さなbashスクリプトを作成しました。

ファイル名:netspeed

#!/bin/bash 

#USAGE: sudo ./netspeed -l limit_in_kbit -s
usage="sudo $(basename "$0") -l speed_limit -s
  -l speed_limit - speed limit with units (eg. 1mbit, 100kbit, more on \`man tc\`)
  -s - remove all limits
"

# default values
LIMIT=0
STOP=0

# hardcoded constats
IFACE=ifb0 # fake interface name which will be used for shaping the traffic
NETFACE=wlan0 # interface which in connected to the internet

# shift all required and leave only optional

while getopts ':hl:s' option; do
  case "$option" in
   l) LIMIT=$OPTARG
      ;;
   s) STOP=1
      ;;
   h) echo "$usage"
      exit
      ;;
  esac
done

#
# functions used in script
#
function limitExists { # detected by ingress on $NETFACE qdisc
   # -n equals true if non-zero string length
  if [[ -n `tc qdisc show | grep "ingress .* $NETFACE"` ]]
  then
    return 0 # true
  else
    return 1 # false
  fi

}
function ifaceExists {
  # -n equals true if non-zero string length
  if [[ -n `ifconfig -a | sed 's/[ \t].*//;/^\(lo\|\)$/d' | grep $IFACE` ]]
  then
    return 0 # true
  else
    return 1 # false
  fi
}
function ifaceIsUp {
  # -n equals true if non-zero string length
  if [[ -n `ifconfig | sed 's/[ \t].*//;/^\(lo\|\)$/d' | grep $IFACE` ]]
  then
    return 0 # true
  else
    return 1 # false
  fi
}
function createLimit {
  #3. redirect ingress
  tc qdisc add dev $NETFACE handle ffff: ingress
  tc filter add dev $NETFACE parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev $IFACE

  #4. apply egress rules to local inteface (like wlan0)
  tc qdisc add dev $NETFACE root handle 1: htb default 10
  tc class add dev $NETFACE parent 1: classid 1:1 htb rate $LIMIT
  tc class add dev $NETFACE parent 1:1 classid 1:10 htb rate $LIMIT

  #5. and same for our relaying virtual interfaces (to simulate ingress)
  tc qdisc add dev $IFACE root handle 1: htb default 10
  tc class add dev $IFACE parent 1: classid 1:1 htb rate $LIMIT
  tc class add dev $IFACE parent 1:1 classid 1:10 htb rate $LIMIT
}
function updateLimit {
  #3. redirect ingress
  tc filter replace dev $NETFACE parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev $IFACE

  #4. apply egress rules to local inteface (like wlan0)
  tc class replace dev $NETFACE parent 1: classid 1:1 htb rate $LIMIT
  tc class replace dev $NETFACE parent 1:1 classid 1:10 htb rate $LIMIT

  #5. and same for our relaying virtual interfaces (to simulate ingress)
  tc class replace dev $IFACE parent 1: classid 1:1 htb rate $LIMIT
  tc class replace dev $IFACE parent 1:1 classid 1:10 htb rate $LIMIT
}
function removeLimit {
  if limitExists ; then
    tc qdisc del dev $NETFACE ingress
    tc qdisc del dev $NETFACE root
    tc qdisc del dev $IFACE root
  fi
  if ifaceIsUp ; then
    ip link set dev $IFACE down
  fi
}

#
# main script
#
if [[ `whoami` != "root" ]]; then
  echo "WARNING: script must be executed with root privileges!"
  echo $usage
  exit 1
fi
if [ $STOP -eq 1 ]; then
  echo "REMOVING limit"
  removeLimit
  echo "limit REMOVED"
elif [ "$LIMIT" != "0" ]; then
  # prepare interface
  if ! ifaceExists ; then
    echo "CREATING $IFACE by modprobe"
    modprobe ifb numifbs=1
    if ! ifaceExists ; then
      echo "creating $IFACE by modprobe FAILED"
      echo "exit with ERROR code 2"
      exit 2
    fi
  fi
  # set interface up
  if ifaceIsUp ; then
    echo "$IFACE is already up"
  else
    echo "set $IFACE up"
    ip link set dev $IFACE up # ( use ifconfig to see results)
    if ifaceIsUp ; then
      echo "$IFACE is up"
    else
      echo "enabling $IFACE by ip link FAILED"
      echo "exit with ERROR code 3"
      exit 3
    fi
  fi

  # create/update limits
  if limitExists ; then
    echo "update limit"
    updateLimit
  else
    echo "create limit"
    createLimit
  fi

  echo "limit CREATED"
  exit 0
else
  echo $usage
fi

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.