Расширенная фильтрация.

Глава 12. Расширенная фильтрация.

Как уже говорилось в разделе Классовые дисциплины обработки очередей, для того, чтобы определить в какую из подочередей направить пакет, используются фильтры-классификаторы.

Ниже приводится неполный список доступных классификаторов:

fw

Решение принимается на основе маркера пакета, установленного брандмауэром (например -- iptables). Наиболее простой классификатор, который можно рекомендовать в том случае, если вам не хочется изучать синтаксис tc. За дополнительной информацией обращайтесь к главе 9.

u32

Решение принимается на основе значений полей в заголовке пакета (например, исходящий IP-адрес и т.п.).

route

Решение принимается на основе маршрута, по которому движется пакет.

rsvp, rsvp6

Маршрутизация пакетов производится на базе RSVP. Применимо только в том случае, если управление сетью полностью находится в ваших руках. В Интернет RSVP не поддерживается.

tcindex

Используется в DSMARK qdisc, см. соответствующий раздел.

Вообще есть множество способов классификации пакетов, но практически все они находятся в прямой зависимости от предпочитаемой вами системы.

Классификаторы, как правило, принимают некоторое количество аргументов. Перечислим их здесь, для удобства.

protocol

Протокол, принимаемый классификатором. Как правило вы будете принимать только IP-трафик.

parent

Существующий класс, к которому должен быть присоединен данный классификатор.

prio

Приоритет классификатора. Чем меньше число -- тем выше приоритет.

handle

Назначение и смысл аргумента зависит от контекста использования.

Во всех следующих разделах мы будем исходить из условия, что формируется трафик, идущий к хосту HostA, что корневой класс сконфигурирован как 1:, а класс, которому посылается выбранный трафик -- как 1:1.

12.1. Классификатор u32.

Фильтр U32 наиболее гибкий из доступных в текущей конфигурации. Он целиком основан на хеш-таблицах, которые повышают устойчивость фильтра при значительном количестве правил фильтрации.

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

Для конфигурирования фильтра используется команда tc filter, состоящая из трех частей: определение фильтра, селектор и действие. Определение фильтра может быть записано как:

tc filter add dev IF [ protocol PROTO ]
                     [ (preference|priority) PRIO ]
                     [ parent CBQ ]        
      
Поле protocol описывает обслуживаемый протокол. Здесь мы будем обсуждать исключительно протокол IP. Поле preference (в качестве синонима можно использовать priority) описывает приоритет определяемого фильтра, что позволяет задавать несколько фильтров (списков правил) с различными приоритетами. Вообще, правила обслуживаются в порядке добавления в список, в случае с приоритетами -- первыми обслуживаются правила, имеющие наивысший приоритет (чем меньше число, тем выше приоритет). Поле parent определяет вершину дерева CBQ (например 10:1), к которой должен быть присоединен данный фильтр.

Описаные выше опции применимы ко всем фильтрам, а не только к U32.

12.1.1. Селектор U32.

Селектор U32 содержит определение шаблона, который будет сопоставляться с обрабатываемым пакетом. Если быть более точным, он определяет -- какие биты в заголовке пакета будут проверяться и не более того, но, не смотря на свою простоту, это очень мощный и гибкий метод. Рассмотрим примеры, взятые из реально работающего и достаточно сложного фильтра:

# tc filter add dev eth0 protocol ip parent 1:0 pref 10 u32 \
  match u32 00100000 00ff0000 at 0 flowid 1:10        
        
Оставим пока первую строку в покое, эти параметры описывают хеш-таблицы фильтра, и сконцентрируем свое внимание на строке селектора, которая содержит ключевое слово match. Этот селектор будет отбирать пакеты, в IP-заголовках которых второй байт будет содержать число 0x10 (0010). Как вы уже наверняка догадались, 00ff -- это маска, которая точно определяет проверяемые биты. Ключевое слово at означает, что поиск совпадения должен начинаться с указанного смещения (в байтах), в данном случае -- с начала пакета. Переведя все это, на человеческий язык, можно сказать, что пакет будет соответствовать селектору, если в его поле TOS (Type of Service) будет установлен бит Minimize-Delay (минимальная задержка). Проанализируем еще одно правило:
# tc filter add dev eth0 protocol ip parent 1:0 pref 10 u32 \
  match u32 00000016 0000ffff at nexthdr+0 flowid 1:10        
        
Параметр nexthdr означает переход к следующему заголовку в IP-пакете, т.е. к заголовку протокола более высокого уровня. Опять же, в данной ситуации поиск будет вестись с начала заголовка. Анализу будет подвергнуто второе 32-х битное слово в заголовке. В протоколах TCP и UDP это поле содержит порт назначения. Число записывается в формате big-endian, т.е. первым указывается старший байт. Таким образом мы получаем номер порта назначения -- 0x0016, или 22 (в десятичной форме). В случае протокола TCP, этот порт соответствует службе SSH. Надеюсь вы понимаете, что данное соответствие бессмысленно обсуждать вне контекста применения, поэтому отложим эту дискуссию на более позднее время.

Уловив все, что говорилось выше, вы без труда поймете смысл следующего селектора: match c0a80100 ffffff00 at 16. Данный селектор будет пытаться найти 3-х байтовую последовательность в IP-заголовке, начиная с 17-го байта, отсчитываемого от начала заголовка, что соответствует любому адресу назначения в сети 192.168.1.0/24.

12.1.2. Селекторы общего назначения.

Селекторы общего назначения задают шаблон, маску и смещение. Используя эти селекторы вы сможете выполнять проверку практически любого, отдельно взятого бита в заголовке IP (или протокола более высокого уровня). При написании и чтении они более сложны, чем селекторы специального назначения, которые будут обсуждаться в следующем разделе. Синтаксис селекторов общего назначения:

match [ u32 | u16 | u8 ] PATTERN MASK [ at OFFSET | nexthdr+OFFSET]        
        
Ключевое слово u32, или u16, или u8 указывает длину шаблона в битах. PATTERN и MASK в обязательном порядке должны иметь длину, указанную в предыдущем ключевом слове. Параметр OFFSET задает смещение от начала заголовка в байтах. Если присутствует ключевое слово nexthdr+, то смещение начинает отсчитываться от начала заголовка протокола более высокого уровня.

Приведем несколько примеров:

Этим селектором будут отобраны пакеты, у которых "время жизни" (поле TTL) равно 64. Поле TTL находится в 9-м (в 8-м, если считать с нуля) байте IP-заголовка.

# tc filter add dev ppp14 parent 1:0 prio 10 u32 \
     match u8 64 0xff at 8 \
     flowid 1:4        
        
Следующие селекторы отберут TCP-пакеты, в которых установлен бит ACK:
# tc filter add dev ppp14 parent 1:0 prio 10 u32 \
     match ip protocol 6 0xff \
     match u8 0x10 0xff at nexthdr+13 \
     flowid 1:3        
        
Отбор ACK-пакетов, длина которых меньше 64 байт:
## отбор ack-пакетов более сложным способом,
## IP protocol 6,
## Длина IP-заголовка 0x5 (32-х битных слов),
## Общая длина 0x34 (ACK + 12 байт опций TCP)
## TCP ACK (бит 5, смещение 33)
# tc filter add dev ppp14 parent 1:0 protocol ip prio 10 u32 \
            match ip protocol 6 0xff \
            match u8 0x05 0x0f at 0 \
            match u16 0x0000 0xffc0 at 2 \
            match u8 0x10 0xff at 33 \
            flowid 1:3        
        
Это правило отберет пакеты TCP, с установленным битом ACK, и не несущие в себе данных. Это яркий пример использования двух селекторов. Конечный результат будет получен логическим умножением (операция "И") результатов каждого из селекторов. Если вы посмотрите на диаграмму TCP-заголовка, то увидите, что флаг ACK -- это младший бит старшей тетрады (0x10) 14-го байта в TCP-заголовке (at nexthdr+13). Что касается второго селектора, то если бы мы хотели усложнить себе жизнь, можно было бы записать match u8 0x06 0xff at 9, вместо специального селектора protocol tcp (здесь число 6 -- это номер протокола TCP), в 10-м байте IP-заголовка. С другой стороны, в этом примере мы не использовали специальный селектор, для отбора по биту ACK, просто потому, что такого селектора не существует.

Фильтр, приведенный ниже, является модификацией предыдущего примера. На этот раз не производится проверка длины IP-заголовка. Вы спросите -- почему? Потому, что фильтр, приведенный выше, работает только на 32-х битных системах.

tc filter add dev ppp14 parent 1:0 protocol ip prio 10 u32 \
     match ip protocol 6 0xff \
     match u8 0x10 0xff at nexthdr+13 \
     match u16 0x0000 0xffc0 at 2 \
     flowid 1:3        
        

12.1.3. Селекторы специального назначения.

Следующая таблица содержит перечень селекторов специального назначения, которые автор данного раздела нашел в исходном коде утилиты tc. Они облегчат вам жизнь и повысят удобочитаемость ваших фильтров.

FIXME: таблица находится в отдельном файле selector.html.

FIXME: Она по-прежнему остается на польском языке :-(

FIXME: надо перевести в формат sgml.

Несколько примеров:

# tc filter add dev ppp0 parent 1:0 prio 10 u32 \
     match ip tos 0x10 0xff \
     flowid 1:4        
        
FIXME: шаблон tcp dport, в примере ниже, не работает.

Правило выше отберет пакеты, в которых поле TOS имеет значение 0x10. Поле TOS -- это второй байт в IP-заголовке, причем это однобайтовое поле (8 бит). Таким образом, эквивалентный селектор можно было бы записать как: match u8 0x10 0xff at 1. Это наталкивает на мысль, что селекторы специального назначения, внутри фильтров U32, преобразуются в селекторы общего назначения и в таком виде сохраняются в памяти ядра. Отсюда следует другое заключение -- селекторы tcp и udp суть есть одно и то же. По этой причине вы не сможете использовать единичный селектор match tcp dport 53 0xffff, для отбора TCP-пакетов, направляющихся на 53-й порт. Этим селектором будут отобраны как TCP-пакеты, так и UDP-пакеты. Вы обязательно должны добавлять селектор типа протокола:

# tc filter add dev ppp0 parent 1:0 prio 10 u32 \
        match tcp dport 53 0xffff \
        match ip protocol 0x6 0xff \
        flowid 1:2