Linux tc命令控制流量

本文介绍Linux下TC工具的基础概念及应用,重点讲解qdisc的概念及其类型,包括tbf限速、netem网络模拟等,并通过实验验证不同qdisc的效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.什么是tc

tc全称为traffic control,是iproute2包中控制内核中流量的工具。在内核的网络协议栈中,专门有这样一个处理网络流量的地方(在XDP之后,netfilter之前),tc就是在这个地方读取网络数据包(此时已经是sk_buffer)进行控制、分发、丢弃等操作。需要注意的是,tc既可以处理传出的数据包(egress),也可以处理传入的数据包(ingress),但对传入的数据包处理的功能较少。本文不涉及ingress内容。

2.核心概念:qdisc

我们使用tc的时候,最先会遇到一个叫做qdisc的名词,其实我们经常能看见它,就是在每次我们执行ip命令的时时候

root@ubuntu:~# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:58:48:55 brd ff:ff:ff:ff:ff:ff
    altname enp2s1

比如在这里,我们可以看到每个网络设备都在mtu后面都有一个qdisc,qdisc后面还有一个词,上面的lo的qdisc是noqueue,ens33的qdisc是fq_codel。

qdisc实际是queueing discipline的缩写,我们可以将其看作一个具有一定规则的队列。当tc处理网络包时,会将包入队到qdisc中,这些包会根据指定的规则被内核按照一定顺序取出。tc中已经内置了很多不同的qdisc,有些qdisc可以带参数,比如ens33上面的qdisc参数是这样的。

root@ubuntu:~# tc qdisc show dev ens33
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn 

注意到lo上面的qdisc是noqueue,那我们可不可以把ens33的qdisc删掉呢,尝试:

root@ubuntu:~# tc qdisc del dev ens33 root
Error: Cannot delete qdisc with handle of zero.

先暂时忽略命令里的root。这条删除命令失败,网络上的一篇文章给出的结论是,对于这种非虚拟的网络设备(虽然是虚拟机的网卡,它也不是虚拟设备哈),不可以将其qdisc删除掉使其设置为noqueue,所以如果我们想修改他的qdisc,不能先删除再添加,但可以通过add和replace进行修改,该文章也提到了add和replace效果是一样的。对此我有不同的看法。

首先试试再添加一个其他类型的qdisc上去:

root@ubuntu:~# tc qdisc add dev ens33 root pfifo
root@ubuntu:~# tc qdisc show dev ens33
qdisc pfifo 8005: root refcnt 2 limit 1000p

添加成功,那我们在添加一个其他类型的qdisc上去:

root@ubuntu:~# tc qdisc add dev ens33 root pfifo_fast
Error: Exclusivity flag on, cannot modify.
root@ubuntu:~# tc qdisc show dev ens33
qdisc pfifo 8005: root refcnt 2 limit 1000p

这样会报错,还是使用原先的qdisc。此时我们尝试使用del进行删除

root@ubuntu:~# tc qdisc del dev ens33 root
root@ubuntu:~# tc qdisc show dev ens33
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn

发现它又变回了fq_codel。如果我们这里使用replace再删除,结果是这样的

root@ubuntu:~# tc qdisc replace dev ens33 root pfifo_fast
root@ubuntu:~# tc qdisc show dev ens33
qdisc pfifo_fast 8006: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
root@ubuntu:~# tc qdisc replace dev ens33 root pfifo
root@ubuntu:~# tc qdisc show dev ens33
qdisc pfifo 8007: root refcnt 2 limit 1000p
root@ubuntu:~# tc qdisc del dev ens33 root
root@ubuntu:~# tc qdisc del dev ens33 root
Error: Cannot delete qdisc with handle of zero.
root@ubuntu:~# tc qdisc show dev ens33
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn

所以我们可以推测:fq_codel是非虚拟设备的默认值,此时网络设备并没有设置qdisc,所以执行删除会报错,但我们可以为其设定qdisc(使用add或者replace),此时设置了qdisc,删除便不会失败,删除后qdisc又变成了默认值。注意到lo的值是noqueue,如果我们用ip link命令手动添加设备,它们的qdisc也是noqueue,所以我们也可以推测虚拟设备的默认值是noqueue。

这里的命令中有一个root参数,实际上这个参数表示对出口流量(egress)处理的根节点,从上面的命令可以看出,一个网络设备的root点只能设置一个qdisc。执行tc qdisc show命令,可以看到当前qdisc的参数,有些qdisc不可以指定参数,有些可以,如果需要指定参数,可以在add命令的结尾添加:

tc qdisc add dev <dev> root <qdisc> <qdisc-param>

qdisc有两类,一类qdisc比较简单,向上面展示的一样,只能设置一些参数然后在设备的(egress)root点生效,这种的叫做classless qdisc。另一类比较复杂,他们内部还包括叫做class的组件,还可以进一步将包传递给其他的qdisc,所有的数据包在一个类似树的结构中流动,这种叫做classful qdisc。这篇文章只会介绍相对简单的classless qdisc。

一些classless qdisc
tc内置了以下的classless qdisc

  • choke
  • codel
  • bfifo、qfifo
  • fq
  • fq_codel
  • gred
  • hhf
  • ingress
  • mqprio
  • multiq
  • netem
  • pfifo_fast
  • pie
  • red
  • rr
  • sfb
  • sfq
  • tbf

这里介绍其中的几种qdisc
tbf
tbf全称为token bucket filter,从名字可以看出,tbf具有令牌和桶两个概念。其工作原理如下图所示。
在这里插入图片描述
到来的数据包需要获取的令牌与数据包的大小有关,并且任意大小的数据包都需要消耗令牌,如果某个数据包没有足够的令牌,将会放入队列中,等待令牌补充,后续到来的数据包同样进入队列中等待,直到队列被填满,后续包将被丢弃。可以看到,如果我们限制桶的容量,或者限制令牌的补充速度,就可以限制整体的包处理速度。

tbf可以配置的参数如下

  • limit:队列可以保存的数据包的容量,bytes单位。或者可以通过另一个参数latency来指定类似的等待效果
  • burst:桶的容量,bytes单位
  • rate:令牌添加数量
  • mpu:指定最少需要的令牌数量
  • peakrate:桶的最大消耗速率,tbf通过添加第二个容量较小的桶来实现
  • mtu/minburst:第二个桶的容量

测试一下tbf限速的效果

安装iperf3启动服务,分别测试noqueue和tbf限制时的效果:

#另一终端执行 iperf3 -s 
​
root@ubuntu:~# ip l show lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
​
root@ubuntu:~# iperf3 -c 127.0.0.1
Connecting to host 127.0.0.1, port 5201
[  5] local 127.0.0.1 port 56294 connected to 127.0.0.1 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  4.49 GBytes  38.6 Gbits/sec    0   3.25 MBytes       
[  5]   1.00-2.00   sec  4.47 GBytes  38.4 Gbits/sec    0   3.25 MBytes       
[  5]   2.00-3.00   sec  4.41 GBytes  37.9 Gbits/sec    0   3.25 MBytes       
[  5]   3.00-4.00   sec  4.39 GBytes  37.8 Gbits/sec    0   3.25 MBytes       
[  5]   4.00-5.00   sec  4.63 GBytes  39.8 Gbits/sec    0   3.25 MBytes       
[  5]   5.00-6.00   sec  4.72 GBytes  40.5 Gbits/sec    0   3.25 MBytes       
[  5]   6.00-7.00   sec  4.58 GBytes  39.4 Gbits/sec    0   3.25 MBytes       
[  5]   7.00-8.00   sec  4.59 GBytes  39.4 Gbits/sec    0   3.25 MBytes       
[  5]   8.00-9.00   sec  4.61 GBytes  39.6 Gbits/sec    0   3.25 MBytes       
[  5]   9.00-10.00  sec  4.66 GBytes  40.0 Gbits/sec    0   3.25 MBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  45.6 GBytes  39.1 Gbits/sec    0             sender
[  5]   0.00-10.00  sec  45.6 GBytes  39.1 Gbits/sec                  receiver
​
iperf Done.
​
​
root@ubuntu:~# tc qdisc add dev lo  root tbf  limit 100mb rate 10mbit burst 5mb
root@ubuntu:~# tc qdisc show dev lo
qdisc tbf 800a: root refcnt 2 rate 10Mbit burst 5Mb lat 79.7s 
root@ubuntu:~# iperf3 -c 127.0.0.1
Connecting to host 127.0.0.1, port 5201
[  5] local 127.0.0.1 port 56304 connected to 127.0.0.1 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  8.75 MBytes  73.3 Mbits/sec    1   1.37 MBytes       
[  5]   1.00-2.00   sec  1.25 MBytes  10.5 Mbits/sec    0   1.37 MBytes       
[  5]   2.00-3.00   sec  1.25 MBytes  10.5 Mbits/sec    0   1.37 MBytes       
[  5]   3.00-4.00   sec  1.25 MBytes  10.5 Mbits/sec    0   1.37 MBytes       
[  5]   4.00-5.00   sec  1.25 MBytes  10.5 Mbits/sec    0   1.37 MBytes       
[  5]   5.00-6.00   sec  1.25 MBytes  10.5 Mbits/sec    0   1.37 MBytes       
[  5]   6.00-7.00   sec  1.25 MBytes  10.5 Mbits/sec    0   1.37 MBytes       
[  5]   7.00-8.00   sec  1.25 MBytes  10.5 Mbits/sec    0   1.37 MBytes       
[  5]   8.00-9.00   sec  1.25 MBytes  10.5 Mbits/sec    0   1.37 MBytes       
[  5]   9.00-10.00  sec  1.25 MBytes  10.5 Mbits/sec    0   1.37 MBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  20.0 MBytes  16.8 Mbits/sec    1             sender
[  5]   0.00-10.09  sec  16.9 MBytes  14.0 Mbits/sec                  receiver
​
iperf Done.

可以看到,网络带宽明显的下降了

比较神奇的是,tc的man page里面介绍tbf是classless qdisc,而tc-tbf里面说它是classful qdisc。尝试一下给tbf添加class,结果会报错,所以tbf应为classless qdisc吧,怀疑tc-tbf的man-page把classic写错成classful了。

root@ubuntu:~# tc qdisc add dev lo root handle 1:0 tbf limit 100M rate 100Mbit burst 100M
root@ubuntu:~# tc qdisc show dev lo
qdisc tbf 1: root refcnt 2 rate 100Mbit burst 100Mb lat 0us 
qdisc ingress ffff: parent ffff:fff1 ---------------- 
root@ubuntu:~# tc class add dev lo parent 1: classid 1:1 htb rate 100Mbit
RTNETLINK answers: File exists

netem
netem时network emulator的缩写,可以模拟网络延迟、丢包率等网络特征。还是拿本地环路接口进行测试,手动将其延迟变为100ms(ping 发送和接收icmp延迟翻倍),以及添加丢包率

root@ubuntu:~# tc qdisc del dev lo root
root@ubuntu:~# ping 127.0.0.1 -c 3
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.034 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.052 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.040 ms
​
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2026ms
rtt min/avg/max/mdev = 0.034/0.042/0.052/0.007 ms
root@ubuntu:~# tc qdisc add dev lo root netem delay 100ms
root@ubuntu:~# ping 127.0.0.1 -c 3
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=200 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=200 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=201 ms
​
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 200.426/200.586/200.855/0.191 ms
​
root@ubuntu:~# tc qdisc del dev lo root
​
root@ubuntu:~# iperf3 -c 127.0.0.1 -u -i 10 -b 100M
Connecting to host 127.0.0.1, port 5201
[  5] local 127.0.0.1 port 58834 connected to 127.0.0.1 port 5201
[ ID] Interval           Transfer     Bitrate         Total Datagrams
[  5]   0.00-10.00  sec   119 MBytes   100 Mbits/sec  3815  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Jitter    Lost/Total Datagrams
[  5]   0.00-10.00  sec   119 MBytes   100 Mbits/sec  0.000 ms  0/3815 (0%)  sender
[  5]   0.00-10.00  sec   119 MBytes   100 Mbits/sec  0.002 ms  0/3815 (0%)  receiver
​
iperf Done.
root@ubuntu:~# tc qdisc add dev lo root netem loss 5%
root@ubuntu:~# iperf3 -c 127.0.0.1 -u -i 10 -b 100M
Connecting to host 127.0.0.1, port 5201
iperf3: error - unable to read from stream socket: Resource temporarily unavailable
root@ubuntu:~# tc qdisc change dev lo root netem loss 2%
root@ubuntu:~# iperf3 -c 127.0.0.1 -u -i 10 -b 100M
Connecting to host 127.0.0.1, port 5201
[  5] local 127.0.0.1 port 45366 connected to 127.0.0.1 port 5201
[ ID] Interval           Transfer     Bitrate         Total Datagrams
[  5]   0.00-10.00  sec   119 MBytes   100 Mbits/sec  3815  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Jitter    Lost/Total Datagrams
[  5]   0.00-10.00  sec   119 MBytes   100 Mbits/sec  0.000 ms  0/3815 (0%)  sender
[  5]   0.00-10.00  sec   117 MBytes  97.9 Mbits/sec  0.002 ms  79/3815 (2.1%)  receiver
​
iperf Done.

bfifo、pfifo
这两个应该是最简单的qdisc了,数据包的处理只遵循先入先出规则,bfifo的队列的容量为能够保存数据包的大小,pfifo的队列容量为包的数目

总结
使用tc可以为网络设备设置一类叫做classless qdisc的排队规则,这类规则依照不同的算法,在网络设备的tc处理点中,对向外部发送的数据包进行处理,可以进行限制发送速度、更改发送顺序、丢包等操作。部分classless qdisc还可以使用参数配置,使其以想要的效果运行。但是,classless qdisc无法将数据包进行分类,不能进行更精确的控制。后续将介绍classful qdisc,我们可以用它来实现更多的功能。

以上内容出自:https://zhuanlan.zhihu.com/p/449755341

3.自己的Demo

tc qdisc add dev eno50 root netem delay 8ms 设置eno50 8毫秒延迟,即可查看其他主机ping的速度延迟8ms
在这里插入图片描述
tc qdisc del dev eno50 root netem delay 8ms 将eno50 延迟删除即可查看到ping的速度正常恢复
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值