tc
是Traffic Control的缩写,即流量控制,是 Linux 内核中用于流量控制的一套功能强大的工具和框架、是内核的一个子系统。提供有命令行工具tc命令,不需要安装,内核自带。
tc提供的能力:控制网络流量,包括带宽限制、流量整形等。
tc的核心概念理解
- qdisc(排队规则):决定数据包如何在网络接口上排队等待传输的规则。
- class(类别):用于将流量分组,以便应用不同的流量控制策略(qdisc)。
- classifier(也称为filter):根据特定的规则将流量分类到不同的组(class)。
- action(动作):要对数据包执行什么动作,例如允许数据包通过(pass)、丢弃数据包(drop)、重新分类(reclassify)等。
概念比较抽象,我们画图来理解:
关系:
- qdisc需要attach到网络接口上,一个网络接口可以有多个qdisc。
- class和filter都需要attach到qdisc上。
- action需与filter关联,即给filter添加action。
流量:
- 流量首先到达网络接口,例如
eth0
。 - 流量通过过滤器(filter/classifier),将流量分发到不同的class。如果这个filter有action,则直接执行action,如丢弃、放行等,不需要将流量发送到 qdisc。
- 根据过滤器的匹配结果,流量被分配到相应的qdisc。
- 经过 qdisc 处理后,数据包将按照配置的规则被发送到网络。
什么是排队规则?
qdisc 是一种排队机制,决定了数据包在发送前如何在队列中等待。不同类型的 qdisc 实现了不同的排队和传输策略,例如 FIFO、优先级队列、令牌桶等。
action概念的理解
action 与 filter 的关联:
- 在某些情况下,过滤器可以直接返回一个动作码(action),而不是一个类别 ID(classid)。这种模式称为“直接动作”(direct action)模式,过滤器的返回值被解释为动作指令而不是类别 ID。
支持的action:
- TC_ACT_OK:允许数据包通过并继续处理。
- TC_ACT_SHOT:丢弃数据包。
- TC_ACT_RECLASSIFY:将数据包重新分类,再次应用分类规则。
- TC_ACT_PIPE:将数据包传递给链中的下一个元素。
实战一下
我们做个使用tc实现丢包的小实验来加深理解。
实验说明:如何使用tc命令实现把来自某个ip发往某个ip的流量做丢包处理?
实验环境:笔者在gcp启动了一个用来临时做实验的机器,IP是xx.yy.zz.kk,然后我想丢包的是这个机器访问www.google.com的流量,用ip138工具获取一个这个域名解析出来的ip,然后拿这个ip实验,例如:31.13.68.169。
实验步骤:
先验证是否能访问这个目的ip。
很好,能访问。
现在开始实现丢包。
一、ifconfig查一下本机的网络接口名,这里是enp1s0。
二、给enp1s0网络接口添加(attach)一个qdisc(流量整形规则)。
tc qdisc add dev enp1s0 root handle 1: htb
tc qdisc add
命令用于添加一个qdisc
,对应的还有tc qdisc del
。dev enp1s0
指定了要添加 qdisc 的网络设备接口,这里网络设备接口是enp1s0
。root
表示将要添加的 qdisc 作为指定网络设备的根 qdisc,根 qdisc 是网络接口上的第一个 qdisc,所有通过该接口的流量都会首先经过它。handle 1:
可以理解为给这个qdisc指定一个id。为这个 qdisc 分配一个句柄(handle),在本例中是1:
,句柄是 qdisc 在网络设备上的唯一标识符。qdisc和filter、class的id通常采用major:minor
的格式,major
和minor
可以是任意的数字,但应该是唯一的。举个例子,假设qdisc的id为99:,class可以是99:10
,filter可以是99:11`。htb
:htb是Hierarchical Token Bucket,一个分层令牌桶 qdisc。
三、创建一个filter并关联drop这个action,将这个filter添加(attach)到我们创建的qdisc。filter关联drop action后,是不需要将流量分派到某个class的,所以我们并没有创建class。
tc filter add dev enp1s0 protocol ip parent 1: prio 1 u32 match ip src xx.yy.zz.kk/32 match ip dst 31.13.68.169/32 action drop
tc filter add
命令用于添加一个filter,对应的还有tc filter del
。dev enp1s0
同tc qdisc add
命令,指定网络设备接口,这里网络设备接口是enp1s0
。protocol ip
指定要匹配的协议为 IP协议。parent 1:
:指定父 qdisc 的句柄,这里根 qdisc 的句柄是1:
。prio 1
:设置过滤器的优先级。u32
:指定使用 u32 匹配规则。match ip src xx.yy.zz.kk/32
:匹配源 IP 地址为xx.yy.zz.kk
的数据包。/32
表示精确匹配该 IP 地址。match ip dst 31.13.68.169/32
:匹配目的 IP 地址为31.13.68.169
的数据包。action drop
对匹配的数据包执行丢包动作。
实验结果:
可以看到,100%丢包了。
清理环境需要删除filter:
tc filter del dev enp1s0 parent 1: protocol ip prio 1 u32
# 如需删除qdisc
# tc qdisc del dev enp1s0 root handle 1:
基于这个实验,那如果我们想实现限速,而不是全部丢包,怎么做呢?
一、还是使用我们前面创建的qdisc,如果删除了需要重新创建回来。
二、创建一个class,并将这个class添加(attach)到我们创建的qdisc。
tc class add dev enp1s0 parent 1: classid 1:1 htb rate 1kbit ceil 1kbit
parent 1:
:指定了 qdisc 的句柄,这里是1:
。classid 1:1
是指定这个class的id。htb
指定了这个 class 使用的 qdisc 类型是htb
。rate 1kbit
:设置了这个 class 的带宽保证(最低速率)为 1 kilobit per second。ceil 1kbit
:设置了这个 class 的带宽峰值(最大速率)为 1 kilobit per second。
我们指定了qdisc是哪个,并且这个qdisc是htb,为何创建class还要指定htb?
- 在
tc
的上下文中,htb
关键字在class
定义中用于明确这个类是 HTB 类型的。尽管父 qdisc 是 HTB 类型,但每个class
都需要明确其类型,因为 HTB qdisc 可以包含多个具有不同参数的子类。 - 在 HTB 类中,每个子类都有自己的
rate
和ceil
参数,这些参数定义了子类的带宽保证和最大带宽限制,在创建class
时需要使用htb
关键字设置这些参数。
三、创建filter,并将匹配的流量分配给这个class。
tc filter add dev enp1s0 parent 1: protocol ip prio 1 u32 match ip src 45.32.123.96/32 match ip dst 31.13.68.169/32 flowid 1:1
flowid 1:1
是将匹配的流量分派到前面创建的class,1:1
是class的id。
清理:
// 删除filter
tc filter del dev enp1s0 parent 1: protocol ip prio 1 u32
// 删除dc
tc class del dev enp1s0 classid 1:1
// 删除qdisc
tc qdisc del dev enp1s0 root handle 1: