Linux内核TC工具链路带宽设计--HTB队列规定及简单的过滤器介绍
1 HTB(Hierarchical Token Bucket, 分层的令牌桶)
HTB 使用令牌和存储桶的概念,以及基于类的系统和过滤器,以允许对流量进行复杂和精细的控制。借助复杂的借用模型,HTB 可以执行各种复杂的流量控制技术。该队列规则允许用户定义所使用的令牌和桶的特征,并允许用户以任意方式嵌套这些桶。当与分类方案结合使用时,可以非常精细地控制流量。HTB 非常适用于这种情况:如果有一个固定速率的链路,希望分割给多种不同的用途使用,为每种用途做出带宽承诺并实现定量的带宽借用。HTB 不同于 CBQ 的是并不靠计算闲置时间来整形。它是一个分类的令牌桶过滤器。
1.1 HTB 的参数介绍
default
每个 HTB 队列的可选参数,默认值为 0,其意义是任何未分类的流量以物理网卡的速率出队,完全绕过连接到根队列的任何分类。如果指定了 default 的值,未分类(不能和 filter 匹配)的流量(默认的)会被送到这个参数所指定的类中。
rate
用来设置限制传输的流量速率。
ceil
用来设置限制传输流量的最大所需速率。如果需要设置共享带宽,则需使用此参数。这个参数可以被认为相当于“可突发带宽”
burst
桶的大小。前面在讨论不可分类的队列规则时提到过令牌桶过滤器 TBF,桶的概念就是其中的令牌,用于处理数据的传输时必要的参数,如果需要具体了解可以参考前面的内容
cburst
这是 ceil 桶的大小。在等待更多的令牌到来之前,HTB 会使 cburst 字节出列。这里涉及令牌流与数据流的概念与关系,具体可参考前面不可分类的队列规定
quantum
每轮当前的类能发送的字节数,这是用来控制租借带宽的关键参数。其默认计算 quantum = rate / r2q。quantum 必须大于 1500 小于 60000。quantum 只在 class 的流量超过了 rate 但是没有超过 ceil 时使用,它的值越小,带宽共享的效果就越好。
1.2 配置范例
这个配置把 WEB 服务器的流量控制为 5Mbps、SMTP 流量控制在 3Mbps。而且二者一共不得超过 6Mbps,互相之间允许借用带宽。
# sudo tc qdisc add dev enp0s5 root handle 1: htb default 30 # sudo tc class add dev enp0s5 parent 1:0 classid 1:1 htb rate 6Mbit burst 15k # sudo tc class add dev enp0s5 parent 1:1 classid 1:10 htb rate 5Mbit burst 15k # sudo tc class add dev enp0s5 parent 1:1 classid 1:20 htb rate 3Mbit ceil 6Mbit burst 15k # sudo tc class add dev enp0s5 parent 1:1 classid 1:30 htb rate 1kbit ceil 6Mbit burst 15k
在那些类的下方放置 SFQ:
# sudo tc qdisc add dev enp0s5 parent 1:10 handle 10: sfq perturb 10 # sudo tc qdisc add dev enp0s5 parent 1:20 handle 20: sfq perturb 10 # sudo tc qdisc add dev enp0s5 parent 1:30 handle 30: sfq perturb 10
添加过滤器,直接把流量导向相应的类:
# U32="sudo tc filter add dev enp0s5 protocol ip parent 1:0 prio 1 u32" # $U32 match ip dport 80 0xffff flowid 1:10 # $U32 match ip sport 25 0xffff flowid 1:20
如果 10: 和 20: 都得到了保证的速率,剩下的就是分割了,它们借用的比率是 5:3。未被分类的流量被送到了 30:,仅有一点点带宽,但是却可以任意借用剩下的带宽。因为其内部使用了 SFQ,而可以公平发包。
控制服务器对外的速度为 20Mbit,服务器只能向外发 20Mbit 的数据包。
# sudo tc qdisc del dev enp0s5 root 2> /dev/null > /dev/null # sudo tc qdisc add dev enp0s5 root handle 1: htb # sudo tc class add dev enp0s5 parent 1:0 classid 1:1 rate 100Mbit ceil 100Mbit # sudo tc class add dev enp0s5 parent 1:1 classid 1:10 rate 20Mbit ceil 20Mbit # sudo tc qdisc add dev enp0s5 parent 1:10 sfq perturb 10 # sudo tc filter add dev enp0s5 protocol ip parent 1:0 prio 2 u32 match ip dst 192.168.224.60 flowid 1:1 # sudo tc filter add dev enp0s5 protocol ip parent 1:0 prio 2 u32 match ip dst 0.0.0.0/0 flowid 1:10
上面的命令中创建了两个分类,第一个分类的速率为 100Mbit,假设服务器的 IP 为 192.168.224.60,第一个过滤器是用来处理发往服务器的数据包,让所有发往服务器的数据包由 1:1 直接处理,相当于不受限制。而发往其他目标主机的数据包由 1:10 处理,即服务器只能向外发送 20Mbit 的数据包。如果这里只创建了一个分类,即上面的 1:10,则所有没有被分类的数据包会被网卡直接处理,也能达到不对其限制的目的,但是发往服务器的数据包也会被 1:10 所处理,但这并不是我们所需要的,因此需要再创建一个分类,用来单独处理发往服务器的数据包,使其不受限制,这个分类的速率可以尽情的设置很大。
2 使用过滤器对数据包进行分类
为了决定用哪个类处理数据包,必须调用“分类器链”进行选择。这个链中包含了这个分类队列规定所需的所有过滤器。在前面的队列规定中已经用到了过滤器对数据分类进行处理了,但是没有做任何介绍,这里先对其简单的说明,后面会对常用的过滤器再做稍微详细的说明。
重复前面那棵树:
当一个数据包入队的时候,每一个分支处都会咨询过滤器链如何进行下一步。典型的配置是在 1:1 处有一个过滤器把数据包交给 12:,然后 12: 处的过滤器在把包交给 12:2。你可以把后一个过滤器同时放在 1:1 处,从而提高效率。另外,不能用过滤器把数据包向“上”送。而且,使用 HTB 的时候应该把所有的规则放到根上。再次强调:数据包只能向“下”进行入队操作,只有出队的时候才会上到网卡所在的位置来。他们不会落到树的最底层后送到网卡。
2.1 过滤器的一些简单范例
下面我们就开始,从简单地匹配一些比较明显的特征开始。比方说,我们有一个 PRIO 队列规定,叫做“10:”,包含 3 个类,我们希望把去往 22 口的数据流发送到最优先的频道中去。应该这样设置过滤器:
# sudo tc filter add dev enp0s5 protocol ip parent 10: prio 1 u32 match ip dport 22 0xffff flowid 10:1 # sudo tc filter add dev enp0s5 protocol ip parent 10: prio 1 u32 match ip sport 80 0xffff flowid 10:1 # sudo tc filter add dev enp0s5 protocol ip parent 10: prio 2 flowid 10:2
上述配置是说:向 enp0s5 上的 10: 节点添加一个 u32 过滤规则,它的优先权是 1,凡是去往 22 端口(精确匹配)的 IP 数据包,发送到频道 10:1。向 enp0s5 上的 10: 节点添加一个 u32 过滤规则,它的优先权是 1,凡是来自 80 端口(精确匹配)的 IP 数据包,发送到频道 10:1。向 enp0s5 上的 10: 节点添加一个过滤规则,它的优先权是 2,凡是上面未匹配的 IP 数据包,发送到频道 10:2。别忘了添加“dev enp0s5”(你的网卡或许叫别的名字),因为每个网卡的句柄都有完全相同的命名空间。如果想通过 IP 地址进行筛选,可以采用如下命令:
# sudo tc filter add dev enp0s5 parent 10:0 protocol ip prio 1 u32 match ip dst 4.3.2.1/32 flowid 10:1 # sudo tc filter add dev enp0s5 parent 10:0 protocol ip prio 1 u32 match ip src 1.2.3.4/32 flowid 10:1 # sudo tc filter add dev enp0s5 protocol ip parent 10: prio 2 flowid 10:2
这个例子把去往 4.3.2.1 和来自 1.2.3.4 的数据包送到了最高优先的队列,其它的则送到次高优先的队列。可以连续使用 match,如果想匹配来自 1.2.3.4 的 80 口的数据包,可以做如下配置:
# sudo tc filter add dev enp0s5 parent 10:0 protocol ip prio 1 u32 match ip src 4.3.2.1/32 match ip sport 80 0xffff flowid 10:1
2.2 常用到的过滤命令一览
这里列出的绝大多数命令都根据这个命令改编而来:
# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 1 u32 ⋯⋯
这些是“u32”的匹配,可以匹配数据包的任意部分。
根据源/目的地址:
源地址段 'match ip src 1.2.3.0/24'
目的地址段 'match ip dst 4.3.2.0/24'
单个 IP 地址使用“/32”作为掩码即可。
根据源/目的端口,所有 IP 协议:
源 'match ip sport 80 0xffff'
目的 'match ip dport 80 0xffff'
根据 IP 协议(tcp, udp, icmp, gre, ipsec) :
使用 /etc/protocols 所指定的数字。比如: icmp 是 1: 'match ip protocol 1 0xff'。
根据 fwmark:
可以使用 ipchains/iptables 给数据包做上标记,并且这个标记会在穿过网卡的路由过程中保留下来。如果希望对来自 eth0 并从 eth1 发出的数据包做整形,这就很有用了。语法是这样的:
# sudo tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 6fw flowid 1:1
注意,这不是一个 u32 匹配。可以象这样给数据包打标记:
# sudo iptables -A PREROUTING -t mangle -i eth0 -j MARK --set-mark 6
数字 6 是可以任意指定的。
按TOS 字段:
选择交互和最小延迟的数据流:
# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 10 u32 atch ip tos 0x10 0xff flowid 1:4
想匹配大量传输的话,使用“0x08 0xff”。