参考文献
iproute2 发行版中的示例。
白皮书 - QoS 协议和架构 和 IP QoS 常见问题解答,均来自 服务质量论坛。
本章由 Esteve Camps <esteve@hades.udg.es> 编写。
首先,首先,强烈建议您阅读关于此的 RFC(RFC2474、RFC2475、RFC2597 和 RFC2598),请访问 IETF DiffServ 工作组网站 和 Werner Almesberger 网站 (他编写了在 Linux 上支持区分服务的代码)。
Dsmark 是一种队列规则,它提供了差分服务(也称为 DiffServ 或简称为 DS)中所需的功能。DiffServ 是两种实际的 QoS 架构之一(另一种称为集成服务),它基于 IP 报头 DS 字段中数据包携带的值。
IP 中旨在提供一定 QoS 水平的首批解决方案之一是 IP 报头中的服务类型字段 (TOS 字节)。通过更改该值,我们可以选择高/低吞吐量、延迟或可靠性级别。但这并没有为新服务(例如实时应用程序、交互式应用程序等)的需求提供足够的灵活性。此后,出现了新的架构。其中之一是 DiffServ,它保留了 TOS 位并重命名了 DS 字段。
差分服务是面向群组的。我的意思是,我们不了解任何关于流的信息(这将是集成服务的目的);我们了解流聚合,并且我们将根据数据包所属的聚合应用不同的行为。
当数据包到达边缘节点(进入差分服务域的入口节点)进入差分服务域时,我们将需要策略、整形和/或标记这些数据包(标记是指为 DS 字段分配一个值。就像奶牛 :-) 一样)。这将是差分服务域上的内部/核心节点将查看的标记/值,以确定应用哪种行为或 QoS 级别。
正如您可以推断出的那样,差分服务涉及一个域,所有 DS 规则都必须在该域上应用。实际上,您可以认为我将对进入我的域的所有数据包进行分类。一旦它们进入我的域,它们将受到我的分类所指示的规则的约束,并且每个遍历的节点都将应用该 QoS 级别。
实际上,您可以在本地域中应用自己的策略,但在连接到其他 DS 域时,应考虑一些服务级别协议。
此时,您可能有很多问题。DiffServ 比我解释的要复杂得多。实际上,您可以理解我无法在短短 50 行中概括 3 个以上的 RFC :-)。
正如差分服务参考书目中指定的那样,我们区分边界节点和内部节点。这是流量路径中的两个重要点。两种类型都在数据包到达时执行分类。它的结果可以在 DS 过程中的不同位置使用,然后在将数据包发布到网络之前使用。正因为如此,diffserv 代码提供了一个名为 sk_buff 的结构,其中包括一个名为 skb->tc_index 的新字段,我们将在其中存储初始分类的结果,该结果可以在 DS 处理中的多个点使用。
skb->tc_index 值最初将由 DSMARK 队列规则设置,从每个接收数据包的 IP 报头中的 DS 字段检索它。此外,cls_tcindex 分类器将读取 skb->tcindex 值的全部或部分,并使用它来选择类。
但是,首先,看一下 DSMARK 队列规则命令及其参数
... dsmark indices INDICES [ default_index DEFAULT_INDEX ] [ set_tc_index ] |
indices: (mask,value) 对表的 size。最大值为 2^n,其中 n>=0。
Default_index:如果分类器未找到匹配项,则为默认表条目索引。
Set_tc_index: 指示 dsmark 队列规则检索 DS 字段并将其存储到 skb->tc_index 中。
此队列规则将应用以下步骤
如果我们在队列规则命令中声明了 set_tc_index 选项,则会检索 DS 字段并将其存储到 skb->tc_index 变量中。
调用分类器。分类器将被执行,它将返回一个类 ID,该类 ID 将存储在 skb->tc_index 变量中。如果未找到过滤器匹配项,我们认为 default_index 选项是要存储的 classId。如果既未声明 set_tc_index 也未声明 default_index,则结果可能是不可预测的。
在发送到内部队列规则后,您可以在其中重用过滤器的结果,内部队列规则返回的 classid 将存储到 skb->tc_index 中。我们将来将使用此值来索引 mask-value 表。要分配给数据包的最终结果将是下一个操作的结果
New_Ds_field = ( Old_DS_field & mask ) | value |
因此,新值将通过 "与" ds_field 和 mask 值得到,然后,此结果与 value 参数进行 "或" 运算。请参阅下图以了解整个过程
skb->ihp->tos - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > | | ^ | -- If you declare set_tc_index, we set DS | | <-----May change | value into skb->tc_index variable | |O DS field | A| |R +-|-+ +------+ +---+-+ Internal +-+ +---N|-----|----+ | | | | tc |--->| | |--> . . . -->| | | D| | | | | |----->|index |--->| | | Qdisc | |---->| v | | | | | |filter|--->| | | +---------------+ | ---->(mask,value) | -->| O | +------+ +-|-+--------------^----+ / | (. , .) | | | | ^ | | | | (. , .) | | | +----------|---------|----------------|-------|--+ (. , .) | | | sch_dsmark | | | | | +-|------------|---------|----------------|-------|------------------+ | | | <- tc_index -> | | | |(read) | may change | | <--------------Index to the | | | | | (mask,value) v | v v | pairs table - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -> skb->tc_index |
如何进行标记?只需更改要重新标记的类的 mask 和 value 即可。请参阅下一行代码
tc class change dev eth0 classid 1:1 dsmark mask 0x3 value 0xb8 |
现在,我们将解释 TC_INDEX 过滤器的工作原理以及如何适应这种情况。此外,TCINDEX 过滤器可以用于其他配置,而不仅仅是包括 DS 服务的配置。
这是声明 TC_INDEX 过滤器的基本命令
... tcindex [ hash SIZE ] [ mask MASK ] [ shift SHIFT ] [ pass_on | fall_through ] [ classid CLASSID ] [ police POLICE_SPEC ] |
首先,假设我们收到一个标记为 EF 的数据包。如果您阅读 RFC2598,您会看到 EF 流量的 DSCP 建议值为 101110。这意味着 DS 字段将为 10111000(请记住 TOS 字节中不太重要的位在 DS 中未使用),或十六进制编码中的 0xb8。
TC INDEX FILTER +---+ +-------+ +---+-+ +------+ +-+ +-------+ | | | | | | | |FILTER| +-+ +-+ | | | | | |----->| MASK | -> | | | -> |HANDLE|->| | | | -> | | -> | | | | . | =0xfc | | | | |0x2E | | +----+ | | | | | | | . | | | | | +------+ +--------+ | | | | | | . | | | | | | | | | -->| | . | SHIFT | | | | | | | |--> | | . | =2 | | | +----------------------------+ | | | | | | | | | CBQ 2:0 | | | | | +-------+ +---+--------------------------------+ | | | | | | | +-------------------------------------------------------------+ | | DSMARK 1:0 | +-------------------------------------------------------------------------+ |
数据包到达,然后在 DS 字段中设置为 0xb8 值。正如我们之前解释的那样,示例中由 1:0 id 标识的 dsmark 队列规则检索 DS 字段并将其存储在 skb->tc_index 变量中。示例中的下一步将对应于与此队列规则关联的过滤器(示例中的第二行)。这将执行以下操作
Value1 = skb->tc_index & MASK Key = Value1 >> SHIFT |
在示例中,MASK=0xFC 且 SHIFT=2。
Value1 = 10111000 & 11111100 = 10111000 Key = 10111000 >> 2 = 00101110 -> 0x2E in hexadecimal |
返回值将对应于队列规则内部过滤器句柄(在示例中,标识符 2:0)。如果存在具有此 id 的过滤器,则将验证策略和计量条件(如果该过滤器包含这些条件),并且将返回 classid(在我们的示例中,classid 2:1)并将其存储在 skb->tc_index 变量中。
但是,如果未找到任何具有该标识符的过滤器,则结果将取决于 fall_through 标志声明。如果是这样,则 value 键作为 classid 返回。如果不是,则返回错误,并且进程继续处理其余过滤器。如果您使用 fall_through 标志,请小心;如果 skb->tc_index 变量的值与类 id 之间存在简单关系,则可以这样做。
要评论的最新参数是 hash 和 pass_on。第一个参数与哈希表大小有关。Pass_on 将用于指示如果未找到等于此过滤器结果的 classid,请尝试下一个过滤器。默认操作是 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 |
这种过滤器非常强大。有必要探索所有可能性。此外,此过滤器不仅用于差分服务配置。您可以将其用作任何其他类型的过滤器。
我建议您查看 iproute2 发行版中包含的所有差分服务示例。我保证我会尽快尝试补充本文。此外,我所解释的所有内容都是大量测试的结果。如果您发现我在任何地方犯了错误,请告诉我。