DSMARK.

14.3. DSMARK.

Esteve Camps

Этот текст -- отрывки из моих тезисов Поддержка QoS в Linux, сентябрь 2000 года.

Исходные документы:

Автор главы: Esteve Camps .

14.3.1. Введение.

Прежде всего, было бы неплохо, если бы вы предварительно ознакомились с RFC, посвященными данной теме (RFC2474, RFC2475, RFC2597 и RFC2598) по адресам: IETF DiffServ working Group и домашняя страничка проекта diffserv.

14.3.2. Что такое Dsmark и с чем его "едят"?

Dsmark -- это дисциплина организации очереди, которая предлагает возможности, необходимые в "Differentiated Services" (известной также, как DiffServ, или просто -- DS). DiffServ -- фактически одна из двух архитектур QoS (вторая называется "Integrated Services"), которая базируется на значении поля DS в заголовке IP-пакетов.

Одним из первых решений в IP, которое предлагало некоторый уровень QoS, был "Type of Service" (Тип Обслуживания) -- поле TOS в IP-заголовке. Изменяя это поле, можно было выбрать высокую/низкую пропускную способность, минимальную задержку или высокую надежность. Но это решение не обеспечивало достаточной гибкости, которую требовали вновь появляющиеся услуги (например, приложения реального времени, интерактивные приложения и т.п.). С появлением новых требований, появились и новые архитектуры. Одна из них -- DiffServ, которая подменяет первые шесть битов ToS в пакете IPv4 или октет "класс трафика" в пакете IPv6, полем, с названием DS, в котором можно указать до 64 классов трафика.

14.3.3. Основные принципы.

Differentiated Services (Дифференцированное Обслуживание) ориентирован на группы. Имеется ввиду, что эта технология ничего не знает о потоках, она ориентирована на группы, а применяемые правила зависят от того, к какой группе направляется пакет.

Сеть маршрутизаторов с поддержкой механизмов DiffServ называют "облаком DiffServ" (или "доменом DiffServ"). Классификация, формирование и установка меток (под установкой меток понимается установка значений в поле DS) происходит на входе в "облако". Внутри домена метка определяет -- какой уровень QoS должен применяться к трафику внутренними маршрутизаторами сети.

Самым большим преимуществом модели DiffServ является то, что она действует на границе "облака". После того как данные пересекли границу, внутренним маршрутизаторам можно не заниматься поддержанием информации о статусе QoS и полностью сосредоточиться на своей основной функции -- маршрутизации.

Фактически, внутри своих локальных доменов, вы можете диктовать любую политику обслуживания, но при соединении с другими DS-доменами вы должны следовать Соглашению об Уровне Обслуживания (SLA).

К этому моменту у вас наверняка возникла масса вопросов. Diffsrv -- это много больше, чем я смог сказать. Вы должны понять, что я не в состоянии в 50 строках изложить содержимое трех RFC. :-)

14.3.4. Как работать с Dsmark.

Как уже было определено выше, в случае с DiffServ, пограничные и внутренние узлы различаются между собой. Это два важных пункта на пути трафика. Оба типа узлов выполняют классификацию трафика. Результат классификации может использоваться для различной DS-обработки, прежде чем пакет уйдет в сеть. Код diffserv представляет пакет в виде структуры sk_buff, в которой имеется поле skb->tc_index. В данном поле сохраняется результат начальной классификации, который может использоваться для различной интерпретации DS на пограничных и внутренних маршрутизаторах.

Значение skb->tc_index изначально устанавливается дисциплиной DSMARK qdisc для каждого входящего пакета, в соответствии с полем DS в IP-заголовке. Кроме того, классификатор cls_tcindex считывает, целиком или частично, значение skb->tcindex и использует его для выбора нужного класса.

Для начала рассмотрим команду DSMARK qdisc и ее параметры:

... dsmark indices INDICES [ default_index DEFAULT_INDEX ] [ set_tc_index ]
        
Каково назначение этих параметров?

  • indices: размер таблицы пар маска-значение. Максимальное значение 2^n, где n >= 0.

  • Default_index: индекс в таблице, принимаемый по-умолчанию, если классификатор не находит ни одного совпадения.

  • Set_tc_index: инструкция, которая считывает значение поля DS и записывает его в skb->tc_index.

14.3.5. Как работает SCH_DSMARK.

Эта дисциплина выполняет следующие шаги:

  • Если вставлена инструкция set_tc_index, то считывается поле DS и сохраняется в skb->tc_index.

  • Вызывается классификатор. Он возвращает идентификатор класса, который будет сохранен в skb->tc_index. Если такой класс не найден, то используется класс по-умолчанию из параметра default_index. Если ни set_tc_index, ни default_index не объявлены, то результат может оказаться непредсказуемым.

  • После этого управление передается внутренней qdisc, где вы можете повторно использовать результаты фильтрации. Идентификатор класса, возвращаемый внутренней qdisc, запоминается в skb->tc_index. Это значение будет использоваться в качестве индекса таблицы маска-значение. Конечный результат, который будет связан с пакетом, получается из выражения:

    New_Ds_field = ( Old_DS_field & mask ) | value            
                

  • Таким образом, конечный результат получается в результате объединения по "И" ds_field и маски, и затем объединения по "ИЛИ" с параметром value. Следующая диаграмма иллюстрирует этот процесс:

                             skb->ihp->tos
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >
         |                                                       |     ^
         | -- Если объявлена инструкция set_tc_index,            |     |  <-----Значение поля
         |    то значение DS переписывается в skb->tc_index      |     |O       DS может измениться
         |                                                      A|     |R
       +-|-+      +------+    +---+-+    внутренняя +-+     +---N|-----|----+
       | | |      | tc   |--->|   | |-->  . . .  -->| |     |   D|     |    |
       | | |----->|index |--->|   | |     Qdisc     | |---->|    v     |    |
       | | |      |filter|--->| | | +---------------+ |   ---->(mask,value) |
    -->| O |      +------+    +-|-+--------------^----+  /  |  (.  ,  .)    |
       | | |          ^         |                |       |  |  (.  ,  .)    |
       | | +----------|---------|----------------|-------|--+  (.  ,  .)    |
       | | sch_dsmark |         |                |       |                  |
       +-|------------|---------|----------------|-------|------------------+
         |            |         | <- tc_index -> |       |
         |            |(read)   |    может       |       |  <--------------Индекс в таблице
         |            |         |    измениться  |       |                    (mask,value)
         v            |         v                v       |                               
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->
                             skb->tc_index            
                

Как установить метку? Просто измените mask и value класса. См. следующий код:
tc class change dev eth0 classid 1:1 dsmark mask 0x3 value 0xb8        
        
Это изменение пары (mask,value) в хеш-таблице, пометит пакеты, принадлежащие классу 1:1.

Теперь перейдем к описанию фильтра 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        
        
(Это неполный код, я просто привел часть примера EFCBQ, включенного в состав дистрибутива iproute2).

Будем исходить из предположения, что мы получаем пакет, помеченный как EF. Если вы прочитаете RFC2598, то увидите, что рекомендуемое значение DSCP для EF трафика -- 101110. Это означает, что в поле DS будет записано 10111000 (не забывайте, что младшие биты в поле TOS не используются в DS), или 0xb8, в шестнадцатиричном представлении.

              TC INDEX
              FILTER
   +---+      +-------+    +---+-+    +------+                +-+    +-------+
   |   |      |       |    |   | |    |FILTER|  +-+    +-+    | |    |       |
   |   |----->| MASK  | -> |   | | -> |HANDLE|->| |    | | -> | | -> |       |
   |   |  .   | =0xfc |    |   | |    |0x2E  |  | +----+ |    | |    |       |
   |   |  .   |       |    |   | |    +------+  +--------+    | |    |       |
   |   |  .   |       |    |   | |                            | |    |       |
-->|   |  .   | SHIFT |    |   | |                            | |    |       |-->
   |   |  .   | =2    |    |   | +----------------------------+ |    |       |
   |   |      |       |    |   |       CBQ 2:0                  |    |       |
   |   |      +-------+    +---+--------------------------------+    |       |
   |   |                                                             |       |
   |   +-------------------------------------------------------------+       |
   |                          DSMARK 1:0                                     |
   +-------------------------------------------------------------------------+        
        
Полученный пакет имеет значение 0xb8 в поле DS. Дисциплина с идентификатором 1:0 считывает это значение и помещает его в skb->tc_index. На следующем шаге (вторая строка в примере), описанный фильтр выполняет следующие действия:
Value1 = skb->tc_index & MASK
Key = Value1 >> SHIFT        
        
В нашем примере MASK=0xFC и SHIFT=2.
Value1 = 10111000 & 11111100 = 10111000
Key = 10111000 >> 2 = 00101110 -> 0x2E (в шестнадцатиричном виде)
        
Возвращаемое значение будет соответствовать фильтру внутренней qdisc (в примере, идентификатор 2:0). Если фильтр с заданным идентификатором найден, то условия фильтра будут проверены (в случае, если фильтр включает в себя эти условия) и будет возвращен classid (в нашем примере classid 2:1), который далее будет записан в skb->tc_index. Если фильтр не будет найден, то результат будет зависеть от объявления флага fall_through. Если объявление fall_through присутствует, то его значение будет воспринято как classid. В противном случае продолжится просмотр остальных фильтров. Будьте предельно внимательны, при использовании флага fall_through -- его использование рекомендуется только в том случае, когда значение skb->tc_index и идентификаторы классов ссвязаны простыми (в смысле несложными) отношениями.

И последние два параметра, которые мы опишем, это hash и pass_on. Первый из них определяет размер хеш-таблицы. Pass_on -- означает, что если не будет найден classid, равный результату этого фильтра, то необходимо попробовать применить следующий фильтр. Действие по-умолчанию -- fall_through (см. следующую таблицу).

В заключение посмотрим -- какие значения параметров TCINDEX допустимы:

TC Name                 Value           Default
-----------------------------------------------------------------
Hash                    1...0x10000     Зависит от реализации   
Mask                    0...0xffff      0xffff
Shift                   0...15          0
Fall through / Pass_on  Flag            Fall_through
Classid                 Major:minor     None
Police                  .....           None        
        
Это очень мощный тип фильтров. Кроме того, он может использоваться не только в конфигурации DiffServ, но и как любой другой тип фильтров.

Я настоятельно рекомендую вам внимательно просмотреть все примеры DiffServ, включаемые в дистрибутив iproute2. Со своей стороны я обещаю, что дополню этот текст, как только найду время. Все, что я здесь описал -- есть результат длительных экспериментов. Я буду весьма признателен, если вы сообщите мне об обнаруженных ошибках.