Linux TC 流量控制介绍
前段时间在做一些测试的时候接触到了Linux tc,因为需要对数据包添加延迟,用到了tc中的netem。添加简单的延迟非常简单,像这样一条命令就搞定了:$ tc qdisc add dev eth0 root netem delay 1s,你甚至不需要完全理解命令中参数的含义。但是当你想做一些更加特殊的限制的时候,(比如只对某个特定的ip端口添加延迟、或者只对入站的流量添加延迟),事情就变得有些棘手了,简单的百度貌似已经满足不了要求了。你不得不了解TC中的一些基本概念,以及tc[2]命令中相关参数的含义。
本文正是带你了解这些TC中的基本概念,并通过一个实际例子,将这些概念与tc命令联系起来。
本博客已迁移至CatBro's Blog,那是我自己搭建的个人博客,欢迎关注。本文链接
示例命令
考虑到这是一个科普向的介绍,这里只举了一个最简单的例子,但是基本上包含了重要的概念。本文的期望是,让读者在阅读后可以完全理解下面的例子,并且知道如何根据自身的需求编写自己的命令。
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
TC基本概念
QDISCS
全称是queueing discipline,我们姑且称其为排队规则吧。它是协议栈和网络接口之间的一个缓冲层。你可以在qdisc上对数据包做一些你想做的操作,比如分类、整形、调度等。
qdisc分为无类(classless)qdisc和有类(classful)qdisc。无类qdisc不再内部细分类,有类qdisc可进一步包含多个分类,每个class上可以进一步包含子qdisc,子qdisc也可以是有类qdisc,这样就形成了树状的分层结构。
CLASSES
有类qdisc可以有多个子类(class),有些qdisc预定义了子类(如prio),有些则需要用户添加类。一个类上又可以附加其他类。最末端没有子类的类称为叶子类,它上面附加了一个qdisc。当创建一个class的时候,默认会附加一个fifo qdisc,它只是一个简单的队列,不对数据包进行任何的操作。当在这个类上增加子类的时候,这个默认的qdisc被移除。你可以将这个默认的fifo qdisc替换成其他任意你想用的qdisc。
FILTERS
过滤器,用于有类qdisc中,决定将包入队到哪个类中。每当一个包到达有子类的类时,就需要进行分类。其中一种分类的方法就是使用过滤器(另外两个是ToS和skb->priority)。所有附加到类上的过滤器会被依次调用,直到其中一个返回裁决。一个filter包含了一些条件,当一个包到达该节点时,会根据包的特征判断是否匹配。
以上3个是TC中最基本的3个概念,任何复杂的流量控制都是通过这个三元组递归实现的。
层级结构
每个接口有一个egress 'root qdisc',默认是pfifo_fast。每个qdisc和class都分配一个句柄handle,句柄用于在后续的配置语句中进行引用。除了egress qdisc,一个接口也可以有一个ingress qdisc,负责管制入站的流量。但是ingress qdisc相比classful qdisc其可能性是非常有限。(所以才有所谓的控发不控收,对入站流量进行控制通常需要借助ifb[6]或者imq)。
这些qdisc的handles有两个部分组成,一个major数和一个minor数:<major>:<minor>。习惯上将root qdisc命名为1:,等价于1:0。一个qdisc的minor数总是0。
子类需要跟它们的parent有相同的major数。major数在一个egress或ingress内必须是唯一的,minor数在一个qdisc和它的class中必须是唯一的。
一个典型的层级结构如下:
1: root qdisc
|
1:1 child class
/ | \
/ | \
/ | \
/ | \
1:10 1:11 1:12 child classes
| | |
| 11: | leaf class
| |
10: 12: qdisc
/ \ / \
10:1 10:2 12:1 12:2 leaf classes
内核只跟root qdisc进行通信,每当包需要入队或者出队的时候,都需要从root节点开始,最终到达叶子节点,从而决定入队到哪里,或者从哪里出队。
比如当一个包入队时,它可能会经过如下路径:
1: -> 1:1 -> 1:12 -> 12: -> 12:2
当然也可能直接走如下路径:
1: -> 12:2
这种情况,就是root qdisc上的过滤器决定把包直接送到12:2。
{% label warning@注意:%}入队和出队时虽然节点的拓扑图是一样的,但是每个节点表示的含义却有所不同[4]。入队时是根据过滤器和包的特征决定走哪条路径,而出队时则取决于qdisc本身的调度算法,比如FIFO、优先级队列、SFQ的顺序调度等。
过滤器
前面已经提到了过滤器用于将包分类到子类,那么具体是如何对包进行分类的呢?tc支持很多类型的分类器,它们根据数据包相关的不同信息来作出决策。其中最常用的就是u32分类器,它根据数据包中的字段做出决策(例如源IP地址等)。还有比如fw分类器,根据防火墙如何标记数据包来做出决策,你可以使用iptables标记目标数据包,然后通过fw分类器进行过滤。另外还有诸如route分类器、cgroup分类器、bpf分类器等,篇幅原因不再赘述。下面仅介绍最常见的u32分类器。
公共参数
分类器一般接收以下几个公共的参数:
protocol
分类器接受的协议,通常你只接受IP流量。必须。
parent
分类器附加到哪个handle上。这个handle必须是一个已经存在的类。必须。
prio|perf
分类器的优先级。数字越小的越先进行匹配尝试。
handle
这个handle对于不同的过滤器表示不同的含义。
u32分类器[3]
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子网的包。
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的包。
示例解析
好了,现在我们可以回过头来看最初的那个示例了,看看这些命令到底是什么意思。
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[5]是一个用于提供网络仿真功能的无类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子类中,然后根据优先级依次发送。
画出该例子的分层结构图,大致如下:
1: root qdisc (prio)
/ | \ \
/ | \ \
/ | \ \
1:1 1:2 1:3 1:4 classes
| | | |
40: qdiscs
pfifo pfifo pfifo netem
band 0 1 2 3
后记
本文只介绍了tc的基本概念和简单用法。prio qdisc只对包做了一个分类,并没有进行整形。实际上,你也可以使用更复杂的带整形的qdisc,比如CBQ、HTB等,也可以增加更多的层级。你还可以在叶子节点上添加SFQ qdisc以实现会话级的带宽公平性。相信理解了TC的这些基本概念,再根据自身需求使用其他qdisc也不是什么难事了。
参考资料
Linux TC 流量控制介绍的更多相关文章
- linux tc流量控制
tc流量控制 项目背景 vintage3.0接口lookupforupdage增加一个策略,当带宽流量tx或rx超过40%,75%随机返回304:超过60%,此接口均返回304 为了对测试机器进行流量 ...
- Linux TC流量控制HOWTO中文版
<本文摘自Linux的高级路由和流量控制HOWTO中文版 第9章节>网人郭工进行再次编译: 利用队列,我们可以控制数据发送的方式.记住我们只能对发送数据进行控制(或称为整形).其实,我们无 ...
- Linux内核策略介绍
Linux内核策略介绍学习笔记 主要内容 硬件 策略 CPU 进程调度.系统调用.中断 内存 内存管理 外存 文件IO 网络 协议栈 其他 时间管理 进程调度 内核的运行时间 系统启动.中断发 ...
- Linux实战教学笔记07:Linux系统目录结构介绍
第七节 Linux系统目录结构介绍 标签(空格分隔):Linux实战教学笔记 第1章 前言 windows目录结构 C:\windows D:\Program Files E:\你懂的\精品 F:\你 ...
- Linux的简单介绍和常用命令的介绍
Linux的简单介绍和常用命令的介绍 本说明以Ubuntu系统为例 Ubuntu系统的安装自行百度,或者参考http://www.cnblogs.com/CoderJYF/p/6091068.html ...
- Linux性能工具介绍
l Linux性能工具介绍 p CPU高 p 磁盘I/O p 网络 p 内存 p 应用程序跟踪 l 操作系统与应用程序的关系比喻为“唇亡齿寒”一点不为过 l 应用程序的性能问题/功能问 ...
- Linux core 文件介绍
Linux core 文件介绍 http://www.cnblogs.com/dongzhiquan/archive/2012/01/20/2328355.html 1. core文件的简单介绍在一个 ...
- Linux 启动参数介绍
Linux 启动参数介绍 取自2.6.18 kernel Documentation/i386/boot.txt 文件中介绍 vga= 这里的不是一个整数(在C语言表示法中,应是十进制,八进制或者十六 ...
- Linux系统启动过程介绍
Linux系统启动过程介绍 学习操作系统有必要了解一下系统的启动过程,这样在面对各种系统故障的时候能快速定位解决问题,下面以Centos来分析linux系统的启动过程. 1.BIOS自检:当开机的时候 ...
随机推荐
- 使用grpcui测试gRPC服务
grpcui类似Swagger UI,可以用来测试gRPC服务,使用起来特别简单. 其原理是通过自动发现gRPC服务协议(当然前提是gRPC服务暴露了Protobuf协议),然后启动一个带界面的Web ...
- 如何在 Xamarin 中快速集成 Android 版认证服务 - 邮箱地址篇
Xamarin 作为微软提供的移动服务多系统开发平台,成为很多开发者首选的应用开发平台.AppGallery Connect(以下简称 AGC)也在逐步的支持 Xamarin 的 SDK.认证服务也是 ...
- 《PHP程序员面试笔试宝典》——如果面试问题曾经遇见过,是否要告知面试官?
如何巧妙地回答面试官的问题? 本文摘自<PHP程序员面试笔试宝典> 面试中,大多数题目都不是凭空想象出来的,而是有章可循,只要求职者肯花时间,耐得住寂寞,复习得当,基本上在面试前都会见过相 ...
- opencv笔记--Active contours
Active Contours 也称作 Snake,通过定义封闭区域曲线的能量函数,并使其最小化得到最终曲线. Active Contours 被用作物体边界精确定位上,opencv 给出了一个实现, ...
- 关于IDA无法从symbol server下载pdb的问题
在ida目录下,symsrv.dll同目录下创建一个symsrv.yes文件. symsrv.yes将可下载: symsrv.no将失败: 没有相关文件将会弹出授权询问,选择yes和no将创建对应文件 ...
- Spring Boot数据访问之多数据源配置及数据源动态切换
如果一个数据库数据量过大,考虑到分库分表和读写分离需要动态的切换到相应的数据库进行相关操作,这样就会有多个数据源.对于一个数据源的配置在Spring Boot数据访问之数据源自动配置 - 池塘里洗澡的 ...
- Springboot整合kaptcha验证码
01.通过配置类来配置kaptcha 01-01.添加kaptcha的依赖: <!-- kaptcha验证码 --> <dependency> <groupId>c ...
- nginx加大缓存
http { server { listen 0.0.0.0:81; server_name localhost; -- proxy_buffer_size 128k; proxy_buffers 3 ...
- restFul接口设计规范[仅供参考]
域名 应该尽量将API部署在专用域名之下. https://api.example.com 如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下. https://www.example.or ...
- 传输层隧道技术之lcx内网端口转发
传输层技术包括TCP隧道.UDP隧道和常规端口转发等.在渗透测试中,如果内网防火墙阻止了指定端口的访问,在获得目标机器的权限后,可以使用IPTABLES打开指定端口.如果内网中存在一系列防御系统,TC ...