您的位置:首页 > 运维架构 > Linux

关于linux tc多网段限速的解决方案

2012-09-13 21:31 1191 查看
同样原先发布在:http://www.ywjt.org/index/archives/556.html,现在发布在这里分享一下:

以192.168.1.2为客户端为例,tc限制下载的关键命令:

IDEV=eth1
ODEV=eth0
/sbin/tc qdisc del dev $IDEV root handle 10:
/sbin/tc qdisc add dev $IDEV root handle 10: cbq bandwidth 100Mbit avpkt 1000
/sbin/tc class add dev $IDEV parent 10:0 classid 10:1 cbq bandwidth 100Mbit rate 100Mbit allot 1514 weight 1Mbit prio 8 maxburst 20 avpkt 1000
/sbin/tc class  add dev $IDEV parent 10:1 classid 10:12 cbq bandwidth 100Mbit rate $DOWNLOAD allot 1514 weight 20Kbit prio 5 maxburst 20 avpkt 1000 bounded
/sbin/tc qdisc  add dev $IDEV parent 10:12 sfq quantum 1514b perturb 15
/sbin/tc filter add dev $IDEV parent 10:0 protocol ip prio 100 u32 match ip dst192.168.1.2 flowid 10:12


先把原先的规则删除,然后创建一个排队规则qdisc:

QDisc(排队规则)是queueing discipline的简写,它是理解流量控制(traffic control)的基础。无论何时,内核如果需要通过某个网络接口发送数据包,它都需要按照为这个接口配置的qdisc(排队规则)把数据包加入队列。然后,内核会尽可能多地从qdisc里面取出数据包,把它们交给网络适配器驱动模块。

使用最简单的qdisc,纯粹的先进先出。只有一个参数:limit,用来设置队列的长度,pfifo是以数据包的个数为单位;bfifo是以字节数为单位。

一个QDisc会被分配一个主序列号,叫做句柄(handle),然后把从序列号作为类的命名空间。句柄采用象10:一样的表达方式。习惯上,需要为有子类的QDisc显式地分配一个句柄。

关于类(class),在同一个QDisc里面的类分享这个QDisc的主序列号,但是每个类都有自己的从序列号,叫做类识别符(classid)。类识别符只与父QDisc有关,和父类无关。类的命名习惯和QDisc的相同。

我们在内网网卡上创建一个句柄handle 10:

然后定义好限速的带宽信息:

/sbin/tc qdisc add dev $IDEV root handle 10: cbq bandwidth 100Mbit avpkt 1000

接着定义好实行规则的根分类和子分类比如10:1之类的。

/sbin/tc class add dev $IDEV parent 10:1 classid 10:12 cbq bandwidth 100Mbit rate $DOWNLOAD allot 1514 weight 20Kbit prio 5 maxburst 20 avpkt 1000 bounded
/sbin/tc qdisc add dev $IDEV parent 10:12 sfq quantum 1514b perturb 15

然后针对具体的ip进行分类,对每个ip定义一个flowid,以目标ip为准,创建相应的过滤器,绑定到指定的子类(10:1)里面去:

tc filter add dev $IDEV parent 10:0 protocol ip prio 100 u32 match ip dst 192.168.1.2 flowid 10:12

这样可以搞定针对指定ip的限速,但是现在需要的是针对十几个网段,我们之前的方案是来一个循环,比如match ip dst 后面循环接指定的所有ip,然后匹配到flowid这个唯一的标识码,这样就针对这些ip达到了限速功能。实现方法:

INET="192.168.2. 192.168.3. 192.168.4. 192.168.5. 192.168.6. 192.168.7. 192.168.8. 192.168.15.0 192.168.100. 192.168.30."
IPS=2
IPE=254
IDEV=eth1
ODEV=eth0
/sbin/tc qdisc del dev $IDEV root handle 10:
/sbin/tc qdisc add dev $IDEV root handle 10: cbq bandwidth 100Mbit avpkt 1000
/sbin/tc class add dev $IDEV parent 10:0 classid 10:1 cbq bandwidth 100Mbit rate 100Mbit allot 1514 weight 1Mbit prio 8 maxburst 20 avpkt 100
0
net_down(){
COUNTER=$IPS
while [ $COUNTER -le $IPE ]
do
/sbin/tc class  add dev $IDEV parent 10:1 classid 10:1$COUNTER cbq bandwidth 100Mbit rate $DOWNLOAD allot 1514 weight 20Kbit prio 5 maxbu
rst 20 avpkt 1000 bounded
/sbin/tc qdisc  add dev $IDEV parent 10:1$COUNTER sfq quantum 1514b perturb 15
/sbin/tc filter add dev $IDEV parent 10:0 protocol ip prio 100 u32 match ip dst $1$COUNTER flowid 10:1$COUNTER
COUNTER=` expr $COUNTER + 1 `
done
}
for i in $INET
do
net_down "$i"
done


这个是以前的方案,看似没有逻辑错误了,循环下来,针对每个ip段的2到254ip,相应地跑一下之前的tc filter这样就ok了。跑一下脚本,发现有这种报错

以为是系统内核的反馈,是正常现象,但是,用久了就发现了其中的bug,比如192.168.2.2在下载东西,2.2的网络包队列满了,卡了,不仅2.2卡了,同样会影响到所有的2为结尾的ip,比如3.2 4.2这些ip都会被限速,表现为到网关延迟非常大。

仔细研究一下用了一年多的脚本,发现有地方不对劲:位数是一样的会造成flowid重复,即每个段,同尾数的ip会共用一个flowid,找到bug了,马上解决,思路就是把flowid分开就可以了,本想以ip为标识符,把小数点去掉,当作flowid跑一下,这样就不会重复了,不过发现报错:Illegal “classid”,感觉很奇怪。。找不到问题所在,对照了一下之前跑的脚本,发现出来这个数字有点大,比如19216822,觉得是这里超出了定义内置的范围,把他改小一点发现正是这个问题,经测试用10:11到10:19999这些是正常的,即每个子分类只有998个ip可以用,好说,我们多定义几个父类就可以了,

/sbin/tc class add dev $IDEV parent 10:0 classid 10:1 cbq bandwidth 100Mbit rate 100Mbit allot 1514 weight 1Mbit prio 8 maxburst 20 avpkt 100

把这里的10:1多复制几个10:2 10:3之类的就可以了。

/sbin/tc class add dev $IDEV parent 10:0 classid 10:1 cbq bandwidth 100Mbit rate 100Mbit allot 1514 weight 1Mbit prio 8 maxburst 20 avpkt 1000
/sbin/tc class add dev $IDEV parent 10:0 classid 10:2 cbq bandwidth 100Mbit rate 100Mbit allot 1514 weight 1Mbit prio 8 maxburst 20 avpkt 1000


每个子类跑3个网段,2到254的,然后把原先的 flowid 10:1$COUNTER里面的COUNTER变量改为一个不重复的数字即可, 这样就解决了范围过大和flowid重复的问题。

最后修改的地方:

net_down1(){
COUNTER=$IPS
while [ $COUNTER -le $IPE ]
do
a=$(($a+1))
COUNT=$a
echo  "$1$COUNTER $COUNT" >>  tcc.log
#   以下三句限制各IP的下载带宽
/sbin/tc class  add dev $IDEV parent 10:1 classid 10:1$COUNT cbq bandwidth 100Mbit rate $DOWNLOAD allot 1514 weight 20Kbit prio 5 maxburst 20 avpkt 1000 bounded
/sbin/tc qdisc  add dev $IDEV parent 10:1$COUNT sfq quantum 1514b perturb 15
/sbin/tc filter add dev $IDEV parent 10:0 protocol ip prio 100 u32 match ip dst 1$COUNTER flowid 10:1$COUNT
COUNTER=` expr $COUNTER + 1 `
done
}


记得得把定义的ip段重新分段一下:

INET1=”192.168.2. 192.168.4. 192.168.5. 192.168.8. ”

INET2=”192.168.15. 192.168.100. 192.168.30.”

这样下面再对这些ip段跑一下netdown的函数就可以了

a=$(($a+1))

COUNT=$a

echo “$1$COUNTER $COUNT” >> tcc.log

其中这里的作用是为了要另外做一个解速的命令,在临时下载大文件时候用得到:我们把所有的ip和对应的flowid记录起来了,然后做过解速脚本:

#!/bin/sh
echo "$1" |grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' >/dev/null 2>&1|| { echo "第一个参数是ip"; exit 1 ;}
ip=$1
IP=`echo $ip | awk -F . '{$NF="";OFS=".";print $0}' `
inet=`grep ^INET tc.sh`
U=`echo "$inet" |grep $IP | awk -F = '{print $1}' | grep -oE "[0-9]{1,}"`
IIPP=`grep "$ip " tcc.log | awk '{print $2}'`
UIP=${U}${IIPP}
limit=4000
test -z $2 || limit=$2
tc class change dev eth1 parent 10:$U classid 10:$UIP cbq bandwidth 100Mbit rate ${limit}Kbit allot 1514 weight 20Kbit prio 5 maxburst 20 avpkt 1000 bounded


后面接具体ip,默认是解速到4兆的网络,后面可以再接具体的带宽。

这样结合之前的iptables限QQ号码和微博之类的方法,让linux开源网关更完美一些了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: