使用tc模拟网络延迟和丢包
一:综述:
linux系统中的流量控制器(TC)主要是在输出端口处建立一个队列进行流量控制。
TC是一个可以根据数据包的任何一个部分的特征对其进行分类的工具,并且可以为各类数据提供不同带宽,从而控制他们的传输速度。TC是iproute2的一部分,集成在2.2.及以上版本的内核中,还可以与linux内核里的各种架构(如Netfilter netem)协同工作。
二:TC的组件
TC主要由队列规定(qdisc),类(class)和过滤器(filter)这3个组件组成,绘图中一般用圆形表示队列规定,用矩形表示类:
1:qdisc:TC的核心组件,也被称为队列,是管理网卡输入,输出数据的一个算法,用于确定数据包的发送方式。
队列规定可分为两类:
(1) 不分类的qdisc:内部不包含可配置的子类,对进入队列的数据包不进行区分对待,而只是对数据包进行重新编排,延迟发送或者丢弃,主要有:pfifo-fast.TBF.SFQ等
(2) 分类队列规定:内部可包含一个或多个子类,使用过滤器对数据包进行分类,然后交给相应的子类处理,分类队列规定有CBQ,HTB等
2:类:
就是数据的类别,各种数据通过过滤器进行分类,最后被放入类的队列规定里面进行排队。
如果一个类没有子类,那么这个类被称为叶子类,否则就被成为内部类。1:1和1:12是内部类,其他均为叶子类,叶子类有一个负责为这个类发送数据的队列规定,而且这个qdisc可以是分类的,如1:10有一个分类的队列规定。TC中通常把类的队列规定称为叶子qdisc(只有叶子类才有队列规定)
3:过滤器
就是一些规则,根据这些规则对数据包进行分类,过滤器可以属于队列规定,也可以属于内部类,若需要在叶子类上再实现分类,那就必须将过滤器与叶子类的分类队列规定关联起来,而不能与叶子类相关联。
最常用的是U32过滤器,由一个过滤器和一个动作组成,选择器用来对数据包进行匹配,一旦匹配成功就执行该动作。
三:TC的结构
都是以一个根qdisc开始的,若根qdisc是不分类的队列规定,那它就没有子类,因此不可能包含其他的子对象,也不会有过滤器与之关联,发送数据时,数据包进入这个队列里面排队,然后根据该队列规定的处理方式将数据包发送出去。
分类的qdisc内部包含一个或多个类,而每个类可以包含一个队列规定或者包含若干个子类,这些子类友可以包含分类或者不分类的队列规定,如此递归,形成了一个树。
句柄号:qdisc和类都使用一个句柄进行标识,且在一棵树中必须是唯一的,每个句柄由主号码和次号码组成qdisc的次号码必须为0(0通常可以省略不写)
根qdisc的句柄为1:,也就是1:0。类的句柄的主号码与它的父辈相同(父类或者父qdisc),如类1:1的主号码与包含他的队列规定1:的主号码相同,1:10和1:11与他们的父类1:1的主号码相同,也为1。
新建一个类时,默认带有一个pfifo_fast类型的不分类队列规定,当添加一个子类时,这个类型的qdisc就会被删除,所以,非叶子类是没有队列规定的,数据包最后只能到叶子类的队列规定里面排队。
若一个类有子类,那么允许这些子类竞争父类的带宽,但是,以队列规定为父辈的类之间是不允许相互竞争带宽的。
四:TC的工作原理:
对互联网而言,一切都是数据包,操控网络实际上是在操控数据包,操控它如何产生,路由,传输,分片等等。TC在数据包离开系统的时候进行控制,在IP层与网卡之间做手脚,实际上,负责将数据包传递到物理层的正是TC模块,这意味着在系统内核中,TC作为数据包的调度者是一直运作的,甚至在你不想用他的时候,一般情况下,TC维持 一个先进先出的数据队列。
数据包入队的时候首先调用根队列规定的过滤器,根据过滤器定义的规则将数据包交给某个类,如果该类不是叶子类,将会调用该类定义的过滤器进一步分类,若该类没有定义过滤器,就会交给包含他的队列规定的默认类来处理,若接收到数据包的类是叶子类,数据包将进入到叶子类的队列规定里面排队,需要注意的是:过滤器只能将数据包交给某个类,类再将数据包放入自己的队列规定进行排队,而不能直接交给某个队列规定。
接受包从数据接口进来后,经过流量限制,丢弃不合规定的数据包,然后输入多路分配器判断:若接受包的目的地是本主机,那么将该包送给上册处理,否则需转发,将接受包交到转发块处理。转发块同时也接收本主机上层产生的包。转发块通过查看路由表,决定所处理包的下一跳,然后对包排序以便将他们传送到输出接口。
Linux的TC主要是在输出接口排列时进行处理和实现的。
五:TC命令
1:add命令:在一个节点里加入一个qdisc,类或者过滤器。添加时,需要传递一个祖先作为参数,传递参数时既可以使用ID也可以直接传递设备的根,若建一个qdisc或者filter,可以使用句柄来命名,若建一个类,使用类识别符来命名。
2:remove:删除由某个句柄指定的qdisc,根qdisc也可以被删除,被删除的qdisc上的所有子类以及附属于各个类的过滤器都会被自动删除。
3:change:以替代方式修改某些项目,句柄和祖先不能修改,change和add语法相同。
4:replace:对一个现有节点进行近于原子操作的删除/添加,如果节点不存在,这个命令就会建立节点。
5:link:只适用于qdisc,替代一个现有的节点
tc qdisc [add|change|replace|link] dev DEV [parent qdisc-id |root] [handle qdisc-id ] qdisc [qdisc specific parameters]
tc class [add|change|replace] dev DEV parent qdisc-id [classid class-id] qdisc [qdisc specific parameters]
tc filter [add|change|replace] dev DEV [parent qdisc-id|root] protocol Protocol prio Priority filtertype [filtertype specific parameters] flowid flow-id
tc [-s|-d] qdisc show [dev DEV]
tc [-s|-d] class show dev DEV
tc filter show dev DEV
六:TBF队列:(令牌桶队列)
tc qdisc add tbf limit BYTES burst BYTES rate KBPS [mtu BYTES] [peakrate KBPS] [latency TIME] [overhead BYTES] [linklayer TYPE]
rate是第一个令牌桶的填充速率
peakrate是第二个令牌桶的填充速率
peakrate>rate
burst是第一个令牌桶的大小
mtu是第二个令牌桶的大小
burst>mtu
若令牌桶中令牌不够,数据包就需要等待一定时间,这个时间由latency参数控制,如果等待时间超过latency,那么这个包就会被丢弃
limit参数是设置最多允许多少数据可以在队列中等待
latency=max((limit-burst)/rate,(limit-mtu)/peakrate);
burst应该大于mtu和rate
overhead表示ADSL网络对数据包的封装开销
linklayer指定了链路的类型,可以是以太网或者ATM或ADSL
ATM和ADSL报头开销均为5个字节。
netem简介
netem是linux内核提供的Network emulation服务,可以用来模拟广域网下的延迟、丢包、重复、损坏和乱序等问题。
2.6版本后的linux发行版都已经在内核中启用了netem,netem内核组件在以下情况下启用:
Networking -->
Networking Options -->
QoS and/or fair queuing -->
Network emulator
重要:netem 是直接添加到网卡上的,也就是说所有从网卡发送出去的包都会收到配置参数的影响!
假如需要回退到正常情况,请谨记下面这个命令:
# 删除 eth0网卡之前添加的netem配置
tc qdisc del dev eth0 root
netem延迟设置
DELAY := delay TIME [ JITTER [ CORRELATION ]]]
[ distribution { uniform | normal | pareto | paretonormal } ]
- TIME:延迟的时间
- JITTER:抖动,增加一个随机时间长度,让延迟时间出现在某个范围
- CORRELATION:相关,下一个报文延迟时间和上一个报文的相关系数
- distribution:分布,延迟的分布模式,可以选择的值有 uniform、normal、pareto 和 paretonormal
# eth0 网卡延迟增加100ms
tc qdisc add dev eth0 root netem delay 100ms
# 报文延迟的时间在 100ms ± 20ms 之间(90ms - 110ms)
tc qdisc add dev eth0 root netem delay 100ms 20ms
# 因为网络状况是平滑变化的,短时间里相邻报文的延迟应该是近似的而不是完全随机的。这个值是个百分比,如果为 100%,就退化到固定延迟的情况;如果是 0% 则退化到随机延迟的情况
tc qdisc change dev eth0 root netem delay 100ms 20ms 50%
# distribution 参数来限制它的延迟分布模型。比如让报文延迟时间满足正态分布
tc qdisc change dev eth0 root netem delay 100ms 20ms distribution normal
netem模拟丢包设置
# 发送的报文有 50% 的丢包率
tc qdisc change dev eth0 root netem loss 50%
# 发送的报文有 0.3% ~ 25% 的丢包率
tc qdisc change dev eth0 root netem loss 0.3% 25%
丢包也支持 state(4-state Markov 模型) 和 gemodel(Gilbert-Elliot 丢包模型) 两种模型的丢包配置。不过相对比较复杂,这里我们就不再详细描述。
netem模拟报文重复\损坏设置
# 随机产生 50% 重复的包
# tc qdisc change dev eth0 root netem loss 50% # 原错误命令
tc qdisc change dev eth0 root netem duplicate 50%
# 随机产生 2% 损坏的报文(在报文的随机位置造成一个比特的错误)
tc qdisc change dev eth0 root netem corrupt 2%
netem模拟包乱序
网络传输并不能保证顺序,传输层 TCP 会对报文进行重组保证顺序,所以报文乱序对应用的影响比上面的几种问题要小。
# 固定的每隔一定数量的报文就乱序一次
tc qdisc change dev eth0 root netem reorder 50% gap 3 delay 100ms
# 使用概率来选择乱序的报文
tc qdisc change dev eth0 root netem reorder 50% 15% delay 300ms
其他工具
- wondershaper
- comcast
- Netfilter
参考:
转载自 linux下使用tc(Traffic Control)流量控制命令模拟网络延迟和丢包
TC流量控制的四种方式:SHAPING(对发出的流量进行带宽的限定)、SCHEDULING(对发出的流量按照优先级分配带宽)、POLICING(对接收的流量进行带宽限定)、DROPPING(对发出和接收的流量超过某个带宽时丢弃该数据包)。
还介绍了流量的处理的三种对象控制:qdisc(内核按照数据包的排队规则,将数据包放入队列,交给网卡驱动模块,系统默认规则为pfifo_fast )、class(类别)和filter(过滤器)。
本篇主要针对qdisc,进行模拟网络异常。
0)查看网卡配置
# tc qdisc show dev eth0
该命令查看并显示 eth0网卡的相关传输配置
1)模拟网络延迟传输
# tc qdisc add dev eth0 root netem delay 100ms
该命令将 eth0 网卡的传输设置为延迟 100 毫秒发送。
2)模拟网络延迟波动
# tc qdisc add dev eth0 root netem delay 100ms 10ms
该命令将 eth0 网卡的传输设置为延迟 100ms ± 10ms (90 ~ 110 ms 之间的任意值)发送。
3)模拟网络延迟波动随机性
# tc qdisc add dev eth0 root netem delay 100ms 10ms 30%
该命令将 eth0 网卡的传输设置为 100ms ,同时,大约有 30% 的包会延迟 ± 10ms 发送。
4)模拟网络丢包:
# tc qdisc add dev eth0 root netem loss 1%
该命令将 eth0 网卡的传输设置为随机丢掉 1% 的数据包
5)模拟网络丢包成功率
# tc qdisc add dev eth0 root netem loss 1% 30%
该命令将 eth0 网卡的传输设置为随机丢掉 1% 的数据包,成功率为 30%
6)模拟包重复
# tc qdisc add dev eth0 root netem duplicate 1%
该命令将 eth0 网卡的传输设置为随机产生 1% 的重复数据包
7)模拟包损坏
# tc qdisc add dev eth0 root netem corrupt 0.2%
该命令将 eth0 网卡的传输设置为随机产生 0.2% 的损坏的数据包 (内核版本需在 2.6.16 以上)。
8)模拟包乱序
# tc qdisc change dev eth0 root netem delay 10ms reorder 25% 50%
该命令将 eth0 网卡的传输设置为:有 25% 的数据包(50%相关)会被立即发送,其他的延迟10 秒。
新版本中如下命令也会在一定程度上打乱发包的次序 # tc qdisc add dev eth0 root netem delay 100ms 10ms
9)删除相关配置
# tc qdisc del dev eth0 root netem
10)查看丢包率
# tc -s qdisc show dev eth0
################################ tc qdisc del dev eth0.2 root tc qdisc del dev eth0.2 ingress tc qdisc del dev ifb0 root tc qdisc del dev ifb1 root tc qdisc del dev eth0.2 netem tc qdisc del dev ifb0 netem tc qdisc del dev ifb1 netem ip link set dev ifb0 down ip link set dev ifb1 down tc qdisc del dev ifb0 root tc qdisc del dev ifb1 root tc qdisc show ################################ rmmod ifb modprobe ifb numifbs=2 ip link set dev ifb0 txqueuelen 32 ip link set dev ifb1 txqueuelen 32 ip link set dev ifb0 up ip link set dev ifb1 up tc qdisc show ################################ # download limit tc qdisc del dev ifb0 root tc qdisc add dev ifb0 root handle 1: htb default 10 tc class add dev ifb0 parent 1: classid 1:1 htb rate 10mbit tc class add dev ifb0 parent 1:1 classid 1:10 htb rate 10mbit tc qdisc add dev ifb0 parent 1:10 handle 10: netem limit 5000 delay 586ms 113ms 60% loss 1% 10% duplicate 3% corrupt 1% reorder 2% 10% tc qdisc del dev eth0.2 ingress tc qdisc add dev eth0.2 handle ffff: ingress tc filter add dev eth0.2 parent ffff: protocol ip u32 match u32 0 0 action connmark action mirred egress redirect dev ifb0 tc qdisc show ################################ # upload limit tc qdisc del dev ifb1 root tc qdisc add dev ifb1 root handle 1: htb default 10 tc class add dev ifb1 parent 1: classid 1:1 htb rate 2mbit tc class add dev ifb1 parent 1:1 classid 1:10 htb rate 2mbit tc qdisc add dev ifb1 parent 1:10 handle 10: netem limit 1000 delay 875ms 269ms 80% loss 5% 60% duplicate 9% corrupt 5% reorder 6% 30% tc qdisc del dev eth0.2 root tc qdisc add dev eth0.2 root handle 1: htb tc filter add dev eth0.2 parent 1: protocol ip u32 match u32 0 0 action connmark action mirred egress redirect dev ifb1 tc qdisc show ################################
see https://unix.stackexchange.com/questions/288959/how-is-the-ifb-device-positioned-in-the-packet-flow-of-the-linux-kernel
I would like to know the exact position of the following device in the packet flow for ingress traffic shaping:
- IFB: Intermediate Functional Block
I would like to better understand how packets are flowing to this device and exactly when this happens to understand what methods for filtering / classification can be used of the following:
tc filter ... u32 ...
iptables ... -j MARK --set-mark ...
iptables ... -j CLASSIFY --set-class ...
It seems hard to find documentation on this topic, any help where to find official documentation would be greatly appreciated as well.
Documentation as far as I know:
tc
: tldp.org HOWTO, lartc.org HOWTOifb
: linuxfoundation.org, tc-mirred manpage, wiki.gentoo.orgnetfilter
packet flow: kernel_flow, docum.org kptd
From the known documentation I interpret the following:
Basic traffic control
figure 1
+-------+ +------+
|ingress| +---------+ |egress|
|qdisc +--->netfilter+--->qdisc |
|eth0 | +---------+ |eth0 |
+-------+ +------+
IFB?
tc filter add dev eth0 parent ffff: protocol all u32 match u32 0 0 action mirred egress redirect dev ifb0
will result in?
figure 2
+-------+ +-------+ +------+ +------+
|ingress| |ingress| |egress| +---------+ |egress|
|qdisc +--->qdisc +--->qdisc +--->netfilter+--->qdisc |
|eth0 | |ifb0 | |ifb0 | +---------+ |eth0 |
+-------+ +-------+ +------+ +------+
I think I finally understood how redirecting ingress
to IFB
is working:
+-------+ +------+ +------+
|ingress| |egress| +---------+ |egress|
|qdisc +--->qdisc +--->netfilter+--->qdisc |
|eth1 | |ifb1 | +---------+ |eth1 |
+-------+ +------+ +------+
My initial assumption in figure 2
, that the ifb
device is inserted between ingress eth1
and netfilter
and that packets first enter the ingress ifb1
and then exit through egress ifb1
was wrong.
In fact redirecting traffic from an interface's ingress
or egress
to the ifb's egress
is done directly by redirecting ("stealing") the packet and directly placing it in the egress
of the ifb device.
Mirroring/redirecting traffic to the ifb's ingress
is currently not supported as also stated in the documentation, at least on my version:
root@deb8:~# tc -V
tc utility, iproute2-ss140804
root@deb8:~# dpkg -l | grep iproute
ii iproute2 3.16.0-2
root@deb8:~# uname -a
Linux deb8 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt25-1 x86_64 GNU/Linux
Documentation
I was able to get this information thanks to the following documentation:
- linux-ip.net Intermediate Functional Block
- dev.laptop.org ifb-README
- people.netfilter.org Linux Traffic Control Classifier-Action Subsystem Architecture Paper
Debugging
And some debugging using iptables -j LOG
and tc filter action simple
, which I used to print out messages to syslog
when an icmp
packet is flowing through the netdevs.
The result is as follows:
Jun 14 13:02:12 deb8 kernel: [ 4273.341087] simple: tc[eth1]ingress_1
Jun 14 13:02:12 deb8 kernel: [ 4273.341114] simple: tc[ifb1]egress_1
Jun 14 13:02:12 deb8 kernel: [ 4273.341229] ipt[PREROUTING]raw IN=eth1 OUT= MAC=08:00:27:ee:8f:15:08:00:27:89:16:5b:08:00 SRC=10.1.1.3 DST=10.1.1.2 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=53979 DF PROTO=ICMP TYPE=8 CODE=0 ID=1382 SEQ=1
Jun 14 13:02:12 deb8 kernel: [ 4273.341238] ipt[PREROUTING]mangle IN=eth1 OUT= MAC=08:00:27:ee:8f:15:08:00:27:89:16:5b:08:00 SRC=10.1.1.3 DST=10.1.1.2 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=53979 DF PROTO=ICMP TYPE=8 CODE=0 ID=1382 SEQ=1
Jun 14 13:02:12 deb8 kernel: [ 4273.341242] ipt[PREROUTING]nat IN=eth1 OUT= MAC=08:00:27:ee:8f:15:08:00:27:89:16:5b:08:00 SRC=10.1.1.3 DST=10.1.1.2 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=53979 DF PROTO=ICMP TYPE=8 CODE=0 ID=1382 SEQ=1
Jun 14 13:02:12 deb8 kernel: [ 4273.341249] ipt[INPUT]mangle IN=eth1 OUT= MAC=08:00:27:ee:8f:15:08:00:27:89:16:5b:08:00 SRC=10.1.1.3 DST=10.1.1.2 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=53979 DF PROTO=ICMP TYPE=8 CODE=0 ID=1382 SEQ=1
Jun 14 13:02:12 deb8 kernel: [ 4273.341252] ipt[INPUT]filter IN=eth1 OUT= MAC=08:00:27:ee:8f:15:08:00:27:89:16:5b:08:00 SRC=10.1.1.3 DST=10.1.1.2 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=53979 DF PROTO=ICMP TYPE=8 CODE=0 ID=1382 SEQ=1
Jun 14 13:02:12 deb8 kernel: [ 4273.341255] ipt[INPUT]nat IN=eth1 OUT= MAC=08:00:27:ee:8f:15:08:00:27:89:16:5b:08:00 SRC=10.1.1.3 DST=10.1.1.2 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=53979 DF PROTO=ICMP TYPE=8 CODE=0 ID=1382 SEQ=1
Jun 14 13:02:12 deb8 kernel: [ 4273.341267] ipt[OUTPUT]raw IN= OUT=eth1 SRC=10.1.1.2 DST=10.1.1.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=37735 PROTO=ICMP TYPE=0 CODE=0 ID=1382 SEQ=1
Jun 14 13:02:12 deb8 kernel: [ 4273.341270] ipt[OUTPUT]mangle IN= OUT=eth1 SRC=10.1.1.2 DST=10.1.1.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=37735 PROTO=ICMP TYPE=0 CODE=0 ID=1382 SEQ=1
Jun 14 13:02:12 deb8 kernel: [ 4273.341272] ipt[OUTPUT]filter IN= OUT=eth1 SRC=10.1.1.2 DST=10.1.1.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=37735 PROTO=ICMP TYPE=0 CODE=0 ID=1382 SEQ=1
Jun 14 13:02:12 deb8 kernel: [ 4273.341274] ipt[POSTROUTING]mangle IN= OUT=eth1 SRC=10.1.1.2 DST=10.1.1.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=37735 PROTO=ICMP TYPE=0 CODE=0 ID=1382 SEQ=1
Jun 14 13:02:12 deb8 kernel: [ 4273.341278] simple: tc[eth1]egress_1
Jun 14 13:02:12 deb8 kernel: [ 4273.341280] simple: tc[ifb0]egress_1
The debugging was done using the following settings:
iptables -F -t filter
iptables -F -t nat
iptables -F -t mangle
iptables -F -t raw
iptables -A PREROUTING -t raw -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[PREROUTING]raw '
iptables -A PREROUTING -t mangle -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[PREROUTING]mangle '
iptables -A PREROUTING -t nat -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[PREROUTING]nat '
iptables -A INPUT -t mangle -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[INPUT]mangle '
iptables -A INPUT -t filter -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[INPUT]filter '
iptables -A INPUT -t nat -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[INPUT]nat '
iptables -A FORWARD -t mangle -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[FORWARD]mangle '
iptables -A FORWARD -t filter -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[FORWARD]filter '
iptables -A OUTPUT -t raw -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[OUTPUT]raw '
iptables -A OUTPUT -t mangle -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[OUTPUT]mangle '
iptables -A OUTPUT -t nat -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[OUTPUT]nat '
iptables -A OUTPUT -t filter -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[OUTPUT]filter '
iptables -A POSTROUTING -t mangle -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[POSTROUTING]mangle '
iptables -A POSTROUTING -t nat -p icmp --icmp-type 8 -j LOG --log-level 7 --log-prefix 'ipt[POSTROUTING]nat '
iptables -A PREROUTING -t raw -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[PREROUTING]raw '
iptables -A PREROUTING -t mangle -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[PREROUTING]mangle '
iptables -A PREROUTING -t nat -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[PREROUTING]nat '
iptables -A INPUT -t mangle -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[INPUT]mangle '
iptables -A INPUT -t filter -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[INPUT]filter '
iptables -A INPUT -t nat -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[INPUT]nat '
iptables -A FORWARD -t mangle -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[FORWARD]mangle '
iptables -A FORWARD -t filter -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[FORWARD]filter '
iptables -A OUTPUT -t raw -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[OUTPUT]raw '
iptables -A OUTPUT -t mangle -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[OUTPUT]mangle '
iptables -A OUTPUT -t nat -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[OUTPUT]nat '
iptables -A OUTPUT -t filter -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[OUTPUT]filter '
iptables -A POSTROUTING -t mangle -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[POSTROUTING]mangle '
iptables -A POSTROUTING -t nat -p icmp --icmp-type 0 -j LOG --log-level 7 --log-prefix 'ipt[POSTROUTING]nat '
export TC="/sbin/tc"
$TC qdisc del dev eth1 root
$TC qdisc del dev eth1 ingress
ip link set dev ifb0 down
ip link set dev ifb1 down
$TC qdisc del dev ifb0 root
$TC qdisc del dev ifb1 root
rmmod ifb
modprobe ifb numifbs=2
$TC qdisc add dev ifb0 root handle 1: htb default 2
$TC class add dev ifb0 parent 1: classid 1:1 htb rate 2Mbit
$TC class add dev ifb0 parent 1: classid 1:2 htb rate 10Mbit
$TC filter add dev ifb0 parent 1: protocol ip prio 1 u32 \
match ip protocol 1 0xff flowid 1:1 \
action simple "tc[ifb0]egress"
$TC qdisc add dev ifb0 ingress
$TC filter add dev ifb0 parent ffff: protocol ip prio 1 u32 \
match ip protocol 1 0xff \
action simple "tc[ifb0]ingress"
$TC qdisc add dev ifb1 root handle 1: htb default 2
$TC class add dev ifb1 parent 1: classid 1:1 htb rate 2Mbit
$TC class add dev ifb1 parent 1: classid 1:2 htb rate 10Mbit
$TC filter add dev ifb1 parent 1: protocol ip prio 1 u32 \
match ip protocol 1 0xff flowid 1:1 \
action simple "tc[ifb1]egress"
$TC qdisc add dev ifb1 ingress
$TC filter add dev ifb1 parent ffff: protocol ip prio 1 u32 \
match ip protocol 1 0xff \
action simple "tc[ifb1]ingress"
ip link set dev ifb0 up
ip link set dev ifb1 up
$TC qdisc add dev eth1 root handle 1: htb default 2
$TC class add dev eth1 parent 1: classid 1:1 htb rate 2Mbit
$TC class add dev eth1 parent 1: classid 1:2 htb rate 10Mbit
$TC filter add dev eth1 parent 1: protocol ip prio 1 u32 \
match ip protocol 1 0xff flowid 1:1 \
action simple "tc[eth1]egress" pipe \
action mirred egress redirect dev ifb0
$TC qdisc add dev eth1 ingress
$TC filter add dev eth1 parent ffff: protocol ip prio 1 u32 \
match ip protocol 1 0xff \
action simple "tc[eth1]ingress" pipe \
action mirred egress redirect dev ifb1
=========
来源 https://www.jianshu.com/p/7e1be5ec623d
转载自 linux下使用tc(Traffic Control)流量控制命令模拟网络延迟和丢包
Linux TC(traffic control),从名字可以看出是一个能控制流量的工具。
TC原理
Linux TC对Linux内核的流量控制,利用队列规定建立处理数据包的队列,并定义队列中的数据包被发送的方式,从而实现对流量的控制。TC实现流量控制功能使用的队列规定分为两类,一类是无类队列规定,另一类是分类队列规定。无类队列规定相对简单,而分类队列规定则引出了分类和过滤器等概念,使其流量控制功能增强。
无类队列规定是对进入网络设备(网卡)的数据流不加区分统一对待的队列规定。使用无类队列规定形成的队列能够接收数据包以及重新编排、延迟或丢弃数据包。这类队列规定形成的队列可以对整个网络设备(网卡)的流量进行整形,但不能细分各种情况。常用的无类队列规定主要有pfifo_fast (先进先出)、TBF(令牌桶过滤器)、SFQ(随机公平队列)、ID(前向随机丢包)等等。这类队列规定使用的流量整形手段主要是排序、限速和丢包。
分类队列规定是对进入网络设备的数据包根据不同的需求以分类的方式区分对待的队列规定。数据包进入一个分类的队列后,它就需要被送到某一个类中,也就是说需要对数据包做分类处理。对数据包进行分类的工具是过滤器,过滤器会返回一个决定,队列规定根据这个决定把数据包送入相应的类进行排队。每个子类都可以再次使用它们的过滤器进一步的分类。直到不需要进一步分类时,数据包才进入该类包含的队列排队。除了能够包含其他队列规定之外,绝大多数分类的队列规定还能够对流量进行整形。这对于需要同时进行调度(如使用SFQ)和流量控制的场合非常有用。
Linux流量控制的基本原理
接收包从输入接口(Input Interface)进来后,经过流量限制(Ingress Policing)丢弃不符合规定的数据包,由输入多路分配器(Input De-Multiplexing)进行判断选择。如果接收包的目的地是本主机,那么将该包送给上层处理,否则需要进行转发,将接收包交到转发块(Forwarding Block)处理。转发块同时也接收本主机上层(TCP、UDP等)产生的包。转发块通过查看路由表,决定所处理包的下一跳。然后,对包进行排列以便将它们传送到输出接口(Output Interface)。一般我们只能限制网卡发送的数据包,不能限制网卡接收的数据包,所以我们可以通过改变发送次序来控制传输速率。Linux流量控制主要是在输出接口排列时进行处理和实现的。
TC规则
一、流量控制方式
流量控制包括一下几种方式:SHAPING、SCHEDULING、POLICING、DROPPING;
SHAPING(限制):当流量被限制时,它的传输速率就被控制在某个值以下。限制值可以大大小于有效带宽,这样可以平滑突发数据流量,使网络更为稳定。SHAPING(限制)只适用于向外的流量。
SCHEDULING(调度):通过调度数据包的传输,可以在带宽范围内,按照优先级分配带宽。SCHEDULING(调度)也只适用于向外的流量。
POLICING(策略):SHAPING(限制)用于处理向外的流量,而POLICING(策略)用于处理接收到的数据。
DROPPING(丢弃):如果流量超过某个设定的带宽,就丢弃数据包,不管是向内还是向外。
二、流量控制处理对象
流量的处理由三种对象控制,它们是:qdisc(排队规则)、class(类别)和filter(过滤器)。
qdisc(排队规则 queueing discipline),是理解流量控制(traffic control)的基础。无论何时,内核如果需要通过某个网络接口发送数据包,都需要按照这个接口配置的qdisc(排队规则)把数据包加入队列。然后,内核会尽可能多的从qdisc里面取出数据包,把它们交给网络适配器驱动模块。最简单的qdisc是pfifo他不对进入的数据包做任何的处理,数据包采用先进先出的方式通过队列。不过,它会保存网络接口一时无法处理的数据包。
qddis(排队规则)分为 CLASSLESS QDISC和 CLASSFUL QDISC;
CLASSLESS QDISC (无类别QDISC)包括:
1) [ p | b ]fifo 最简单的qdisc(排队规则),纯粹的先进先出。只有一个参数:limit ,用来设置队列的长度,pfifo是以数据包的个数为单位;bfifo是以字节数为单位。
2) pfifo_fast 在编译内核时,如果打开了高级路由器(Advanced Router)编译选项,pfifo_fast 是系统的标准qdisc(排队规则)。它的队列包括三个波段(band)。在每个波段里面,使用先进先出规则。而三个波段(band)的优先级也不相同,band 0 的优先级最高,band 2的最低。如果band 0里面有数据包,系统就不会处理band 1里面的数据包,band 1 和 band 2 之间也是一样的。数据包是按照服务类型(Type Of Service,TOS )被分配到三个波段(band)里面的。
3) red (Random Early Detection随机早期探测)当带宽的占用接近与规定的带宽时,系统会随机的丢弃一些数据包。非常适合高带宽的应用。
4) sfq(Stochastic Fairness Queueing) 按照会话(session --对应与每个TCP 连接或者UDP流)为流量进行排序,然后循环发送每个会话的数据包。
5) tbf(Token Bucket Filter),适用于把流速降低到某个值。
CLASSLESS QDISC(无类别QDISC)的配置:
如果没有可分类qdisc,不可分类qdisc只能附属于设备的根,其用法:tc qdisc add dev DEV root QDISC QDISC_PARAMETERS
要删除一个不可分类qdisc :tc qdisc del dev DEV root
一个网络接口上如果没有设置qdisc,pfifo_fast就作为缺省的qdisc。
CLASSFUL QDISC(可分类 QDISC)包括:
1) CBQ (Class Based Queueing基于类别排队),实现了一个丰富的连接共享类别结构,既有限制(shaping)带宽的能力,也具有带宽优先级别管理的能力。带宽限制是通过计算连接的空闲时间完成的。空闲时间的计算标准是数据包离队事件的频率和下层连接(数据链路层)的带宽。
2) HTB (Hierarchy Token Bucket)。通过在实践基础上的改进,它实现一个丰富的连接共享类别体系。HTB可以很容易地保证每个类别的带宽,虽然它也允许特定的类可以突破带宽上限,占用别的类的带宽。HTB可以通过TBF(Token Bucket Filter)实现带宽限制,也能够划分类别的优先级。
3) PRIO不能限制带宽,因为属于不同类别的数据包是顺序离队的。PRIO qdisc 可以很容易对流量进行优先级管理,只有属于高优先级类别的数据包全部发送完毕,参会发送属于低优先级类别的数据包。为了方便管理,使用iptables 或者 ipchains 处理数据包的服务类型(Type Of Service,TOS)。
TC操作原理
类(class)组成一个树,每个类都只有一个父类,而一个类可以有多个子类。某些qdisc (例如:CBQ和 HTB)允许在运行时动态添加类,而其它的qdisc(如PRIO)不允许动态建立类。允许动态添加类的qdisc可以有零个或者多个子类,由它们为数据包排队。此外,每个类都有一个叶子qdisc,默认情况下,这个也在qdisc有可分类,不过每个子类只能有一个叶子qdisc。 当一个数据包进入一个分类qdisc,它会被归入某个子类。我们可以使用一下三种方式为数据包归类,不过不是所有的qdisc都能够使用这三种方式。
如果过滤器附属于一个类,相关的指令就会对它们进行查询。过滤器能够匹配数据包头所有的域,也可以匹配由ipchains或者iptables做的标记。
树的每个节点都可以有自己的过滤器,但是高层的过滤器也可以一直接用于其子类。如果数据包没有被成功归类,就会被排到这个类的叶子qdisc的队中。相关细节在各个qdisc的手册页中。
TC命名规则
所有的qdisc、类、和过滤器都有ID。ID可以手工设置,也可以由内核自动分配。ID由一个主序列号和一个从序列号组成,两个数字用一个冒号分开。
qdisc:一个qdisc会被分配一个主序列号,叫做句柄(handle),然后把从序列号作为类的命名空间。句柄才有像1:0 一样的表达方式。习惯上,需要为有子类的qdisc显式的分配一个句柄。
类(Class):在同一个qdisc里面的类共享这个qdisc的主序列号,但是每个类都有自己的从序列号,叫做类识别符(classid)。类识别符只与父qdisc有关,与父类无关。类的命名习惯和qdisc相同。
过滤器(Filter):过滤器的ID有三部分,只有在对过滤器进行散列组织才会用到。详情请参考tc-filtes手册页。
TC单位
tc命令所有的参数都可以使用浮点数,可能会涉及到以下计数单位。
带宽或者流速单位:
kbps 千字节/秒
mbps 兆字节/秒
kbit KBits/秒
mbit MBits/秒
bps或者一个无单位数字 字节数/秒
数据的数量单位:
kb或者k 千字节
mb或者m 兆字节
mbit 兆bit
kbit 千bit
b或者一个无单位数字 字节数
时间的计量单位:
s、sec或者secs 秒
ms、msec或者msecs 分钟
us、usec、usecs或者一个无单位数字 微秒
TC命令
tc可以使用以下命令对qdisc、类和过滤器进行操作:
add: 在一个节点里加入一个qdisc、类、或者过滤器。添加时,需要传递一个祖先作为参数,传递参数时既可以使用ID也跨越式直接传递设备的根。如果要建立一个qdisc或者过滤器,可以使用句柄(handle)来命名。如果要建立一个类,可以使用类识别符(classid)来命名。
remove:删除由某个句柄(handle)指定的qdisc,根qdisc(root)也可以删除。被删除qdisc上所有的子类以及附属于各个类的过滤器都会被自动删除。
change:以替代的方式修改某些条目。除了句柄(handle)和祖先不能修改以外,change命令的语法和add命令相同。换句话说,change命令不能指定节点的位置。
replace: 对一个现有节点进行近于原子操作的删除/添加。如果节点不存在,这个命令就会建立节点。
link: 只适用于qdisc,替代一个现有的节点。
摘取自 Gitbook的流量控制和Linux TC(Traffic Control)框架原理解析
流量控制(Traffic Control)是Linux内核提供的流量限速、整形和策略控制机制。
递归控制(分层次控制)
很多资料介绍TC是对队列组织中的数据包进行延迟,丢包等处理。但很少有人在介绍队列组织前介绍什么是递归控制,所谓的递归控制是分层次地控制,而对于每个层次,控制方式都是一致的。熟悉CFS调度的都知道,对于组调度和task调度都采用了完全相同的调度方式,然而显然组和task是属于不同层次的。
不光是控制逻辑的组织,就连Linux在实现UNIX进程模型时,也采用了这种树形的递归控制逻辑,每一个层次都是一个两层的树。
递归控制是分形的,如果能用立体的图展示会更好些,对于上图而言,除了叶子节点之外的每一个节点都是一颗独立的小树,不管是大树还是小树,对于控制逻辑或者组织逻辑而言,其性质是完全一样的。
递归的控制便于控制逻辑的任意叠加,在协议栈的设计中看到过,比如X over Y,简称XoY,比如PPPoE,IP over UDP(tun模式的OpenVPN),TCP over IP(原生的TCP/IP栈)...对于TC而言,考虑下面一个需求:
1.将整个带宽按照2:3的比例分给TCP和UDP;
2.在TCP流量中,按照源IP地址段将其划分为不同的优先级;
3.在相同的优先级队列中,按照2:8的比例将带宽分给HTTP应用和其它;
这是一个递归控制的需求,其中1和3均使用了带宽比例分配,但是显而易见,这是属于不同层次的。整个架构看起来应该是下面这个样子:
但是事情远非想象的那个单纯,虽然上面的图已经让你看出了TC框架的端倪,然而对于实现它却没有一点帮助。几个典型的问题摆在那里,怎么甄别数据包到不同的队列,图中的非叶子节点要呈现成什么数据结构,既然不是真正的队列却又要有队列的行为,那么如何表达它们?
队列组织
Linux在实现TC的时候,对“队列”进行了抽象,维护了两个回调函数指针,一个是enqueue入队操作,一个是dequeue出队操作。不管是enqueue还是dequeue,都不一定真正将数据包排入队列,而仅仅是“执行一系列的操作”。这个“执行一系列的操作”可以是:
1.对于叶子节点,真正排入一个真实的队列或者从真正的队列拉出一个数据包;
2.递归调用其它抽象队列的enqueue/ dequeue。
注意上面的第2点,提到了“其它抽象队列”,那么如何来定位这个抽象队列呢?这就需要一个抉择,也就是一个选择器,根据数据包的特征来将数据包归入一个抽象队列,这个时候,TC的设计框图可以用下图来表达:
上图以用一种递归控制的形式来定义TC框架。下图以经典的“队列规程,类别,过滤器”三元组套在这幅图上。
从上图中看到,tc由qdisc、fitler和class三部分组成:
1) qdisc通过队列将数据包缓存起来,用来控制网络收发的速度
2) class用来表示控制策略
3) filter用来将数据包划分到具体的控制策略中
qdisc
qdisc通过队列将数据包缓存起来,用来控制网络收发的速度。实际上,每个网卡都有一个关联的qdisc。它包括以下几种:
无分类qdisc(只能应用于root队列)
[p|b]fifo:简单先进先出
pfifo_fast:根据数据包的tos将队列划分到3个band,每个band内部先进先出
red:Random Early Detection,带带宽接近限制时随机丢包,适合高带宽应用
sfq:Stochastic Fairness Queueing,按照会话对流量排序并循环发送每个会话的数据包
tbf:Token Bucket Filter,只允许以不超过事先设定的速率到来的数据包通过 , 但可能允许短暂突发流量朝过设定值
有分类qdisc(可以包括多个队列)
cbq:Class Based Queueing,借助EWMA(exponential weighted moving average, 指数加权移动均值 ) 算法确认链路的闲置时间足够长 , 以达到降低链路实际带宽的目的。如果发生越限 ,CBQ 就会禁止发包一段时间。
htb:Hierarchy Token Bucket,在tbf的基础上增加了分层
prio:分类优先算法并不进行整形 , 它仅仅根据你配置的过滤器把流量进一步细分。缺省会自动创建三个FIFO类。
注意,一般说到qdisc都是指egress出向 qdisc。每块网卡实际上还可以添加一个ingress qdisc,不过它有诸多的限制
1) ingress qdisc不能包含子类,而只能作过滤
2) ingress qdisc只能用于简单的整形
如果相对ingress方向作流量控制的话,可以借助ifb( Intermediate Functional Block)内核模块。因为流入网络接口的流量是无法直接控制的,那么就需要把流入的包导入(通过 tc action)到一个中间的队列,该队列在 ifb 设备上,然后让这些包重走 tc 层,最后流入的包再重新入栈,流出的包重新出栈。
filter
filter用来将数据包划分到具体的控制策略中,包括以下几种:
u32:根据协议、IP、端口等过滤数据包
fwmark:根据iptables MARK来过滤数据包
tos:根据tos字段过滤数据包
class
class用来表示控制策略,只用于有分类的qdisc上。每个class要么包含多个子类,要么只包含一个子qdisc。当然,每个class还包括一些列的filter,控制数据包流向不同的子类,或者是直接丢掉。