介绍
报文分组从输入网卡(入口)接收进来,经过路由的查找,以确定是发给本机的,还是需要转发的。如果是发给本机的,就直接向上递交给上层的协议,比如TCP,如果是转发的,则会从输出网卡(出口)发出。网络流量的控制通常发生在输出网卡处,Linux内核中由TC(Traffic Control)实现。TC是利用队列规定建立处理数据包的队列,并定义队列中的数据包被发送的方式,从而实现流量控制。基本原理:
接收包从输入接口(Input Interface)进来后,经过流量限制(Ingress Policing)丢弃不符合规定的数据包,由输入多路分配器(Input De-Multiplexing)进行判断选择。如果接收包的目的地是本主机,那么将该包送给上层处理,否则需要进行转发,将接收包交到转发块(Forwarding Block)处理。转发块同时也接收本主机上层(TCP、UDP等)产生的包。转发块通过查看路由表,决定所处理包的下一跳。然后,对包进行排列以便将它们传送到输出接口(Output Interface)。一般我们只能限制网卡发送的数据包,不能限制网卡接收的数据包,所以我们可以通过改变发送次序来控制传输速率。
流量控制对象
流量控制对象 :QDISC(排队规则), CLASS(类别) , FILTER(过滤器)
流量控制的一个基本概念是队列(Qdisc),每个网卡都与一个队列(Qdisc)相联系。每当内核需要将报文分组从网卡发送出去,都会首先将该报文分组添加到该网卡所配置的队列中,由该队列决定报文分组的发送顺序。因此可以说,所有的流量控制都发生在队列中。有些队列的功能是非常简单的,它们对报文分组实行先来先走的策略。有些队列则功能复杂,会将不同的报文分组进行排队、分类,并根据不同的原则,以不同的顺序发送队列中的报文分组。为实现这样的功能,这些复杂的队列需要使用不同的过滤器(Filter)来把报文分组分成不同的类别(Class)。这里把这些复杂的队列称为可分类(Classful)的队列。通常,要实现功能强大的流量控制,可分类的队列是必不可少的。因此,类别(Class)和过滤器(Filter)也是流量控制的另外两个重要的基本概念。类别(Class)和过滤器(Filter)是队列的内部结构,并且可分类的队列可以包含多个类别,同时,一个类别又可以进一步包含有子队列,或者子类别。所有进入该类别的报文分组可以依据不同的原则放入不同的子队列 或子类别中,以此类推。而过滤器(Filter)是队列用来对数据报文进行分类的工具,它决定一个数据报文将被分配到哪个类别中。
在Linux中,可以配置很多类型的队列,比如CBQ、HTB等,其中CBQ 比较复杂,不容易理解。HTB(Hierarchical Token Bucket)是一个可分类的队列, 与其他复杂的队列类型相比,HTB具有功能强大、配置简单及容易上手等优点。
在TC中,使用"major:minor"这样的句柄来标识队列和类别,其中major和minor都是数字。对于队列来说,minor总是为0,即"major:0"这样的形式,也可以简写为"major: "。比如,队列1:0可以简写为1:。需要注意的是,major在一个网卡的所有队列中必须是惟一的。对于类别来说,其major必须和它的父类别或父队列的major相同,而minor在一个队列内部则必须是惟一的(因为类别肯定是包含在某个队列中的)。举个例子,如果队列2:包含两个类别,则这两个类别的句柄必须是2:x这样的形式,并且它们的x不能相同,比如2:1和2:2。
基本使用步骤
Linux流量控制主要分为建立队列、建立分类和建立过滤器三个方面。
-
- 针对网络物理设备(如以太网卡eth0)绑定一个队列qdisc;
- 在该队列上建立分类class;
- 为每一分类建立一个基于路由的过滤器filter;
- 最后与过滤器相配合,建立特定的路由表。
相关单位
tc命令所有的参数都可以使用浮点数,可能会涉及到以下计数单位 :
- 带宽或流速单位:
kbps 千字节/s mbps 兆字节/s (bps或者一个无单位数字,表示字节数/s)
kbit KBits/s mbit MBits/s
- 数据的数量单位
kb或k 千字节 mb或m 兆字节 (b或者一个无单位数字,字节)
kbit 千bit mbit 兆bit
- 时间的计量单位
s, sec或secs 秒 ms, msec或mescs 毫秒
us, use, usecs或一个无单位数字 微s
举例
创建根队列 → 创建类别class → 为类别创建对应过滤器,对应网卡接收的数据包会一层一层下发到过滤器进行过滤或相关处理
- 为网卡创建根队列或htb队列或cbq队列
- 有关队列的TC命令的一般形式为:tc qdisc [add | change | replace | link] dev DEV [parent qdisk-id |root] [handle qdisc-id] qdisc [qdisc specific parameters]
- sar -n DEV 1 2 命令查看当前机器所使用的以太网卡是哪个
- tc qdisc add dev eth1 root handle 1: prio ; 为网卡eth1建议一个队列,名字为root,句柄为1
- tc qdisc add dev eth1 root handle 1:htb default 11 ; 为网卡eth1添加root根队列,"handle 1:"表示队列的句柄为1: ,"htb"表示要添加的队列为HTB队列,命令最后的"default 11"是htb特有的队列参数,意思是所有未分类的流量都将分配给类别1:11。
- tc qdisc add dev xgbe0 root handle 1:0 cbq bandwidth 100Mbit avpkt 1000 cell 8. ; 为网卡xbge0创建一个cbq队列,限制网卡总带宽为100Mbit/s,avpkt表示平均包大小,单位字节,avpkt 1000表示平均包大小为1000B,cell表示一个数据包被发送出去的平均时间,通常设置为8,必须是2的整数次幂
- 为根队列创建相应的类别,对于句柄编号一般根队列为一位数,子类别为两位数
- tc class [add | change | replace] dev DEV parent qdisc-id [classid class-id] qdisc [qdisc specific parameters]
-
可以利用下面这三个命令为根队列1创建三个类别,分别是1:11、1:12和1:13,它们分别占用40、40和20mbit的带宽。
tc
class
add dev eth1 parent
1
: classid
1
:
11
htb rate 40mbit ceil 40mbit
tc
class
add dev eth1 parent
1
: classid
1
:
12
htb rate 40mbit ceil 40mbit
tc
class
add dev eth1 parent
1
: cllassid
1
:
13
htb rate 20mbit ceil 20mbit
命令中,"parent 1:"表示类别的父亲为根队列1: 。"classid1:11"表示创建一个标识为1:11的类别,"rate 40mbit"表示系统将为该类别确保带宽40mbit,"ceil 40mbit",表示该类别的最高可占用带宽为40mbit。
- 为各个类别设置过滤器
- 有关过滤器的TC 命令的一般形式为: tc filter [add | change | replace] dev DEV [parent qdisc-id | root] protocol protocol prio priority filtertype [filtertype specific parameters] flowid flow-id
-
比如,需要将WWW、E-mail、Telnet三种流量分配到三个类别,即上述1:11、1:12和1:13,因此,需要创建三个过滤器,如下面的三个命令 :
tc filter add dev eth0 parent
1
:
0
protocol ip prio
1
u32 match ip dport
80
0xffff
flowid
1
:
11
tc filter add dev eth0 parent
1
:
0
protocol ip prio
1
u32 match ip dport
25
0xffff
flowid
1
:
12
tc filter add dev eth0 parent
1
:
0
protocol ip prio
1
u32 match ip dport
23
oxffff flowid
1
:
13
这里,"protocol ip"表示该过滤器应该检查报文分组的协议字段。"prio 1"表示它们对报文处理的优先级是相同的。对于不同优先级的过滤器,系统将按照从小到大的优先级顺序来执行过滤器,对于相同的优先级,系统将按照命令的先后顺序执行。这几个过滤器还用到了u32选择器(命令中u32后面的部分)来匹配不同的数据流。以第一个命令为例,判断的是dport字段,如果该字段与0xffff进行与操作的结果是80,则"flowid 1:11"表示将把该数据流分配给类别1:11。更加详细的有关TC的用法可以参考TC的手册页。
- 查看当前网卡上配过的流量控制规则
-
tc [-s | -d ] qdisc show [ dev DEV ] ; tc -s qdisc show dev eth1
- tc [-s | -d ] class show dev DEV
- tc filter show dev DEV
- tc -s -d qdisc ls
-
- 删除已配置的规则
- tc qdisc del dev eth1 root
项目应用举例
-
例1、 物理接口为出接口 egress
tc qdisc delete dev enx30b49e672c31 root
tc qdisc add dev enx30b49e672c31 root handle
1
: htb
default
11
tc
class
add dev enx30b49e672c31 parent
1
: classid
1
:
11
htb rate
32000000
.0kbit
tc
class
add dev enx30b49e672c31 parent
1
: classid
1
:
12
htb rate 1600kbit
tc qdisc add dev enx30b49e672c31 parent
1
:
12
handle 1ab7: netem limit
1000
tc filter add dev enx30b49e672c31 protocol ip parent
1
: prio
2
u32 match ip dst
192.168
.
8.80
/
32
match ip src
0.0
.
0.0
/
0
flowid
1
:
12
- 第1行:删除网卡队列规定
- 第2行:创建网卡句柄为1: 的根队列, 采用HTB分类队列,默认情况使用类别 11 转发数据
- 第3行:添加根队列的类别为11【 classid 1:11, major (队列根) : minor (类别) 】, 指明父队列parent为根队列句柄:1,限速为32Gbit
- 第4行:添加根队列的类别 12【 classid 1:12, major (队列根) : minor (类别) 】, 指明父队列parent为根队列句柄:1,限速为1600kbit
- 第5行:类别2创建子队列,指明子队列的父队列parent为类别2的classid 1:12 ,指定唯一handle 1ab7:,然后用netem配置弱网,如limit(队列深度),丢包,延时,乱序,重复包,包损坏等
- 第6行:为类别2创建过滤规则
-
例2 、物理接口为入接口 ingress,用虚拟接口ifb实现出接口的弱网设置
modprobe ifb
ip link add ifb6711 type ifb
ip link set dev ifb6711 up
tc qdisc add dev enx30b49e672c31 ingress
tc filter add dev enx30b49e672c31 parent ffff: protocol ip u32 match u32
0
0
flowid
1
: action mirred egress redirect dev ifb6711
tc qdisc add dev ifb6711 root handle
1
: htb
default
11
tc
class
add dev ifb6711 parent
1
: classid
1
:
11
htb rate
32000000
.0kbit
tc
class
add dev ifb6711 parent
1
: classid
1
:
12
htb rate
500
.0Kbit ceil
500
.0Kbit burst
6
.25KB cburst
6
.25KB
tc qdisc add dev ifb6711 parent
1
:
12
handle 1ab7: netem loss
30
% delay 100ms
tc filter add dev ifb6711 protocol ip parent
1
: prio
2
u32 match ip dst
0.0
.
0.0
/
0
match ip src
192.168
.
5.101
/
32
flowid
1
:
12