以192.168.1.2为客户端为例,tc限制下载的关键命令:linux
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:shell
QDisc(排队规则)是queueing discipline的简写,它是理解流量控制(traffic control)的基础。不管什么时候,内核若是须要经过某个网络接口发送数据包,它都须要按照为这个接口配置的qdisc(排队规则)把数据包加入队列。而后,内核会尽量多地从qdisc里面取出数据包,把它们交给网络适配器驱动模块。网络
使用最简单的qdisc,纯粹的先进先出。只有一个参数:limit,用来设置队列的长度,pfifo是以数据包的个数为单位;bfifo是以字节数为单位。函数
一个QDisc会被分配一个主序列号,叫作句柄(handle),而后把从序列号做为类的命名空间。句柄采用象10:同样的表达方式。习惯上,须要为有子类的QDisc显式地分配一个句柄。测试
关于类(class),在同一个QDisc里面的类分享这个QDisc的主序列号,可是每一个类都有本身的从序列号,叫作类识别符(classid)。类识别符只与父QDisc有关,和父类无关。类的命名习惯和QDisc的相同。spa
咱们在内网网卡上建立一个句柄handle 10:code
而后定义好限速的带宽信息:htm
/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开源网关更完美一些了。