Linux(openwrt)下iptables+tc工具实现网络流量限速控制(QoS)

基础介绍

Netfilter是Linux操作系统核心层内部的一个数据包处理模块,它具有如下功能:网络地址转换(Network Address Translate)数据包内容修改以及数据包过滤的防火墙功能。Netfliter框架不仅仅在ipv4中有应用,bridge,ipv4,ipv6,decnet,这四种协议中都有应用,其中ipv4中又分开了arp和ip的两种。其实netfliter是个大的框架,在ipv4中对应的应用层工具是iptables,在bridge中对应的应用层工具是ebtables,在arp中对应的应用层工具是arptables

  • iptables 中有raw,filter,nat,mangle,4个table,
  • ebtables 中有broute,filter,nat,3个table,
  • arptables 中有filter,1个table

Netfilter框架被设计用来在网络协议栈的内核路径上过滤数据包,就像在一条路上的关卡一样,Netfilter在协议栈处理网络数据包的路径上的5个位置设置了这样的关卡,一个数据包在被处理的路径上经过这些关卡被检查,结果就是若干个动作:接受,丢弃,排队,导入其它路径等,框架只需针对一个数据包得出一个结果即可,关卡内部提供什么服务在Netfilter框架中并没有任何规定。

TC旨在对数据包或者数据流提供一种服务,比如限速,整形等,而这并不是一个类似Netfilter的结果可以表达的,提供这些服务需要执行一系列的动作,如何来规划和组织这些动作的执行是TC框架设计的关键,也就是说,TC框架关注的是如何执行而不是仅仅想要得到一个要执行的动作。换句话说,Netfilter框架关注做什么,而TC框架关注怎么做。

QoS的全称是Quality of Service,意即服务质量。是专门用于解决拥堵网络上的信号质量一视同仁的问题。Linux内核内置了一个Traffic Control框架,可以实现流量限速,流量整形,策略应用(丢弃,NAT等), 结合tc和netfilter可以实现基于IP或基于端口的限速需求。

关于iptables的使用可以参考博客:朱双印个人日志

TC基础介绍

大多数排队规则(qdisc)都是用于输出方向的,输入方向只有一个排队规则,即ingress qdisc。ingress qdisc本身的功能很有限。

TC操作原理
类(class)组成一个树,每个类都只有一个父类,而一个类可以有多个子类。某些qdisc (例如:CBQ和 HTB)允许在运行时动态添加类,而其它的qdisc(例如:PRIO)不允许动态建立类。允许动态添加类的qdisc可以有零个或者多个子类,由它们为数据包排队。此外,每个类都有一个叶子qdisc,默认情况下,这个也在qdisc有可分类,不过每个子类只能有一个叶子qdisc。当一个数据包进入一个分类qdisc,它会被归入某个子类。我们可以使用一下三种方式为数据包归类,不过不是所有的qdisc都能够使用这三种方式。

如果过滤器附属于一个类,相关的指令就会对它们进行查询。过滤器能够匹配数据包头所有的域,也可以匹配由ebtables或者iptables做的标记。树的每个节点都可以有自己的过滤器,但是高层的过滤器也可以一直接用于其子类。如果数据包没有被成功归类,就会被排到这个类的叶子qdisc的队中。相关细节在各个qdisc的手册页中。

TC命名规则
所有的qdisc、类、和过滤器都有ID。ID可以手工设置,也可以由内核自动分配。ID由一个主序列号和一个从序列号组成,两个数字用一个冒号分开。qdisc,一个qdisc会被分配一个主序列号,叫做句柄(handle),然后把从序列号作为类的命名空间。句柄才有像1:0一样的表达方式。习惯上,需要为有子类的qdisc显式的分配一个句柄。类(Class),在同一个qdisc里面的类共享这个qdisc的主序列号,但是每个类都有自己的从序列号,叫做类识别符(classid)。类识别符只与父qdisc有关,与父类无关。类的命名习惯和qdisc相同。过滤器(Filter),过滤器的ID有三部分,只有在对过滤器进行散列组织才会用到。

TC流量的处理由三种对象控制,它们是:qdisc(排队规则)、class(类别)和filter(过滤器)。
qdisc 分类:

  • 1.CLASSLESS QDisc(不可分类QDisc)

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

pfifo_fast
在编译内核时,如果打开了高级路由器(Advanced Router)编译选项,pfifo_fast就是系统的标准QDISC。它的队列包括三个波段(band)。在每个波段里面,使用先进先出规则。而三个波段(band)的优先级也不相同,band 0的优先级最高,band 2的最低。如果band里面有数据包,系统就不会处理band 1里面的数据包,band 1和band 2之间也是一样。数据包是按照服务类型(Type of Service,TOS)被分配多三个波段(band)里面的。

red:
red是Random Early Detection(随机早期探测)的简写。如果使用这种QDISC,当带宽的占用接近于规定的带宽时,系统会随机地丢弃一些数据包。它非常适合高带宽应用。

sfq:
sfq是Stochastic Fairness Queueing(随机公平排队)的简写。它按照会话(session–对应于每个TCP连接或者UDP流)为流量进行排序,然后循环发送每个会话的数据包。

tbf
tbf是Token Bucket Filter的简写,适合于把流速降低到某个值。

  • 2.Classful QDisc(分类QDisc)

可分类的qdisc:
CBQ
CBQ是Class Based Queueing(基于类别排队)的缩写。它实现了一个丰富的连接共享类别结构,既有限制(shaping)带宽的能力,也具有带宽优先级管理的能力。带宽限制是通过计算连接的空闲时间完成的。空闲时间的计算标准是数据包离队事件的频率和下层连接(数据链路层)的带宽。

HTB等级令牌桶
HTB是Hierarchy Token Bucket的缩写。通过在实践基础上的改进,它实现了一个丰富的连接共享类别体系。使用HTB可以很容易地保证每个类别的带宽,虽然它也允许特定的类可以突破带宽上限,占用别的类的带宽。HTB可以通过TBF(Token Bucket Filter)实现带宽限制,也能够划分类别的优先级

PRIO
PRIO QDisc不能限制带宽,因为属于不同类别的数据包是顺序离队的。使用PRIO QDisc可以很容易对流量进行优先级管理,只有属于高优先级类别的数据包全部发送完毕,才会发送属于低优先级类别的数据包FILTER(过滤器),Filter(过滤器)用于为数据包分类,决定它们按照何种QDisc进入队列
TC 中的 Filter 规则
filter 用来将用户划入到具体的控制策略中(即不同的 class 中).比如,现在,我们想对 xxa,xxb两个 IP 实行不同的控制策略(A,B),这时,我们可用 filter 将 xxa 划入到控制策略 A,将 xxb 划入到控制策略 B,filter 划分的标志位可用 u32 打标功能或 IPtables的 set-mark (大多使用iptables 来做标记)功能来实现。
目前,TC 可以使用的过滤器有:fwmark 分类器,u32 分类器,基于路由的分类器和 RSVP分类器(分别用于 IPV6、IPV4)等;其中,fwmark分类器允许我们使用 Linux netfilter 代码选择流量,而 u32 分类器允许我们选择基于 ANY 头的流量 .需要注意的是,filter (过滤器)是在 QDisc 内部,它们不能作为主体。
4、命名规则
所有的QDisc、类和过滤器都有ID。ID可以手工设置,也可以有内核自动分配。
ID由一个主序列号和一个从序列号组成,两个数字用一个冒号分开。

关于TC,我们当前限速使用的有htb与sfq。

tc示例:

#增加ccinet0根队列,未标记数据默认走28
tc qdisc add dev ccinet0 root handle 1: htb default 28
#增加总流量规则
tc class add dev ccinet0 parent 1: classid 1:1 htb rate 1000Mbit burst 15k quantum 60000
#增加子类
tc class add dev ccinet0 parent 1:1 classid 1:11 htb rate 6000kbit ceil 6000kbit prio 0
#为子类添加SFQ公平队列,每10秒重置
tc qdisc add dev ccinet0 parent 1:11 handle 11: sfq perturb 10
#子类过滤规则,匹配标记值为21的数据包
tc filter add dev ccinet0 parent 1:0 prio 0 protocol ip handle 21 fw flowid 1:11

查看tc相关规则:

#查看网络状态信息
ifconfig
#查看网桥信息
brctl show
#查看所有网口的tc信息
tc qdisc
#查看ccinet0 tc信息
tc -s class ls dev ccinet0
#查看ccinet0 tc具体限速规则
tc -s filter ls dev ccinet0

关于tc快速上手可以参考:tc命令详解

需求分析

当前需要通过IP/Mac对特定设备或者ip段进行限速,或者通过固定端口(如USB,WIFI的某个ssid,或者通过LAN口接入的)进行限速。在当前情况下,只能从IP/MAC/端口三者中选一种模式(如果想要同时生效需要考虑冲突的情况以及兼容性处理,可自行实现)

明确网口结构

在正式开始前,需要明确一下要做限速的设备的interface结构,以我自身做过的项目为例,上行网口为ccinet0或者eth1(视具体项目而定),下行接口统一归到三层虚拟网桥br-lan上。
在这里插入图片描述

Linux中的QoS分为入口(ingress)部分和出口(egress)部分,ingress和egress是对router来说的,对于上行数据,设备数据进入到每个连接的网口是ingress方向,最后到ccinet0发出是egress方向,对于下行数据,进入ccinet0是ingress方向,从设备连接的网口发给设备是egress方向。

实现思路

整体的实现思路很简单:就是通过iptables或者ebtables等工具将数据包进行打标,在tc工具中根据打标值分流限速即可。

具体实现过程

一、基于端口限速的实现

主要思路是:
通过ebtables或者iptables对数据包打标,在tc规则中对打标数据进行管控从而进行速率限制。其中上行数据打标后统一放到ccinet0网口子类规则中进行处理,下行数据在每个网口子类规则中进行处理。

打标方法:

  • 1.通过iptables来实现打标

iptables可以在mangle表中对数据包进行打标,使用physdev模块
模块来做,具体示例如下:

#需要该命令开启才能正常打标。
sysctl -w net.bridge.bridge-nf-call-iptables=1

iptables -t mangle -N AUTOLANIN
iptables -t mangle -I PREROUTING -m physdev --physdev-in autolan -j AUTOLANIN
iptables -t mangle -A AUTOLANIN -s 0.0.0.0/0 -j MARK --set-mark 21

iptables -t mangle -N AUTOLANOUT
iptables -t mangle -I PREROUTING -m physdev --physdev-out autolan -j AUTOLANOUT
iptables -t mangle -A AUTOLANOUT -s 0.0.0.0/0 -j MARK --set-mark 20
  • 2.通过ebtables来实现打标。

打标也可以放到二层来做,由ebtables实现:

ebtables -t filter -I INPUT -i autolan -j mark --mark-set 21 --mark-target CONTINUE
ebtables -t filter -I OUTPUT -o autolan -j mark --mark-set 20 --mark-target CONTINUE
  • 3.其他可能的思路:
    ifb设备,根据tc相关文档描述,使用tc ingress限速,功能有限,似乎只能选择丢弃,并且也不支持分类。实际应用中,我们可以将业务流重定向到ifb设备上,业务流从这个ifb设备中出去,再又相应的端口接收,那我们就可以像正常使用tc对egress限速一样,来对ifb设备进行egress限速,就可以达到对接收方向的限速了(实际是将ingress方向重定向到虚拟设备ifb0,这样可以变相使用tc出口方向规则对流量进行控制,需要安装启用虚拟设备ifb,个人不采用此方式)

限速实现示例:

#需要关闭fastpath才能正常匹配数据包,否则匹配到数量非常少。之所以需要这个是因为当前设备采用的是ASR的芯片,其有一套内核加速机制,不关闭的话会导致无法打标
echo 1 > /sys/kernel/fastpath/fp_forward/bypass_fastpath
#打标
ebtables -t filter -I INPUT -i autolan -j mark --mark-set 21 --mark-target CONTINUE
ebtables -t filter -I OUTPUT -o autolan -j mark --mark-set 20 --mark-target CONTINUE
#上行速率限制
tc qdisc add dev ccinet0 root handle 1: htb default 28
tc class add dev ccinet0 parent 1: classid 1:1 htb rate 1000Mbit burst 15k quantum 60000
tc class add dev ccinet0 parent 1:1 classid 1:11 htb rate 6000kbit ceil 6000kbit prio 0
tc qdisc add dev ccinet0 parent 1:11 handle 11: sfq perturb 10
tc filter add dev ccinet0 parent 1:0 prio 0 protocol ip handle 21 fw flowid 1:11
#下行速率限制
tc qdisc add dev autolan root handle 1: htb default 28
tc class add dev autolan parent 1: classid 1:1 htb rate 1000Mbit burst 15k quantum 60000
tc class add dev autolan parent 1:1 classid 1:12 htb rate 8000kbit ceil 8000kbit prio 0
tc qdisc add dev autolan parent 1:12 handle 12: sfq perturb 10
tc filter add dev autolan parent 1:0 prio 0 protocol ip handle 20 fw flowid 1:12

参考实现:

#初始化tc队列(当前仅针对ccinet0,如果下行在br-lan中统一处理也放到此处)
qos_init_dev_tc_rule(){
    #增加根队列,未标记数据默认走28
    tc qdisc add dev ${UP_DEV} root handle 1: htb default 28
    #增加上行总流量限制规则
    tc class add dev ${UP_DEV} parent 1: classid 1:1 htb rate 1000Mbit burst 15k quantum 60000
}
#清空接口tc规则
qos_clear_dev_limit_rule(){
    local dev=$1
    tc qdisc del dev $dev root
}
#删除ebtables打标规则
qos_dev_delete_mark(){
    ebtables -t filter -D INPUT -i $1 -j mark --mark-set $2 --mark-target CONTINUE
    ebtables -t filter -D OUTPUT -o $1 -j mark --mark-set $3 --mark-target CONTINUE
}
#添加ebtables二层打标规则
qos_dev_set_mark(){
    ebtables -t filter -I INPUT -i $1 -j mark --mark-set $2 --mark-target CONTINUE
    ebtables -t filter -I OUTPUT -o $1 -j mark --mark-set $3 --mark-target CONTINUE
    #也可以使用physdev模块进行iptables打标,示例如下:
    #sysctl -w net.bridge.bridge-nf-call-iptables=1
    #iptables -t mangle -N ETH02
    #iptables -t mangle -I PREROUTING -m physdev --physdev-out eth0.2 -j ETH02
    #iptables -t mangle -A ETH02 -s 0.0.0.0/0 -j MARK --set-mark 21
}
#端口限速设置
qos_set_dev_limit(){
    local config=$1
    local up_bw
    local down_bw
    local dev_name
    local real_name
    local pri_num
    local dw_classid
    local up_classid
    local dw_mark
    local up_mark
    local enable
    config_get enable $config enable
    config_get dev_name $config dev_name
    config_get down_bw $config down_bw
    config_get up_bw $config up_bw
    
    if [ "$dev_name" == "LAN1" ]; then
    	real_name="autolan"
    	pri_num=0
    	classid=20
    	dw_mark=20
    	up_mark=30
    elif [ "$dev_name" == "SSID1_24G" ]; then
    	real_name="wlan0"
    	pri_num=0
    	classid=21
    	dw_mark=21
    	up_mark=31
    elif [ "$dev_name" == "SSID2_24G" ]; then
    	real_name="wlan0-va0"
    	pri_num=0
    	classid=22
    	dw_mark=22
    	up_mark=32
    elif [ "$dev_name" == "SSID3_24G" ]; then
    	real_name="wlan0-va1"
    	pri_num=0
    	classid=23
    	dw_mark=23
    	up_mark=33
    elif [ "$dev_name" == "SSID1_5G" ]; then
    	real_name="wlan1"
    	pri_num=0
    	classid=24
    	dw_mark=24
    	up_mark=34
    elif [ "$dev_name" == "SSID2_5G" ]; then
    	real_name="wlan1-va0"
    	pri_num=0
    	classid=25
    	dw_mark=25
    	up_mark=35
    elif [ "$dev_name" == "SSID3_5G" ]; then
    	real_name="wlan1-va1"
    	pri_num=0
    	classid=26
    	dw_mark=26
    	up_mark=36
    else
    	echo "dev_name is $dev_name" > /dev/kmsg
    	return 0
    fi
    echo "real_name is $real_name" > /dev/kmsg
    
    #先清空一下针对该端口的规则
    qos_clear_dev_limit_rule ${real_name}
    qos_dev_delete_mark ${real_name} ${up_mark} ${dw_mark}
    
    if [ "$enable" == "0" ]; then
    	echo "$dev_name enable is 0" > /dev/kmsg
    	return 0
    fi
    
    echo "dev_name is $dev_name, down_bw is $down_bw, up_bw is $up_bw" > /dev/kmsg
    #设置打标规则
    qos_dev_set_mark ${real_name} ${up_mark} ${dw_mark}

#基于端口的上行限速设置
if [ "$up_bw" != "0" ]; then
    #增加子类
    tc class add dev ${UP_DEV} parent 1:1 classid 1:${classid} htb rate ${up_bw}Mbit ceil ${up_bw}Mbit prio 0
    #为子类添加SFQ公平队列,每10秒重置
    tc qdisc add dev ${UP_DEV} parent 1:${classid} handle ${classid}: sfq perturb 10
    #根据打标值过滤
    tc filter add dev ${UP_DEV} parent 1:0 prio 0 protocol ip handle ${up_mark} fw flowid 1:${classid}
fi

#基于端口的下行限速设置
if [ "$down_bw" != "0" ]; then
    tc qdisc add dev ${real_name} root handle 1: htb default 28
    tc class add dev ${real_name} parent 1: classid 1:1 htb rate 1000Mbit burst 15k quantum 60000
    #添加子类
    tc class add dev ${real_name} parent 1:1 classid 1:${classid} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit prio 0
    #为子类添加SFQ公平队列
    tc qdisc add dev ${real_name} parent 1:${classid} handle ${classid}: sfq perturb 10
    #过滤规则
    tc filter add dev ${real_name} parent 1:0 prio 0 protocol ip handle ${dw_mark} fw flowid 1:${classid}
fi
}
qos_set_dev_rule(){
    qos_init_dev_tc_rule
    config_foreach qos_set_dev_limit qos_dev_limit
}

二、基于MAC限速的实现

主要思路是:

  • 1.老思路:通过mac查arp表,最后通过ip来限速

存在的问题及应对思路:
CPE重启后执行脚本时设备未连接,arp表中查找不到mac对应ip会导致规则设置失败,后续设备连接后出现速度不受限情况

应对:新连接进来设备时重新执行脚本(在代码中操作不修改shell脚本,当前已采用新思路,未使用该方法)

老思路参考实现:

#初始化基于ip和mac的tc规则
qos_init_tc_rule(){
	#新建限速队列
	tc qdisc add dev ${UP_DEV} root handle 100: htb default 100
	tc qdisc add dev ${DN_DEV} root handle 200: htb default 200
	#新建根分类
	tc class add dev ${UP_DEV} parent 100: classid 100:1 htb rate 1000Mbit burst 15k quantum 60000
	tc class add dev ${DN_DEV} parent 200: classid 200:1 htb rate 1000Mbit burst 15k quantum 60000
}
#删除旧链
qos_delete_mac_limit_chain(){
	iptables -t mangle -D FORWARD -j QOS_MAC_FORWARD
	iptables -t mangle -F QOS_MAC_FORWARD
	iptables -t mangle -X QOS_MAC_FORWARD
}
#创建qos链
qos_create_mac_limit_chain(){
	iptables -t mangle -N QOS_MAC_FORWARD
	iptables -t mangle -I FORWARD -j QOS_MAC_FORWARD
}
qos_set_mac_limit(){
    local config=$1
    local up_bw
    local down_bw
    local mac_addr
    local mac_addr_lower
    local ip_addr
    local ip_tag
    local enable
    config_get enable $config enable
    config_get mac_addr $config mac_addr
    config_get down_bw $config down_bw
    config_get up_bw $config up_bw
    
    echo "mac_addr is $mac_addr" > /dev/kmsg
    
    if [ "$enable" == "0" ]; then
    	echo "$mac_addr enable is 0" > /dev/kmsg
    	return 0
    fi
    #将mac地址转换为小写,不然大写的匹配不到ip
    mac_addr_lower=$(echo $mac_addr | tr '[A-Z]' '[a-z]')
    local ip_addr=`cat /proc/net/arp |grep $mac_addr_lower | awk -F ' ' '{print $1}'`
    
    if [ "$ip_addr" == "" ]; then
    	echo "the ip addr of $mac_addr is null" > /dev/kmsg
    	return 0
    fi
    
    local ip_tag=`echo $ip_addr | awk -F '.' '{print $4}'`
    echo "ip_addr is $ip_addr, ip_tag is $ip_tag" > /dev/kmsg

if [ "$up_bw" != "0" ]; then
	#创建子分类
    tc class add dev ${UP_DEV} parent 100:1 classid 100:${ip_tag} htb rate ${up_bw}Mbit ceil ${up_bw}Mbit burst 15k prio 1
    #创建过滤器	
    tc filter add dev ${UP_DEV} protocol all parent 100: prio 1 handle $ip_tag fw classid 100:${ip_tag}
	#创建公平队列
    tc qdisc add dev ${UP_DEV} parent 100:${ip_tag} handle ${ip_tag}: sfq perturb 10
    iptables -t mangle -A QOS_MAC_FORWARD -s $ip_addr/32 -j MARK --set-mark ${ip_tag}		
fi

if [ "$down_bw" != "0" ]; then
    tc class add dev ${DN_DEV} parent 200:1 classid 200:${ip_tag} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
    tc filter add dev ${DN_DEV} protocol all parent 200: prio 2 handle $ip_tag fw classid 200:${ip_tag}
    tc qdisc add dev ${DN_DEV} parent 200:${ip_tag} handle ${ip_tag}: sfq perturb 10
    iptables -t mangle -A QOS_MAC_FORWARD -d $ip_addr/32 -j MARK --set-mark ${ip_tag}
fi
}
qos_set_mac_rule(){
    qos_init_tc_rule
    qos_create_mac_limit_chain
    config_foreach qos_set_mac_limit qos_mac_limit
}
  • 2.新思路:通过mac模块对mac打标直接进行限速

存在的问题及应对思路:
–mac-source 只能用于PREROUTING/INPUT/FORWARD这三个链,下行数据在nat转换前数据包目的mac非目标设备mac,如果直接在iptables中打标会不成功,所以采用ebtables进行打标,但是ebtables打标就无法在br-lan上做下行的限速

应对:在br-lan网桥下挂的所有网口都设定tc规则

#新思路参考实现

qos_init_mac_tc_rule(){
	#新建限速队列
    tc qdisc add dev ${UP_DEV} root handle 100: htb default 100
    tc qdisc add dev autolan root handle 200: htb default 200
    tc qdisc add dev wlan0 root handle 300: htb default 300
    tc qdisc add dev wlan0-va0 root handle 400: htb default 400
    tc qdisc add dev wlan0-va1 root handle 500: htb default 500
    tc qdisc add dev wlan1 root handle 600: htb default 600
    tc qdisc add dev wlan1-va0 root handle 700: htb default 700
    tc qdisc add dev wlan1-va1 root handle 800: htb default 800
	
	#新建根分类
    tc class add dev ${UP_DEV} parent 100: classid 100:1 htb rate 1000Mbit burst 15k quantum 60000
    tc class add dev autolan parent 200: classid 200:1 htb rate 1000Mbit burst 15k quantum 60000
    tc class add dev wlan0 parent 300: classid 300:1 htb rate 1000Mbit burst 15k quantum 60000
    tc class add dev wlan0-va0 parent 400: classid 400:1 htb rate 1000Mbit burst 15k quantum 60000
    tc class add dev wlan0-va1 parent 500: classid 500:1 htb rate 1000Mbit burst 15k quantum 60000
    tc class add dev wlan1 parent 600: classid 600:1 htb rate 1000Mbit burst 15k quantum 60000
    tc class add dev wlan1-va0 parent 700: classid 700:1 htb rate 1000Mbit burst 15k quantum 60000
    tc class add dev wlan1-va1 parent 800: classid 800:1 htb rate 1000Mbit burst 15k quantum 60000
}
#删除ebtables旧链
qos_delete_mac_limit_chain(){
    ebtables -D INPUT -j QOS_MAC_CHAIN
    ebtables -D OUTPUT -j QOS_MAC_CHAIN
    ebtables -F QOS_MAC_CHAIN
    ebtables -X QOS_MAC_CHAIN
}
#创建qos链
qos_create_mac_limit_chain(){
    ebtables -t filter -N QOS_MAC_CHAIN
    ebtables -t filter -I INPUT -j QOS_MAC_CHAIN
    ebtables -t filter -I OUTPUT -j QOS_MAC_CHAIN
}
#基于mac的初始打标值
mac_mark_value=10
#mac限速设置
qos_set_mac_limit(){
    local mark_value
    local config=$1
    local up_bw
    local down_bw
    local mac_addr
    local mac_addr_lower
    local ip_addr
    local ip_tag
    local enable
    config_get enable $config enable
    config_get mac_addr $config mac_addr
    config_get down_bw $config down_bw
    config_get up_bw $config up_bw
    
    echo "mac_addr is $mac_addr" > /dev/kmsg
    
    if [ "$enable" == "0" ]; then
    	echo "$mac_addr enable is 0" > /dev/kmsg
    	return 0
    fi
    #统一换成小写mac地址
    mac_addr_lower=$(echo $mac_addr | tr '[A-Z]' '[a-z]')
    
    if [ "$mac_addr_lower" == "" ]; then
    	echo "the mac addr is null" > /dev/kmsg
    	return 0
    fi
    mark_value=$mac_mark_value
    #打标值递增,区分下一mac打标值
    mac_mark_value=$(($mac_mark_value+1))  
    
    echo "mac_addr is $mac_addr_lower, mac_tag is $mark_value" > /dev/kmsg
	
	#上行统一在各个子类中处理
if [ "$up_bw" != "0" ]; then
    #创建子分类
    tc class add dev ${UP_DEV} parent 100:1 classid 100:${mark_value} htb rate ${up_bw}Mbit ceil ${up_bw}Mbit burst 15k prio 1
    #创建过滤器	
    tc filter add dev ${UP_DEV} protocol all parent 100: prio 1 handle $mark_value fw classid 100:${mark_value}
    #创建公平队列
    tc qdisc add dev ${UP_DEV} parent 100:${mark_value} handle ${mark_value}: sfq perturb 10
    
    ebtables -t filter -A QOS_MAC_CHAIN -s "$mac_addr_lower" -j mark --mark-set ${mark_value} --mark-target CONTINUE
fi

if [ "$down_bw" != "0" ]; then
    ebtables -t filter -A QOS_MAC_CHAIN -d "$mac_addr_lower" -j mark --mark-set ${mark_value} --mark-target CONTINUE
    
    tc class add dev autolan parent 200:1 classid 200:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
    tc filter add dev autolan protocol all parent 200: prio 2 handle $mark_value fw classid 200:${mark_value}
    tc qdisc add dev autolan parent 200:${mark_value} handle ${mark_value}: sfq perturb 10
    
    tc class add dev wlan0 parent 300:1 classid 300:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
    tc filter add dev wlan0 protocol all parent 300: prio 2 handle $mark_value fw classid 300:${mark_value}
    tc qdisc add dev wlan0 parent 300:${mark_value} handle ${mark_value}: sfq perturb 10
    
    tc class add dev wlan0-va0 parent 400:1 classid 400:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
    tc filter add dev wlan0-va0 protocol all parent 400: prio 2 handle $mark_value fw classid 400:${mark_value}
    tc qdisc add dev wlan0-va0 parent 400:${mark_value} handle ${mark_value}: sfq perturb 10
    
    tc class add dev wlan0-va1 parent 500:1 classid 500:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
    tc filter add dev wlan0-va1 protocol all parent 500: prio 2 handle $mark_value fw classid 500:${mark_value}
    tc qdisc add dev wlan0-va1 parent 500:${mark_value} handle ${mark_value}: sfq perturb 10
    
    tc class add dev wlan1 parent 600:1 classid 600:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
    tc filter add dev wlan1 protocol all parent 600: prio 2 handle $mark_value fw classid 600:${mark_value}
    tc qdisc add dev wlan1 parent 600:${mark_value} handle ${mark_value}: sfq perturb 10
    
    tc class add dev wlan1-va0 parent 700:1 classid 700:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
    tc filter add dev wlan1-va0 protocol all parent 700: prio 2 handle $mark_value fw classid 700:${mark_value}
    tc qdisc add dev wlan1-va0 parent 700:${mark_value} handle ${mark_value}: sfq perturb 10
    
    tc class add dev wlan1-va1 parent 800:1 classid 800:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
    tc filter add dev wlan1-va1 protocol all parent 800: prio 2 handle $mark_value fw classid 800:${mark_value}
    tc qdisc add dev wlan1-va1 parent 800:${mark_value} handle ${mark_value}: sfq perturb 10
fi
}
qos_set_mac_rule(){
    qos_init_mac_tc_rule
    qos_create_mac_limit_chain
    config_foreach qos_set_mac_limit qos_mac_limit
}

三、基于IP限速的实现

主要思路是:
在mangle表中使用-s、-d参数或者使用iprange模块对数据进行打标,然后在ccinet0和br-lan上使用tc规则进行限速处理

参考实现:

#初始化基于ip和mac的tc规则
qos_init_tc_rule(){
    #新建限速队列
    tc qdisc add dev ${UP_DEV} root handle 100: htb default 100
    tc qdisc add dev ${DN_DEV} root handle 200: htb default 200
    #新建根分类
    tc class add dev ${UP_DEV} parent 100: classid 100:1 htb rate 1000Mbit burst 15k quantum 60000
    tc class add dev ${DN_DEV} parent 200: classid 200:1 htb rate 1000Mbit burst 15k quantum 60000
}
#删除旧链
qos_delete_ip_limit_chain(){
	iptables -t mangle -D FORWARD -j QOS_IP_FORWARD
	iptables -t mangle -F QOS_IP_FORWARD
	iptables -t mangle -X QOS_IP_FORWARD
}
#创建qos链
qos_create_ip_limit_chain(){
	iptables -t mangle -N QOS_IP_FORWARD
	iptables -t mangle -I FORWARD -j QOS_IP_FORWARD
}
qos_set_ip_limit(){
    local config=$1
    local up_bw
    local down_bw
    local start_ip
    local end_ip
    local enable
    config_get enable $config enable
    config_get start_ip $config start_ip
    config_get end_ip $config end_ip
    config_get down_bw $config down_bw
    config_get up_bw $config up_bw
    
    if [ "$enable" == "0" ]; then
    	echo "$start_ip enable is 0" > /dev/kmsg
    	return 0
    fi
    
    local start_last=`echo $start_ip | awk -F '.' '{print $4}'`
    local end_last=`echo $end_ip | awk -F '.' '{print $4}'`
    
    echo "start_last is $start_last, end_last is $end_last" > /dev/kmsg

if [ "$up_bw" != "0" ]; then
    #创建子分类
    tc class add dev ${UP_DEV} parent 100:1 classid 100:${start_last} htb rate ${up_bw}Mbit ceil ${up_bw}Mbit burst 15k prio 1
    #创建过滤器	
    tc filter add dev ${UP_DEV} protocol all parent 100: prio 1 handle $start_last fw classid 100:${start_last}
    #创建公平队列
    tc qdisc add dev ${UP_DEV} parent 100:${start_last} handle ${start_last}: sfq perturb 10
fi

if [ "$down_bw" != "0" ]; then
    tc class add dev ${DN_DEV} parent 200:1 classid 200:${start_last} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
    tc filter add dev ${DN_DEV} protocol all parent 200: prio 2 handle $start_last fw classid 200:${start_last}
    tc qdisc add dev ${DN_DEV} parent 200:${start_last} handle ${start_last}: sfq perturb 10
    fi
    
    if [ "$start_ip" == "$end_ip" ]; then
    iptables -t mangle -A QOS_IP_FORWARD -s $start_ip/32 -j MARK --set-mark ${start_last}
    iptables -t mangle -A QOS_IP_FORWARD -d $start_ip/32 -j MARK --set-mark ${start_last}
    else
    iptables -t mangle -A QOS_IP_FORWARD -m iprange --src-range ${start_ip}-${end_ip} -j MARK --set-mark ${start_last}
    iptables -t mangle -A QOS_IP_FORWARD -m iprange --dst-range ${start_ip}-${end_ip} -j MARK --set-mark ${start_last}
fi
	
}
qos_set_ip_rule(){
	qos_init_tc_rule
	qos_create_ip_limit_chain
	config_foreach qos_set_ip_limit qos_ip_limit
}

四、整体shell脚本

#!/bin/sh
. /lib/functions.sh
. /lib/config/uci.sh

QOS_FILE="qos"
g_qos_enable=""
g_limit_mode=""
UP_DEV="ccinet0"
DN_DEV="br-lan"

LAN_NAME=""
DEVICE_LAN_SWITCH=""
#LAN_NAME="eth0.2"  #with switch
#LAN_NAME="eth0"  #without switch

#######################################################start of qos based on dev
#初始化tc队列(当前仅针对ccinet0,如果下行在br-lan中统一处理也放到此处)
qos_init_dev_tc_rule(){
	#增加根队列,未标记数据默认走28
	tc qdisc add dev ${UP_DEV} root handle 1: htb default 28
	#增加上行总流量限制规则
	tc class add dev ${UP_DEV} parent 1: classid 1:1 htb rate 1000Mbit burst 15k quantum 60000
}
#清空接口tc规则
qos_clear_dev_limit_rule(){
	local dev=$1
	tc qdisc del dev $dev root
}
#删除ebtables打标规则
qos_dev_delete_mark(){
  ebtables -t filter -D INPUT -i $1 -j mark --mark-set $2 --mark-target CONTINUE
  ebtables -t filter -D OUTPUT -o $1 -j mark --mark-set $3 --mark-target CONTINUE
}
#添加ebtables二层打标规则
qos_dev_set_mark(){
	ebtables -t filter -I INPUT -i $1 -j mark --mark-set $2 --mark-target CONTINUE
	ebtables -t filter -I OUTPUT -o $1 -j mark --mark-set $3 --mark-target CONTINUE
	#也可以使用physdev模块进行iptables打标,示例如下:
	#sysctl -w net.bridge.bridge-nf-call-iptables=1
	#iptables -w -t mangle -N ETH02
	#iptables -w -t mangle -I PREROUTING -m physdev --physdev-out eth0.2 -j ETH02
	#iptables -w -t mangle -A ETH02 -s 0.0.0.0/0 -j MARK --set-mark 21
}
#端口限速设置
qos_set_dev_limit(){
	local config=$1
	local up_bw
	local down_bw
	local dev_name
	local real_name
	local pri_num
	local dw_classid
	local up_classid
	local dw_mark
	local up_mark
	local enable
	config_get enable $config enable
	config_get dev_name $config dev_name
	config_get down_bw $config down_bw
	config_get up_bw $config up_bw
	
	if [ "$dev_name" == "LAN1" ]; then
		real_name="${LAN_NAME}"
		pri_num=0
		classid=20
		dw_mark=20
		up_mark=30
	elif [ "$dev_name" == "SSID1_24G" ]; then
		real_name="wlan0"
		pri_num=0
		classid=21
		dw_mark=21
		up_mark=31
	elif [ "$dev_name" == "SSID2_24G" ]; then
		real_name="wlan0-va0"
		pri_num=0
		classid=22
		dw_mark=22
		up_mark=32
	elif [ "$dev_name" == "SSID3_24G" ]; then
		real_name="wlan0-va1"
		pri_num=0
		classid=23
		dw_mark=23
		up_mark=33
	elif [ "$dev_name" == "SSID1_5G" ]; then
		real_name="wlan1"
		pri_num=0
		classid=24
		dw_mark=24
		up_mark=34
	elif [ "$dev_name" == "SSID2_5G" ]; then
		real_name="wlan1-va0"
		pri_num=0
		classid=25
		dw_mark=25
		up_mark=35
	elif [ "$dev_name" == "SSID3_5G" ]; then
		real_name="wlan1-va1"
		pri_num=0
		classid=26
		dw_mark=26
		up_mark=36
	else
		echo "dev_name is $dev_name" > /dev/kmsg
		return 0
	fi
	echo "real_name is $real_name" > /dev/kmsg
	
	#先清空一下针对该端口的规则
	qos_clear_dev_limit_rule ${real_name}
	qos_dev_delete_mark ${real_name} ${up_mark} ${dw_mark}
	
	if [ "$enable" == "0" ]; then
		echo "$dev_name enable is 0" > /dev/kmsg
		return 0
	fi
	
	echo "dev_name is $dev_name, down_bw is $down_bw, up_bw is $up_bw" > /dev/kmsg
	#设置打标规则
	qos_dev_set_mark ${real_name} ${up_mark} ${dw_mark}

	#基于端口的上行限速设置
	if [ "$up_bw" != "0" ]; then
		#增加子类
		tc class add dev ${UP_DEV} parent 1:1 classid 1:${classid} htb rate ${up_bw}Mbit ceil ${up_bw}Mbit prio 0
		#为子类添加SFQ公平队列,每10秒重置
		tc qdisc add dev ${UP_DEV} parent 1:${classid} handle ${classid}: sfq perturb 10
		#根据打标值过滤
		tc filter add dev ${UP_DEV} parent 1:0 prio 0 protocol ip handle ${up_mark} fw flowid 1:${classid}
	fi
	
	#基于端口的下行限速设置
	if [ "$down_bw" != "0" ]; then
		tc qdisc add dev ${real_name} root handle 1: htb default 28
		tc class add dev ${real_name} parent 1: classid 1:1 htb rate 1000Mbit burst 15k quantum 60000
		#添加子类
		tc class add dev ${real_name} parent 1:1 classid 1:${classid} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit prio 0
		#为子类添加SFQ公平队列
		tc qdisc add dev ${real_name} parent 1:${classid} handle ${classid}: sfq perturb 10
		#过滤规则
		tc filter add dev ${real_name} parent 1:0 prio 0 protocol ip handle ${dw_mark} fw flowid 1:${classid}
	fi
}
qos_set_dev_rule(){
	#上行速率控制统一放到ccinet0子类中分开处理,下行速率控制放到每一个dev中处理(如果不生效考虑在br-lan中用子类进行处理)
	#`echo 1 > /sys/kernel/fastpath/fp_forward/bypass_fastpath`
	qos_init_dev_tc_rule
	config_foreach qos_set_dev_limit qos_dev_limit
}
#######################################################end of qos based on dev

#######################################################start of qos based on mac
#当前只能在ebtables上对特定mac打标,处理采用在每个dev上都做限制

#初始化基于mac的tc规则
qos_init_mac_tc_rule(){
	#新建限速队列
	tc qdisc add dev ${UP_DEV} root handle 100: htb default 100
	tc qdisc add dev ${LAN_NAME} root handle 200: htb default 200
	tc qdisc add dev wlan0 root handle 300: htb default 300
	tc qdisc add dev wlan0-va0 root handle 400: htb default 400
	tc qdisc add dev wlan0-va1 root handle 500: htb default 500
	tc qdisc add dev wlan1 root handle 600: htb default 600
	tc qdisc add dev wlan1-va0 root handle 700: htb default 700
	tc qdisc add dev wlan1-va1 root handle 800: htb default 800
	
	#新建根分类
	tc class add dev ${UP_DEV} parent 100: classid 100:1 htb rate 1000Mbit burst 15k quantum 60000
	tc class add dev ${LAN_NAME} parent 200: classid 200:1 htb rate 1000Mbit burst 15k quantum 60000
	tc class add dev wlan0 parent 300: classid 300:1 htb rate 1000Mbit burst 15k quantum 60000
	tc class add dev wlan0-va0 parent 400: classid 400:1 htb rate 1000Mbit burst 15k quantum 60000
	tc class add dev wlan0-va1 parent 500: classid 500:1 htb rate 1000Mbit burst 15k quantum 60000
	tc class add dev wlan1 parent 600: classid 600:1 htb rate 1000Mbit burst 15k quantum 60000
	tc class add dev wlan1-va0 parent 700: classid 700:1 htb rate 1000Mbit burst 15k quantum 60000
	tc class add dev wlan1-va1 parent 800: classid 800:1 htb rate 1000Mbit burst 15k quantum 60000
}
#删除ebtables旧链
qos_delete_mac_limit_chain(){
	#iptables -w -t mangle -D FORWARD -j QOS_MAC_FORWARD
	#iptables -w -t mangle -F QOS_MAC_FORWARD
	#iptables -w -t mangle -X QOS_MAC_FORWARD
	ebtables -D INPUT -j QOS_MAC_CHAIN
	ebtables -D OUTPUT -j QOS_MAC_CHAIN
	ebtables -F QOS_MAC_CHAIN
	ebtables -X QOS_MAC_CHAIN
}
#创建qos链
qos_create_mac_limit_chain(){
	#iptables -w -t mangle -N QOS_MAC_FORWARD
	#iptables -w -t mangle -I FORWARD -j QOS_MAC_FORWARD
	ebtables -t filter -N QOS_MAC_CHAIN
	ebtables -t filter -I INPUT -j QOS_MAC_CHAIN
	ebtables -t filter -I OUTPUT -j QOS_MAC_CHAIN
}
#基于mac的初始打标值
mac_mark_value=10
#mac限速设置
qos_set_mac_limit(){
	local mark_value
	local config=$1
	local up_bw
	local down_bw
	local mac_addr
	local mac_addr_lower
	local ip_addr
	local ip_tag
	local enable
	config_get enable $config enable
	config_get mac_addr $config mac_addr
	config_get down_bw $config down_bw
	config_get up_bw $config up_bw
	
	echo "mac_addr is $mac_addr" > /dev/kmsg
	
	if [ "$enable" == "0" ]; then
		echo "$mac_addr enable is 0" > /dev/kmsg
		return 0
	fi
	#统一换成小写mac地址
	mac_addr_lower=$(echo $mac_addr | tr '[A-Z]' '[a-z]')
	
	if [ "$mac_addr_lower" == "" ]; then
		echo "the mac addr is null" > /dev/kmsg
		return 0
	fi
	mark_value=$mac_mark_value
	#打标值递增,区分下一mac打标值
	mac_mark_value=$(($mac_mark_value+1))  
	
	echo "mac_addr is $mac_addr_lower, mac_tag is $mark_value" > /dev/kmsg
	
	#上行统一在各个子类中处理
	if [ "$up_bw" != "0" ]; then
		#创建子分类
		tc class add dev ${UP_DEV} parent 100:1 classid 100:${mark_value} htb rate ${up_bw}Mbit ceil ${up_bw}Mbit burst 15k prio 1
		#创建过滤器	
		tc filter add dev ${UP_DEV} protocol all parent 100: prio 1 handle $mark_value fw classid 100:${mark_value}
		#创建公平队列
		tc qdisc add dev ${UP_DEV} parent 100:${mark_value} handle ${mark_value}: sfq perturb 10
		
		ebtables -t filter -A QOS_MAC_CHAIN -s "$mac_addr_lower" -j mark --mark-set ${mark_value} --mark-target CONTINUE
	fi

	if [ "$down_bw" != "0" ]; then
		#iptables mac模块--mac-source只能在PREROUTING/INPUT/FORWARD中使用,nat转换之前的目的mac非设备mac无法正确匹配
		#针对真实mac地址无法在nat修改mac后打标,只能在ebtables上打标,br-lan无法处理所以在br-lan下每一个端口都加上tc规则(另一种方式:接入设备重新加载脚本,未采用)
		
		ebtables -t filter -A QOS_MAC_CHAIN -d "$mac_addr_lower" -j mark --mark-set ${mark_value} --mark-target CONTINUE
		
		tc class add dev ${LAN_NAME} parent 200:1 classid 200:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
		tc filter add dev ${LAN_NAME} protocol all parent 200: prio 2 handle $mark_value fw classid 200:${mark_value}
		tc qdisc add dev ${LAN_NAME} parent 200:${mark_value} handle ${mark_value}: sfq perturb 10
		
		tc class add dev wlan0 parent 300:1 classid 300:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
		tc filter add dev wlan0 protocol all parent 300: prio 2 handle $mark_value fw classid 300:${mark_value}
		tc qdisc add dev wlan0 parent 300:${mark_value} handle ${mark_value}: sfq perturb 10
		
		tc class add dev wlan0-va0 parent 400:1 classid 400:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
		tc filter add dev wlan0-va0 protocol all parent 400: prio 2 handle $mark_value fw classid 400:${mark_value}
		tc qdisc add dev wlan0-va0 parent 400:${mark_value} handle ${mark_value}: sfq perturb 10
		
		tc class add dev wlan0-va1 parent 500:1 classid 500:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
		tc filter add dev wlan0-va1 protocol all parent 500: prio 2 handle $mark_value fw classid 500:${mark_value}
		tc qdisc add dev wlan0-va1 parent 500:${mark_value} handle ${mark_value}: sfq perturb 10
		
		tc class add dev wlan1 parent 600:1 classid 600:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
		tc filter add dev wlan1 protocol all parent 600: prio 2 handle $mark_value fw classid 600:${mark_value}
		tc qdisc add dev wlan1 parent 600:${mark_value} handle ${mark_value}: sfq perturb 10
		
		tc class add dev wlan1-va0 parent 700:1 classid 700:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
		tc filter add dev wlan1-va0 protocol all parent 700: prio 2 handle $mark_value fw classid 700:${mark_value}
		tc qdisc add dev wlan1-va0 parent 700:${mark_value} handle ${mark_value}: sfq perturb 10

		tc class add dev wlan1-va1 parent 800:1 classid 800:${mark_value} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
		tc filter add dev wlan1-va1 protocol all parent 800: prio 2 handle $mark_value fw classid 800:${mark_value}
		tc qdisc add dev wlan1-va1 parent 800:${mark_value} handle ${mark_value}: sfq perturb 10
		
	fi
}
qos_set_mac_rule(){
	qos_init_mac_tc_rule
	qos_create_mac_limit_chain
	config_foreach qos_set_mac_limit qos_mac_limit
}
#######################################################end of qos based on mac

#######################################################start of qos based on ip
#初始化基于ip的tc规则
qos_init_ip_tc_rule(){
	#新建限速队列
	tc qdisc add dev ${UP_DEV} root handle 100: htb default 100
	tc qdisc add dev ${DN_DEV} root handle 200: htb default 200
	#新建根分类
	tc class add dev ${UP_DEV} parent 100: classid 100:1 htb rate 1000Mbit burst 15k quantum 60000
	tc class add dev ${DN_DEV} parent 200: classid 200:1 htb rate 1000Mbit burst 15k quantum 60000
}
#删除旧链
qos_delete_ip_limit_chain(){
	iptables -w -t mangle -D FORWARD -j QOS_IP_FORWARD
	iptables -w -t mangle -F QOS_IP_FORWARD
	iptables -w -t mangle -X QOS_IP_FORWARD
}
#创建qos链
qos_create_ip_limit_chain(){
	iptables -w -t mangle -N QOS_IP_FORWARD
	iptables -w -t mangle -I FORWARD -j QOS_IP_FORWARD
}
qos_set_ip_limit(){
	local config=$1
	local up_bw
	local down_bw
	local start_ip
	local end_ip
	local enable
	config_get enable $config enable
	config_get start_ip $config start_ip
	config_get end_ip $config end_ip
	config_get down_bw $config down_bw
	config_get up_bw $config up_bw
	
	if [ "$enable" == "0" ]; then
		echo "$start_ip enable is 0" > /dev/kmsg
		return 0
	fi
	
	local start_last=`echo $start_ip | awk -F '.' '{print $4}'`
	local end_last=`echo $end_ip | awk -F '.' '{print $4}'`
	
	echo "start_last is $start_last, end_last is $end_last" > /dev/kmsg
	
	if [ "$up_bw" != "0" ]; then
		#创建子分类
		tc class add dev ${UP_DEV} parent 100:1 classid 100:${start_last} htb rate ${up_bw}Mbit ceil ${up_bw}Mbit burst 15k prio 1
		#创建过滤器	
		tc filter add dev ${UP_DEV} protocol all parent 100: prio 1 handle $start_last fw classid 100:${start_last}
		#创建公平队列
		tc qdisc add dev ${UP_DEV} parent 100:${start_last} handle ${start_last}: sfq perturb 10
	fi

	if [ "$down_bw" != "0" ]; then
		tc class add dev ${DN_DEV} parent 200:1 classid 200:${start_last} htb rate ${down_bw}Mbit ceil ${down_bw}Mbit burst 15k prio 2
		tc filter add dev ${DN_DEV} protocol all parent 200: prio 2 handle $start_last fw classid 200:${start_last}
		tc qdisc add dev ${DN_DEV} parent 200:${start_last} handle ${start_last}: sfq perturb 10
	fi

	if [ "$start_ip" == "$end_ip" ]; then
		iptables -w -t mangle -A QOS_IP_FORWARD -s $start_ip/32 -j MARK --set-mark ${start_last}
		iptables -w -t mangle -A QOS_IP_FORWARD -d $start_ip/32 -j MARK --set-mark ${start_last}
	else
		iptables -w -t mangle -A QOS_IP_FORWARD -m iprange --src-range ${start_ip}-${end_ip} -j MARK --set-mark ${start_last}
		iptables -w -t mangle -A QOS_IP_FORWARD -m iprange --dst-range ${start_ip}-${end_ip} -j MARK --set-mark ${start_last}
	fi
	
}
qos_set_ip_rule(){

	qos_init_ip_tc_rule
	qos_create_ip_limit_chain
	config_foreach qos_set_ip_limit qos_ip_limit
}
#######################################################end of qos based on ip

qos_del_all_rule(){
	tc qdisc del dev ${UP_DEV} root
	tc qdisc del dev ${DN_DEV} root
	tc qdisc del dev ${LAN_NAME} root
	tc qdisc del dev wlan0 root
	tc qdisc del dev wlan0-va0 root
	tc qdisc del dev wlan0-va1 root
	tc qdisc del dev wlan1 root
	tc qdisc del dev wlan1-va0 root
	tc qdisc del dev wlan1-va1 root
	
	#qos_delete_dev_limit_chain
	qos_delete_mac_limit_chain
	qos_delete_ip_limit_chain
	
}

#qos_init
qos_init(){
	config_load "$QOS_FILE"
	config_get g_qos_enable qos_ctrl qos_enable
	config_get g_limit_mode qos_ctrl current_mode
	#config_get LAN_NAME qos_ctrl lan_name
	DEVICE_LAN_SWITCH=$(uci get properties.DEVICE_LAN_SWITCH)
	if [ "$DEVICE_LAN_SWITCH" == "1" ];then
		LAN_NAME="eth0.2"
	else
		LAN_NAME="eth0"
	fi
	echo "DEVICE_LAN_SWITCH: $DEVICE_LAN_SWITCH" > /dev/kmsg
	qos_del_all_rule
	
	echo "g_qos_enable=$g_qos_enable g_limit_mode=$g_limit_mode" > /dev/kmsg
	echo "modified on 2023.8.1" > /dev/kmsg
	
	if [ -z "$g_qos_enable" ]; then
		echo "g_qos_enable is null" > /dev/kmsg
		exit 0
	fi

	if [ "$g_qos_enable" = "1" ] ; then
		`echo 1 > /sys/kernel/fastpath/fp_forward/bypass_fastpath`
		
		echo "qos_enable is 1, start config!" > /dev/kmsg
		if [ "$g_limit_mode" == "dev" ]; then
			qos_set_dev_rule
		elif [ "$g_limit_mode" == "mac" ]; then
			qos_set_mac_rule
		elif [ "$g_limit_mode" == "ip" ]; then
			qos_set_ip_rule
		else
			echo "g_limit_mode is error, qos exit!" > /dev/kmsg
		fi
	elif [ "$g_qos_enable" = "0" ] ; then	
		echo "qos_enable is 0, exit!" > /dev/kmsg
		`echo 0 > /sys/kernel/fastpath/fp_forward/bypass_fastpath`
		exit 0
	else
		echo "g_qos_enable value is error" > /dev/kmsg
	fi
}

input_type=$1
case $input_type in
	"init")
	qos_init ;;
esac 



五、注意事项

  • 1.确认上行网口名称,当前上行为UP_DEV="ccinet0"下行为DN_DEV=“br-lan”
  • 2.确认LAN口和wifi的实际名称,该信息在基于端口和基于MAC的限速中会用到
  • 3.手动测试时需要将fastpath关闭否则打标匹配存在问题 echo 1 > /sys/kernel/fastpath/fp_forward/bypass_fastpath
  • 4.如果需要对WAN接入做限制,需要对应修改相关内容

六、其他补充信息

常用的一些命令:

#显示信息
ebtables -L --Lc
iptables -t mangle -nvL
tc qdisc
tc  qdisc show dev ccinet0
tc -s class ls dev ccinet0
tc -s filter ls dev ccinet0
#清空tc规则
tc qdisc del dev ${UP_DEV} root
tc qdisc del dev ${DN_DEV} root
tc qdisc del dev autolan root
tc qdisc del dev wlan0 root
tc qdisc del dev wlan0-va0 root
tc qdisc del dev wlan0-va1 root
tc qdisc del dev wlan1 root
tc qdisc del dev wlan1-va0 root
tc qdisc del dev wlan1-va1 root
#调用函数清空打标规则
qos_delete_mac_limit_chain
qos_delete_ip_limit_chain

常用限速单位

kbps                            千字节/秒
mbps                            兆字节/秒
kbit                            KBits/秒
mbit                            MBits/秒
bps或者一个无单位数字             字节数/秒

Ref:
https://blog.51cto.com/u_15065849/3803690
http://doc.aiwaly.com/docs/arm/arm-1ek8uo3rs27s1
https://blog.csdn.net/Van_male/article/details/98938160
https://www.cnblogs.com/yxwkf/p/5424383.html

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-17 14:28:05       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-17 14:28:05       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-17 14:28:05       58 阅读
  4. Python语言-面向对象

    2024-07-17 14:28:05       69 阅读

热门阅读

  1. 机器学习:LayerNorm和BatchNorm的区别

    2024-07-17 14:28:05       23 阅读
  2. Go语言 函数

    2024-07-17 14:28:05       17 阅读
  3. Linux系统中通过Wine运行软件实现关机功能

    2024-07-17 14:28:05       21 阅读
  4. 基于redis的分布式锁

    2024-07-17 14:28:05       22 阅读
  5. 3 万字 25 道 Nginx经典面试题总结

    2024-07-17 14:28:05       21 阅读
  6. 翁恺-C语言程序设计-10-0. 说反话

    2024-07-17 14:28:05       14 阅读
  7. MVC、MVP和MVVM这三种设计模式的区别

    2024-07-17 14:28:05       25 阅读
  8. SCP 使用教程

    2024-07-17 14:28:05       29 阅读
  9. 【微信】签名生成-用户态签名

    2024-07-17 14:28:05       25 阅读
  10. 创建React项目:使用 create-react-app 创建 React 应用

    2024-07-17 14:28:05       19 阅读
  11. 【多线程】线程安全的单例模式

    2024-07-17 14:28:05       22 阅读
  12. 什么是区块链

    2024-07-17 14:28:05       23 阅读
  13. 速盾:ddos高防ip哪里好用?

    2024-07-17 14:28:05       25 阅读
  14. Feign客户端是什么,它跟Ribbon有什么区别?

    2024-07-17 14:28:05       22 阅读