Bert Hubert - Linux Advanced Routing & Traffic Control HOWTO
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
12.2. Классификатор route.
Этот классификатор тесно связан с таблицами маршрутизации. Когда пакет достигает такого фильтра, то дальнейшая его судьба зависит от информации, находящейся в таблицах маршрутизации.
# tc filter add dev eth1 parent 1:0 protocol ip prio 100 route
В данном случае, к узлу 1:0 добавлен классификатор с приоритетом 100. Когда пакет достигнет этого узла (поскольку он является корневым, то это произойдет немедленно), то классификатор проанализирует таблицу маршрутизации. Если совпадение будет подтверждено, то пакет будет передан данному классу с приоритетом, равным 100. Однако, для того, чтобы все это заработало, необходимо добавить соответствующую запись в таблицу маршрутизации:
Тут вся хитрость состоит в том, чтобы определить некую 'область' (realm), основываясь на адресе отправителя или получателя. Примерно таким образом:
# ip route add Host/Network via Gateway dev Device realm RealmNumber
Для примера зададим номер 'области', равным 10, для нашей сети 192.168.10.0.
# ip route add 192.168.10.0/24 via 192.168.10.1 dev eth1 realm 10
В фильтрах, основанных на классификаторе route, мы можем использовать номер 'области' (realm) для представления сетей или отдельных узлов сети.
# tc filter add dev eth1 parent 1:0 protocol ip prio 100
route to 10 classid 1:10
Под действие данного правила попадут пакеты, направляющиеся в сеть 192.168.10.0
Фильтры данного типа можно строить и на основе адреса отправителя. Рассмотрим пример, когда к маршрутизатору, под управлением Linux, через интерфейс eth2 подключена сеть.
# ip route add 192.168.2.0/24 dev eth2 realm 2
# tc filter add dev eth1 parent 1:0 protocol ip prio 100
route from 2 classid 1:2
Этот фильтр говорит о том, что пакеты из подсети 192.168.2.0 (область 2) будут отнесены к идентификатору класса 1:2.
12.3. Фильтры-ограничители трафика.
Более сложные системы управления трафиком можно строить на основе фильтров, которые зависят от пропускной способности и загруженности канала. Вы можете объявить фильтр, который не будет срабатывать, если загрузка канала превысила некоторую величину или наоборот, срабатывать только в том случае, если пропускная способность превысила заданное значение.
Так, если вы решили ограничить 5 Мбитный канал величиной в 4 Мбит/сек, вы можете вообще прекратить прием пакетов, если загруженность канала превысит 4 Мбит/сек, либо отбросить 1 Мбит/сек, а 4 Мбит/сек передать заданному классу.
При превышении заданного порога вы можете отбрасывать "лишние" пакеты, переклассифицировать их или передать другим фильтрам.
12.3.1. Способы ограничения.
Существует две основных возможности ограничения. Если вы собрали ядро с "функциями оценки" (estimators), то оно сможет более или менее точно измерять объем трафика, переданного тому или иному фильтру. Устройство функции оценки достаточно несложное — 25 раз в секунду подсчитывается объем переданных данных, на основе которого вычисляется загруженность канала.
Другой способ — Token Bucket Filter, который реализуется в пределах вашего фильтра. Это типичный шейпер, который может использоваться, если вам нужно просто ограничить полосу пропускания по какому-нибудь критерию.
12.3.1.1. Оценочная функция в ядре (estimator).
Тут все очень просто и имеется всего один параметр: avrate. Либо объем трафика остается ниже avrate и фильтр классифицирует его как сконфигурированный classid, либо, в случае превышения заданной границы, выполняется указанное действие, которое по-умолчанию выполняет переклассификацию.
Для оценки загруженности канала, ядро использует экспоненциальное взвешенное смещенное среднее значение, которое менее чувствительно к коротким пикам.
12.3.1.2. Token Bucket Filter.
Используются следующие параметры:
• burst/buffer/maxburst
• mtu/minburst
• mpu
• rate
Которые ведут себя идентично описанным в разделе Token Bucket Filter. Обратите внимание, если вы установите mtu слишком низким, то входящий трафик будет подавлен, в то время как исходящий TBF qdisc передаст с более низкой скоростью.
Еще одно важное отличие такого фильтра – он может только либо пропустить пакет, либо отбросить. Он не может задержать пакет на какое-то время.
12.3.2. Действия в случае превышения ограничения.
Если правило "решит", что произошло превышение заданного предела, то оно выполнит соответствующее действие. Имеются четыре вида действий:
continue
Выполняется в случае несовпадения с условной частью правила, чтобы передать пакет следующему фильтру.