iproute2和流量控制(ip和tc工具)---iproute2/ip/tc/qdisc实现Linux下的QoS控制

iproute2是一个软件包,这个软件包包含了多种用于高级路由,隧道和流量控制配置工具软件。
iproute2提供了Linux内核对QoS的实现,你可以在以下网站中找到英文原版的信息osdl.orglartc.org。这些工具软件中,最重要的当数ip和tc这二个工具。

1、网络配置: "ip"工具

ip工具提供了Linux网络配置所需要的大部分功能。通过它,你可以配置网络接口, ARP,路由策略,隧道等等。ip工具支持IPv4和IPv6,并且它的语法简单易懂。最重要的是我们要学会在什么时候使用这个工具来完成什么功能。
首先,ip工具可以用于配置 动态路由协议(BGP, OSPF, and RIP) 。

我们可以通过ip help来查看一下ip工具支持哪些功能。

xxx@raspberrypi:~ $ ip help
Usage: ip [ OPTIONS ] OBJECT {
    
     COMMAND | help }
       ip [ -force ] -batch filename
where  OBJECT := {
    
     link | address | addrlabel | route | rule | neigh | ntable |
                   tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |
                   netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |
                   vrf | sr | nexthop | mptcp }
       OPTIONS := {
    
     -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |
                    -h[uman-readable] | -iec | -j[son] | -p[retty] |
                    -f[amily] {
    
     inet | inet6 | mpls | bridge | link } |
                    -4 | -6 | -I | -D | -M | -B | -0 |
                    -l[oops] {
    
     maximum-addr-flush-attempts } | -br[ief] |
                    -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |
                    -rc[vbuf] [size] | -n[etns] name | -N[umeric] | -a[ll] |
                    -c[olor]}

  • ip link命令:
    显示出所有可以用ip link set命令去修改的配置项。本命令用于修改网络接口设备的属性,但不能用于修改IP地址。

  • ip addr命令
    显示所有网络接口设备的IP地址(等同于ip addr show)。ip addr add命令可以用于给网络接口设置增加主或者第二IP地址。ip addr del命令用于删除网络接口设备的IP地址。还有其它一些用途,比如ip addr flush dynamic命令可以直接清除掉所有通过动态路由协议添加到内核的路由表项。

  • ip neighbor命令
    用于管理Arp表,它包含了add, change, replace, delete和flush多个子功能。

  • ip tunnel命令
    用于管理隧道连接。支持gre, ipip和sit等多种隧道协议。用于创建管理隧道。

  • ip monitor命令
    ip工具提供了实时查看路由,地址,设备状态的的功能。

  • ip route命令
    此命令用于对内核的路由表进行操作,包括add, change, replace, delete, show, flush和get等功能。

  • ip rule命令
    iproute2还有一个非常重要的功能就是它能通过ip rule和ip route二种命令实现策略路由。

2、流量控制工具Traffic Control: tc工具

tc工具让使用Linux来代替昂贵的QoS设备成为可能,因为tc工具可以让工程师在Linux主机上配置各种QoS策略。Linux甚至比常用的QoS设备支持更多的QoS功能,这使得我们用Linux机器当做便宜又强大的QoS设备。这个的前提是你的Linux发行版的内核在编译里已经支持QoS选项(CONFIG_NET_QOS=“Y” 和 CONFIG_NET_SCHED=“Y”,这个一般目前能拿到的通用的Linux发行版都是默认打开的,如果你自己编译内核才需要注意这二个配置)。

2.1、流量整形:Queuing Packets(也称做traffic shaping)

Linux系统里,使用Queuing队列机制来决定数据如何被发送,比如控制发送数据包的速度与多少,控制数据包发送的优先级等等。注意队列机制只能用于管理发出的数据包,不能用来管理收到的数据包(这个很好理解,收到的数据包的多少与速度是由发送发决定的,接收方只能被动接收)。如下图所示:我们可以看到,在路由器上,我们只能控制上下行二个方向的发送的QoS。

  • 下行方向
    路由器只能控制路由器从网口eth1 向PC发送的数据包;
  • 上行方向
    路由器只能控制路由器从网口eth0向网络上的服务器发送的数据包。
    在这里插入图片描述
    虽然只能控制发送一个方向的数据流量,但考虑到TCP协议的流控功能 (即:通信双方能通过TCP协议的ACK报文的发送来控制对方的发包速度),控制发送速度与数量就可以达到很好的双向流量整形的效果。UDP不支持协议层面上的流量控制,但大多数基于UDP协议的上层应用层协议带了流量控制功能,所以同样可以达到流量整形的效果。
    但因为只能控制发送方向的数据,不能控制接收方向数据包的QoS,还是带来一些问题,比如泛洪攻击flood attacks导致的DoS的存在就是因为无法控制接收方向上的数据包的多少与速度。
    用于流量整形的队列机制queuing disciplines可以分成二种,classless和classful。

开始讨论这二种Qdiscs之前,让我们来回顾一下qdiscs工作在哪里,ingress Qdiscs工作在进入Linux的TCP/IP协议栈之前, egress Qdiscs工作在报文出了CP/IP协议栈之后。
在这里插入图片描述

2.2、Classless Queuing Disciplines (Classless qdiscs)无分类Qdiscs

Classless qdiscs只支持最简单的规则,比如accept, drop, delay 或者 reschedule数据包。这个classless Qdiscs可以被配置在一个网络接口设备上,也只能对该网络接口设备上的所有数据包做整形,不知道对数据包和流量作分类整形。
Linux内核支持以下几种classless Qdiscs:

  • FIFO (pfifo 和 bfifo):
    最基本的qdisc, 这个只支持最基本的先进先出原则。FIFO算法分成二类,如果是pfifo, 那么它定义了一个队列长度限制;如果是bfifo, 那么它定义了一个缓冲区字节长度限制。
  • pfifo_fast:
    这个是Linux网络接口设备默认的Qdisc。后面我们会详解pfifo_fast的具体工作原理。
  • Token Bucket Filter (tbf):
    这个通常被称为令牌桶,用于限定某个网络接口设备的发送速率。这个机制允许短时间的超出限制,称为burst,是一种CPU占用率友好型的Qdisc。
  • Stochastic Fair Queuing (SFQ):
    这是使用最为广泛的Qdisc。SFQ的原则是尽可能公平合理的调度传输多个数据流flow上的数据包。
  • Enhanced Stochastic Fair Queuing (ESFQ):
    非Linux内核原生支持的Qdisc,它的工作原理与SFQ相似,但为用户提供了更多配置项如depth (flows) limit, hash table size options (SFQ中这个参数是固定值)和 hash types等,以让用户可以更好的控制流量。
  • Random Early Detection and Generic Random Early Detection (RED and GRED):
    一种适用于骨干网的qdiscs。

除了上面列出来的Qdisc之外,还有很多各种流量整形的方法。通常SFQ和ESFQ能对流量做出非常好的整形和控制效果。

Linux默认的Qdisc是pfifo_fast。 它是一种以数据包为单位的先进先出的机制,但实际上,它之所以被称为_fast是因为它提供了三个等级,0, 1和 2, 数据包按照TOS字节的值被分成这三个等级。然后以下面的规则进行发送:

  • 定义为0级的报文拥有最高的优先级
  • 定义为1级的报文,只有在定义为0级的报文全部发送完毕后才能被发送
  • 定义为2级的报文的优先级最低,只有定义为0和1的所有报文全部发送完毕后才能被发送

如下图我们可以看到系统是数据根据每个报文的TOS将数据包映射为0,1,2三个级别的:
在这里插入图片描述TOS字节里,3-6bits被定义为TOS比特,每个bit的意义的定义如下:
在这里插入图片描述
然后按下表,映射成3个等级:
在这里插入图片描述由于Linux默认使用pfifo_fast这种Qdisc,所以Linux是会根据数据包的TOS字节定义的优先级进行传输的,通常,类似Telnet, FTP, SMTP这些协议就是通过定义TOS 字节来优化传输效率的。

2.3、 Classful Queuing Disciplines (Classfull qdiscs)

分类classful qdiscs用于对不同类型的数据流进行分类整形,按流据流类型的不同,提供不同的QoS服务。最觉的classful qdiscs是CBQ (Class Based Queuing,基于流类型的流量整形)和HTB (Hierarchical Token Bucket分层令牌桶)。
首先,我们来理解一下classful queuing disciplines 是怎么工作的。第一个是分层,每个网络接口设备都有一个root qdisc。其次,有一个子类child class附属于这个root qdisc。 这个子分类child class有自己的一个或者多个子子分类child classes,每人子子类又有自己的去真正配置如何进行数据包的传输调度qdiscs和一个或者多个的叶子分类leaf classes。如下图所示的一个5层结构:
在这里插入图片描述
所以CBQ 或者 HTB qdiscs 允许我们去创建自己的CBQ 或 HTB子分类 classes,以支持对不同的数据流进行不同的数据整形QoS服务。

3、tc的 qdisc, class和filter三个概念之间的关系

上图中显示的树状层次关系配置,我们需要用到以下tc命令:

  • tc qdisc
    配置queuing disciplines.
  • tc class
    配置classes.
  • tc filter
    配置filters, 用于对数据流进行分类

我们可以通过调整CBQ 和 HTB的参数,来优化它们的性能。在本文中可以看到针对不同的App产生的不同流量,我们使用了不同的参数。

CBQ 的qdiscs和classes命令格式如下:

xxx@xxx-OptiPlex-3080:~$ tc qdisc add cbq help
Usage: ... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]
               [ cell BYTES ] [ ewma LOG ]
xxx@xxx-OptiPlex-3080:~$ tc class add cbq help
Usage: ... cbq	bandwidth BPS rate BPS maxburst PKTS [ avpkt BYTES ]
		[ minburst PKTS ] [ bounded ] [ isolated ]
		[ allot BYTES ] [ mpu BYTES ] [ weight RATE ]
		[ prio NUMBER ] [ cell BYTES ] [ ewma LOG ]
		[ estimator INTERVAL TIME_CONSTANT ]
		[ split CLASSID ] [ defmap MASK/CHANGE ]
		[ overhead BYTES ] [ linklayer TYPE ]

HBQ 的qdiscs和classes命令格式如下:

xxx@xxx-OptiPlex-3080:~$ tc qdisc add htb help
What is "help"?
Usage: ... qdisc add ... htb [default N] [r2q N]
                      [direct_qlen P] [offload]
 default  minor id of class to which unclassified packets are sent {
    
    0}
 r2q      DRR quantums are computed as rate in Bps/r2q {
    
    10}
 debug    string of 16 numbers each 0-3 {
    
    0}

 direct_qlen  Limit of the direct queue {
    
    in packets}
 offload  enable hardware offload
... class add ... htb rate R1 [burst B1] [mpu B] [overhead O]
                      [prio P] [slot S] [pslot PS]
                      [ceil R2] [cburst B2] [mtu MTU] [quantum Q]
 rate     rate allocated to this class (class can still borrow)
 burst    max bytes burst which can be accumulated during idle period {
    
    computed}
 mpu      minimum packet size used in rate computations
 overhead per-packet size overhead used in rate computations
 linklay  adapting to a linklayer e.g. atm
 ceil     definite upper class rate (no borrows) {
    
    rate}
 cburst   burst but for ceil {
    
    computed}
 mtu      max packet size we create rate map for {
    
    1600}
 prio     priority of leaf; lower are served first {
    
    0}
 quantum  how much bytes to serve from leaf at once {
    
    use r2q}

TC HTB version 3.3
xxx@xxx-OptiPlex-3080:~$ tc class add htb help
Usage: ... qdisc add ... htb [default N] [r2q N]
                      [direct_qlen P] [offload]
 default  minor id of class to which unclassified packets are sent {
    
    0}
 r2q      DRR quantums are computed as rate in Bps/r2q {
    
    10}
 debug    string of 16 numbers each 0-3 {
    
    0}

 direct_qlen  Limit of the direct queue {
    
    in packets}
 offload  enable hardware offload
... class add ... htb rate R1 [burst B1] [mpu B] [overhead O]
                      [prio P] [slot S] [pslot PS]
                      [ceil R2] [cburst B2] [mtu MTU] [quantum Q]
 rate     rate allocated to this class (class can still borrow)
 burst    max bytes burst which can be accumulated during idle period {
    
    computed}
 mpu      minimum packet size used in rate computations
 overhead per-packet size overhead used in rate computations
 linklay  adapting to a linklayer e.g. atm
 ceil     definite upper class rate (no borrows) {
    
    rate}
 cburst   burst but for ceil {
    
    computed}
 mtu      max packet size we create rate map for {
    
    1600}
 prio     priority of leaf; lower are served first {
    
    0}
 quantum  how much bytes to serve from leaf at once {
    
    use r2q}

TC HTB version 3.3

Filters 是用于对数据流进行分类的。我们可以通过基于防火墙规则的fw classifier, 基于IP头的u32 classifier, route classifier或者rsvp/rsvp6 classifiers。

tc filter的命令格式如下:

xxx@xxx-OptiPlex-3080:~$ tc filter help
Usage: tc filter [ add | del | change | replace | show ] [ dev STRING ]
       tc filter [ add | del | change | replace | show ] [ block BLOCK_INDEX ]
       tc filter get dev STRING parent CLASSID protocol PROTO handle FILTERID pref PRIO FILTER_TYPE
       tc filter get block BLOCK_INDEX protocol PROTO handle FILTERID pref PRIO FILTER_TYPE
       [ pref PRIO ] protocol PROTO [ chain CHAIN_INDEX ]
       [ estimator INTERVAL TIME_CONSTANT ]
       [ root | ingress | egress | parent CLASSID ]
       [ handle FILTERID ] [ [ FILTER_TYPE ] [ help | OPTIONS ] ]

       tc filter show [ dev STRING ] [ root | ingress | egress | parent CLASSID ]
       tc filter show [ block BLOCK_INDEX ]
Where:
FILTER_TYPE := {
    
     rsvp | u32 | bpf | fw | route | etc. }
FILTERID := ... format depends on classifier, see there
OPTIONS := ... try tc filter add <desired FILTER_KIND> help

一般来说,最大的需求是根据源IP地址,目标IP地址,源端口,目标端口来对数据流进行分类,所以最常用的classifier是u32 classifier。本文中,我们会同时使用fw classifier和u32 classifier。

u32 classifier 的命令参数如下:

xxx@xxx-OptiPlex-3080:~$ tc filter add u32 help
Usage: ... u32 [ match SELECTOR ... ] [ link HTID ] [ classid CLASSID ]
               [ action ACTION_SPEC ] [ offset OFFSET_SPEC ]
               [ ht HTID ] [ hashkey HASHKEY_SPEC ]
               [ sample SAMPLE ] [skip_hw | skip_sw]
or         u32 divisor DIVISOR

Where: SELECTOR := SAMPLE SAMPLE ...
       SAMPLE := {
    
     ip | ip6 | udp | tcp | icmp | u{
    
    32|16|8} | mark }
                 SAMPLE_ARGS [ divisor DIVISOR ]
       FILTERID := X:Y:Z

NOTE: CLASSID is parsed at hexadecimal input.

fw classifier的命令参数格式如下:

wangsheng@wangsheng-OptiPlex-3080:~$ tc filter add fw help
Usage: ... fw [ classid CLASSID ] [ indev DEV ] [ action ACTION_SPEC ]
	CLASSID := Push matching packets to the class identified by CLASSID with format X:Y
		CLASSID is parsed as hexadecimal input.
	DEV := specify device for incoming device classification.
	ACTION_SPEC := Apply an action on matching packets.
	NOTE: handle is represented as HANDLE[/FWMASK].
		FWMASK is 0xffffffff by default.

3.1、一个详细配置例子

如下图,我们准备将10M带宽分给home-user, office和 另外一个网络三者去使用,如下图所示:
在这里插入图片描述

在这里插入图片描述
我们准备给home user 分配1Mbps带宽,给office分配4Mbps带宽,给另外一个网络分配5Mbps带宽。

3.1.1、下面我们看一下用CBQ如何来实现。

  1. 第一步,我们在eth1网卡下添加root qdisc:
tc qdisc add dev eth1 root handle 10: cbq bandwidth 100Mbit avpkt 1000

这条命令用于

  • 在设备eth1下增加一个Qdisc。
  • root这个参数表示这是一个root qdisc。
  • 然后把这个root qdisc的handle设置成10。
  • cbq 表示root qdics 的类型是cbq。
  • bandwidth设置带宽成100Mbps,表示当前网络接口上的网络的最大带宽
  • avpkt表示报文的平均长度为1000
  1. 第二步,创建child class
    这个子类是所有其它类的父节点。这个child class的带宽是等于root qdisc的带宽的,与网络最大可用带宽一至。
tc class add dev eth1 parent 10:0 classid 10:10 cbq bandwidth 100Mbit rate 100Mbit allot 1514 weight 10Mbit prio 5 maxburst 20 avpkt 1000 bounded
  • 配置child classes的parent class,它是10:0,在这里是指root class。

  • 配置classid 10:10

  • 可用带宽100Mbit

  • rate参数,限制可用带宽为100Mbps。

  • allot参数:定义一次当着class可以发送的数据量。

  • weight参数:是给CBQ使用的,CBQ会用这个参数和allot参数去计算一次可以发送的数据量,定义了以字节为单位的数据发送速率。

      注意本文中只用到了bandwidth, rate和weight三个参数, 如果你有其它参数的问题的话,请自行查阅http://www.lartc.org/howto/lartc.qdisc.classful.html#AEN939
    
  1. 第三步,针对这里的家用电脑,办公室和子网络,我们为它们各定义一个leaf classes, qdiscs 和 filters
  • 为家用电脑定义:
tc class add dev eth1 parent 10:10 classid 10:100 cbq bandwidth 100Mbit rate 1Mbit allot 1514 weight 128Kbit prio 5 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth1 parent 10:100 sfq quantum 1514b perturb 15
tc filter add dev eth1 parent 10:0 protocol ip prio 5 u32 match ip dst 1.1.1.1 flowid 10:100

我们先创建一个叫10:100的child class(这个class的rate是1Mbit, weight是128Kbit。然后,我们把一个sfq的qdisc和一个过滤目标地址为1.1.1.1的u32 filter挂在这个10:100的class下。 bounded参数表示这个class 10:100最大速率不会超过1Mbps。

  • 为办公室定义:
#the office
tc class add dev eth1 parent 10:10 classid 10:200 cbq bandwidth 100Mbit rate 4Mbit allot 1514 weight 512Kbit prio 5 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth1 parent 10:200 sfq quantum 1514b perturb 15
tc filter add dev eth1 parent 10:0 protocol ip prio 5 u32 match ip dst 1.1.2.0/24 flowid 10:200
  • 为子网络定义:
#the ISP
tc class add dev eth1 parent 10:10 classid 10:300 cbq bandwidth 100Mbit rate 5Mbit allot 1514 weight 640Kbit prio 5 maxburst 20 avpkt 1000 bounded
tc qdisc add dev eth1 parent 10:300 sfq quantum 1514b perturb 15
tc filter add dev eth1 parent 10:0 protocol ip prio 5 u32 match ip dst 1.1.1.2 flowid 10:300
tc filter add dev eth1 parent 10:0 protocol ip prio 5 u32 match ip dst 1.1.3.0/24 flowid 10:300

在子网终的例子里,我们可以看到,多个filters可以被加入到同一个class里。

  • 验证我们的配置
    使用tc class show dev eth1 命令来查看:
root@router:~# tc class show dev eth1
class cbq 10: root rate 100000Kbit (bounded,isolated) prio no-transmit
class cbq 10:100 parent 10:10 leaf 806e: rate 1000Kbit (bounded) prio 5
class cbq 10:10 parent 10: rate 100000Kbit (bounded) prio 5
class cbq 10:200 parent 10:10 leaf 806f: rate 4000Kbit (bounded) prio 5
class cbq 10:300 parent 10:10 leaf 8070: rate 5000Kbit (bounded) prio 5

现在我们可以看出来,class是用来做流量整形的,我们发送3个ping报文到1.1.1.1, 然后使用tc -s class show dev eth1命令去查看CBQ class是不是命中了这三个报文。

root@router:~# tc -s class show dev eth1 | fgrep -A 2 10:100
class cbq 10:100 parent 10:10 leaf 806e: rate 1000Kbit (bounded) prio 5
Sent 294 bytes 3 pkts (dropped 0, overlimits 0)
borrowed 0 overactions 0 avgidle 184151 undertime 0

我们可以看到,所有的配置都正常生效了。

3.1.2、下面我们看一下用HTB如何来实现。

  1. 删除之前添加的CBQ root qdisc:
root@router:~# tc qdisc del root dev eth1
  1. 添加HTB的root qdisc:
tc qdisc add dev eth1 root handle 10: htb
  1. 创建child class 10:10
tc class add dev eth1 parent 10:0 classid 10:10 htb rate 100Mbit
  1. 为家用电脑,办公室室和子网络创建class, qdisc和filter:
# home user
tc class add dev eth1 parent 10:10 classid 10:100 htb rate 1Mbit
tc qdisc add dev eth1 parent 10:100 sfq quantum 1514b perturb 15
tc filter add dev eth1 protocol ip parent 10:0 prio 5 u32 match ip dst 1.1.1.1 flowid 10:100

#the office
tc class add dev eth1 parent 10:10 classid 10:200 htb rate 4Mbit
tc qdisc add dev eth1 parent 10:200 sfq quantum 1514b perturb 15
tc filter add dev eth1 parent 10:0 protocol ip prio 5 u32 match ip dst 1.1.2.0/24 flowid 10:200

#the ISP
tc class add dev eth1 parent 10:10 classid 10:300 htb rate 5Mbit
tc qdisc add dev eth1 parent 10:300 sfq quantum 1514b perturb 15
tc filter add dev eth1 parent 10:0 protocol ip prio 5 u32 match ip dst 1.1.1.2 flowid 10:300
tc filter add dev eth1 parent 10:0 protocol ip prio 5 u32 match ip dst 1.1.3.0/24 flowid 10:300
  1. 用tc class show dev eth1确认配置
root@router:~# tc class show dev eth1
class htb 10:10 root rate 100000Kbit ceil 100000Kbit burst 126575b cburst 126575b
class htb 10:100 parent 10:10 leaf 8072: prio 0 rate 1000Kbit ceil 1000Kbit burst 2849b cburst 2849b
class htb 10:200 parent 10:10 leaf 8073: prio 0 rate 4000Kbit ceil 4000Kbit burst 6599b cburst 6599b
class htb 10:300 parent 10:10 leaf 8074: prio 0 rate 5000Kbit ceil 5000Kbit burst 7849b cburst 7849b
  1. 验证数据包是不是命中class
root@router:~# tc -s class show dev eth1 | fgrep -A 4 10:100
class htb 10:100 parent 10:10 leaf 8072: prio 0 rate 1000Kbit ceil
1000Kbit burst 2849b cburst 2849b
Sent 294 bytes 3 pkts (dropped 0, overlimits 0)
rate 24bit
lended: 3 borrowed: 0 giants: 0
tokens: 18048 ctokens: 18048

4、汇总

本文介绍了netfilter/iptables和iproute2。 我们如果想构建自己的防火墙的话,很重要的是理解报文是在哪里如何被分析的。所以在qdiscs工作在哪里这篇文章中,我们介绍了数据报文进入Linux后,是如何在filter, nat, mangle , raw这些table里转发的。使用iptables命令,我们可以在tables的chain里(append, insert, delete, list)规则,然后给命中这些规则的报文设置一个target (DROP, ACCEPT, REJECT, LOG) 来告诉Linux应该如何处理这些被规则命中的报文。
我们介绍了 iproute2的二个工具软件ip和tc。同时我们学习了Qdiscs的工作原理,并以CBQ和HTB这二种Qdiscs为例,讲述了如何 配置Qdiscs以实现QoS。

猜你喜欢

转载自blog.csdn.net/meihualing/article/details/130475877
今日推荐