3.分类的队列规定( Classful Queueing Disciplines)
如果你有多种数据流需要进行区别对待,分类的队列规定就非常有用了。众多分类的队列规定中的一种——CBQ(Class Based
Queueing,基于类的队列)——经常被提起, 以至于造成大家认为 CBQ 就是鉴别队列是否分类的标准,这是不对的。CBQ
不过是家族中最大的孩子而已,同时也是最复杂的。它并不能为你做所有的事情。
3.1分类的队列规定及其类中的数据流向
一旦数据包进入一个分类的队列规定(qdisc),它就得被送到某一个类中——也就是需要分类。对数据包进行分类的工具是过滤器(fliter)。“过
滤器”是从队列规定内部调用的,而不是从别处。过滤器会返回一个决定,
队列规定就根据这个决定把数据包送入相应的类进行排队。每个子类都可以再次使用它们的过滤器进行进一步的分类。直到不需要进一步分类时,数据包才进入该类
包含的队列规定排队。
除了能够包含其它队列规定之外,绝大多数分类的队列规定也能够流量整形(shaping)。这对于需要同时进行调度(如使用 SFQ)和流量控制的场合非常有用。
3.2队列规定家族:根、句柄、兄弟和父辈--The qdisc family: roots, handles, siblings and parents
每块网卡都有一个出口“根队列规定(root
qdisc)”,缺省情况下是前面提到的pfifo_fast队列规定。每个队列规定和分类都分配了一个句柄,以便以后的配置语句能够引用这个队列规定。
除了出口队列规定之外,每块网卡还可以有一个入口队列规定(ingress qdisc),以便policies进入的数据流。
队列规定的句柄有两个部分:一个主号码和一个次号码。习惯上把根队列规定称为“1:”,等价于“1:0”。根队列规定的次号码永远是
0。类的主号码必须与它们父辈的主号码一致。这个主号码在一个进入流(ingress)或者出流(egress)建立后必须唯一。次号码在一个队列规定和
它的分类中必须保持唯一。
3.3如何用过滤器进行分类?
一个典型的分层关系:
数据包是在根队列规定处入队和出队的,而内核只同根打交道。
一个数据包可能是按照下面这个链状流程进行分类的:
1: -> 1:1 -> 12: -> 12:2。
数据包现在应该处于 12:2 下属的某个队列规定中的某个队列中。在这个例子中,树的每个节点都附带着一个过滤器,用来选择下一步进入哪个分支。这样比较直观。然而,这样也是允许的:
1: -> 12:2
也就是说,根所附带的一个过滤器要求把数据包直接交给 12:2。
3.4数据包如何出队并交给硬件?
当内核决定把一个数据包发给网卡的时候,根队列规定1:会得到一个出队请求,然后把它传给
1:1,然后依次传给 10:,11:和12:,which each query their
siblings,然后试图从它们中进行dequeue()操作。也就是说,内核和需要遍历整棵树,因为只有12:2中才有这个数据包。换句话说,类及其
兄弟仅仅与其“父队列规定”进行交谈,而不会与网卡进行交谈。只有根队列规定才能获取由内核发出的出队请求!
更进一步,任何类的出队操作都不会比它们的父类更快。这恰恰是你所需要的:我们可以把 SFQ 作为一个子类,放到一个可以进行流量整形的父类中,从而能够同时得到 SFQ 的调度功能和其父类的流量整形功能。
3.5PRIO 队列规定(The PRIO qdisc)
3.5.1PRIO队列规定原理
PRIO 队列规定并不进行整形,它仅仅根据你配置的过滤器把流量进一步细分。你可以认为 PRIO
队列规定是 pfifo_fast 的一种衍生物,区别在每个频道都是一个单独的类,而非简单的
FIFO。当数据包进入PRIO队列规定后,将根据你给定的过滤器设置选择一个类。缺省情况下有三个类,这些类仅包含纯 FIFO
队列规定而没有更多的内部结构。你可以把它们替换成你需要的任何队列规定。每当有一个数据包需要出队时,首先处理:1
类。只有当标号更小的类中没有需要处理的包时,才会标号大的类。
当你希望不仅仅依靠包的 TOS,而是想使用 tc
所提供的更强大的功能来进行数据包的优先权划分时,可以使用这个队列规定。它也可以包含更多的队列规定,而 pfifo_fast 却只能包含简单的
fifo 队列规定。因为它不进行整形,所以使用时与 SFQ
有相同的考虑:要么确保这个网卡的带宽确实已经占满,要么把它包含在一个能够整形的分类的队列规定的内部。
3.5.2PRIO参数与使用
tc 识别下列参数:
1)bands
创建频道的数目。每个频道实际上就是一个类。如果你修改了这个数值,你必须同时修改priomap。
2)priomap
如果你不给 tc 提供任何过滤器,PRIO队列规定将参考TC_PRIO 的优先级来决定如何给数据包入队。
频道是类,缺省情况下命名为主标号:1 到主标号:3。如果你的 PRIO 队列规定是12:,把数据包过滤到 12:1 将得到最高优先级。
注意:0 频道的次标号是 1。
1 频道的次标号是 2,以此类推。
3.5.2PRIO队列规定配置实例
我们想创建这个树:
大批量数据使用 30:,交互数据使用 20:或 10:。
命令如下:
# tc qdisc add dev eth0 root handle 1: prio
## 这个命令立即创建了类: 1:1, 1:2, 1:3
# tc qdisc add dev eth0 parent 1:1 handle 10: sfq
# tc qdisc add dev eth0 parent 1:2 handle 20: tbf rate 20kbit buffer 1600 limit 3000
# tc qdisc add dev eth0 parent 1:3 handle 30: sfq
现在,我们看看结果如何:
# tc -s qdisc ls dev eth0
qdisc sfq 30: quantum 1514b
Sent 0 bytes 0 pkts (dropped 0, overlimits 0)
qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms
Sent 0 bytes 0 pkts (dropped 0, overlimits 0)
qdisc sfq 10: quantum 1514b
Sent 132 bytes 2 pkts (dropped 0, overlimits 0)
qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
Sent 174 bytes 3 pkts (dropped 0, overlimits 0)
如你所见,0 频道已经有了一些流量,运行这个命令之后发送了一个包!
现在我们来点大批量数据传输(使用能够正确设置 TOS 标记的工具) :
# scp tc ahu@10.0.0.11:./
ahu@10.0.0.11's password:
tc 100% |*****************************| 353 KB 00:00
# tc -s qdisc ls dev eth0
qdisc sfq 30: quantum 1514b
Sent 384228 bytes 274 pkts (dropped 0, overlimits 0)
qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms
Sent 2640 bytes 20 pkts (dropped 0, overlimits 0)
qdisc sfq 10: quantum 1514b
Sent 2230 bytes 31 pkts (dropped 0, overlimits 0)
qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
Sent 389140 bytes 326 pkts (dropped 0, overlimits 0)
如你所见,所有的流量都是经过 30:处理的,优先权最低。现在我们验证一下交互数据传输经过更高优先级的频道,我们生成一些交互数据传输:
# tc -s qdisc ls dev eth0
qdisc sfq 30: quantum 1514b
Sent 384228 bytes 274 pkts (dropped 0, overlimits 0)
qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms
Sent 2640 bytes 20 pkts (dropped 0, overlimits 0)
qdisc sfq 10: quantum 1514b
Sent 14926 bytes 193 pkts (dropped 0, overlimits 0)
qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
Sent 401836 bytes 488 pkts (dropped 0, overlimits 0)
正常——所有额外的流量都是经 10:这个更高优先级的队列规定处理的。与先前的整个 scp 不同,没有数据经过最低优先级的队列规定。
3.6CBQ 队列规定
除了可以分类之外,CBQ 也是一个整形器,但是从表面上看来工作得并不好。它应该是这样的:
如果你试图把一个 10Mbps 的连接整形成 1Mbps 的速率,就应该让链路 90%的时间处于闲置状态,必要的话我们就强制,以保证
90%的闲置时间。但闲置时间的测量非常困难,所以 CBQ
就采用了它一个近似值——来自硬件层的两个传输请求之间的毫秒数——来代替它。这个参数可以近似地表征这个链路的繁忙程度。这样做相当慎重,而且不一定能
够得到正确的结论。比如,由于驱动程序方面或者其它原因造成一块网卡的实际传输速率不能够达到它的标称速率,该怎么办?由于总线设计的原因,PCMCIA
网卡永远也不会达到 100Mbps。那么我们该怎么计算闲置时间呢?如果我们引入非物理网卡——像
PPPoE、PPTP——情况会变得更糟糕。因为相当一部分有效带宽耗费在了链路维护上。那些实现了测量的人们都发现 CBQ
总不是非常精确甚至完全失去了其本来意义。但是,在很多场合下它还是能够很好地工作。
3.6.1CBQ整形的原理
如前所述,CBQ
整形的工作机制是确认链路的闲置时间足够长,以达到降低链路实际带宽达到指定速率的目的。为此,它要计算两个数据包的平均发送间隔。实际的操作中,有效闲
置时间的测量使用 EWMA(exponential weighted moving
average,指数加权移动平均),也就是说最近处理的数据包的权值比以前的数据包按指数增加。UNIX 的平均负载也是这样算出来的。
计算出来的平均时间值减去EWMA测量值,得出的结果叫做“avgidle”。最佳的链路负载情况下,这个值应当是
0:数据包严格按照计算出来的时间间隔到来。在一个过载的链路上,avgidle 值应当是负的。如果这个负值太严重,CBQ
就会暂时禁止发包,称为“overlimit”(越限)。相反地,一个闲置的链路应该有很大的 avgidle
值,这样闲置几个小时后,会造成链路允许非常大的带宽通过。为了避免这种局面,我们用 maxidle 来限制
avgidle的值不能太大。理论上讲,如果发生越限,CBQ
就会禁止发包一段时间(长度就是事先计算出来的传输数据包之间的时间间隔),然后通过一个数据包后再次禁止发包。但是最好参照一下下面的
minburst 参数。
下面是配置整形时需要指定的一些参数:
avpkt
平均包大小,以字节计。计算 maxidle 时需要,maxidle 从 maxburst 得出。
bandwidth
网卡的物理带宽,用来计算闲置时间。
cell
一个数据包被发送出去的时间可以是基于包长度而阶梯增长的。一个 800字节的包和一个 806 字节的包可以认为耗费相同的时间。也就是说它设置时间粒度。通常设置为 8,必须是 2 的整数次幂。
maxburst
这个参数的值决定了计算 maxidle 所使用的数据包的个数。在 avgidle 跌落到 0 之前,这么多的数据包可以突发传输出去。这个值越高,越能够容纳突发传输。你无法直接设置 maxidle 的值,必须通过这个参数来控制。
minburst
如前所述,发生越限时 CBQ 会禁止发包。实现这个的理想方案是根据事先计算出的闲置时间进行延迟之后,发一个数据包。然而,UNIX 的内核一般来说都有一个固定的调度周期(一般不大于 10ms),所以最好是这样:
禁止发包的时间稍长一些,然后突发性地传输 minburst 个数据包,而不是一个一个地传输。等待的时间叫做 offtime。
从大的时间尺度上说,minburst 值越大,整形越精确。但是,从毫秒级的时间尺度上说,就会有越多的突发传输。
minidle
如果 avgidle 值降到 0,也就是发生了越限,就需要等待,直到 avgidle 的值足够大才发送数据包。为避免因关闭链路太久而引起的以外突发传输,
在 avgidle 的值太低的时候会被强制设置为 minidle 的值。参数 minidle 的值是以负微秒记的。所以 10 代表 avgidle 被限制在-10us上。
mpu
最小包尺寸——因为即使是 0 长度的数据包,在以太网上也要生成封装成64 字节的帧,而需要一定时间去传输。为了精确计算闲置时间,CBQ 需要知道这个值。
rate
期望中的传输速率。也就是“油门”。
在 CBQ 的内部由很多的微调参数。比如,那些已知队列中没有数据的类就不参加计算、越限的类将被惩罚性地降低优先级等等。都非常巧妙和复杂。
3.6.2CBQ在分类方面的行为
除了使用上述 idletime 近似值进行整形之外,CBQ 还可以象 PRIO 队列那样进行分类,将各种类赋予不同的优先级,优先权数值小的类会比优先权值大的类被优先处理。
每当网卡请求把数据包发送到网络上时,都会开始一个 WRR(weighted round
robin,加权轮转)过程,从优先权值小的类开始。那些队列中有数据的类就会被分组并被请求出队。在一个类收到允许若干字节数据出队的请求之后,再尝试
下一个相同优先权值的类。
下面是控制 WRR 过程的一些参数:
allot
当从外部请求一个 CBQ 发包的时候,它就会按照“priority”参数指定的顺序轮流尝试其内部的每一个类的队列规定。当轮到一个类发数据时,它只能发送一定量的数据。"allot"参数就是这个量的基值。更多细节请参照“weight”参数。
prio
CBQ 可以象 PRIO 设备那样工作。其中“prio”值较低的类只要有数据就必须先服务,其他类要延后处理。
weight
“weight”参数控制 WRR
过程。每个类都轮流取得发包的机会。如果其中一个类要求的带宽显著地高于其他的类,就应该让它每次比其他的类发送更多的数据。 CBQ
会把一个类下面所有的 weight
值加起来后归一化,所以数值可以任意定,只要保持比例合适就可以。人们常把“速率/10”作为参数的值来使用,实际工作得很好。归一化值后的值乘以
“allot”参数后,决定了每次传输多少数据。
请注意,在一个 CBQ 内部所有的类都必须使用一致的主号码!
3.6.3决定链路的共享和借用的 CBQ 参数
除了纯粹地对某种数据流进行限速之外,CBQ 还可以指定哪些类可以向其它哪些类借用或者出借一部分带宽。
Isolated/sharing
凡是使用“isolated”选项配置的类,就不会向其兄弟类出借带宽。如果你的链路上同时存在着竞争对手或者不友好的其它人,你就可以使用这个选项。
选项“sharing”是“isolated”的反义选项。
bounded/borrow
一个类也可以用“bounded”选项配置,意味着它不会向其兄弟类借用带宽。选项“borrow”是“bounded”的反义选项。
一个典型的情况就是你的一个链路上有多个客户都设置成了“isolated”和“bounded”,那就是说他们都被限制在其要求的速率之下,且互相之间不会借用带宽。
在这样的一个类的内部的子类之间是可以互相借用带宽的。
3.6.4配置范例
这个配置把 WEB 服务器的流量控制为 5Mbps、SMTP 流量控制在 3Mbps 上。而且二者一共不得超过 6Mbps,互相之间允许借用带宽。我们的网卡是 100Mbps
的。
# tc qdisc add dev eth0 root handle 1:0 cbq bandwidth 100Mbit \
avpkt 1000 cell 8
# tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 100Mbit\
rate 6Mbit weight 0.6Mbit prio 8 allot 1514 cell 8 maxburst 20 \
avpkt 1000 bounded
这部分按惯例设置了根为 1:0,并且绑定了类 1:1。也就是说整个带宽不能超过6Mbps。
如前所述,CBQ 需要调整很多的参数。其实所有的参数上面都解释过了。相应的 HTB 配置则要简明得多。
# tc class add dev eth0 parent 1:1 classid 1:3 cbq bandwidth 100Mbit \
rate 5Mbit weight 0.5Mbit prio 5 allot 1514 cell 8 maxburst 20 \
avpkt 1000
# tc class add dev eth0 parent 1:1 classid 1:4 cbq bandwidth 100Mbit \
rate 3Mbit weight 0.3Mbit prio 5 allot 1514 cell 8 maxburst 20 \
avpkt 1000
建立了 2 个类。注意我们如何根据带宽来调整 weight 参数的。两个类都没有配置成“bounded”,但它们都连接到了类 1:1
上,而 1:1 设置了“bounded”。所以两个类的总带宽不会超过 6Mbps。别忘了,同一个 CBQ 下面的子类的主号码都必须与 CBQ
自己的号码相一致!
# tc qdisc add dev eth0 parent 1:3 handle 30: sfq
# tc qdisc add dev eth0 parent 1:4 handle 40: sfq
缺省情况下,两个类都有一个 FIFO 队列规定。但是我们把它换成 SFQ 队列,以保证每个数据流都公平对待。
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
sport 80 0xffff flowid 1:3
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
sport 25 0xffff flowid 1:4
这些命令规定了根上的过滤器,保证数据流被送到正确的队列规定中去。
注意:我们先使用了“tc class add” 在一个队列规定中创建了类,然后使用“tc qdisc add”在类中创建队列规定。
那些没有被那两条规则分类的数据流怎样处理了呢?从这个例子来说,它们被 1:0 直接处理,没有限制。如果 SMTP+web
的总带宽需求大于 6Mbps,那么这 6M 带宽将按照两个类的weight 参数的比例情况进行分割:WEB 服务器得到 5/8 的带宽,SMTP
得到 3/8的带宽。这里,可以认为:WEB数据流总是会得到5/8*6Mbps=3.75Mbps的带宽。
3.6.5其他CBQ参数:split 和 defmap
如前所述, 一个分类的队列规定需要调用过滤器来决定一个数据包应该发往哪个类去排队。
除了调用过滤器,CBQ 还提供了其他方式,defmap 和 split。很难掌握,但好在无关大局。
因为你经常是仅仅需要根据 TOS 来进行分类,所以提供了一种特殊的语法。当CBQ
需要决定了数据包要在哪里入队时,要检查这个节点是否为“split
节点”。如果是,子队列规定中的一个应该指出它接收所有带有某种优先权值的数据包,权值可以来自 TOS 字段或者应用程序设置的套接字选项。
数据包的优先权位与 defmap
字段的值进行“或”运算来决定是否存在这样的匹配。换句话说,这是一个可以快捷创建仅仅匹配某种优先权值数据包的过滤器的方法。如果 defmap 等于
0xff,就会匹配所有包,0 则是不匹配。这个简单的配置可以帮助理解:
# tc qdisc add dev eth1 root handle 1: cbq bandwidth 10Mbit allot 1514 \
cell 8 avpkt 1000 mpu 64
# tc class add dev eth1 parent 1:0 classid 1:1 cbq bandwidth 10Mbit \
rate 10Mbit allot 1514 cell 8 weight 1Mbit prio 8 maxburst 20 \
avpkt 1000
一个标准的 CBQ 前导。
Defmap 参照 TC_PRIO 位:
TC_PRIO..的数值对应它右面的 bit。关于 TOS 位如何换算成优先权值的细节可以参照 pfifo_fast 有关章节。
然后是交互和大吞吐量的类:
# tc class add dev eth1 parent 1:1 classid 1:2 cbq bandwidth 10Mbit \
rate 1Mbit allot 1514 cell 8 weight 100Kbit prio 3 maxburst 20 \
avpkt 1000 split 1:0 defmap c0
# tc class add dev eth1 parent 1:1 classid 1:3 cbq bandwidth 10Mbit \
rate 8Mbit allot 1514 cell 8 weight 800Kbit prio 7 maxburst 20 \
avpkt 1000 split 1:0 defmap 3f
“split 队列规定”是 1:0,也就是做出选择的地方。c0 是二进制的 11000000,3F是
00111111,所以它们共同匹配所有的数据包。第一个类匹配第 7 和第 6
位,也就是负责“交互”和“控制”的数据包。第二个类匹配其余的数据包。
节点 1:0 现在应该有了这样一个表格:
为了更有趣,你还可以传递一个“change 掩码”,确切地指出你想改变哪个优先权值。你只有在使用了“tc class change”的时候才需要。比如,往 1:2 中添加 best effort 数据流,应该执行:
# tc class change dev eth1 classid 1:2 cbq defmap 01/01
现在,1:0 上的优先权分布应该是:
3.7HTB(Hierarchical Token Bucket, 分层令牌桶)
HTB能够很好地满足这样一种情况:你有一个固定速率的链路,希望分割给多种不同的用途使用,为每种用途做出带宽承诺并实现定
量的带宽借用。
HTB就象CBQ一样工作,但是并不靠计算闲置时间来整形。它是一个分类的令牌桶过滤器。它只有很少的参数,并且在它的网站能够找到很好的文档。随着你
的HTB配置越来越复杂, 你的配置工作也会变得复杂。但是使用 CBQ 的话,即使在很简单的情况下配置也会非常复杂!HTB3
(关于它的版本情况,请参阅它的网站)已经成了官方内核的一部分(2.4.20-pre1、2.5.31 及其后)。然而,你可能仍然要为你的 tc
命令打上 HTB3 支持补丁,否则你的 tc 命令不理解 HTB3。如果你已经有了一个新版内核或者已经打了补丁,请尽量考虑使用 HTB。
详细说明与配置实例参见Bert Hubert:《Linux Advanced Routing & Traffic Control HOWTO》第九章9.5.5
3.6使用过滤器对数据包进行分类
为了决定用哪个类处理数据包,必须调用所谓的“分类器链” 进行选择。这个链中包含了这个分类队列规定所需的所有过滤器。
重复前面那棵树:
当一个数据包入队的时候,每一个分支处都会咨询过滤器链如何进行下一步。典型的配置是在 1:1 处有一个过滤器把数据包交给 12:,然后
12:处的过滤器在把包交给 12:2。你可以把后一个过滤器同时放在 1:1 处,但是将更具体的过滤器放在树的更低处能提高效率。
另外,你不能用过滤器把数据包向“上”送。而且,使用 HTB 的时候应该把所有的规则放到根上!
再次强调:数据包只能向“下”进行入队操作!只有出队的时候才会向上出队,到达网卡。他们不会落到树的最底层后送到网卡!
3.6.1过滤器的一些简单范例
就象在“分类器”那章所解释的,借助一些复杂的语法你可以详细地匹配任何事情。下面我们就开始,从简单地匹配一些比较明显的特征开始。
比方说,我们有一个 PRIO 队列规定,叫做“10:”,包含 3 个类,我们希望把去往22口的数据流发送到最优先的频道中去。应该这样设置过滤器:
# tc filter add dev eth0 protocol ip parent 10: prio 1 u32 \
match ip dport 22 0xffff flowid 10:1
# tc filter add dev eth0 protocol ip parent 10: prio 1 u32 \
match ip sport 80 0xffff flowid 10:1
# tc filter add dev eth0 protocol ip parent 10: prio 2 flowid 10:2
是说:
向eth0上的10:节点添加一个u32过滤规则,它的优先权是1:凡是去往22口(精确匹配)的IP数据包,发送到频道10:1。
向eth0上的10:节点添加一个u32过滤规则,它的优先权是1:凡是来自80口(精确匹配)的IP数据包,发送到频道10:1。
向eth0上的10:节点添加一个过滤规则,它的优先权是2:凡是上面未匹配的IP数据包,发送到频道 10:2。
必须指定eth0或者别的什么网卡的名字,为每个网卡的句柄都是独一无二的名字空间。
想通过 IP 地址进行筛选的话:
# tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 \
match ip dst 4.3.2.1/32 flowid 10:1
# tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 \
match ip src 1.2.3.4/32 flowid 10:1
# tc filter add dev eth0 protocol ip parent 10: prio 2 \
flowid 10:2
这个例子把去往 4.3.2.1 和来自 1.2.3.4 的数据包送到了最高优先的队列,其它的则送到次高权限的队列。
你可以连续使用 match,想匹配来自 1.2.3.4 的 80 口的数据包的话:
# tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 match ip src 4.3.2.1/32 match ip
sport 80 0xffff flowid 10:1
3.6.2常用到的过滤命令一览
这里列出的绝大多数命令都根据这个命令改编而来:
# tc filter add dev eth0 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 发出的数据包做整形,这就很有用了。语法是这样的:
#tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 6 fw flowid 1:1
注意,这不是一个 u32 匹配!
你可以象这样给数据包打标记:
# iptables -A PREROUTING -t mangle -i eth0 -j MARK --set-mark 6
数字 6 是可以任意指定的。
如果你不想去学习所有的 tc 语法,就可以与 iptables 结合,仅仅学习按fwmark 匹配就行了。
按 TOS 字段
选择交互和最小延迟的数据流:
# tc filter add dev ppp0 parent 1:0 protocol ip prio 10 u32 \
atch ip tos 0x10 0xff flowid 1:4
想匹配大量传输的话,使用“0x08 0xff”。
关于更多的过滤命令,请参照Bert Hubert:《Linux Advanced Routing & Traffic Control HOWTO》
3.7 IMQ(Intermediate queueing device,中介队列设备)
中介队列设备不是一个队列规定, 但它的使用与队列规定是紧密相连的。 就Linux而言,队列规定是附带在网卡上的,所有在这个网卡上排队的数据都排进这个队列规定。根据这个概念,出现了两个局限:
1. 只能进行出口整形(虽然也存在入口队列规定,但在上面实现分类的队列规定的可能性非常小)。
2. 一个队列规定只能处理一块网卡的流量,无法设置全局的限速。
IMQ 就是用来解决上述两个局限的。简单地说,你可以往一个队列规定中放任何东西。被打了特定标记的数据包在netfilter的
NF_IP_PRE_ROUTING和NF_IP_POST_ROUTING两个钩子函数处被拦截,并被送到一个队列规定中,该队列规定附加到一个IMQ
设备上。 对数据包打标记要用到 iptables 的一种处理方法。这样你就可以对刚刚进入网卡的数据包打上标记进行入口整形,
或者把网卡们当
成一个个的类来看待而进行全局整形设置。你还可以做很多事情,比如:把 http流量放到一个队列规定中去、把新的连接请求放到一个队列规定中去、......
详细的配置范例参见Bert Hubert:《Linux Advanced Routing & Traffic Control HOWTO》。
阅读(2243) | 评论(0) | 转发(0) |