linux网络流量控制TC(Traffic Control)简介

2023-08-27  本文已影响0人  Feel_狗焕

目录:

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中的位置如下图所示:

image.jpeg

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

1.1、概念:

报文分组从输入网卡接收进来,经过路由的查找, 以确定是发给本机的,还是需要转发的。如果是发给本机的,就直接向上递交给上层的协议,比如TCP,如果是转发的, 则会从输出网卡发出。

网络流量的控制通常发生在输出网卡处,我们可以通过改变发送次序来控制传输速率。一般说来, 由于我们无法控制自己网络之外的设备, 入口处的流量控制相对较难。

流量控制的一个基本概念是队列,每个网卡都与一个队列相联系,每个队列对应一个QDisc(排队规则), 每当内核需要将报文分组从网卡发送出去, 都会首先将该报文分组添加到该网卡所配置的QDisc中, 由该QDisc决定报文分组的发送顺序,可以说,所有的流量控制都发生在队列中。

为实现复杂QoS,队列需要使用不同的过滤器(Filter)来把报文分组分成不同的类别(Class),因此类别(class)和过滤器(Filter)也是流量控制的另外两个重要的基本概念。

1)、TC相关概念解释:
2)、流量控制的方式术语:
image.jpeg

上图中的框代表 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队列分类:

不可分类规则队列有:

可分类队列规则有:

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中必须是唯一的。一个典型的层级结构如下:

image.jpeg

内核只跟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分类器等。

分类器一般接收以下几个公共的参数:

1)、u32分类器使用:

u32过滤器最简单的格式是设置一组选择器对包进行匹配,匹配的包分到特定的子类中,或者执行一个action。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的包。

特殊选择器可以改写成对应的通用选择器,通用选择器可以匹配 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流量控制主要分为建立队列、建立分类和建立过滤器三个方面。基本实现步骤为:

  1. 针对网络物理设备(如以太网卡eth0)绑定一个队列QDisc;
  2. 在该队列上建立分类class;
  3. 为每一分类建立一个基于路由的过滤器filter;
  4. 最后与过滤器相配合,建立特定的路由表;

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子类中,然后根据优先级依次发送。画出该例子的分层结构图,大致如下:

image.jpeg

2.2、tc使用示例:

tc可以使用以下命令对QDisc、类和过滤器进行操作:

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可以配置的参数如下:

1)、配置tc规则前192.188.88.128机器上的网络流量如下:
image.jpeg

配置tc规则前192.188.88.129机器上的网络流量如下:

image.jpeg
2)、配置tc规则后192.188.88.128机器上的网络流量如下:
image.jpeg

配置tc规则后192.188.88.129机器上的网络流量如下:

image.jpeg

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


上一篇下一篇

猜你喜欢

热点阅读