Keep learning, keep living...

0%

Linux流量控制(Traffic Control)介绍

流量控制Traffic Control简称TC,表示网络设备接收和发送数据包的排队机制。比如,数据包的接收速率、发送速率、多个数据包的发送顺序等。

Linux实现了流量控制子系统,它包括两部分:

  • 内核部分的traffic control框架
  • 用户态的规则配置工具:iproute2软件包中的tc程序

它们有些类似于内核态的netfilter框架和用户态的iptables程序。但相较于netfilter, 关于tc的资料非常少,并且也较为古老,彻底理解它的机制还是需要对照源码。

Traffic Control的作用包括以下几种:

  • 调整(Shaping): 通过推迟数据包发送来控制发送速率,只用于网络出方向(egress)
  • 时序(Scheduling):调度不同类型数据包发送顺序,比如在交互流量和批量下载类型数据包之间进行发送顺序的调整。只用于网络出方向(egress)
  • 监督(Policing): 根据到达速率决策接收还是丢弃数据包,用于网络入方向(ingress)
  • 丢弃(Dropping): 根据带宽丢弃数据包,可以用于出入两个方向

要实现对数据包接收和发送的这些控制行为,需要使用队列结构来临时保存数据包。在Linux实现中,把这种包括数据结构和算法实现的控制机制抽象为结构队列规程:Queuing discipline,简称为qdiscqdisc对外暴露两个回调接口enqueuedequeue分别用于数据包入队和数据包出队,而具体的排队算法实现则在qdisc内部隐藏。不同的qdisc实现在Linux内核中实现为不同的内核模块,在系统的内核模块目录里可以查看前缀为sch_的模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@server ~]# ls -l /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_*
-rw-r--r--. 1 root root 7612 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_atm.ko.xz
-rw-r--r--. 1 root root 9628 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_cbq.ko.xz
-rw-r--r--. 1 root root 4880 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_choke.ko.xz
-rw-r--r--. 1 root root 4488 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_codel.ko.xz
-rw-r--r--. 1 root root 5004 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_drr.ko.xz
-rw-r--r--. 1 root root 5792 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_dsmark.ko.xz
-rw-r--r--. 1 root root 6328 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_fq_codel.ko.xz
-rw-r--r--. 1 root root 6032 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_fq.ko.xz
-rw-r--r--. 1 root root 5716 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_gred.ko.xz
-rw-r--r--. 1 root root 9088 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_hfsc.ko.xz
-rw-r--r--. 1 root root 10868 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_htb.ko.xz
-rw-r--r--. 1 root root 2288 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_ingress.ko.xz
-rw-r--r--. 1 root root 4460 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_mqprio.ko.xz
-rw-r--r--. 1 root root 4116 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_multiq.ko.xz
-rw-r--r--. 1 root root 6556 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_netem.ko.xz
-rw-r--r--. 1 root root 2256 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_plug.ko.xz
-rw-r--r--. 1 root root 3908 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_prio.ko.xz
-rw-r--r--. 1 root root 8924 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_qfq.ko.xz
-rw-r--r--. 1 root root 4940 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_red.ko.xz
-rw-r--r--. 1 root root 5264 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_sfb.ko.xz
-rw-r--r--. 1 root root 7552 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_sfq.ko.xz
-rw-r--r--. 1 root root 5116 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_tbf.ko.xz
-rw-r--r--. 1 root root 4976 Apr 21 2018 /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_teql.ko.xz

qdisc的实现可以非常简单,比如只包含单个队列,数据包先进先出,如: pfifo, 代码位于net/sched/sch_fifo.c。也可以实现相当复杂的调度逻辑。比如,可以根据数据包的属性进行过滤分类,而针对不同的分类:class采用不同的算法来进行处理。class可以理解为qdisc的载体,它还可以包含子类与qdisc。用来实现过滤逻辑的组件叫做filter,也叫做分类器classfier, 它需要挂载在qdisc或者class上。

基于qdisc, classfilter种三元素可以构建出非常复杂的树形qdisc结构,极大扩展流量控制的能力。

对于树形结构的qdisc, 当数据包流程最顶层qdisc时,会层层向下递归进行调用。如,父对象(qdisc/class)的enqueue回调接口被调用时,其上所挂载的所有filter依次被调用,直到一个filter匹配成功。然后将数据包入队到filter所指向的class,具体实现则是调用class所配置的Qdiscenqueue函数。没有成功匹配filter的数据包分类到默认的class中。

如图:

大约在15年的时候,TC框架实现中又加入了Classifier-Action机制。上文提到的filter实际作用就是classifier,当数据包匹配到特定的filter之后,可以执行该filter所挂载的actions对数据包进行处理。

如图:

filteraction也可以实现为独立的内核模块,tc框架的扩展性非常灵活。

一个网络接口有两个默认的qdisc锚点。入方向的锚点叫做ingress, 出方向叫做root。入方向的ingress功能比较有限,不能挂载其他的class,只是做为Classifier-Action机制的挂载点。

qdiscclass的标识符叫做handle, 它是一个32位的整数,分为majorminor两部分,各占16位,表达方式为:m:n, mn省略时,表示0

m:0一般表示qdisc, 对于class, minor一般从1开始,而m使用它所挂载的qdiscmajor号。

root qdischandle一般使用1:0表示,ingress一般使用ffff:0表示。

具体的匹配逻辑可以参考这篇文章:

参考: