traffic control
tc 队列规则分为四大类:ingress,clsact,classless和classful。
ingress
Linux中的QoS分为入口(Ingress)部分和出口(Egress)部分,入口部分主要用于进行入口流量限速(policing),出口部分主要用于队列调度(queuing scheduling)。
大多数排队规则(qdisc)都是用于输出方向的,输入方向只有一个排队规则,即ingress qdisc。ingress qdisc本身的功能很有限,但可用于重定向incoming packets。通过Ingress qdisc把输入方向的数据包重定向到虚拟设备ifb,而ifb的输出方向可以配置多种qdisc,就可以达到对输入方向的流量做队列调度的目的。
The ingress qdisc itself does not require any parameters. It differs from other
qdiscs in that it does not occupy the root of a device. Attach it like this:
# tc qdisc add dev eth0 ingress
This allows you to have other, sending qdiscs on your device besides the ingress qdisc.
Ingress qdisc (known as ffff:) can't have any children classes. (hence the
existence of IMQ) The only thing you can do with the ingress qdisc is attach
filters.
# tc qdisc add dev eth0 ingress
# tc filter add dev eth0 parent ffff: protocol ip prio 10 u32 match ip src 0.0.0.0/0 police rate 2048kbps burst 1m drop flowid :1
clsact
clsact主要用于bpf的hook点。clsact qdisc提供两个特殊的钩子称为ingress和egress,让classifier可以附加上去。ingress和egress的钩子都位于网络数据路径的中心接收和发送位置,设备上的每个包都在这里通过。
ingress钩子从__netif_receive_skb_core() -> sch_handle_ingress()调用,
egress钩子从__dev_queue_xmit() -> sch_handle_egress()调用。
classless 无类队列
用作根队列或者叶子队列,而且不能设置filter规则,常见的无类队列如下:
[p|b]fifo: 使用最简单的qdisc(排队规则),纯粹的先进先出。只
有一个参数:limit ,用来设置队列的长度。
pfifo是以数据包的个数为单位;bfifo是以字节数为单位。
pfifo_fast: 在编译内核时,如果打开了"Advanced Router"编译选
项,pfifo_fast就是系统的标准qdisc(排队规则)。它的队列包括三个
band。在每个band里面,使用先进先出规则。而三个band的优先
级也不相同,band 0 的优先级最高,band 2的最低。如果band 0
里面有数据包,系统就不会处理band 1 里面的数据包,band 1 和
band 2 之间也是一样的。数据包是按照服务类型(Type Of
Service,TOS )被分配到三个band里面的。
red: red是Random Early Detection(随机早期探测)的简写。如
果使用这种qdsic,当带宽的占用接近与规定的带宽时,系统会随
机的丢弃一些数据包。他非常适合高带宽的应用。
sfq: sfq是Stochastic Fairness Queueing 的简写。它会按照会话
(session --对应与每个TCP 连接或者UDP流)为流量进行排序,然
后循环发送每个会话的数据包。
tbf: tbf是 Token Bucket Filter 的简写,适用于把流速降低到某个值。
无类队列的设置
如果没有可分类qdisc,不可分类qdisc 只能附属于设备的根。
它们的用法如下:
#给设备dev添加根队列(指定了root就是根队列)
tc qdisc add dev DEV root QDISC QDISC_PARAMETERS
#要删除一个不可分类qdisc,需要使用如下命令
tc qdisc del dev DEV root
一个网络接口上如果没有设置qdisc,pfifo_fast就作为缺省的
qdisc。可通过"ip link show dev DEV"查看默认或者设置的qdisc。
#比如给网卡eth0设置根队列tbf
tc qdisc add dev eth0 root tbf rate 1024kbit limit 1024kbit burst 1024
classful 分类队列
可分类QDISC包括
CBQ: CBQ是 Class Based Queueing(基于类别排队)的缩写。
它实现了一个丰富的连接共享类别结构,既有限制(shaping)带
宽的能力,也具有带宽优先级别管理的能力。带宽限制是通过计算
连接的空闲时间完成的。空闲时间的计算标准是数据包离队事件的
频率和下层连接(数据链路层)的带宽。
HTB: HTB是Hierarchy Token Bucket 的缩写。通过在实践基础上
的改进,它实现一个丰富的连接共享类别体系。使用HTB可以很容
易地保证每个类别的带宽,虽然它也允许特定的类可以突破带宽上
限,占用别的类的带宽。HTB可以通过TBF(Token Bucket
Filter)实现带宽限制,也能够划分类别的优先级。
PRIO: PRIO qdisc 不能限制带宽,因为属于不同类别的数据包是
顺序离队的。使用PRIO qdisc 可以很容易对流量进行优先级管
理,只有属于高优先级类别的数据包全部发送完毕,参会发送属于
低优先级类别的数据包。为了方便管理,需要使用iptables 或者
ipchains 处理数据包的服务类型(Type Of Service,TOS)。
有类队列的设置
Linux流量控制主要分为建立队列、建立分类和建立过滤器三个方面。
基本实现步骤:
1) 针对网络物理设备(如以太网卡eth0)绑定一个队列qdisc;
2) 在该队列上建立分类class;
3) 为每一分类建立一个基于路由的过滤器filter;
4) 最后与过滤器相配合,建立特定的路由表。
环境模拟实例。
流量控制器上的以太网卡(eth0)的IP地址为 192.168.1.66, 在其上建立一
个CBQ队列。假设包的平均大小为1000字节,包间隔发送单元的大小为8字
节,可接收冲突的发送最长包的数目为20字节。
假如有如下三种类型的流量需要控制:
1)发往主机1的,其IP地址为192.168.1.24。其流量带宽控制在8Mbit,优先级为 2;
2)发往主机2的,其IP地址为192.168.1.30。其流量带宽控制在1Mbit,优先级为1;
3)发往子网1的,其子网号为192.168.1.0。子网掩码为255.255.255.0。流量带宽控制在1Mbit,优先级为6。
a. 建立队列
一般情况下,针对一个网卡只需建立一个队列。
tc qdisc add dev eth0 root handle 1: cbq bandwidth 10Mbit avpkt 1000 cell 8 mpu 64
b. 建立分类
一般情况下,针对一个队列需建立一个根分类,然后再在其上建立子分类。
对于分类,按其分类的编号顺序起作用,编号小的优先;一但符合某个分类
匹配规则,通过该分类发送数据包,则其后的分类不再起作用。
1) 创建根分类1:1;分配带宽为10Mbit,优先级别为8。
该队列的最大可用带宽为10Mbit,实际分配的带宽为10Mbit,可接收
冲突的发送最长包数目为20字节;最大传输单元加MAC头的大小为
1514字节,优先级别为8,包的平均大小为1000字节,包间隔发送单
元的大小为8字节,相当于实际带宽的加权速率为1Mbit。
tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate 10Mbit maxburst 20 allot 1514 prio 8 avpkt 1000 cell 8 weight 1Mbit
2) 创建分类1:2,其父类为1:1,分配带宽为 8Mbit, 优先级别为2。
该队列的最大可用带宽为10Mbit,实际分配的带宽为8Mbit,可接收
冲突的发送最长包数目为20字节;最大传输单元加MAC头的大小为
1514字节,优先级别为2,包的平均大小为1000字节,包间隔发送单
元的大小为8字节,相当于实际带宽的加权速率为800Kbit,分类的分离点为1:0,且不可借用未使用带宽。
tc class add dev eth0 parent 1:1 classid 1:2 cbq bandwidth 10Mbit rate 8Mbit maxburst 20 allot 1514 prio 2 avpkt 1000 cell 8 weight 800Kbit split 1:0 bounded
3) 创建分类1:3,其父分类为1:1,分配带宽为1Mbit,优先级别为1。
该队列的最大可用带宽是10Mbit,实际分配的带宽为1Mbit,可接收冲
突的发送最长包数目为20字节;最大传输单元加MAC头的大小为
1514字节,优先级别为1,包的平均大小为1000字节,包间隔发送单
元的大小为8字节,相当于实际带宽的加权速率为100Kbit,分类的分离点为1:0。
tc class add dev eth0 parent 1:1 classid 1:3 cbq bandwidth 10Mbit rate 1Mbit maxburst 20 allot 1514 prio 1 avpkt 1000 cell 8 weight 100Kbit split 1:0
4) 创建分类 1:4,其父分类为1:1,分配带宽为1Mbit,优先级别为6。
该队列的最大可用带宽为10Mbit,实际分配的带宽为1Mbit,可接收
冲突的发送最长包数目为20字节;最大传输单元加MAC头的大小为
1514字节,优先级别为6,包的平均大小为1000字节,包间隔发送单
元的大小为8字节,相当于实际带宽的加权速率为100Kbit,分类的分离点为1:0
tc class add dev eth0 parent 1:1 classid 1:4 cbq bandwidth 10 Mbit rate 1Mbit maxburst 20 allot 1514 prio 6 avpkt 1000 cell 8 weight 100Kbit split 1:0
c. 创建过滤器
过滤器主要服务于分类。
一般只需针对根分类提供一个过滤器,然后为每个子分类提供路由映射。
1) 应用路由分类器到cbq队列的根,父分类编号为1:0;过滤协议为ip,优先级别为100,过滤器为基于路由表。
tc filter add dev eth0 parent 1:0 protocol ip prio 100 route
2) 建立路由映射分类 1:2 , 1:3 , 1:4
tc filter add dev eth0 parent 1:0 protocol ip prio 100 route to 2 flowid 1:2
tc filter add dev eth0 parent 1:0 protocol ip prio 100 route to 3 flowid 1:3
tc filter add dev eth0 parent 1:0 protocol ip prio 100 route to 4 flowid 1:4
d. 建立路由
该路由是与前面所建立的路由映射一一对应。
1) 发往主机192.168.1.24的数据包通过分类2转发(分类2的速率8Mbit)
ip route add 192.168.1.24 dev eth0 via 192.168.1.66 realm 2
2) 发往主机192.168.1.30的数据包通过分类3转发(分类3的速率1Mbit)
ip route add 192.168.1.30 dev eth0 via 192.168.1.66 realm 3
3) 发往子网192.168.1.0/24 的数据包通过分类4转发(分类4的速率1Mbit)
ip route add 192.168.1.0/24 dev eth0 via 192.168.1.66 realm 4
关于filter和class
队列规则上能不能添加filter,能不能分类取决于 qdisc->ops->cl_ops中的函数指针。
如果cl_ops提供了tcf_chain函数,则支持给qdisc添加filter,
如果cl_ops提供了change函数,则支持给qdisc分类。
关于handle
handle由两部分组成,格式为a:b,其中a表示主序列号,b表示从序列号。
主序列号a唯一标识一个队列规则qdisc,对于ingress和clsact来说,其父为0xFFFFFFF1U,主序列号为0xffff。
从序列号b表示类id,对于ingress和clsact来说,无分类的概念,所以从序列号为0。
参考
http://www.tldp.org/HOWTO/Traffic-Control-HOWTO/classless-qdiscs.html#qs-fifo
https://www.cnblogs.com/ricks/p/10108911.html
https://blog.csdn.net/eydwyz/article/details/53392227
https://blog.csdn.net/pwl999/article/details/82706679