流量控制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