14.3. DSMARK

Esteve Camps

本文摘自我关于 Linux 中的 QoS 支持 的论文,2000 年 9 月。

参考文献

本章由 Esteve Camps <esteve@hades.udg.es> 编写。

14.3.1. 简介

首先,首先,强烈建议您阅读关于此的 RFC(RFC2474、RFC2475、RFC2597 和 RFC2598),请访问 IETF DiffServ 工作组网站 Werner Almesberger 网站 (他编写了在 Linux 上支持区分服务的代码)。

14.3.2. Dsmark 与什么相关?

Dsmark 是一种队列规则,它提供了差分服务(也称为 DiffServ 或简称为 DS)中所需的功能。DiffServ 是两种实际的 QoS 架构之一(另一种称为集成服务),它基于 IP 报头 DS 字段中数据包携带的值。

IP 中旨在提供一定 QoS 水平的首批解决方案之一是 IP 报头中的服务类型字段 (TOS 字节)。通过更改该值,我们可以选择高/低吞吐量、延迟或可靠性级别。但这并没有为新服务(例如实时应用程序、交互式应用程序等)的需求提供足够的灵活性。此后,出现了新的架构。其中之一是 DiffServ,它保留了 TOS 位并重命名了 DS 字段。

14.3.3. 差分服务指南

差分服务是面向群组的。我的意思是,我们不了解任何关于流的信息(这将是集成服务的目的);我们了解流聚合,并且我们将根据数据包所属的聚合应用不同的行为。

当数据包到达边缘节点(进入差分服务域的入口节点)进入差分服务域时,我们将需要策略、整形和/或标记这些数据包(标记是指为 DS 字段分配一个值。就像奶牛 :-) 一样)。这将是差分服务域上的内部/核心节点将查看的标记/值,以确定应用哪种行为或 QoS 级别。

正如您可以推断出的那样,差分服务涉及一个域,所有 DS 规则都必须在该域上应用。实际上,您可以认为我将对进入我的域的所有数据包进行分类。一旦它们进入我的域,它们将受到我的分类所指示的规则的约束,并且每个遍历的节点都将应用该 QoS 级别。

实际上,您可以在本地域中应用自己的策略,但在连接到其他 DS 域时,应考虑一些服务级别协议

此时,您可能有很多问题。DiffServ 比我解释的要复杂得多。实际上,您可以理解我无法在短短 50 行中概括 3 个以上的 RFC :-)。

14.3.4. 使用 Dsmark

正如差分服务参考书目中指定的那样,我们区分边界节点和内部节点。这是流量路径中的两个重要点。两种类型都在数据包到达时执行分类。它的结果可以在 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 ]
这些参数是什么意思?

让我们看看 DSMARK 过程。

14.3.5. SCH_DSMARK 的工作原理。

此队列规则将应用以下步骤

                         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
这会更改哈希表中的 (mask,value) 对,以重新标记属于类 1:1 的数据包。您必须 "更改" 这些值,因为 (mask,value) 最初获取的默认值(请参见下表)。

现在,我们将解释 TC_INDEX 过滤器的工作原理以及如何适应这种情况。此外,TCINDEX 过滤器可以用于其他配置,而不仅仅是包括 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,您会看到 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 发行版中包含的所有差分服务示例。我保证我会尽快尝试补充本文。此外,我所解释的所有内容都是大量测试的结果。如果您发现我在任何地方犯了错误,请告诉我。