linux流量控制(三)

本文详细介绍了Linux流量控制(QoS)中的过滤器、路由分类器和DSCP标记,包括u32选择器、普通选择器、特殊选择器的使用,以及Dsmark、TC_INDEX过滤器的工作原理。内容涵盖流量整形、路由策略和网络服务质量的实现,旨在帮助读者理解和配置Linux网络的流量控制策略。
其它可能性
William Stearns已经利用高级隧道来达到捆绑多重Internet连接的效果.可以在他的隧道网页找到.
本HOWTO将来可能更多地描述这个问题.
/etc/iproute2/rt_tables
# ip rule add fwmark 1 table mail.out
# ip rule ls
0:from all lookup local
32764:
from all fwmark 1 lookup mail.out
32766:
from all lookup main
32767:
from all lookup default
现在我们建立一个通往那条便宜链路的路由,从而生成mail.out路由表:
# /sbin/ip route add default via 195.96.98.253 dev ppp0 table mail.out
这 就做完了.我们可能需要一些例外,有很多方法都能达到目的.我们可以修改netfilter命令来排除一些主机,也可以插入一些优先权值更低的规则把需要 排除的主机的数据包发往main路由表.我们还可以通过识别数据包的TOS位,来给不同服务类型的数据包打上不同的标记,再为它们分别建立规则.你甚至可 以利用这种方法让诸如ISDN线路支持交互业务.
不用说,这当然也可以用于正在进行NAT("伪装")的机器上.重要提醒:我们收到报告说MASQ和SNAT功能与数据包标记有冲突.RustyRussell在这个帖子中作了解释.关闭反方向的过滤就可以正常工作.注意:想给数据包打标记的话,你的内和需要一些配置:
IP: advanced router (CONFIG_IP_ADVANCED_ROUTER) [Y/n/ ]
IP: policy routing (CONFIG_IP_MULTIPLE_TABLES) [Y/n/ ]
IP: use netfilter MARK value as routing key (CONFIG_IP_ROUTE_FWMARK) [Y/n/ ]
参考方便菜谱一章中的15.5.

第12章 对包进行分类的高级过滤器
就象在分类的队列规定一段中解释的,过滤器用与把数据包分类并放入相应的子队列.这些过滤器在分类的队列规定内部被调用.下面就是我们可用的分类器(部分):
fw
根据防火墙如何对这个数据包做标记进行判断.如果你不想学习tc的过
滤器语法,这倒是一个捷径.细节请参见队列那一章.
u32
根据数据包中的各个字段进行判断,如源IP地址等等.
route
根据数据包将被哪条路由进行路由来判断.
rsvp, rsvp6
根据数据包的RSVP情况进行判断.只能用于你自己的网络,互联网并不
遵守RSVP.
tcindex
用于DSMARK队列规定,参见相关章节.
通常来说,总有很多途径可实现对数据包分类,最终取决于你喜欢使用哪种系统.
分类器一般都能接受几个参数,为了方便我们列出来:
protocol
这个分类器所接受的协议.一般来说你只会接受IP数据.必要参数.
parent
这个分类器附带在哪个句柄上.句柄必须是一个已经存在的类.必要参数.
prio
这个分类器的优先权值.优先权值低的优先.
handle
对于不同过滤器,它的意义不同.
后面所有的节都假定你试图对去往HostA的流量进行整形.并且假定根类配置为
1:,并且你希望把选中的数据包送给1:1类.
64

12.1. u32分类器
U32分类器是当前实现中最先进的过滤器.全部基于哈希表实现,所以当有很多
过 滤器的时候仍然能够保持健壮.U32过滤器最简单的形式就是一系列记录,每条记录包含两个部分:一个选择器和一个动作.下面要讲的选择器用来与IP包相匹 配,一旦成功匹配就执行其指定的动作.最简单的动作就是把数据包发送到特定的类队列.用来配置过滤器的tc命令行由三部分组成:过滤器说明,选择器和动 作.一个过滤器可以如下定义:
tc filter add dev IF [ protocol PROTO ]
[ (preference|priority) PRIO ]
[ parent CBQ ]
上 面行中,protocol字段描述了过滤器要匹配的协议.我们将只讨论IP协议的情况.preference字段(也可以用priority代替)设置该 过滤器的优先权.这非常重要,因为你可能有几条拥有不同优先权的过滤器.每个过滤器列表都按照输入的顺序被扫描一遍,然后优先权值低(更高的偏好值)的列 表优先被处理."parent"字段定义了过滤器所属的CBQ的顶部(如1:0).上面描述的选项适用于所有过滤器,而不仅仅适用于U32.

12.1.1. U32选择器
u32选择器包含了能够对当前通过的数据包进行匹配的特征定义.它其实只是定义了IP包头中某些位的匹配而已,但这种看似简单的方法却非常有效.让我们看看这个从实际应用的系统中抄来的例子:
# tc filter add dev eth0 protocol ip parent 1:0 pref 10 u32 /
match u32 00100000 00ff0000 at 0 flowid 1:10
现 在,命令的第一行已经不用解释了,前面都说过了.我们把精力集中在用"match"选项描述选择器的第二行.这个选择器将匹配那些IP头部的第二个字节是 0x10的数据包.你应该猜到了,00ff就是匹配掩码,确切地告诉过滤器应该匹配哪些位.在这个例子中是0xff,所以会精确地匹配这个字节是否等于 0x10.
"at"关键字的意思是指出从数据包的第几个字节开始匹配——本例中是从数据包的开头开始.完全地翻译成人类语言就是:"匹配那些TOS字段带有'最小延迟'属性的数据包".让我们看看另一个例子:
# tc filter add dev eth0 protocol ip parent 1:0 pref 10 u32 /
match u32 00000016 0000ffff at nexthdr+0 flowid 1:10
"nexthdr" 选项意味着封装在IP包中的下一个PDU的头部,也就是说它的上层协议的头.匹配操作就是从这个协议的头部开始的,应该发生在头部开始的第16位处.在 TCP和UDP协议的头部,这个部分存放的是这个报文的目标端口.数字是按照先高厚低的格式存储的,所以0x0016就是十进制的22(如果是TCP
的话就是ssh服务).其实,这个匹配在没有上下文的情况下含义很模糊,我们放在后面讨论.理解了上面的例子之后,下面这条选择器就很好懂了:  match c0a80100 ffffff00 at 16
表示了:匹配从IP头开始数的第17个字节到第19个字节.这个选择器将匹配所有去往192.168.1.0/24的数据包.成功分析完上面这个例子后,我们就已经掌握u32选择器了.

12.1.2. 普通选择器
普通选择器定义了要对数据包进行匹配的特征,掩码和偏移量.使用普通选择器,你实际上可以匹配IP(或者上层协议)头部的任意一个bit,虽然这样的选择器比特殊选择器难读和难写.一般选择器的语法是:
match [ u32 | u16 | u8 ] PATTERN MASK [ at OFFSET | nexthdr+OFFSET]利用u32,u16或u8三个关键字中的一个来指明特征的bit数.然后PATTERN和MASK应该按照它定义的长度紧 挨着写.OFFSET参数是开始进行比较的偏移量(以字节计).如果给出了"nexthdr+"关键字,偏移量就移到上层协议头部开始的位置.一些例子:
# tc filter add dev ppp14 parent 1:0 prio 10 u32 /
match u8 64 0xff at 8 /
flowid 1:4
如果一个数据包的TTL值等于64,就将匹配这个选择器.TTL就位于IP包头的第9个字节.匹配带有ACK位的TCP数据包:
# tc filter add dev ppp14 parent 1:0 prio 10 u32 /
match ip protocol 6 0xff /
match u8 0x10 0xff at nexthdr+13 /
flowid 1:3
用这个匹配小于64字节的ACK包:
## match acks the hard way,
## IP protocol 6,
## IP header length 0x5(32 bit words),
## IP Total length 0x34 (ACK + 12 bytes of TCP options)
## TCP ack set (bit 5, offset 33)
# tc filter add dev ppp14 parent 1:0 protocol ip prio 10 u32 /
match ip protocol 6 0xff /
match u8 0x05 0x0f at 0 /
match u16 0x0000 0xffc0 at 2 /
match u8 0x10 0xff at 33 /
flowid 1:3
这 个规则匹配了带有ACK位,且没有载荷的TCP数据包.这里我们看见了同时使用两个选择器的例子,这样用的结果是两个条件进行逻辑"与"运算.如果我们查 查TCP头的结构,就会知道ACK标志位于第14个字节的第5个bit(0x10).作为第二个选择器,如果我们采用更复杂的表达,可以写成"match u8 0x06 0xffat 9",而不是使用特殊选择器protocol,因为TCP的协议号是6(写在IP头的第十个字节).另一方面,在这个例子中我们不使用特殊选择器也是因为 没有用来匹配TCP的ACK标志的特殊选择器.下面这个选择器是上面选择器的改进版,区别在于它不检查IP头部的长度.为什么呢?因为上面的过滤器只能在 32位系统上工作.
tc filter add dev ppp14 parent 1:0 protocol ip prio 10 u32 /
match ip protocol 6 0xff /
match u8 0x10 0xff at nexthdr+13 /
match u16 0x0000 0xffc0 at 2 /
flowid 1:3

12.1.3. 特殊选择器
下面的表收入了本节文档的作者从tc程序的源代码中找出的所有特殊选择器.它们能够让你更容易,更可靠地配置过滤器.
求助: table placeholder - the table is in separate file ,,selector.html''
求助: it's also still in Polish :-(
求助: must be sgml'ized
一些范例:
# tc filter add dev ppp0 parent 1:0 prio 10 u32 /
match ip tos 0x10 0xff /
flowid 1:4
求助: tcp dport match does not work as described below:
上 述规则匹配那些TOS字段等于0x10的数据包.TOS字段位于数据包的第二个字节,所以与值等价的普通选择器就是:"match u8 0x10 0xff at 1".这其实给了我们一个关于U32过滤器的启示:特殊选择器全都可以翻译成等价的普通选择器,而且在内核的内存中,恰恰就是按这种方式存储的.这也可以 导出另一个结
论:tcp和udp的选择器实际上是完全一样的,这也就是为什么不能仅用"matchtcp dport 53 0xffff"一个选择器去匹配发到指定端口的TCP包,因为它也会匹配送往指定端口的UDP包.你一定不能忘了还得匹配协议类型,按下述方式来表
示:
# tc filter add dev ppp0 parent 1:0 prio 10 u32 /
match tcp dport 53 0xffff /
match ip protocol 0x6 0xff /
flowid 1:2

12.2. 路由分类器
这个分类器过滤器基于路由表的路由结果.当一个数据包穿越一个类,并到达一个标有"route"的过滤器的时候,它就会按照路由表内的信息进行分裂.当一个数据包遍历类,并到达一个标记"路由"过滤器的时候,就会按照路由表的相应信息分类.
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 route
我们向节点1:0里添加了一个优先级是100的路由分类器.当数据包到达这个节点时,就会查询路由表,如果匹配就会被发送到给定的类,并赋予优先级100.要最后完成,你还要添加一条适当的路由项:这里的窍门就是基于目的或者源地址来定义"realm".象这样做:
# ip route add Host/Network via Gateway dev Device realm RealmNumber
例如,我们可以把目标网络192.168.10.0定义为realm 10:
# ip route add 192.168.10.0/24 via 192.168.10.1 dev eth1 realm 10
我们再使用路由过滤器的时候,就可以用realm号码来表示网络或者主机了,并可以用来描述路由如何匹配过滤器:
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 /
route to 10 classid 1:10
这个规则说:凡是去往192.168.10.0子网的数据包匹配到类1:10.路由过滤器也可以用来匹配源策略路由.比如,一个Linux路由器的eth2上连接了一个子网:
# ip route add 192.168.2.0/24 dev eth2 realm 2
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 /
route from 2 classid 1:2
这个规则说:凡是来自192.168.2.0子网(realm 2)的数据包,匹配到1:2.

12.3. 管制分类器
为 了能够实现更复杂的配置,你可以通过一些过滤器来匹配那些达到特定带宽的数据包.你可以声明一个过滤器来来按一定比率抑制传输速率,或者仅仅不匹配那些超 过特定速率的数据包.如果现在的流量是5M,而你想把它管制在4M,那么你要么可以停止匹配整个的5M带宽,要么停止匹配1M带宽,来给所配置的类进行 4M速率的传输.如果带宽超过了配置的速率,你可以丢包,可以重新分类或者看看是否别的过滤器能匹配它.

12.3.1. 管制的方式
有两种方法进行管制.如果你编译内核的时候加上了"Estimators",内核就可以替你为每一个过滤器测
量通过了多少数据,多了还是少了.这些评估对于CPU来讲非常轻松,它只不过是每秒钟累计25次通过了多少数据,计算出速率.另一种方法是在你的过滤器内部,通过TBF(令牌桶过滤器)来实现.TBF只匹配
到达到你配置带宽的数据流,超过的部分则按照事先指定的"越限动作"来处理.
12.3.1.1. 靠内核评估
这 种方式非常简单,只有一个参数"avrate".所有低于avrate的数据包被保留,并被过滤器分到所指定的类中去,而那些超过了avrate的数据包 则按照越限动作来处理,缺省的越限动作是"reclassify"(重分类).内核使用EWMA算法来核算带宽,以防止对瞬时突发过于敏感.
12.3.1.2. 靠令牌桶过滤器
使用下列参数:
buffer/maxburst
mtu/minburst
mpu
rate
它 们的意义与前面介绍TBF时所说的完全一样.但仍然要指出的是:如果把一个TBF管制器的mtu参数设置过小的话,将没有数据包通过,whereas the egressTBF qdisc will just pass them slower.另一个区别是,管制器只能够通过或者丢弃一个数据包,而不能因为为了延迟而暂停发送.
12.3.2. 越限动作
如果你的过滤器认定一个数据包越限,就会按照"越限动作"来处理它.当前,支持三种动作:
continue
让这个过滤器不要匹配这个包,但是其它的过滤器可能会匹配它.
drop
这是个非常强硬的选项——让越限的数据包消失.它的用途不多,经常被用于ingress管制.比如,你的DNS在请求流量大于5Mbps的时候就会失灵,你就可以利用这个来保证请求量不会超标.
Pass/OK
让数据包通过.可用于避免复杂的过滤器,但要放在合适的地方.
reclassify
最经常用于对数据包进行重分类以达到最好效果.这是缺省动作.
12.3.3. 范例
现在最真实的范例就是下面第十五章提到的"防护SYN洪水攻
".求助: if you have used this, please share your experience with us
12.4. 当过滤器很多时如何使用散列表
如果你需要使用上千个规则——比如你有很多需要不同QoS的客户机——你可能会发现内核花了很多时间用于匹配那些规则.缺省情况下,所有的过滤器都是靠一个链表来组织的,链表按priority的降序排
列. 如果你有1000个规则,那么就有可能需要1000次匹配来决定一个包如何处而如果你有256个链表,每个链表4条规则的话,这个过程可以更快.也就是说 如果你能把数据包放到合适的链表上,可能只需要匹配4次就可以了.利用散列表可以实现.比如说你有1024个用户使用一个Cable MODEM,IP地
址范围是1.2.0.0到1.2.3.255,每个IP都需要不同容器来对待,比如"轻量级","中量级"和"重量级".你可能要写1024个规则,象这样:
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src /
1.2.0.0 classid 1:1
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src /
1.2.0.1 classid 1:1
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src /
1.2.3.254 classid 1:3
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src /
1.2.3.255 classid 1:2
为了提高效率,我们应该利用IP地址的后半部分作为散列因子,建立256个散列表项.第一个表项里的规则应该是这样:
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src /
1.2.0.0 classid 1:1
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src /
1.2.1.0 classid 1:1
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src /
1.2.2.0 classid 1:3
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src /
1.2.3.0 classid 1:2
下一个表项应该这么开始:
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 match ip src /
1.2.0.1 classid 1:1
...
这样的话,最坏情况下也只需要4次匹配,平均2次.
具体配置有些复杂,但是如果你真有很多规则的话,还是值得的.我们首先生成root过滤器,然后创建一个256项的散列表:
# tc filter add dev eth1 parent 1:0 prio 5 protocol ip u32
# tc filter add dev eth1 parent 1:0 prio 5 handle 2: protocol ip u32 divisor 256
然后我们向表项中添加一些规则:
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: /
match ip src 1.2.0.123 flowid 1:1
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: /
match ip src 1.2.1.123 flowid 1:2
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: /
match ip src 1.2.3.123 flowid 1:3
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 2:7b: /
match ip src 1.2.4.123 flowid 1:2
这 是第123项,包含了为1.2.0.123,1.2.1.123,1.2.2.123和1.2.3.123准备的匹配规则,分别把它们发给1:1,1: 2,1:3和1:2.注意,我们必须用16进制来表示散列表项,0x7b就是123.然后创建一个"散列过滤器",直接把数据包发给散列表中的合适表项:
# tc filter add dev eth1 protocol ip parent 1:0 prio 5 u32 ht 800:: /
match ip src 1.2.0.0/16 /
hashkey mask 0x000000ff at 12 /
link 2:
好 了,有些数字需要解释.我们定义散列表叫做"800:",所有的过滤都从这里开始.然后我们选择源地址(它们位于IP头的第12,13,14,15字 节),并声明我们只对它的最后一部分感兴趣.这个例子中,我们发送到了前面创建的第2个散列表项.这比较复杂,然而实际上确实有效而且性能令人惊讶.注 意,这个例子我们也可以处理成理想情况——每个表项中只有一个过滤器!
/proc/sys/net/ipv4/conf//log_martians
求助: is setting the conf/[default,all]/* files enough - martijn
13.2. 深层设置
有很多参数可以修改.我们希望能够全列出来.在Documentation/ip-sysctl.txt中也有部分记载.
这些设置中的部分缺省值取决于你在内核配置时是否选择了"Configure as routerand not host".
Oskar Andreasson也有一个网页比我们讨论得更详细的网页:
http://ipsysctl-tutorial.frozentux.net/
第14章 不经常使用的高级队列规定
你应该发现有时候前面提到的那些队列不能满足你的需要,这里列出了一些内核包含的其它更多类型的队列。
14.1. bfifo/pfifo
这 些无类的队列因为没有内部频道而显得比pfifo_fast还要简单,所有的流量都均等处理。但是它们还是有一个好处:他们可以做统计。所以即使你不想整 形或排序,你也可是使用这个队列规定来检查网卡的后台日志。 pfifo的长度单位以包的个数计,而bfifo以字节计。  
14.1.1. 参数与使用
limit
规定了队列的长度。对于bfifo用字节计,对于pfifo用包的个数计。缺省值就是网卡的txqueuelen个包那么长(参见pfifo_fast那一章),对于bfifo就是txqueuelen*mtu个字节。
14.2. Clark-Shenker-Zhang算法 (CSZ)
它的理论性是如此之强,以至于连Alexey(CBQ的主要作者)都不指望去理解它。从它的原始资料来看:
David D. Clark、Scott Shenker和Lixia Zhang  在综合业务分组网络中支持实时应用系统:体系与机制。 据我的理解,它的主要意图是为每一个担保的服务创建WFQ流,并分配剩下的带宽给伪设备flow-0。Flow-0由服务预测和best effort traffic组成,并由一个优先调度器来处理??(谁能帮忙翻一
下?)。As I understand it, the main idea is to create WFQ flows for each guaranteed service and to allocate the rest of bandwith to dummy flow-0. Flow-0 comprises the predictive services and the best effort traffic; it is handled by a priority scheduler with the highest priority
band allocated for predictive services, and the rest --- to the best effort packets. Note that in CSZ flows are NOT limited to their bandwidth. It issupposed that the flow passed admission control at the edge of the QoS network and it doesn't need further shaping. Any attempt to improve the flow or to shape it to a token bucket at intermediate hops will
introduce undesired delays and raise jitter. 迄今为止,CSZ是唯一一个能够真正提供服务担保的调度器。其它的方案(包括CBQ)不提供延迟保证和随机抖动。" 现在看来不是一个好的候选方案,除非你已经阅读并理解了上面提到的文章。
14.3. DSMARK
Esteve Camps < marvin@grn.es>
这些文字是从我2000年9月的《Linux的QoS》支持这篇论文上摘录的。  
参考文档:
* Draft-almesberger-wajhak-diffserv-linux-01.txt.  
* iproute2发行版中的范例。
* QOS论坛的QoS协议与体系白皮书和IP QoS常见问题。
本章作者:Esteve Camps < esteve@hades.udg.es>.
14.3.1. 介绍
首 先的首先,你应该到IETF DiffServ工作组的网站和Werner Almesberger的网站(他为Linux的Differentiated Services撰写了代码)阅读一下有关的RFC(RFC2474、RFC2475、RFC2597和RFC2598).
14.3.2. Dsmark与什么相关?
Dsmark 是一个队列规定,提供了Differentiated Services(也叫DiffServ,简称为DS)所需要的能力。DiffServ是两种QoS体系中的一种(另一种叫做Integrated Services),基于IP头中DS字段的值来工作。 最开始的实现方案中的一种是IP头中的服务类型字段(TOS值),也就是设计由IP来提供一些QoS级别。改变这个值,我们可以选择吞吐量、延迟或者可靠 性的高/低级别。但是这并没有提供足够的灵活性来应付新的服务(比如说实时应用、交互应用等等)。后来,新的体系出现了。其中之一就是DiffServ, 保留了TOS位并renamed DS字段。
14.3.3. Differentiated Services指导
Differentiated Services是面向组的。也就是说,我们没有必要了解数据流(那是Integrated Services的风格),我们只了解“流会聚”,并根据一个数据包属于哪个流会聚来采取不同的行动。 当一个数据包到达边缘节点(entry node to a DiffServ domain),并进入DiffServ 域之后,我们就得进行对它们进行策略控制、整形或标记(关于标记请参考“给DS字段分配一个值”。就象一头牛一样。? )。这个值将被DiffServ域中的内部/核心代码参考,以决定如何处置或者适用什么QoS级别。 如你的推断,DS包括一个“域”的概念,他规定了所有DS规则的作用范围。事实上你可以认为我把所有的数据包都分类到我的域中。他们一旦进入我的域,就会 根据分类而隶属于某条规则,每个所经过的节点都会应用那个QoS级别。
其实你可以在你的本地域内使用你自己的策略控制器,但当与其它的DS域相连时要考虑一些服务级别协议。 这里,你可能由很多问题。DiffServ不仅限于我所解释的。其实你应该能理解我不可能在50行中引用3个RFC。
14.3.4. 使用Dsmark
根 据DiffServ文档的说法,我们区分边界节点和内部节点。这是传输路径上的两个关键点。但数据包到达时二者都会对包进行分类。在数据包被发送到网络上 之前,它的分类结果可能会被这个DS过程的其他地方用到。正是因此,Diffserv的代码提供了一个叫做sk_buff的结构,包含了一个新的字段叫做 skb->tc_index
用来存储一开始分类的结果,以便其它可能用到这个结果的地方引用。
skb->tc_index的值被DSMARK队列规定根据每个IP包头的DS字段的值进行初
始设置。此外,cls_tcindex分类器将读取skb->tcindex的全部或部分,并用来选择类。 但是,首先看一看DSMARK队列规定的命令行和参数:
... dsmark indices INDICES [ default_index DEFAULT_INDEX ] [ set_tc_index ]
这些参数是什么意思呢?
* indices:存储(掩码,数值)的表格尺寸。最大值是2^n (n≥0)。
* Default_index: 当分类器没有找到匹配项时的缺省表格项索引。
* Set_tc_index: 给dsmark设置接收DS字段并存储到skb->tc_index规定。
让我们看看DSMARK的工作过程.
14.3.5. SCH_DSMARK如何工作
这个队列规定将进行下列步骤:
* 如果我们在队列规定的命令行中声明了set_tc_index选项,DS字段的值将被取出并存储在skb->tc_index中。
* 调用分类器。分类器被调用并将返回将被存储于skb_tcindex的类编号。如果没有找到能匹配的过滤器,会返回default_index的值。如果既没有声明set_tc_index又没有声明default_index的值,结果可能是不可预知的。
* 在数据包被发送到你可以重用分类器的结果的内部队列之后,由内部队列规定返回的类代码被保存在skb->tc_index。我们将来检索mask- value 表的时候还会用到这个值。随后的结果将决定数据包下一步的处理: New_Ds_field = ( Old_DS_field & mask ) | value
* 然后,值将与ds_field的值和掩码进行“与”运算,然后再与参数值相“或”。下面的图表有利于你理解这个过程:

如 何进行标记?只需改变你想重新标记的类的掩码和值就行了。看着一行代码: tc class change dev eth0 classid 1:1 dsmark mask 0x3 value 0xb8 它改变了散列表中的(mask,value)记录,来重新标记属于类1:1的数据包。你必须使用“change”命令,因为(mask,value)记录 已经有了缺省值。(see table below). 现在,我们解释一下TC_INDEX过滤器如何工作和适用场合。此外,TC_INDEX过滤器也可以应用于没有DS服务的配置下。
14.3.6. TC_INDEX过滤器
这是声明TC_INDEX 过滤器的基本命令:
... tcindex [ hash SIZE ] [ mask MASK ] [ shift SHIFT ]
            [ pass_on | fall_through ]
            [ classid CLASSID ] [ police POLICE_SPEC ]
接下来,我们说明一下用来解释TC_INDEX操作模式的例子。注意斜体字:
tc qdisc add dev eth0 handle 1:0 root dsmark indices 64 set_tc_index  
tc filter add dev eth0 parent 1:0 protocol ip prio 1 tcindex mask 0xfc shift 2  
tc qdisc add dev eth0 parent 1:0 handle 2:0 cbq bandwidth 10Mbit cell 8 avpkt 1000 mpu 64 # EF
traffic class  
tc class add dev eth0 parent 2:0 classid 2:1 cbq bandwidth 10Mbit rate 1500Kbit avpkt 1000 prio
1 bounded isolated allot 1514 weight 1 maxburst 10 # Packet fifo qdisc for EF traffic  
tc qdisc add dev eth0 parent 2:1 pfifo limit 5 tc filter add dev eth0 parent 2:0 protocol ip
prio 1 handle 0x2e tcindex classid 2:1 pass_on  
(这 里的代码尚未完成。只是从iproute2发行中的EFCBQ范例中摘出来的。)。 首先,假设我们收到标记为“EF”的数据包。如果你读了RFC2598,你就会明白DSCP推荐EF数据包的值是101110。也就是说DS字段的值是 10111000(记住TOS字节中次要的位没有在DS中使用)或者是16进制的0xb8。
               
数据包到来后就把DS字段设置为0xb8。象前面所解释的,例子中被标记为1:0的dsmark队列规定找到DS字段的值并存储在skb->tc_index变量中。下一步将对应关联到这个队列规定的过滤器(例子中的第二行)。这将进行下列操作:
Value1 = skb->tc_index & MASK
Key = Value1 >> SHIFT
在例子中,MASK=0xFC i SHIFT=2.  
Value1 = 10111000 & 11111100 = 10111000
Key = 10111000 >> 2 = 00101110 -> 0x2E in hexadecimal
返 回值将对应一个队列规定的内部过滤器句柄(比如说:2:0)。如果这个编号的过滤器存在的话,将核对管制和测量条件(如果过滤器有这些的话),并返回类编 号(我们的例子中是2:1),存储于skb->tc_index变量。 但是如果找到了那个编号的过滤器,结果将取决于是否声明了fall_through标志。如果有,key值将作为类编号返回。如果没有,将返回一个错误并 继续处理其它过滤器。要当心的是如果你使用了fall_through标志,并且在skb->tc_index和类编号之间存在着一个简单的关系。 Be careful if you use fall_through flag; this can be done if a simple relation exists between values of skb->tc_index variable and class id's. 最后要说的参数是hash和pass_on。前者与散列表的大小有关。后者用于指出如果找不到这个过滤器的返回值指定的类编号,就进行下一个过滤器。缺省 行为是is fall_through (参见下表)。 最后,让我们看一看TCINDEX的参数都有那些可能的值:  
TC Name                 Value           Default
-----------------------------------------------------------------
Hash                    1...0x10000     Implementation dependent
Mask                    0...0xffff        0xffff
Shift                     0...15           0
Fall through / Pass_on  Flag             Fall_through
Classid                  Major:minor     None
Police                      .....           None
这 种过滤器的功能非常强大,有必要挖掘它所有的可能性。此外,这种过滤器不仅仅适用于Diffserv配置,你可以与其它任意类型的过滤器一起使用。 我建议你看看iproute2中自带的所有的Diffserv范例。我保证我会尽力补全这个文档。此外,我所解释的内容是很多测试的结果。如果你能指出我 的错误我将非常感激。
14.4. 入口队列规定
迄今为止所讨论的队列规定都是出口队列规定。每个网卡其实还都可以拥有一个入口队列 规定,它不是用来控制向网络上发数据包的,而是用来让你对从网络上收到的数据包应用tc过滤器的,而不管它们是发到本机还是需要转发。 由于tc过滤器包括了完整地实现了令牌桶过滤器,并且也能够匹配内核的流评估,所以能够实现非常多的功能。这些能够有效地帮助你对输入流进行管制,甚至在 它尚未进入IP协议层之前。
14.4.1. 参数与使用
输入队列规定本身并不需要任何参数。它不同于其它队列规定之处在于它不占用网卡的根。象这样设置:  
# tc qdisc add dev eth0 ingress
这能够让你除了拥有输入队列规定之外,还有其它的发送队列规定。 关于输入队列的一些人为的例子,请参看“方便菜谱”。
14.5. RED(Random Early Detection,随机提前检测)  
这 一节的意图是引入骨干路由,经常包括<100Mbps的速率,这同您家里的ADSL MODEM和CABLE MODEM的实现有所不同。 Internet上路由器里面的队列的普遍行为称作“截尾”。截尾的原理就是排队到一定量之后就开始丢弃后来“溢出”的数据包。这非常的不公平,并且还会 引发重发同步问题,也就是说路由器突然地突发性丢包会导致被丢包者后来突发地重发,进一步地造成路由器拥塞。 为了克服链路上的瞬时拥塞,骨干路由器经常做成大队列。不幸的是,这样做提高了吞吐量的同时也造成了延迟的增加,并导致TCP连接在拥挤的时候速率变得非 常不均衡。 这些由于截尾带来的问题在Internet上不友好的应用程序越来越多而变得越来越棘手。Linux内核提供了RED(Random Early Detection,随机提前检测),更直观的名字叫Random Early Drop(随机提前丢包)。 RED并不能解决所有问题,那些未能实现指数backoff的应用程序仍然会不公平地共享带宽,但是有了RED至少它们不会对其它连接的吞吐量和延迟造成 太大的影响。 RED在数据流没有达到它的带宽硬限制之前就统计地丢弃流中的数据包。让拥塞的骨干链路比较缓和地慢下来,从而防止重传同步问题。因为有意地丢弃了一
些 数据包,从而可控地减小队列尺寸和延迟,这有助于让TCP更快地找到它们“公平的”传输速率。特定连接的包被丢弃的可能性取决于它的带宽占用,而不取决于 它发包的数量。  RED在骨干网上一种很好的队列,尤其是那些无法提供公平队列的场合,因为基于会话状态的分别跟踪很困难。 为了使用RED,你必须确定三个参数:min、max和burst。Min设置了队列达到多少字节时开始进行丢包,Max是一个软上限,让算法尽量不要超 过,burst设置了最多有多少个数据包能够突发通过。 对于min的值,你应该算出可以接受的最高延迟,然后乘以你的带宽。比如,在我的64kbps的ISDN上,我希望基础队列延迟是200ms,所以我设置 min的值是1600字节。min的值设置得太小会影响吞吐量,而太大则会影响延迟。在慢速的链路上,把min设小并不能代替减小MTU来改善交互响应。 为了避免重传同步,你至少要把max的值设置为min的两倍。在慢速链路上,由于min的值较小,最好应该设置max的值是min的四倍以上。 Burst控制RED算法如何响应突发。Burst必须设置大于min/avpkt。经验上讲,我发现 (min+min+max)/(3*avpkt)就不错。 另外,你需要设置limit和avpkt。Limit是一个非常安全的值,当队列中有了这么多字节之后,RED就开始截尾。我一般设置limit值为 max的八倍。Avpkt应该是你的平均包大小,在MTU是1500字节的Internet链路上,1000就可以。 关于技术信息,请阅读Sally Floyd和Van Jacobson 的关于RED队列的文章。
14.6. GRED(Generic Random Early Detection,一般的随机提前检测)
关 于GRED不了解多少。好象GRED有很多内置的队列,根据Diffserv tcindex的值来决定选择哪个内部队列。根据这里找到的一个幻灯片,它包含了CISCO的“分布式加权RED”和Dave Clark的RIO的功能。 每个虚拟队列都能分别设置丢包参数。 求助: get Jamal or Werner to tell us more
14.7. VC/ATM模拟
你可以在TCP/IP套接字上建立虚点路,这是Werner Almesberger的一个只要成果。虚电路是ATM的一个概念。  更多细节请参照ATM on Linux 网页。
14.8. WRR(Weighted Round Robin,加权轮转)
这 个队列规定没有包括在标准内核中,弹你可以从这里下载。迄今,这个队列规定仅在Linux 2.2上测试过,但大概也能够在2.4/2.5上工作。 WRR队列规定在使用了WRR的各个类之间分配带宽。也就是说,想CBQ队列规定那样,它能够包含子类并插入任意队列规定。所有满足要求的类都会按照该类 权值的比例分得带宽。权值可以利用tc程序手工设置。但对于那些要传输达量数据的类也可以设置为自动递减。 队列规定有一个内置分类器,用来把来自或发往不同机器的数据包分到不同的类。可以使用MAC地址/IP地址和源IP地址/目标IP地址。MAC只有在你的 Linux机器作为一个网桥的时候才能使用。根据数据包的情况,自动把类分配到某一台机器上。 这个队列规定对于象宿舍这样许多不相关的个体共享一个Internet连接的时候非常有用。由一套脚本来为WRR中心的机器设置相关的行为。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值