TC流量控制

概述

Linux操作系统中的流量控制器TC(Traffic Control)用于Linux内核的流量控制,它利用队列规定(qdisc)建立处理数据包的队列,并定义队列中的数据包被发送的方式, 从而实现对流量的控制。TC模块实现流量控制功能使用的队列规定分为两类,一类是无类队列规定(classless qdisc), 另一类是分类队列规定(classful qdisc)。 无类队列规定相对简单,而分类队列规定则引出了分类和过滤器等概念,使其流量控制功能增强。

  • classless qdisc对进入网络设备的数据流不加区分的统一对待,
    • 无类别qdisc中,数据包被接收、重新编排、延迟或丢弃,实现对整个设备发出的流量进行整形处理,但是所有数据包统一处理,不能细分各种情况。
    • 常用classless qdisc有pfifo_fast(先进先出,默认)、TBF(令牌桶过滤器)、SFQ(随机公平队列)、ID(向前随机丢包)等。
  • classful qdisc对进入设备的数据包根据不同的需求以分类的方式区分对待的qdisc。
    • 数据包进入一个分类的qdisc中,就需要根据需要被送到某一个类中,在对应的类中进行分类处理,这个分类的根据就是过滤器,过滤器根据设定的规则以及包的属性,觉得该数据包被送往哪个制定的队列,没有匹配到过滤器的包就被送到默认的类中进行处理。
    • 每一个子类也可以包含自己的过滤器,被分配到该子类的数据包,会在子类的过滤器中进一步匹配,直到被匹配到制定的类中,或者被分配到默认的类中。
    • 类除了可以包含其他队列规定之外,绝大多数分类的队列还可以对流量进行整形,这对于同时需要进行调度和流量控制的场合非常有用。

基本原理

TC的基本原理是将准备发给接口发送的报重新进行排序、整理和设置优先级,使得数据包在发送的最后关头,按照设置的限制重新组合。
TC对于包的处理方式有四种:
1. SHAPING:控制,当流量被shaped,它的传输速率就被控制了,但是shaping不仅仅是降低可用的带宽,也被用于平抑流量中的突发流量,可以实现更好的网络体验。shaping只发送在流量出口的地方。
2. SCHEDULING:调度,通过调度数据包的传输,可以提高对需要它的通信的交互性,同时还可以保证带宽的传输。这种重新排序也被称为按照优先级排序,并且只发生在出口。
3. POLICING:监管,与shaping处理数据报的传递对应的,监管属于处理数据报到达时的处理方法,因此POLICING发生在入口处。
4. DROPPING:丢弃,超出指定带宽的数据报可能会立即被抛弃,这在入口和出口都会发生。
一般来说,TC只对网络设备发出的流量进行限制,不对接收的流量进行限制,所以在设计规则时,要注意数据的流向,比如上行速率的限制都是在外网接口进行,下行速率的限制都是在内网接口进行

TC通过三种结构qdisc(队列)、class(分类)和filter(过滤器)来组合成各种定制的数据包分发结构,这种结构类似于一个树,qdisc是树的节点,是每一个结构节点的载体,规定和限制了该节点可以承担怎样的功能,派生出怎样的子节点;class可以看做树上节点的值,它具体定义了该节点对于流量的带宽、优先级、延迟和burst的规定;filter就是节点的闸门,管理者数据包是否应该流入该节点。整个流量在TC树中的流动,就是这样从上到下,从小的minor_id到大的minor_id的类依次轮询,也有例外的就是,如果一个类下面的子类设置有不同的优先级,则流量会优先满足优先级高的子类(prio_id小的)。
首先,进入的是根qdisc,即根class,然后在根class下的filter之前过滤,决定该包进入哪一个子类,如果都没有匹配到,则进入default的子类,如果匹配到一个子类,则进入该子类进行进一步的filter,直到匹配到没有子类的子类,或被分配到默认的子类中。

队列QDISC

无类别qdisc

无类别的qdisc,包括以下三种

  • [p|b]fifo:使用最简单的filter,纯粹的先入先出,只通过一个参数limit限制队列的长度,pfifo中limit以数据包的个数为单位,bfifo中limit以字节数为单位。
  • pfifo_fast:当内核打开“高级路由(Advanced Router)”编译选项时,系统的标准qdisc,它的队列包括三个波段,每个波段都是用先入先出规则,但是三个波段优先级不同,从高到低依次是band0, - - band1和band2。系统优先处理高优先级的波段中的数据报,这些数据报是按照服务类型被分配到三个波段里的。
  • red:Random Early Detection,随机早期探测,使用该qdisc时,当单开的占用接近规定的带宽时,系统会随机的丢弃一些数据包,非常适用于高带宽的应用。
  • sfq:Stochastic Fairness Queueing,随机公平队列,它会按照会话ID(session ID)为流量进行排序,然后循环按时每个会话的数据包。
  • tbf:Token Bucket Filter,令牌桶过滤器,适用于降低带宽到一个精确配置的速率,很适用于大带宽。
    如果没有有类别的qdisc,那么无类别qdisc只能附属于一个设备的根,用法如下:
tc qdisc add dev DEV root QDISC QDISC-PARAMETERS

使用如下命令删除该设备的qdisc:tc qdisc del dev DEV root。在没有配置qdisc时,pfifo_fast就是默认的qdisc。

有类别qdisc

有类别qdisc包括:
- CBQ:Class Based Queueing,基于类别排队,实现了一个具有丰富共享层次结构的类结构,它既有shaping限制带宽的能力,也具有带宽优先级管理的能力,带宽限制是通过计算机连接的空闲时间完成的,而空闲时间的计算标准就是数据报离队事件的频率和下层数据链路层的带宽。
- HTB:Hierarchy Token Bucket,层次结构令牌桶,实现了一个具有丰富链接共享的层次结构,重点是与现有的实践相一致。使用HTB可以很容易的保证每个类别的带宽,虽然它也允许特定的类可以突破带宽上限,占用别的类的带宽。它包含基于TBF的限制元素来限制带宽,可以为类设置优先级。
- PRIO:不能限制带宽,因为不同类别的数据包是顺序离队的,使用PRIO qdisc可以很容易的对流量进行优先级管理,只有高优先级的数据包全部发送完之后,低优先级的数据包才会继续被发送。为了方便管理,需要使用iptables或ipchain(iptables的前身)处理数据包的TOS。

分类Class

分类class和qdisc总是成对出现,class为qdisc指定具体的整形、限流和优先级规则,由于不同的qdisc功能差别较大,其所属class的规则格式也差别很大,在使用时,需要查看具体的文档说明。注意,无类别qdisc没有class。

过滤器Filter

filter用于指定数据包被分配给哪一个类,除了默认的类之外,每一个class都应该有对应的filter来为他匹配数据包。

命名规则

  • 一个qdisc会被分配一个主序列号,被称为handle(句柄),然后把从序列号作为类的命名空间。句柄采用类似10:这样的表达方式。一般一个根qdisc被分配一个handle,类似handle_id:,该handle_id就是其下所有子类的major_id,这个id是表示该子类所属的标志,不能更改。每个子类分配的qdisc,使用该子类的minor_id构建类似minor_id的格式来产生自己的handle。
tc qdisc add dev eth0 root handle 2: htb default 100
  • 一个class的命名格式:major_id:minor_id,其中major_id必须和父类的major_id以及父qdisc的handle_id一致,所以在一个设备下,所有类的major_id都是一致的,这也就意味着该设备下的所有类的minor_id不能重复,不然内核会报出该配置重复的错误RTNETLINK answers: File exists
tc class add dev eth0 parent 2:0 classid 2:1 htb rate 5000kbit burst 15k
tc class add dev eth0 parent 2:1 classid 2:20 htb rate 1000kbit ceil 1000kbit burst 15k
  • 一个filter没有具体的命名,它针对每一个具体的分类建立过滤规则,命令格式如下
"tc filter add dev eth0 parent 2:0 protocol ip handle 0x0101 fw classid 2:20",

TC命令规则

TC的工作原理是在输出端口建立一个队列进行流量控制,控制的对象是从该端口发出数据包,控制的方式是基于路由,即根据数据包的IP地址、目的子网地址、MARK值等进行的流量控制。TC功能主要通过qdisc、class和filter三种功能模块实现,在添加TC命令时,也是通过这三种功能模块和路由(Route、iptables)的组合来实现具体的限速、整形功能。
以下例子中的队列和分类基于常用的HTB实现,过滤器基于iptables功能,实现一个简单的单层TC命令树,实现对内网特定设备的下载速度的限制,控制设备为内网路由器,LAN侧网卡eth0,IP:192.168.2.1,内网设备1,mac:11:11:11:11:11:11,ip:192.168.2.102, 限速1MB/s,内网设备2,mac:22:22:22:22:22:22,ip:192.168.2.103,限速500KB/s,其余设备不限速。
基本步骤如下:

  1. 针对一个网络设备,如eth0,绑定一个根的htb队列
  2. 在该队列上建立分类
  3. 为每一个分类建立一个无类别sfq qdisc,以及一个基于iptables的过滤器
  4. 最后,位于过滤器配置,设置特定的iptables规则

绑定根队列和根分类

为控制内网设备的下载速度,需要控制路由器发给该设备的流量,即路由器eth0接口发往内网设备的流量,所以在eth0上建立tc规则如下:

tc qdisc add dev eth0 root handle 10: htb default 100

tc建立htb根队列,开头和添加其他qdisc一样tc qdisc add;随后指定添加队列的设备dev eht0,并指定这是根队列,没有parent,所以后面跟root;再指定handle 10:,这是qdisc命名的普遍格式,注意随后添加的分类major_id要和handle冒号前的值一致;随后,指出此次添加的qdisc类型为htb,到此为止,之前的格式都是通用的,所有的qdisc的添加都会采用类似的格式,除了根队列需要添加root,而子队列需要制定parent;最后,每种qdisc可能需要不同的参数,有的是必填的,有的是可选的,htb必填的参数只有default,其后跟着流量默认走向的子类的minor_id,该子类可以不去手动添加,系统会自动按照默认的配置生成一个隐藏的子类,但是通过tc show的命令并不能看到该子类,所以一般建议是添加该默认子类,以便对流量进行监控和统计。

然后,为该根队列绑定一个根分类,如下:

tc class add dev eth0 parent 10:0 classid 10:1 htb rate 1000Mbit burst 15k

开头和所有添加class的命令一致tc class add dev eth0,随后指定父类,由于是根class,所以父类为parent 10:0,其实表示的就是根qidsc,10:0的表示和10:的表示是一样的;指定分类IDclassid 10:1,指定分类所属qdisc类型htb,随后是针对该分类的属性配置,rate 1000Mbit表示分配给该类的带宽,注意这里是bit而不是B,换算成网速需要除以8,而且如果对于整个网络设备的整个带宽不做限制的话,这里的rate其实是远远超过实际带宽的,如果需要对整个带宽做限制,在这里就需要对rate做出具体的限制;burst 15k表示在指定带宽基础上,允许的burst脉冲,即1000Mbit带宽基础上,允许超出该值得最大波动,即带宽波动到(1000000+15)kbit都是合理且允许的。

建立所需分类

根据要求,eth0下面有两个设备需要限速,其余设备不进行限速,所以需要建立两个class为这两个设备限速进行使用,再建立一个默认的class作为默认的流量通道。

tc class add dev eth0 parent 10:1 classid 10:2 htb rate 8000kbit ceil 8000kbit burst 15k
tc class add dev eth0 parent 10:1 classid 10:3 htb rate 4000kbit ceil 4000kbit burst 15k
tc class add dev eth0 parent 10:1 classid 10:100 htb rate 100kbit ceil 1000Mbit burst 15k

第一条为设备1的tc规则,classid 10:2,限速rate 8000kbit即1MB/s,ceil 8000kbit是htb的一个特殊属性,在htb中存在一种借用机制,即同一个父类下面的子类,允许向其他子类借用其闲置的带宽资源,但是这个借用的大小有限制,借用的带宽+自身rate的带宽<=ceil,这里对设备1进行限速,所以讲rate和ceil都设为8000kbit(其实默认情况下,ceil会被自动设置为rate一样的值)。
第二条为设备2的tc规则,classid 10:3,格式与1一样,只是限速值为4000kbit
第三条为默认子类的tc规则,这里rate 100kbit是一个很小的值,但是ceil 1000Mbit是整个设置的带宽的大小,这表示其余设备虽然被分配了很小的带宽,但是允许借用设备下所有的限制带宽流量,这也是为了在网络拥堵的情况下,防止陌生设备过度占用带宽。

为每个分类建立sfq qdisc和filter

tc为每个分类默认的定义一个pfifo_fast的qdisc,用于处理该分类所接收的流量,但是如果遇到某一设备下的数据量很大,大量占用带宽的情况,其余走同一分类的流量较小的设备就无法及时得到处理和响应。所以,这里不采用默认的pfifo_fast队列,而是采用sfq队列,该队列会将数据包打乱发送,以避免上述情况发生,使得每个设备的数据都有较为公平的机会被处理。
由于这里针对具体设备进行过滤,并且知道设备的mac地址,所以tc为每个分类建立的filter可以基于iptables,通过iptables为制定mac和流向的数据打上mark,在tc中通过mark来匹配响应控制的流量。

tc qdisc add dev eth0 parent 10:2 handle 2: sfq perturb 10
tc filter add dev eth0 parent 10:0 protocol ip handle 0x0002 fw classid 10:2

tc qdisc add dev eth0 parent 10:3 handle 3: sfq perturb 10
tc filter add dev eth0 parent 10:0 protocol ip handle 0x0003 fw classid 10:3

tc qdisc add dev eth0 parent 10:100 handle 100: sfq perturb 10

为一个分类建立qdisc时,需要注意parent 10:2为该分类的classidhandle为该分类classidminor_id,指定qdisc类型为sfqsfq必须的参数为perturb 10,表示每10s产生一次随机算法的扰动,即每10s对随机排队算法进行一次小的调整,防止固定的随机算法会同样造成默写设备的流量被一直阻塞或被排在较低的优先级。该值不能太小,一般建议在60,否则会导致一些数据包的丢失或重新录入。
建立filter时,开头一致为tc filter add dev eth0,随后是指定parent 10:0,因为这里的filter是挂在根class下面的,所以父节点为根class的classid,随后指定过滤器起作用的协议为ipprotocol iphandle 0x0002表示过滤的目标为mark = 0x0002的数据包,fw classid 10:2表示匹配到目标数据包后将其调度到classid10:2的分类中。
最后一条为默认分类建立sfq,不再需要添加filter

设置特定iptables规则

由于需要限制eth0->设备1以及eth0->设备2的流量,所以将该方向上的数据加上mark,注意set-mark的动作只能在mangle表中进行。

iptables -t mangle -A FOR_WARD -J tc_ctl
iptables -t mangle -A tc_ctl -d 192.168.2.102 -o eth0 -j MARK --set-mark 0x0002
iptables -t mangle -A tc_ctl -d 192.168.2.103 -o eth0 -j MARK --set-mark 0x0003

监控

在tc运行过程中,通过tc show命令来查看tc命令是否正确的添加和生效,通过iptables -t mangle -nvL命令查看添加的iptables命令。

  • 查看qdisc,可以显示qdisc的handle、默认分类等信息,加-s查看详细信息,包括发送的数据包总数,便于查看设置的限速规则是否正确生效。
#tc qdisc dev eth0 show
qdisc htb 10: root refcnt 2 r2q 10 default 100 direct_packets_stat 0
qdisc sfq 2: parent 10:2 limit 127p quantum 1514b divisor 1024 perturb 10sec 
qdisc sfq 3: parent 10:3 limit 127p quantum 1514b divisor 1024 perturb 10sec
#tc -s qdisc dev eth0 show
qdisc htb 10: root refcnt 2 r2q 10 default 20 direct_packets_stat 0
 Sent 62218 bytes 229 pkt (dropped 0, overlimits 2 requeues 0) 
 backlog 0b 0p requeues 0 

qdisc sfq 2: parent 2:10 limit 127p quantum 1514b divisor 1024 perturb 10sec 
 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 

qdisc sfq 3: parent 2:20 limit 127p quantum 1514b divisor 1024 perturb 10sec 
 Sent 62218 bytes 222 pkt (dropped 0, overlimits 0 requeues 0) 
 backlog 0b 0p requeues 0
  • 查看class,加-s查看详细信息
# tc class show dev eth0
class htb 10:2 parent 10:1 leaf 2: prio 0 rate 8000Kbit ceil 8000Kbit burst 15Kb cburst 1600b 
class htb 10:1 root rate 1000Mbit ceil 1000Mbit burst 15125b cburst 1375b 
class htb 10:3 parent 10:1 leaf 3: prio 0 rate 4000kbit ceil 4000kbit burst 15Kb cburst 1375b
class htb 10:100 parent 10:100 leaf 100: prio 0 rate 100kbit ceil 1000Mbit burst 15Kb cburst 1375b
# tc -s class show dev eth0
class htb 10:2 parent 10:1 leaf 2: prio 0 rate 8000Kbit ceil 8000Kbit burst 15Kb cburst 1600b 
 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
 rate 0bit 0pps backlog 0b 0p requeues 0 
 lended: 0 borrowed: 0 giants: 0
 tokens: 480000 ctokens: 50000

class htb 10:3 parent 10:1 leaf 3: prio 0 rate 4000Kbit ceil 4000Kbit burst 15Kb cburst 1600b 
 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
 rate 0bit 0pps backlog 0b 0p requeues 0 
 lended: 0 borrowed: 0 giants: 0
 tokens: 480000 ctokens: 50000

class htb 10:1 root rate 1000Mbit ceil 1000Mbit burst 15125b cburst 1375b 
 Sent 92361 bytes 608 pkt (dropped 0, overlimits 0 requeues 0) 
 rate 264bit 0pps backlog 0b 0p requeues 0 
 lended: 7 borrowed: 0 giants: 0
 tokens: 1906 ctokens: 187

class htb 10:100 parent 10:1 leaf 100: prio 0 rate 100000bit ceil 1000Mbit burst 15Kb cburst 1375b 
 Sent 92361 bytes 608 pkt (dropped 0, overlimits 0 requeues 0) 
 rate 264bit 0pps backlog 0b 0p requeues 0 
 lended: 594 borrowed: 7 giants: 0
 tokens: 19120000 ctokens: 187
  • 查看过滤器
# tc filter show dev eth0
filter parent 10: protocol ip pref 49152 fw 
filter parent 10: protocol ip pref 49152 fw handle 0x2 classid 10:2
filter parent 10: protocol ip pref 49152 fw handle 0x3 classid 10:3
  • 查看iptables规则
# iptables -t mangle -nvL
...

Chain FORWARD (policy ACCEPT 42 packets, 2608 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   42  2608 tc_ctl     all  --  *      *       0.0.0.0/0            0.0.0.0/0           

...

Chain ip_ctl (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MARK       all  --  --    eth0   0.0.0.0/0            192.168.2.102/24            MARK set 0x2
    0     0 MARK       all  --  --    eth0   0.0.0.0/0            192.168.2.103/24            MARK set 0x3

维护

tc运行过程中,需要根据实际运行情况动态调整tc规则,tc提供了changadddelete等命令可以动态的调整tc规则,但是要注意,有些qdisc可以动态调整,但是有些是不能动态调整的。

你可能感兴趣的:(linux,通讯协议)