目录:
1、简介
1.1、概念
1.2、qdisc队列分类
1.3、分类排队规则和分类处理(Classful qdisc & class 中的 flow)
1.4、qdisc排队规则的层级结构
1.5、过滤器简介
2、tc使用
2.1、tc配置规则案例解析
2.2、tc使用示例
3、tc工具常用命令
1、简介:
Linux TC的诞生是为了实现QoS,它在netdev设备的入方向和出方向增加了挂载点,进而控制网络流量的速度,延时,优先级等。
Linux TC在整个Linux Kernel Datapath中的位置如下图所示:
Linux操作系统中的流量控制器TC(Traffic Control)用于Linux内核的流量控制,它利用一些队列规则来处理数据包的队列,并定义队列中的数据包被发送的方式, 从而实现对流量的控制。TC使用的队列规则分为两类,一类是无类别队列规则, 另一类是分类队列规则。 无类别队列规则相对简单,而分类队列规则则引出了分类和过滤器等概念,使其流量控制功能增强。
1.1、概念:
报文分组从输入网卡接收进来,经过路由的查找, 以确定是发给本机的,还是需要转发的。如果是发给本机的,就直接向上递交给上层的协议,比如TCP,如果是转发的, 则会从输出网卡发出。
网络流量的控制通常发生在输出网卡处,我们可以通过改变发送次序来控制传输速率。一般说来, 由于我们无法控制自己网络之外的设备, 入口处的流量控制相对较难。
流量控制的一个基本概念是队列,每个网卡都与一个队列相联系,每个队列对应一个QDisc(排队规则), 每当内核需要将报文分组从网卡发送出去, 都会首先将该报文分组添加到该网卡所配置的QDisc中, 由该QDisc决定报文分组的发送顺序,可以说,所有的流量控制都发生在队列中。
为实现复杂QoS,队列需要使用不同的过滤器(Filter)来把报文分组分成不同的类别(Class),因此类别(class)和过滤器(Filter)也是流量控制的另外两个重要的基本概念。
1)、TC相关概念解释:
- QDisc(排队规则):是queueing discipline的简写,它是理解流量控制(traffic control)的基础。无论何时,内核如果需要通过某个网络接口发送数据包,它都需要按照为这个接口配置的qdisc(排队规则)把数据包加入队列。然后,内核会尽可能多地从qdisc里面取出数据包,把它们交给网络适配器驱动模块。最简单的QDisc规则是pfifo(也是默认规则)它不对进入的数据包做任何的处理,数据包采用先入先出的方式通过队列。不过,它会保存网络接口一时无法处理的数据包;QDisc队列根据规则分为两类,一类是无类别队列规则, 另一类是分类队列规则。 无类别队列规则相对简单,而分类队列规则则引出了分类和过滤器等概念,使其流量控制功能增强(简单的说即有类别(或称分类别)排队规则是一个排队规则中又包含其他 排队规则)。
- class(类别):每个 classful qdisc 可能会包含几个 class,这些都是 qdisc 内部可见的。对于每 个 class,也是可以再向其添加其他 class 的。因此,一个 class 的 parent 可以 是一个 qdisc,也可以是另一个 class。Leaf class 是没有 child class 的 class。这种 class 中 attach 了一个 qdisc ,负责该 class 的数据发送。创建一个 class 时会自动 attach 一个 fifo qdisc。而当向这个 class 添加 child class 时,这个 fifo qdisc 会被自动删除。对于 leaf class,可以用一个更合适的 qdisc 来替换掉这个fifo qdisc。你甚至能用一个 classful qdisc 来替换这个 fifo qdisc,这样就可以添加其他 class了(简单的说就是QDisc(排队规则)可以包含一些其他类别组成层级的树状结构,不同的类别中可以包含更深入的QDisc(排队规则),通过这些细分的QDisc还可以为进入的队列的数据包排队。通过设置各种类别数据包的离队次序,QDisc可以为设置网络数据流量的优先级);
- filter(过滤器):Filter(过滤器)用于为数据包分类,决定它们按照何种QDisc进入队列。无论何时数据包进入一个划分子类的类别中,都需要进行分类。分类的方法可以有多种,使用fileter(过滤器)就是其中之一。使用filter(过滤器)分类时,内核会调用附属于这个类(class)的所有过滤器,直到返回一个判决。如果没有判决返回,就作进一步的处理,而处理方式和QDISC有关。需要注意的是,filter(过滤器)是在QDisc内部,它们不能作为主体;
2)、流量控制的方式术语:
- Scheduling(调度):在分类器的协助下,一个 qdisc 可以判断某些包是不是要先于其他包发送出去,这 个过程称为调度,可以通过例如前面提到的 pfifo_fast qdisc 完成。调度也被 称为重排序(reordering),但后者容易引起混淆;
- Shaping(整形):在包发送出去之前进行延迟处理,以达到预设的最大发送速率的过程。整形是在 egress 做的(前面提到了,ingress 方向的不叫 shaping,叫 policing,译者注)。 不严格地说,丢弃包来降低流量的过程有时也称为整形;
- Policing(执行策略,决定是否丢弃包):延迟或丢弃(delaying or dropping)包来达到预设带宽的过程。 在 Linux 上, policing 只能对包进行丢弃,不能延迟 —— 没有“入向队列”(”ingress queue”);
- DROPPING(丢弃):如果流量超过某个设定的带宽,就丢弃数据包,不管是向内还是向外;
- Work-Conserving qdisc(随到随发 qdisc):work-conserving qdisc 只要有包可发送就立即发送。换句话说,只要网卡处于可发送状态(对于 egress qdisc 来说),它永远不会延迟包的发送;
- non-Work-Conserving qdisc(非随到随发 qdisc):某些 qdisc,例如 TBF,可能会延迟一段时间再将一个包发送出去,以达到期望的带宽 。这意味着它们有时即使有能力发送,也不会发送;
上图中的框代表 Linux 内核。最左侧的箭头表示流量从外部网络进入主机。然后进入 Ingress Qdisc,这里会对包进行过滤(apply Filters),根据结果决定是否要丢弃这个包。这个过程称为 “Policing”。这个过程发生在内核处理的很早阶段,在穿过大部分内核基础设施之前。因此在这里丢弃包是很高效的,不会消耗大量 CPU。
如果判断允许这个包通过,那它的目的端可能是本机上的应用(local application),这 种情况下它会进入内核 IP 协议栈进行进一步处理,最后交给相应的用户态程序。另外,这 个包的目的地也可能是其他主机上的应用,这种情况下就需要通过这台机器 Egress Classifier 再发送出去。主机程序也可能会发送数据,这种情况下也会通过 Egress Classifier 发送。
Egress Classifier 中会用到很多 qdisc。默认情况下只有一个:pfifo_fast qdisc ,它永远会接收包,这称为“入队”(”enqueueing”)。
此时包位于 qdisc 中了,等待内核召唤,然后通过网络接口(network interface)发送出去。 这称为“出队”(”dequeueing”)。
以上画的是单网卡的情况。在多网卡的情况下,每个网卡都有自己的 ingress 和 egress hooks。
1.2、qdisc队列分类:
不可分类规则队列有:
- [p|b]fifo: 使用最简单的qdisc,纯粹的先进先出。只有一个参数:limit,用来设置队列的长度,pfifo是以数据包的个数为单位;bfifo是以字节数为单位;
- pfifo_fast:就是系统的标准QDISC。在编译内核时,如果打开了高级路由器(Advanced Router)编译选项,pfifo_fast就是系统的标准QDISC。它的队列包括三个波段(band)。在每个波段里面,使用先进先出规则。而三个波段(band)的优先级也不相同,band 0的优先级最高,band 2的最低。如果band0里面有数据包,系统就不会处理band 1里面的数据包,band 1和band 2之间也是一样。数据包是按照服务类型(Type of Service,TOS)被分配多三个波段(band)里面的;
- red:是Random Early Detection(随机早期探测)的简写。如果使用这种QDISC,当带宽的占用接近于规定的带宽时,系统会随机地丢弃一些数据包。它非常适合高带宽应用;
- sfq:是Stochastic Fairness Queueing的简写。它按照会话(session--对应于每个TCP连接或者UDP流)为流量进行排序,然后循环发送每个会话的数据包;
- tbf:是Token Bucket Filter的简写,适合于把流速降低到某个值;
可分类队列规则有:
- CBQ:是Class Based Queueing(基于类别排队)的缩写。它实现了一个丰富的连接共享类别结构,既有限制(shaping)带宽的能力,也具有带宽优先级管理的能力。带宽限制是通过计算连接的空闲时间完成的。空闲时间的计算标准是数据包离队事件的频率和下层连接(数据链路层)的带宽;
- HTB:是Hierarchy Token Bucket的缩写。通过在实践基础上的改进,它实现了一个丰富的连接共享类别体系。使用HTB可以很容易地保证每个类别的带宽,它也允许特定的类可以突破带宽上限,占用别的类的带宽。HTB可以通过TBF(Token Bucket Filter)实现带宽限制,也能够划分类别的优先级;
- PRIO:QDisc不能限制带宽,因为属于不同类别的数据包是顺序离队的。使用PRIO QDisc可以很容易对流量进行优先级管理,只有属于高优先级类别的数据包全部发送完毕,才会发送属于低优先级类别的数据包。为了方便管理,需要使用iptables或者ipchains处理数据包的服务类型(Type Of Service,ToS);
1.3、分类排队规则和分类处理(Classful qdisc & class 中的 flow):
当流量进入一个 classful qdisc 时,该 qdisc 需要将其发送到内部的某个 class —— 即 需要对这个包进行“分类”。而要这个判断过程,实际上是查询所谓的“过滤器”( ‘filters’)。过滤器是在 qdisc 中被调用的,而不是其他地方,理解一点非常重要!
过滤器返回一个判决结果给 qdisc,qdisc 据此将包 enqueue 到合适的 class。 每个 subclass 可能会进一步执行其他 filters,以判断是否需要进一步处理。如果没有其他过滤器,这个 class 将把包 enqueue 到它自带的 qdisc。
除了能包含其他 qdisc,大部分 classful qdisc 还会执行流量整形。这对包调度(packet scheduling,例如,基于 SFQ)和速率控制(rate control)都非常有用。 当高速设备(例如,以太网)连接到一个低速设备(例如一个调制解调器)时,会用到这个 功能。
如果只运行 SFQ,那接下来不会发生什么事情,因为包会无延迟地进入和离开路由 器:网卡的发送速度要远大于真实的链路速度。瓶颈不在主机中,就无法用“队列”(queue )来调度这些流量。
1.4、qdisc排队规则的层级结构:
每个接口有一个egress 'root qdisc',默认是pfifo_fast。每个qdisc和class都分配一个句柄handle,句柄用于在后续的配置语句中进行引用。除了egress qdisc,一个接口也可以有一个ingress qdisc,负责管制入站的流量。但是ingress qdisc相比classful qdisc其可能性是非常有限。(所以才有所谓的控发不控收,对入站流量进行控制通常需要借助ifb或者imq)。
所有的QDisc、类和过滤器都有ID。ID可以手工设置,也可以有内核自动分配。ID由一个主序列号和一个从序列号组成,两个数字用一个冒号分开,主序列号在tc中称为handle,用于子类的引用。这些qdisc的ID就是tc中的handles在定义上有两个部分组成,一个major数和一个minor数:<major>:<minor>。习惯上将root qdisc命名为1:,等价于1:0。
子类需要跟它们的parent有相同的major数。major数在一个egress或ingress内必须是唯一的,minor数在一个qdisc和它的class中必须是唯一的。一个典型的层级结构如下:
内核只跟root qdisc进行通信,每当包需要入队或者出队的时候,都需要从root节点开始,最终到达叶子节点,从而决定入队到哪里,或者从哪里出队。比如当一个包入队时,它可能会经过如下路径:
1: -> 1:1 -> 1:12 -> 12: -> 12:2
当然也可能直接走如下路径(这种情况,就是root qdisc上的过滤器决定把包直接送到12:2):
1: -> 12:2
入队时是根据过滤器和包的特征决定走哪条路径,而出队时则取决于qdisc本身的调度算法,比如FIFO、优先级队列、SFQ的顺序调度等。
1.5、过滤器简介:
过滤器类别:
前面已经提到了过滤器用于将包分类到子类,那么具体是如何对包进行分类的呢?
tc支持很多类型的分类器,它们根据数据包相关的不同信息来作出决策。其中最常用的就是u32分类器,它根据数据包中的字段做出决策(例如源IP地址等)。还有比如fw分类器,根据防火墙如何标记数据包来做出决策,你可以使用iptables标记目标数据包,然后通过fw分类器进行过滤。另外还有诸如route分类器、cgroup分类器、bpf分类器等。
分类器一般接收以下几个公共的参数:
- protocol:分类器接受的协议,通常你只接受IP流量。必须;
- parent:分类器附加到哪个handle上。这个handle必须是一个已经存在的类。必须;
- prio|perf:分类器的优先级。数字越小的越先进行匹配尝试;
- handle:这个handle对于不同的过滤器表示不同的含义;
- flowid:表示将filter匹配的包分类到指定的子类中去;
1)、u32分类器使用:
u32过滤器最简单的格式是设置一组选择器对包进行匹配,匹配的包分到特定的子类中,或者执行一个action。u32分类器提供了多种不同的选择器,可以大致分成特殊选择器和通用选择器两类。
- u32特殊选择器:
常用的有ip选择器和tcp选择器。特殊选择器简化了一些常用字段的设置,可以匹配包头中的各种字段,比如:
tc filter add dev eth0 protocol ip parent 1:0 prio 10 u32 \
match ip src 192.168.8.0/24 flowid 1:4
上例匹配ip源地址在192.168.8.0/24子网的包,将匹配的到使用flowid分类到1:4子类中(即qdisc层级树当中的1:4节点中去)。
tc filter add dev eth0 protocol ip parent 1:0 prio 10 u32 \
match ip protocol 0x6 0xff \
match tcp dport 53 0xffff \
flowid 1:2
上例匹配TCP协议(0x6)、且目的端口为53的包。
- u32通用选择器:
特殊选择器可以改写成对应的通用选择器,通用选择器可以匹配 IP(或上层)头中的几乎任何位(简单的说就是匹配数据包中的原始字段),不过相比特殊选择器较难编写和阅读。其语法如下:
match [ u32 | u16 | u8 ] PATTERN MASK at [OFFSET | nexthdr+OFFSET]
其中u32|u16|u8指定pattern的长度,分别为4个字节、2个字节、1个字节。PATTERN表示匹配的包的pattern,MASK告诉过滤器匹配哪些位,at表示从包的指定偏移处开始匹配。
来看一个例子:
tc filter add dev eth0 protocol ip parent 1:0 pref 10 u32 \
match u32 00100000 00ff0000 at 0 flowid 1:10
选择器会匹配IP头第二个字节为0x10的包,at 0表示从头开始匹配,mask为00ff0000所以只匹配第二个字节,pattern为00100000即第二个字节为0x10。
再来看另一个例子:
tc filter add dev eth0 protocol ip parent 1:0 pref 10 u32 \
match u32 00000016 0000ffff at nexthdr+0 flowid 1:10
nexthdr选项表示封装在IP包里的下一个头,即上层协议的头。at nexthdr+0表示从下一个头第一个字节开始匹配。因为mask为0000ffff,所以匹配发生在头的第三和第四个字节。在TCP和UDP协议中这两个字节是包的目的端口。数字是由大段格式给出的,所以pattern 00000016转换成十进制是22。即该选择器会匹配目的端口为22的包。
2、tc使用:
Linux 中的 QoS 分为入口 (Ingress) 部分和出口 (Egress) 部分,入口部分主要用于进行入口流量限速 (Policing),出口部分主要用于队列调度 (Queuing Scheduling)。大多数排队规则 (QDisc) 都是用于输出方向的,输入方向只有一个排队规则,即 Ingressqdisc。
Ingressqdisc 本身的功能很有限,输入方向只有一个排队规则,即 IngressqDisc(因为没有缓存只能实现流量的 Drop)但可用于重定向 Incomingpackets。通过 IngressqDisc 把输入方向的数据包重定向到虚拟设备 ifb,而 ifb 的输出方向可以配置多种 QDisc,就可以达到对输入方向的流量做队列调度的目的。
Linux流量控制主要分为建立队列、建立分类和建立过滤器三个方面。基本实现步骤为:
- 针对网络物理设备(如以太网卡eth0)绑定一个队列QDisc;
- 在该队列上建立分类class;
- 为每一分类建立一个基于路由的过滤器filter;
- 最后与过滤器相配合,建立特定的路由表;
2.1、tc配置规则案例解析:
$sudo tc qdisc add dev eth0 root handle 1: prio bands 4
$sudo tc qdisc add dev eth0 parent 1:4 handle 40: netem loss 10% delay 40ms
$sudo tc filter add dev eth0 protocol ip parent 1:0 prio 4 u32 match ip dst 192.168.190.7 match ip dport 36000 0xffff flowid 1:4
解析如下:
我们一行行来看,第一行在设备eth0上添加了一个root qdisc,句柄为1:,qdisc类型为prio,bands数为4。
prio是一个有类的qdisc。它的作用跟默认的qdisc pfifo_fast类似。pfifo_fast有三个所谓的band,不同band的流量具有不同的优先级。每个band内,则应用FIFO规则。
prio qdisc,默认会创建3个子类,包含纯FIFO qdisc,默认根据ToS位进行分类。你可以使用过滤器来对流量进行分类,你也可以在子类上附加其他qdisc替换默认的FIFO。
接下来看第二个命令,parent 1:4表示在子类1:4上,"handle 40:"表示句柄为"40:",netem表示添加一个"netem qdisc",loss 10% delay 40ms则是netem的参数,表示丢包10%、延迟40ms。netem是一个用于提供网络仿真功能的无类qdisc,可以模拟延迟、丢包、包重复、包失序等各种情况。
第三个命令则是添加了一个过滤器,parent 1:0表示在根节点上添加该过滤器,prio 4是过滤器的优先级,如果有很多过滤器会根据优先级的值按顺序进行尝试。u32表示使用u32分类器。match ip dst 192.168.190.7表示匹配ip地址为192.168.190.7的包,match ip dport 36000 0xffff表示匹配目的端口为36000的包,多个选择器之间是“与”的关系,flowid 1:4表示将匹配的包分类到1:4子类中。
所以最终的效果是,发往192.168.190.7且目的端口为36000的包,会分类到1:4子类,添加40ms的延迟,且有10%的丢包率。其他包则还是默认的行为,即根据ToS字段分类到1:1、1:2或1:3子类中,然后根据优先级依次发送。画出该例子的分层结构图,大致如下:
2.2、tc使用示例:
tc可以使用以下命令对QDisc、类和过滤器进行操作:
- add: 在一个节点里加入一个QDisc、类或者过滤器。添加时,需要传递一个祖先作为参数,传递参数时既可以使用ID也可以直接传递设备的根。如果要建立一个QDisc或者过滤器,可以使用句柄(handle)来命名;如果要建立一个类,可以使用类识别符(classid)来命名。
- remove: 删除有某个句柄(handle)指定的QDisc,根QDisc(root)也可以删除。被删除QDisc上的所有子类以及附属于各个类的过滤器都会被自动删除。
- change: 以替代的方式修改某些条目。除了句柄(handle)和祖先不能修改以外,change命令的语法和add命令相同。换句话说,change命令不能指定节点的位置。
- replace:对一个现有节点进行近于原子操作的删除/添加。如果节点不存在,这个命令就会建立节点。
- link:只适用于DQisc,替代一个现有的节点。
2.2.1、tbf流量控制:
测试机器 192.188.88.128 与 192.188.88.129,在192.188.88.128 上设置qdisc 进行流量控制,控制128节点出去的带宽流量为10m。这里使用不可分类的排队规则qdisc tbf;
由于tbf 属于不可分类qdisc,配置的话只需在对应网卡中配置如下tc规则即可:
$tc qdisc add dev enp1s0 handle 1: root tbf rate 10mbit burst 15k limit 20mbit
为enp1s0网卡创建一个qdisc根队列:root表示根队列,handle 表示队列的句柄为"1:"省略则随机给定,tbf表示添加的队列类型为tbf类型;
tbf可以配置的参数如下:
- limit:队列可以保存的数据包的容量即带宽大小,bytes单位,对没有获取到tokent 排队中包进行限制,达到limit 限定则丢弃。或者可以通过另一个参数latency指定了一个数据包可以在队列中等待令牌可用的最长时间,该参数会影响令牌桶大小、以及令牌生成速率的计算。这两个参数是互斥的,配置时同时指定其中一个;
- burst:桶的容量,bytes单位。也就是一次可以突发传输的数据量,越大的速率约需要越大的令牌桶,如果大速率配置一个小桶,会导致每次软中断调度只能发送较小的数据量,最终的结果就是速率难以达到实际配置的值;
- rate:即令牌生成的速率,也就是要限制的数据传输速率;
- mpu:指定最少需要的令牌数量;
- peakrate:桶的最大消耗速率,tbf通过添加第二个容量较小的桶来实现;
- mtu/minburst:第二个桶的容量,指定了小队列的长度,要想实现精准的控制,该值应该是网络设备的MTU;
1)、配置tc规则前192.188.88.128机器上的网络流量如下:
配置tc规则前192.188.88.129机器上的网络流量如下:
2)、配置tc规则后192.188.88.128机器上的网络流量如下:
配置tc规则后192.188.88.129机器上的网络流量如下:
3、tc工具常用命令:
查看所有网卡的qdisc队列规则设置情况
$tc qdisc ls
查看指定网卡的详细队列规则
$tc -s -d qdisc ls dev eth0
查看指定网卡的的分类状况
$tc -s -d class ls dev eth0
查看指定网卡的的过滤器状况
$tc -s -d filter ls dev eth0
清除所有队列规则
$tc qdis del dev eth0 root
修改tc规则
$tc qdisc change dev ens33 root tbf rate 10mbit burst 2k latency 5ms