原文出处:
相对于其它的队列规定来说,DSMARK的设定是非常简单的。人们经常抱怨说DSMARK很难用,那是因为现有的文档都太高深啦。我将试着在我的理解能力之内在这一节把DSMARK队列规定解释清楚。
首先我们要明白,与其它队列规定相反,DSMARK不进行整形,控制流量等。它也不对数据包进行优先,延时,重发或者丢弃等动作。它只是用DS域对数据包进行标记。亲爱滴读者,如果你直接跳到这一节是为了尽快了解DSMARK是怎么样工作的,我建议你还是先回去浏览一下1.3节,那里有解释什么是DS域。
我们用一个图表来开始我们的介绍,对应一个DSMARK的qdisc:
恩,DSMARK看起来像一个有类的队列,没错,它确实就是。类被标记为1,2,3,4,...,n-1,其中n是一个用来实现内部队列行为时需要的一个参数。这个参数被称为 indices.假设队列中的一个元素q,那么q:0就是它自已的主列队。从q:1 到q:n-1都是队列规定(图中黄色的那部分)。每一个类都可以选择一个filter(过滤器)来对应这个队列规则(图中用绿色表示的部分);数据包通过过滤器进入到对应的DSMARK类中。
DSMARK跟其它有类的qdisc,例如HTB,有什么差别呢?以类的行为为例,一个HTB类会控制通过它上行的数据的速率,而一个DSMARK仅仅只对通过它的数据包打标记而已。
那DSMARK是在哪里,什么时候,给数据包打上什么标记的呢?数据包是在DS域里被打标记的。它们是被打上我们在设置排队队列时定义的一个整数。在数据包将要离开队列规定,被放到网络接口的驱动层输出时,它们被打上这些标记。
要建立一个新的DSMARK队列规定,我们可以用这个命令:
标注黄色部分的值是必须提供的。我们来细细分析一下。这段命令把DSMARK设成为eth0的根队列。是这个队列的句柄。定义了这个队列中类的数量(id-1)。是默认的索引,那些跟已知类都不吻合的包就会被扔到那里,这个是可选项。最后一个参数是set_tc_index;等下我们会再慢慢讨论它。
我们可以用这样的配置命令:
这个命令在eth0上建立了一个DSMARK队列规定。这个qdisc被标为1:0,包含了32个元素的表(元素1-元素32)。元素1-31可以被类1:1到1:31使用。
这个命令以类1:1(从其它qdisc派生)为父类建立了一个DSMARK队列规定。这个qdisc被标记为2:0,并有包含8个元素的表。默认的索引是7,对应dsmark类2:7。
非常极其以及特别的好。但是,DSMARK是怎么标记数据包的呢?我们需要祭出一张DSMARK内部表格的示意图。下面这图将有助于理解这些东西:
图上我们从左边带着一个类数字进去找索引值。比说,类是1:3(DSMARK1:0中的索引号3)。内部表有两列,分别是整数mark和value。这里我们将得到的数值是:
mask = 0x3;
value = 0x68.
带着这两个数,DSMARK进入数据包,拿到原来的DS域的值,然后进行下列动作:
新的DS值 = (旧的DS值 & mask) | value
其中&和|就是位操作中的与和或操作。新计算出来的DS域的值被放回原来数据包的DS域中。在图中一个带着0x0(best-effort DSCP)的DS域的数据包进来了。DSMARK响应把DS域标识为0x68并且将包导向到AF31类。这个数据包,以一个正常的best-effort包进来,走的时候还顺便带走我们的一堆金钱,当然,作为豪爽的AF31,我们是非常乐意的哈。
我们现在的任务是要学会写一个内部表。这就是意味着我们要将索引号(或者类id)和mark-value值对应起来。但首先让我们看看下面这个图,对于我们理解这些复杂的DSCPs和DSs有点帮助,而且我们也要用二进制和十六进制的形式来用它们。
非常极其地好!在表里我们有了AF1到AF4,还有EF这几个DS类。DP是指“Drop precedence(优先抛弃)”。从左到右我们有DS定义的DSCP值,二进制2 b-DSCP(在DSCP值左边加两位0),十六进制的x-DSCP值,二进制的b-DS值(在DSCP值右边加两个零),最后是十六进制的x—DS值。
用这些mask和value的值我们可以来做我们想做的一些练习啦:
想干嘛: mask value
我们想把所有进来的包扔到AF23类 0x0 0x58
我们想把除了有ECN位以外所有进来的包扔到AF12类 0x3 0x30
我们想把从任何一个DSAF进来的包置为“抛弃优先”并且不想动ECN位(0xe3是11100011;他保护了前三个类标志位和后两个ECN位,并把DP位置零,0x10是00010000,她把DP位置成2 ) 0xe3 0x10
我们想把进来的任何一个包置 为AF3.但DP位和ECN位要留下。(0x1f是00011111,他把AF类标志位置零,并且没有动DP位和ECN位。而0x60是01100000,她把AF类标识位置为3) 0x1f 0x60
好啦同志们,我们差不多就是这方面的专家啦。我们现在来看看怎么样产生一个DSMARK的表吧,假设我们现在要产生下面这样的一个表格:
这个表格是用来干嘛的呢?这问题留给大家作为家庭作业。要配置这个表格还是很轻松的,我们将使用一个八个DSMARK元素的表格:
真是没什么好解释的了。第一个命令建立DSMARK队列。接下来七个命令改变每一个类(已经在第一个命令中就建好的)来建立我们的表格。
好的同志们,云雾已经拨开但是我们还剩下要把进入DSMARK的包放到不同类的任务。我们需要DSMARK对应的过滤器的帮助,我们将用u32过滤器来将从1到7这七个类的包分别放入到192.168.1/24到192.168.7/24这七个类中(只是告诉你这是怎么回事,所以简单点好)。你能选择更复杂的参数来做你的测试。
例如,这个过滤器将从192.168.1/24网络来的包放到类1:1 ,从这个网络来的包在离开DSMARK的时候还将带着0xb8的DS域值,这个值代表DS类EF(Expedited Forwarding PHB)。
DSMARK工作地非常好,我们已经知道怎么样来设置规定,怎么样建立类并且将过滤器对应到类上。但是,那个set_tc_index参数呢?要回答这个问题,我们要用到从Differentiated Services on Linux里偷来的一个图表:
这个图表说明了一个DSMARK排队队列。还记否,当我们在学习GRED的DS域时,LINUX的设计者们在包的缓冲区里加了一个叫做tc_index的域。这个数据包缓冲区用skb指针来访问。所以,要访问这个域就要用skb->tc_index了。在图中用最底下那条直线来代表这个域。
数据包的缓冲区结构(struct sk_buff)包含一个指针iph指向另一个结构(struct iphdr)。这个结构包括了tos域,当数据包的缓冲区建立的时候,TOS域就是从这里复制过去的。
当你使用set_tc_index选项来建立一个新的DSMARK时,这个队列规定将把数据包skb->iph->tos字段中TOS域(实际上是DS域)的值复制到skb->tc_index中。在图中由从上到下的一条虚线代表了这个动作。这个时候你禁不住要问了吧:为什么呢?
skb->tc_index域是Linux区分服务中非常重要的一部分,靠右4个位是在GRED队列规定中用来选择进入哪一个虚拟的RED队列的。
但是skb->tc_index可不单单只有这个用处。它还被tcindex类过滤器用来作为类的识别码。在下一条从底到顶的虚线处,我们可以看到:tcindex类过滤器读出skb->tc_index的值,也许还会对其做一些位操作,再用之后的结果来选择下一个要进入的队列规定。这个在表现在图上就是从绿色的tcindex过滤器到黄色的队列规定classid的那一条箭头。
当tcindex类过滤器返回以上解释过程中得到的那个类标识之后,这个规定(不是分类器,一个绝顶聪明的德国学生在一次令人愉快的电子邮件交流中帮我指出了这个问题),把这个值重新写回一次到skb->tc_index域中,这次是从黄色的classid块写到底部那条skb->tc_index.
skb->tc_index 值还用来选择一个类标识符,来进入内部表以取得mask和value值。例如命令tc class change dev eth0 classid 1:1 dsmark mask 0x3 value 0x28,实际上是说:skb->tc_index域设成1的数据包们,必须到DSMARK内部表索引1处去取得
mask 和value值。接下来,数据包将从skb->iph->tos域读值到DS域,做与和或的位操作,这个结果将被最终复制回到skb->iph->tos处,而且也还会在离开队列规定进入到网络驱动层时再一次被复制到实际包的DS域中去。这整个过程体现在上图的右边。
我认为(当然你应该也会这么想),tcindex分类器在Linux 区分服务体系中是那么地重要,因此我把这个魔鬼留到了下一节。现在,课就到这里啦,同学们再见。
×××××××××××××××××××××××××××
水平所限,加之工作之余,翻译仓促,如有错误敬请扔砖
邮箱:springwateryy小老鼠gmail.com
博客:
http://www.suqier.com.cn/blog/