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设备不显示任何流量统计信息。如果我注释掉入口要点回购代码(上面引用),则ifb0设备将显示已传输的数据包数量。这些行也不能一起执行:

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

我得到文件存在错误。那么,我该如何调整WAN_INTERFACE上的入口,同时又调整通过ifb0设备进入LAN的流量?

Answers:


41

IFB是tc过滤器的替代选择,用于处理入口流量,方法是将其重定向到虚拟接口并将其视为出口流量。每个物理接口需要一个ifb接口,以将入口流量从eth0重定向到ifb0,将eth1重定向到ifb1等等上。

插入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树。


做得很好。看到专业人士突然出现摇滚明星的第一答案总是很高兴。
麦哲伦

这可能是一个幼稚的问题,但是我找不到特定的信息。基于这个答案(顺便说一句),是否有可能ifb0专门针对cgroup classid 获得统计信息?也就是说,我可以使用classid过滤器成功获取cgroup的出口统计信息。但是,是否也可以按每个cgroup计入传入流量?
jdi 2013年

请注意,如果使用iptable标记数据包然后对其进行过滤,则不能使用ifb,因为所有入口流量都将在标记之前转发。这样您就可以确保您的课程保持为0,并且全部转发到默认值。IMQ似乎是iptables用户的严格解决方案。
ornoone

@SérgioCarvalho我似乎无法使它与cgroups的net_cls控制器协同工作。我有些困惑,因为我可以和net一起使用net_cls限制正常的传出网络流量(出口)?我最好的猜测是,对于以某种方式使用ifb的原因来说,自ifb出来的出口数据包自进入时就没有被正确标记?谁能证实这一点或提出我可以采取的方法?
Rooster

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.