14.3. DSMARK

元になった文書:

この章は Esteve Camps <esteve@hades.udg.es> が執筆しました。

14.3.1. はじめに

まず最初に、このテーマについて書かれた RFC (RFC2474, RFC2475, RFC2597, RFC2598) を読むことを強くお奨めします。 これらは IETF DiffServ working Group web site および Werner Almesberger web site にあります (彼は Linux で Differentiated Services の機能をサポートするコードを書きました)。

14.3.2. Dsmark の対象

Dsmark は、Differentiated Services (DiffServ とか、 単に DS とも呼ばれます) で必要となる機能を提供するキューイング規則です。 DiffServ は 2 つある実際の QoS 機構のうちのひとつ (もうひとつは Integrated Services と呼ばれています) で、 パケットの IP ヘッダの DS フィールドに含まれている値に基づいて動作します。

IP で、何らかの QoS レベルのサービスを得るために設計された最初の解は、 IP ヘッダの Type of Service フィールド (TOS バイト) でした。 この値を変えることにより、 スループット・遅延・信頼性などのレベルの高低を選択できたのでした。 しかしこれは、最近のサービス (リアルタイムアプリケーション、 対話的アプリケーションなど) で必要とされる、 十分な柔軟性を持っていませんでした。その後、新たな機構が現れました。 そのひとつが DiffServ で、これは TOS の各ビットを占有し、 DS フィールドとして再定義して使用します。

14.3.3. Differentiated Services のガイドライン

DiffServ はグループ指向です。 つまり、フローについては何も関知しないのです (これは Integrated Services の目的となります)。 対象にするのはフローの集合で、 パケットがどの集合に属しているかによって、 異なる動作を適用することになります。

パケットが境界ノード (DiffServ ドメインのエントリノード) に到着すると、 DiffServ ドメインへ入る際に、これらのパケットには制限・帯域制限・マーキング などを行う必要があります (マーキングとは、DS フィールドに値を代入することです。 牛みたいなもんです :-) )。 DiffServ ドメインの内部 (コア) ノードで、 どのような動作や QoS レベルを適用するかは、 このマーク (値) を見て判断されます。

ご想像の通り、Differentiated Services にはドメインがあり、 すべての DS 規則はここで適用されます。 実際、ドメインに入ってきたすべてのパケットをクラス選別する、 と考えることも可能です。パケットはドメインに入ると、 クラス選別機構が命ずる規則に従うことになり、 経由するノードのすべてでその QoS レベルが適用されるのです。

実のところ、自分のローカルなドメインには自前の制限を適用できますが、 他の DS ドメインと接続する際には、 Service Level Agreements を考慮しなければなりません。

この段階では、おそらくたくさんの疑問があるでしょう。 DiffServ は、ここで説明した以上の内容を含んでいます。 3 つの RFC を 50 行で要約するのが不可能であることは、 理解していただけるでしょう :-)

14.3.4. Dsmark を利用する

DiffServe の文献が示す通り、我々は境界ノードと内部ノードを差別化します。 これらはトラフィック経路のうち、重要な 2 つのポイントです。 いずれにおいても、パケットが到着するとクラス選別が行われます。 その結果は、DS プロセスがパケットをネットワークに放つにあたって、 どこか別の場所で用いられることになるでしょう。 diffserv のコードに skb->tc_index というフィールドを含む sk_buff という構造体があるのは、要するにこのためなのです。 ここには初期クラス選別の結果が保存され、 これが DS 処理のいくつかの場所で用いられることになります。

最初 skb->tc_index の値は、 すべての到着パケットの IP ヘッダの DS フィールドから抽出され、 DSMARK qdisk によって設定されます。 また、cls_tcindex 選別器は、 skb->tcindex の全体を読み、その結果をクラス選別に用います。

しかし、まず最初に、DSMARK qdisc のコマンドとパラメータを見てみましょう。
... dsmark indices INDICES [ default_index DEFAULT_INDEX ] [ set_tc_index ]
これらのパラメータの意味するものはなんでしょうか?

では DSMARK のプロセスを見てみましょう。

14.3.5. SCH_DSMARK の動作

この qdisc は次のような段階からなります:

                         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

マーキングはどのように行うのでしょうか。 単に再マークしたいクラスのマスクと値を変えるだけです。 次のコードを見てください。
tc class change dev eth0 classid 1:1 dsmark mask 0x3 value 0xb8
これはハッシュテーブルの (mask,value) ペアを変更し、 class 1:1 に属しているパケットを再マークします。 (mask,value) は最初デフォルトの値を取る (以下の表を参照) ので、 この値は「変更」しなければなりません。

これで、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 トラフィッククラス

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

# EF トラフック用の pfifo qdisc

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 では用いられないことに注意)、 あるいは 16 進では 0xb8 となります。

              TC INDEX
              FILTER
   +---+      +-------+    +---+-+    +------+                +-+    +-------+
   |   |      |       |    |   | |    |FILTER|  +-+    +-+    | |    |       |
   |   |----->| MASK  | -> |   | | -> |HANDLE|->| |    | | -> | | -> |       |
   |   |  .   | =0xfc |    |   | |    |0x2E  |  | +----+ |    | |    |       |
   |   |  .   |       |    |   | |    +------+  +--------+    | |    |       |
   |   |  .   |       |    |   | |                            | |    |       |
-->|   |  .   | SHIFT |    |   | |                            | |    |       |-->
   |   |  .   | =2    |    |   | +----------------------------+ |    |       |
   |   |      |       |    |   |       CBQ 2:0                  |    |       |
   |   |      +-------+    +---+--------------------------------+    |       |
   |   |                                                             |       |
   |   +-------------------------------------------------------------+       |
   |                          DSMARK 1:0                                     |
   +-------------------------------------------------------------------------+

パケットが到着すると、DS フィールドには 0xb8 という値が設定されます。 先に説明した通り、1:0 という id の dsmark qdisc は、 DS フィールドを抽出して skb->tc_index 変数に保存します。 この例における次の段階は、この qdisc に関連付けられたフィルタに対応します (例の 2 行目)。これは次の操作を実行します。
Value1 = skb->tc_index & MASK
Key = Value1 >> SHIFT

この例では MASC=0xFC で SHIFT=2 です。
Value1 = 10111000 & 11111100 = 10111000
Key = 10111000 >> 2 = 00101110 -> 0x2E (16 進)

返り値は qdisc の内部フィルタハンドル (この例では id が 2:0 のもの) に対応します。この id を持ったフィルタが実際に存在すれば、 制限条件と測定条件が (フィルタにこれらが含まれていれば) 調査され、 classid が返され (我々の例では 2:1)、 skb->tc_index 変数に保存されます。

しかしこの id を持つフィルタが見つからなければ、 結果は fall_through フラグの宣言に依存します。 宣言されていれば、value キーが class id として返されます。 宣言されていないとエラーが返され、プロセスは残りのフィルタへ続きます。 fall_through フラグの利用には注意が必要です。 skb->tc_index 変数の値と クラス id の間に単純な関係があれば、 これは行われてしまいます。

説明が必要な残りのパラメータは、hash と pass_on でしょうか。 hash はハッシュテーブルサイズに関連しています。 pass_on は、 このフィルタの結果と等しい class id が無い場合は次のフィルタを用いる、 ということを示すために用いられます。 デフォルトの動作は 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 の例をすべて見ておくことをお奨めします。 ここの文章も、できるだけ早く補完するつもりです。 ところで、ここで説明した内容は、多くのテストの結果です。 もしどこかで間違っていたら、指摘してくださると幸いです。