流量控制Traffic Control
简称TC
,表示网络设备接收和发送数据包的排队机制。比如,数据包的接收速率、发送速率、多个数据包的发送顺序等。
Linux实现了流量控制子系统,它包括两部分:
- 内核部分的
traffic control
框架 - 用户态的规则配置工具:
iproute2
软件包中的tc
程序
它们有些类似于内核态的netfilter
框架和用户态的iptables
程序。但相较于netfilter
, 关于tc
的资料非常少,并且也较为古老,彻底理解它的机制还是需要对照源码。
Traffic Control
的作用包括以下几种:
- 调整(
Shaping
): 通过推迟数据包发送来控制发送速率,只用于网络出方向(egress
) - 时序(
Scheduling
):调度不同类型数据包发送顺序,比如在交互流量和批量下载类型数据包之间进行发送顺序的调整。只用于网络出方向(egress
) - 监督(
Policing
): 根据到达速率决策接收还是丢弃数据包,用于网络入方向(ingress
) - 丢弃(
Dropping
): 根据带宽丢弃数据包,可以用于出入两个方向
要实现对数据包接收和发送的这些控制行为,需要使用队列结构来临时保存数据包。在Linux实现中,把这种包括数据结构和算法实现的控制机制抽象为结构队列规程:Queuing discipline
,简称为qdisc
。qdisc
对外暴露两个回调接口enqueue
和dequeue
分别用于数据包入队和数据包出队,而具体的排队算法实现则在qdisc
内部隐藏。不同的qdisc
实现在Linux内核中实现为不同的内核模块,在系统的内核模块目录里可以查看前缀为sch_
的模块:
1 | [root@server ~]# ls -l /usr/lib/modules/3.10.0-862.el7.x86_64/kernel/net/sched/sch_* |
qdisc
的实现可以非常简单,比如只包含单个队列,数据包先进先出,如: pfifo
, 代码位于net/sched/sch_fifo.c
。也可以实现相当复杂的调度逻辑。比如,可以根据数据包的属性进行过滤分类,而针对不同的分类:class
采用不同的算法来进行处理。class
可以理解为qdisc
的载体,它还可以包含子类与qdisc
。用来实现过滤逻辑的组件叫做filter
,也叫做分类器classfier
, 它需要挂载在qdisc
或者class
上。
基于qdisc
, class
和filter
种三元素可以构建出非常复杂的树形qdisc
结构,极大扩展流量控制的能力。
对于树形结构的qdisc
, 当数据包流程最顶层qdisc
时,会层层向下递归进行调用。如,父对象(qdisc/class
)的enqueue
回调接口被调用时,其上所挂载的所有filter
依次被调用,直到一个filter
匹配成功。然后将数据包入队到filter
所指向的class
,具体实现则是调用class
所配置的Qdisc
的enqueue
函数。没有成功匹配filter
的数据包分类到默认的class
中。
如图:
大约在15年的时候,TC
框架实现中又加入了Classifier-Action
机制。上文提到的filter
实际作用就是classifier
,当数据包匹配到特定的filter
之后,可以执行该filter
所挂载的actions
对数据包进行处理。
如图:
filter
和action
也可以实现为独立的内核模块,tc
框架的扩展性非常灵活。
一个网络接口有两个默认的qdisc
锚点。入方向的锚点叫做ingress
, 出方向叫做root
。入方向的ingress
功能比较有限,不能挂载其他的class
,只是做为Classifier-Action
机制的挂载点。
qdisc
和class
的标识符叫做handle
, 它是一个32
位的整数,分为major
和minor
两部分,各占16
位,表达方式为:m:n
, m
或n
省略时,表示0
。
m:0
一般表示qdisc
, 对于class
, minor
一般从1
开始,而m
使用它所挂载的qdisc
的major
号。
root qdisc
的handle
一般使用1:0
表示,ingress
一般使用ffff:0
表示。
具体的匹配逻辑可以参考这篇文章:
参考:
- https://tldp.org/HOWTO/Traffic-Control-HOWTO/index.html
- https://www.almesberger.net/cv/papers/tcio8.pdf
- https://github.com/shemminger/iproute2/blob/main/doc/actions/mirred-usage
- https://access.redhat.com/solutions/3370551
- https://github.com/torvalds/linux/commit/3431205e03977aaf32bce6d4b16fb8244b510056
- https://medium.com/swlh/traffic-mirroring-with-linux-tc-df4d36116119
- https://man7.org/linux/man-pages/man8/tc.8.html
- https://man7.org/linux/man-pages/man8/tc-actions.8.html
- https://man7.org/linux/man-pages/man8/mirred.8.html
- https://people.netfilter.org/pablo/netdev0.1/papers/Linux-Traffic-Control-Classifier-Action-Subsystem-Architecture.pdf
- https://arthurchiao.art/blog/understanding-tc-da-mode-zh/
- http://linux-ip.net/gl/tc-filters/tc-filters.html
- https://blog.csdn.net/dog250/article/details/40483627
- https://zhuanlan.zhihu.com/p/449755341
- https://blog.kelu.org/tech/2021/07/11/linux-tc-tutorial.html
- https://arthurchiao.art/blog/lartc-qdisc-zh/
- https://zhuanlan.zhihu.com/p/57870521
- https://www.softprayog.in/tutorials/network-traffic-control-with-tc-command-in-linux
- https://qmonnet.github.io/whirl-offload/2020/04/11/tc-bpf-direct-action/
- https://blog.csdn.net/wdscq1234/article/details/51926808
- https://medium.com/criteo-engineering/demystification-of-tc-de3dfe4067c2
- https://www.slideshare.net/Netronome/unifying-network-filtering-rules-for-the-linux-kernel-with-ebpf