Шейпер

Материал из Belgorod Linux User Group - Белгород

Версия от 06:29, 14 декабря 2011; 107 (обсуждение | вклад)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Шейпер — ограничение, классификация и разделение трафика

Содержание

Задачи

  • Разделение трафика для локальной сети поровну
  • Выставление приоритета трафика
  • Ограничение по скорости
  • Выделение VIP канала

Знакомство

Шейпер — средство или совокупность средств для придания определенной формы трафику (от англ. shape - форма).

Шейпинг — процесс придания определенной формы трафику. Здесь под формой трафика понимается скорость канала, последовательность пакетов (приоритет), задержки.

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

Этих двух статей в принципе достаточно, чтобы понимать основную терминологию и принципы построения шейпера под Linux. Потратив всего пол часа на чтение, вы свободно сможете ориентироваться во всяких страшных словах типа: parent 1:0 protocol ip prio 1 u32 match ip protocol 6 0xff flowid 1:10.

Если вкратце, то шейпинг в Linux организуется посредством утилиты tc из пакета iproute2 (и iptables по необходимости). В Linux удобно шейпить можно только исходящий трафик. По этому если представить схему:

интернет <--> (eth1 LINUX_ROUTER eth2) <--> LAN

То для такой схемы, со стороны локальной сети исходящий трафик (в интернет) — это исходящий трафик для eth1, а входящий трафик (из интернета) — это исходящий трафик для eth2. По этому правила шейпера будет описываться для обеих сетевых интерфейсов в отдельности, как исходящий трафик.

Со всеми тонкостями и описаниями шейпера под Linux познакомимся по ходу описания примера.

Дано

Дано: роутер пусть с тремя сетевыми устройствами: eth0, eth1, eth2. Каждое устройство подключено к отдельной подсети. Пусть eth2 — локальная сеть (очень быстрая, 1Gbit), eth1 — интернет (довольно медленный 2Mbit), eth2 — внешняя компания, назовем ее BE (средняя скорость, порядка 100Mbit).

Схема:

BE <------>eth0+--------+
               | Router |eth2<------> LAN
INET <---->eth1+--------+

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

Решение

Как было сказано выше, управлять мы можем только исходящим трафиком, по этому забываем про входящий. Любое устройство рассматриваем как устройство, только отдающее трафик. Возьмем к примеру eth2, подключенный к локальной сети. Это устройство отдает трафик в локальную сеть. Трафик по ней будет идти разношерстный, собранный и с eth0, и с eth1, и даже с lo0 (localhost). И все это будет выталкиваться в локалку через eth2. Чтобы не путаться назовем интерфейсы в соответствии с тем, куда они подключены, т.е. куда будут "выталкивать" трафик. Так eth2 у нас будет DEV_LAN, eth1 - DEV_INET, eth0 - DEV_BE.

Константы

Начинаем писать скрип.

#!/bin/bash
# Переменные
TC=/sbin/tc

# Макросы
ICMP="match ip protocol 1 0xff"
TCP="match ip protocol 6 0xff"
UDP="match ip protocol 17 0xff"
DPORT="match ip dport"
SPORT="match ip sport"
SRC="match ip src"
DST="match ip dst"
MARK="match mark"
U32="protocol ip u32"
##############
# Hardware
#
# Исходящий трафик в Локальную сеть.
DEV_LAN=eth2
# Исходящий трафик в Интернет
DEV_INET=eth1
# Исходящий трафик в БЭ
DEV_BE=eth0
#
##############

Тут все понятно, описали те константы, про которые шла речь выше (устройства), плюс добавили макросы для упрощения дальнейшего написания кода, в них можно не вникать.

Плюс опишем наши подсети в константы:

#############
# Networks
IP_LAN=172.32.0.0/16
IP_BE=172.22.0.0/16
IP_SERVER=172.32.0.0/24
IP_USER=172.32.1.0/24
# Server
IP_INET_IVT=79.140.77.242
#
#############

Трубы

схематично

Рассмотрим для начала eth2, т.е. DEV_LAN, отдающий все в локалку. Изображаем всю нашу структуру трафика визуально. То есть рисуем трубы (полосы), делим их по пропускной способности, подписываем.

DEV_LAN
------------------------------------------------------------
   ------------------------------------------------
                SERVER        240 kbit         
   INET  -----------------------------     950kbit
                USER          660 kbit
   ------------------------------------------------
                                                         200mbit
   BE                                      59mbit           
  
   ------------------------------------------------

   LAN                                    100mbit
  
   -------------------------------------------------
------------------------------------------------------------

Как видно на этой схеме я взял «трубу» в 200mbit из нашего 1Gbit сетевого устройства DEV_LAN. Т.к. мы рассматриваем только исходящий трафик, то примерно делю ёмкость пополам. Т.е. 500mbit устройство способно пропустить, но мне 500mbit много, по этому взял 200mbit. Всегда надо брать меньше, чем устройство способно пропустить, чтобы шейпер не "захлебывался" при пиковых нагрузках.

Внутри этой трубы я создал еще 3 «подтрубы» поменьше. Интернет у нас 2Mbit, я поделил его примерно "пополам", мы рассматриваем только исходящий трафик, я ему дал трубу в 950mbit, а в нем еще создал 2 подкласса - SERVER и USER, которым выдал по 240kbit и 660kbit.

Внимание! Тут очень важный момент для понимания:

Для DEV_LAN это является исходящим трафиком. Но т.к. он выходит в локалку, то для SERVER и USER он будет входящим. Т.о. получается, что в полосе LAN_INET_SERVER я дал серверам входящего трафика на 240 kbit, а пользователям на 660kbit. Хотя повторюсь, со стороны устройства DEV_LAN этот трафик является исходящим. Если этот момент не понятен — дальше пока не читайте. Вернитесь к схеме.

Потом внутри классов LAN_INET_SERVER и LAN_INET_USER мы выделим еще подклассы для различного типа трафика (http, shell, mail и т.п.) с приоритезацией.

Для исходящего трафика от корпоративной компании BE я дал 59 мбит. Если там 100 мбитка, то мне будет еще 40 мбит на исходящий туда.

Для исходящего локального трафика от роутера (к примеру если там веб серер и т.п.) я дал 100 мбит.

Сумма всех труб получилась примерно 160 мбит, что укладывается в отведенные нами 200 мбит.

Сложно? Ничего, дальше должно стать понятнее, примеры полегче.

Рассмотрим остальные устройства нашего роутера. На очереди DEV_INET - устройство, к которому подключен Интернет, т.е. это устройство, отдающее трафик в интернет. Как сказано в задаче выше канал в Интернет у нас 2Mbit - суммарный. 1 мбит я у него отнял уже выше, на исходящим устройстве в локальную сеть DEV_LAN. Теперь отниму еще 1 мбит на устройстве, отдающим трафик в интернет, т.е. на DEV_INET

DEV_INET
------------------------------------------------------------
   ------------------------------------------------

   SERVER                                 660kbit           
  
   ------------------------------------------------   950kbit

   USER                                   240kbit
  
   -------------------------------------------------
------------------------------------------------------------

Как видно я также немного урезал корневую трубу до 950 кбит чтобы был запас и дал 660 кбит серверам на отдачу в интернет и 240 кбит - пользователям, на их исходящий в Интернет через DEV_INET.

Пришла очередь корпоративной сети, исходящий трафик на которую идет через DEV_BE

DEV_BE
------------------------------------------------------------


                                           39 mbit


------------------------------------------------------------

Тут я не делил на подклассы, и оставшиеся 40 мбит пустил на всю полосу.

скрипт

Опишем то, что мы назвали «трубами» в константы, которые будем использовать при написании шейпера:

##############
# Rates
# LAN
#Вся труба в Локалку
RATE_LAN=200mbit
   #Труба в Локалку из Интернета ( ~1mbit )
   RATE_LAN_INET=950kbit
       #ПодТруба в Локалку из Интернета для Серверов
       RATE_LAN_INET_SERVER=240kbit
       #ПодТруба в Локалку из Интернета для Юзеров
       RATE_LAN_INET_USER=660kbit
           # ползунки
           RATE_LAN_INET_USER_MIN=$[660/8]kbit
           RATE_LAN_INET_USER_MAX=$[660/3]kbit
   #Труба в Локалку из БЭ ( ~60mbit )
   RATE_LAN_BE=59mbit
   #Труба в Локалку из Локалки (локальные ресурсы) ( ~100mbit )
   RATE_LAN_LAN=100mbit
# INET
# Вся труба в Интернет ( ~1mbit )
RATE_INET=950kbit
   # ПодТруба в Интернет от Серверов
   RATE_INET_SERVER=660kbit
   # ПодТруба в Интернет от Юзеров
   RATE_INET_USER=240kbit
       # ползунки
       RATE_INET_USER_MIN=$[240/8]kbit
       RATE_INET_USER_MAX=$[240/3]kbit
# BE
# Вся труба в БЭ ( ~40mbit )
RATE_BE=39mbit
#
################

Это то, что нарисовано в наших картинках.

Классы

Такие понятия как Классы, Фильтры, Дисциплины можно изобразить так:

         ++    ++                 +-----+   +------------+   ++     ++ .++
         || .  ||     +------+    |     |-->| Дисциплина |-->||     ||  ||
         ||    ||---->|Фильтр|--->|Класс|   +------------+   ||-+   ||  ||
         ||    ||  |  +------+    |     +--------------------+| |   ||  ||
         || .  ||  |              +---------------------------+ |   || .||
         || .  ||  |  +------+                                  |   ||  ||
         ||    ||  +->|Фильтр|-_  +-----+   +------------+   ++ |   || .||
         || -->||  |  +------+  ->|     |-->| Дисциплина |-->|| |   ||->||
         || .  ||  |              |Класс|   +------------+   ||-+-->|| .||
      -->||    ||  |  +------+ _->|     +--------------------+|     ||  ||
         ||    ||  +->|Фильтр|-   +---------------------------+     || .||
         ||    ||     +------+                                      || .||
         || .  |+---------------------------------------------------+|  ||
         ||    |       Внутренняя дисциплина обработки очереди       | .||
         || .  +-----------------------------------------------------+ .||
         || . . .. . . .. . .                 . .. .. .. .           .. ||
         |+-------------------------------------------------------------+|
         |               Корневая дисциплина обработки очереди           |
         |               (подключена к исходящему устройству)            |
         +---------------------------------------------------------------+

Т.е. у нас есть корневая Дисциплина (root), внутри которой мы описываем Классы. Класс обрабатывается в соответствии с его дочерней Дисциплиной. Трафик берется из корневой дисциплины и уходит в соответствующий Класс (трубу) в соответствии с Фильтром. Т.е. если попадет под фильтр. Также есть понятие фильтра по умолчанию. Указывается в Классе с параметром default - это тот Класс, куда пойдет трафик, если не сработает ни один фильтр.

схематически

В терминологии шейпера трубам будут соответствовать классы. Т.е. труба будет заворачиваться в класс с соответствующей дисциплиной. Но об этом позже. Пока запомним, что трубы — это классы.

Для обработки трафика будем использовать HTB (Hierarchichal Token Bucket — классовая дисциплина обработки очереди), как одну из самых распространенных, удобных (по сравнению с CBQ) и гибких дисциплин.

Из называния видно, что классы могут иметь иерархическую структуру, можно строить дерево из них, они могут заимствовать токены у родительских.

Теперь нам надо нарисовать в классах то, что мы описали в решении схематически. То есть нарисовать те трубы в виде иерархических классов, дерева:

Смотрим на картинку с трубами, нарисованную для DEV_LAN и перерисовываем трубы как классы:

DEV_LAN
+------------+
| 1:  root   |
+------------+
|
+-----------------------------------------------------------------------------------+
| 1:1 LAN                                                                           |
+-----------------------------------------------------------------------------------+
                          |                                 |                |
+------------------------------------------------+  +-------------+  +--------------+
| 1:11 LAN_INET                                  |  | 1:12 LAN_BE |  | 1:13 LAN_LAN |
+------------------------------------------------+  +-------------+  +--------------+
            |                        | 
+-----------------------+  +---------------------+
| 1:111 LAN_INET_SERVER |  | 1:112 LAN_INET_USER |
+-----------------------+  +---------------------+
                              |         |        |
                           +--------+ +--------+ .
                           | 1:1121 | | 1:1122 | .
                           |  http  | |  mail  | . 
                           +--------+ +--------+

«Труба» здесь идет не горизонтально, а вертикально, как-бы перевернув картинку с трубами. Плюс у классов появились цифры. У каждого уровня класса добавляется цифра. Так первый уровень классов обозначаю одной цифрой после 1: (1:1), классы второго уровня — двумя цифрами (1:11) и т.д. 1: root - корневое устройство. У нас это DEV_LAN. Далее. Рассмотрим классы:

  • 1:1 LAN - класс со всей полосой для локальной сети (это та, которой мы давали 200 мбит)
    • 1:11 LAN_INET - труба для интернета. Дочерний от 1:1
      • 1:111 LAN_INET_SERVER - серверная труба внутри LAN_INET. Дочерний от 1:11
      • 1:112 LAN_INET_USER - пользовательская труба внутри LAN_INET. Дочерний от 1:11
        • 1:1121 LAN_INET_USER_HTTP - подкласс пользовательского трафика для http (классификация трафика между пользователями). Дочерний от 1:112
        • 1:1122 LAN_INET_USER_MAIL - подкласс пользовательского трафика для mail. Дочерний от 1:112
        • 1:112x - тут еще будет много классов ( im, ping ) - опишем их все ниже потом
    • 1:12 LAN_BE - труба для корпоративной сети BE. Дочерняя от 1:1
    • 1:13 LAN_LAN - труба для локального трафика

В этих классах визуально описана емкость. Т.е. дочерние классы инкапсулируются в родительские. Повторюсь: трубы тут идут вертикально по сравнению со схемой, которую рисовали в задаче.

Рисуем остальные классы:

DEV_INET
+------------+
| 1:  root   |
+------------+
|
+------------------------------------------------+
| 1:1 INET                                       |
+------------------------------------------------+
            |                        | 
+-----------------------+  +---------------------+
| 1:11 INET_SERVER      |  | 1:12 INET_USER      |
+-----------------------+  +---------------------+
                              |         |        |
                           +--------+ +--------+ .
                           | 1:121  | | 1:122  | .
                           |  http  | |  mail  | . 
                           +--------+ +--------+

Тут мы описали трубы, нарисованные для исходящего трафика в интернет по устройству DEV_INET

Теперь для DEV_BE:

+------------+
| 1:  root   |
+------------+
|
+------------------------------------------------+
| 1:1 BE                                         |
+------------------------------------------------+ 


скрипт

Теперь попытаемся описать эту структуру в скрипте.


  •  ! ВНИМАНИЕ!

Описывать желательно в порядке убывания широты канала, т.е. сначала - самый широкий. Трафик как правило классифицируется по первому удовлетворившему его правилу, и если первым будет узкий - оно пойдет по нему и будет забивать и так узкий, хотя, возможно этот трафик предназначался для широкого канала.

Начнем с устройства DEV_LAN.

Корневой класс 1:1

echo -e "LAN"
# LAN

Опишем корневое устройство 1: root как дисциплину (о ней ниже).

$TC qdisc add dev $DEV_LAN root handle 1: htb default 1127

Теперь сам корневой класс:

$TC class add dev $DEV_LAN parent 1: classid 1:1 htb rate $RATE_LAN

как видно он является дочерним от 1: root (указана опция parent)

Теперь внутри корневого класса создаем трубу 1:13 (см.схему) для локального трафика LAN_LAN (самая широкая). С rate= $RATE_LAN_LAN

   # LAN_LAN
   echo -e " LAN_LAN"
   $TC class add dev $DEV_LAN parent 1:1 classid 1:13 htb rate $RATE_LAN_LAN prio 1
       $TC qdisc add dev $DEV_LAN parent 1:13 handle 13: pfifo
       $TC filter add dev $DEV_LAN parent 1:0 prio 1 $U32 $SRC $IP_LAN classid 1:13
       $TC filter add dev $DEV_LAN parent 1:0 prio 1 $U32 $SRC $IP_INET_IVT $DST $IP_LAN classid 1:13
       $TC filter add dev $DEV_LAN parent 1:0 prio 1 $U32 $SRC $IP_LAN $DST $IP_LAN classid 1:13

Тут уже пошли дисциплины и фильтры, по этому немного разъясним их.

Дисциплины

Дисциплины описывают каким именно образом будет распределяться трафик - алгоритмы его обработки. Самые распространенные дисциплины :

Входящие в ядро:

  • pfifo - обычное FIFO - самыя простая очередь - первым пришел - первым вышел, как в аппаратной реализации.
  • sfq - Stochastic Fair Queuing - равномерное распределение очереди по потокам. Работает по принципу "мясорубки" - весь трафик входит в эту дисциплину - а выходит равномерно по потокам. Дисциплина всем хороша, но не учитывает многопоточных качалок - так что у того, кто использует многопоточную качалку - будет несколько потоков и каждый из них выделится от sfq как отдельный.
В свежих ядрах эту проблему можно решить включив в ядре так называемый Flow classifier (модуль ядра называется cls_flow) - он позволяет выбрать правила классификации трафика для SFQ. К примеру выставив правило классификации hash keys dst - sfq будет определять уникальность потока (flow) по адресу назначения (dst), что решит проблему многопоточности с одного адреса.
  • red - Random Early Detection - случайное удаление пакетов из очередей, для придания определенной скорости. Используется для транспортных протоколов (TCP), и как правило для высокоскоростных каналов.

Не входящие в ядно (на момент написания статьи)

  • esfq - Extended Stochastic Fair Queuing - аналогично SQF, но позволяет выставлять limit-ы, и хеши по src (источнику), что позволяет бороться с многопоточными качалками
  • SRR - Simple Round Robin - честное деление трафика поровну. Подробнее: http://mordor.strace.net/sched-srr/ на русском.

Повторюсь в нашем примере выше дисциплина описана так:

       $TC qdisc add dev $DEV_LAN parent 1:13 handle 13: pfifo

Что означает, что дисциплина является дочерней от класса 1:13 ( LAN_LAN ), т.е. класс LAN_LAN будет обрабатываться этой дисциплиной по принципу очереди pfifo

Фильтры

Трафик будет подпадать под определенный класс с помощью фильтров. Наверное это самая трудоемкая работа - описать все что нужно.

В нашем примере выше фильтр описан так:

       $TC filter add dev $DEV_LAN parent 1:0 prio 1 $U32 $SRC $IP_LAN $DST $IP_LAN classid 1:13

Что означает, что все что имеет исходный (SRC) адрес $IP_LAN и идет на $IP_LAN (DST) - будет классифицироваться как Класс 1:13, т.е. наш LAN_LAN


Весь трафик фильтруется алгоритмом U32 (входит в ядро), и фильтроваться может по признаку протокола и исходящего порта (т.е. от куда он идет).

Теперь, когда мы знаем основные понятия - опишем остальные правила для наших труб:

Корпоративная сеть:

   # LAN_BE
   echo -e " LAN_BE"
   $TC class add dev $DEV_LAN parent 1:1 classid 1:12 htb rate $RATE_LAN_BE prio 2
       $TC qdisc add dev $DEV_LAN parent 1:12 handle 12: sfq perturb 10 
       $TC filter add dev $DEV_LAN parent 1:0 prio 1 $U32 $SRC $IP_BE classid 1:12

Тут тоже ничего сложного. Описали трубу класс (class), у которой родитель (parent) является труба 1:1, т.е. LAN, дали ей номер (classid) 1:12, выставили тип HTB. Далее описали дисциплину (qdisc) этого класса (parent 1:12) - очередь методом sfq (поровну между потоками). И написали фильтр - все то, что идет из корневой дисциплины root (parent 1:0) с $SRC $IP_BE, т.е. из корпоративной сети БЕ - классифицируется как описанный класс 1:12. Приоритет у нее 1.

Приоритет

Приоритет (prio), указываемый в классе (class) указывает на то, что если в родительском классе будет избыток трафика - он распределится по классам в соответствии с выставленным приоритетом. Чем ниже приоритет (меньше цифра) - тем приоритетнее класс.

Приоритет (prio), указанный в фильтре (filter) указывает на то, с каким приоритетом будет использоваться трафик в этом фильтре. Т.е. чем ниже цифра - тем приоритетнее трафик из этого фильтра в очереди.

Скрипт

Продолжаем описывать наши трубы. Локальную LAN_LAN трубу и трубу LAN_BE мы описали. Дошли до самого сложного - фильтрации трафика, идущего из Интернета для пользователей, т.е. LAN_INET. Напомню происходит это на устройстве DEV_LAN, т.е. устройстве, отдающим трафик в LAN.

Тут мы будем не просто раздавать интернет поровну (sfq) для всей трубы, а создадим подтрубы (дочерние классы) для каждого из типов трафика.


Труба LAN_INET:

   # LAN_INET 
   echo -e " LAN_INET"
   $TC class add dev $DEV_LAN parent 1:1 classid 1:11 htb rate $RATE_LAN_INET prio 3

дочерняя труба для серверов:

       # LAN_INET_SERVER
       echo -e "  LAN_INET_SERVER"
       $TC class add dev $DEV_LAN parent 1:11 classid 1:111 htb rate $RATE_LAN_INET_SERVER prio 1
           $TC qdisc add dev $DEV_LAN parent 1:111 handle 111: sfq perturb 10 
           $TC filter add dev $DEV_LAN parent 1:0 prio 1 $U32 $DST $IP_SERVER classid 1:111
           $TC filter add dev $DEV_LAN parent 1:0 prio 1 $U32 $SRC 217.14.50.110 classid 1:111 # VTB client-bank

дочерняя труба для пользователей:

       # LAN_INET_USER
       echo -e "  LAN_INET_USER"
       $TC class add dev $DEV_LAN parent 1:11 classid 1:112 htb rate $RATE_LAN_INET_USER prio 2

а теперь внутри этой трубы создаем еще классы, указывая parent (родительскую) - эту трубу. Также будем использовать так называемые "ползунки", т.е. не явно фиксированные скорости (ширину труб), а гибкую, задавая ее диапазоном: $RATE_LAN_INET_USER_MIN ceil $RATE_LAN_INET_USER_MAX. Трафик внутри родительского класса 1:11 будет двигать эти ползунки в зависимости от их загрузки, приоритета и свободного трафика внутри своего класса 1:11 в пределах $RATE_LAN_INET_USER

           # LAN_INET_USER_SHELL
           echo -e "    * shell"
           $TC class add dev $DEV_LAN parent 1:112 classid 1:1121 htb rate $RATE_LAN_INET_USER_MIN ceil $RATE_LAN_INET_USER_MAX prio 2
               $TC qdisc add dev $DEV_LAN parent 1:1121 handle 1121: sfq perturb 10   
               $TC filter add dev $DEV_LAN parent 1:0 prio 1 $U32 $TCP $SPORT 22 0xfffe classid 1:1121   # remote ssh server
               $TC filter add dev $DEV_LAN parent 1:0 prio 1 $U32 $TCP $DPORT 22 0xfffe classid 1:1121   # our ssh server

как видимо под фильтр попал трафик как удаленных, так и локальных ssh серверов

           # LAN_INET_USER_PING
           echo -e "    * ping"
           $TC class add dev $DEV_LAN parent 1:112 classid 1:1122 htb rate $RATE_LAN_INET_USER_MIN ceil $RATE_LAN_INET_USER_MAX prio 2
               $TC qdisc add dev $DEV_LAN parent 1:1122 handle 1122: sfq perturb 10
               $TC filter add dev $DEV_LAN parent 1:0 prio 2 $U32 $ICMP classid 1:1122     # ICMP
               $TC filter add dev $DEV_LAN parent 1:0 prio 2 $U32 $SPORT 53 0xffff classid 1:1122 # DNS
           # LAN_INET_USER_IM
           echo -e "    * im"
           $TC class add dev $DEV_LAN parent 1:112 classid 1:1123 htb rate $RATE_LAN_INET_USER_MIN ceil $RATE_LAN_INET_USER_MAX prio 3
               $TC qdisc add dev $DEV_LAN parent 1:1123 handle 1123: sfq perturb 10
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $SPORT 5190 0xffff classid 1:1123 # icq
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $SPORT 5222 0xfffe classid 1:1123 # jabber
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $DPORT 5222 0xffff classid 1:1123 # our jabber
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $DPORT 5269 0xffff classid 1:1123 # our icq-t
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $DPORT 5556 0xffff classid 1:1123 # our jabber connector
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $SPORT 13279 0xffff classid 1:1123 # Skype
           # LAN_INET_USER_MAIL
           echo -e "    * mail, https"
           $TC class add dev $DEV_LAN parent 1:112 classid 1:1124 htb rate $RATE_LAN_INET_USER_MIN ceil $RATE_LAN_INET_USER_MAX prio 4
               $TC qdisc add dev $DEV_LAN parent 1:1124 handle 1124: sfq perturb 10
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $SPORT 443 0xffff classid 1:1124  # https
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $SPORT 3389 0xffff classid 1:1124 # remote desktop 
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $SPORT 110 0xffff classid 1:1124  # pop3
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $SPORT 25 0xffff classid 1:1124   # sendmail
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $SPORT 143 0xffff classid 1:1124  # imap
               $TC filter add dev $DEV_LAN parent 1:0 prio 3 $U32 $TCP $SPORT 220 0xffff classid 1:1124  # imap3

следующие два класса могут занимать всю полосу, обратите внимание, у них диапазон $RATE_LAN_INET_USER_MIN ceil $RATE_LAN_INET_USER, т.е. от минимально гарантированного значения, до всей полосы (если такое возможно).


           # LAN_INET_USER_WEB
           echo -e "    * web"
           $TC class add dev $DEV_LAN parent 1:112 classid 1:1125 htb rate $RATE_LAN_INET_USER_MIN ceil $RATE_LAN_INET_USER prio 5  # can use all RATE
               $TC qdisc add dev $DEV_LAN parent 1:1125 handle 1125: sfq perturb 10
               $TC filter add dev $DEV_LAN parent 1125: protocol ip handle 1125 flow hash keys dst divisor 1024 #nfct-dst
               $TC filter add dev $DEV_LAN parent 1:0 prio 4 $U32 $TCP $SPORT 21 0xffff classid 1:1125   # ftp
               $TC filter add dev $DEV_LAN parent 1:0 prio 4 $U32 $TCP $SPORT 80 0xffff classid 1:1125   # http
               $TC filter add dev $DEV_LAN parent 1:0 prio 4 $U32 $TCP $SPORT 8080 0xffff classid 1:1125 # http proxy
               $TC filter add dev $DEV_LAN parent 1:0 prio 4 $U32 $TCP $SPORT 8081 0xffff classid 1:1125 # http proxy

           # LAN_INET_USER_DEFAULT
           echo -e "    * default"
           $TC class add dev $DEV_LAN parent 1:112 classid 1:1127 htb rate $RATE_LAN_INET_USER_MIN ceil $RATE_LAN_INET_USER prio 7 # can use all RATE
               $TC qdisc add dev $DEV_LAN parent 1:1127 handle 1127: sfq perturb 10
               $TC filter add dev $DEV_LAN parent 1127: protocol all handle 1127 flow hash keys dst divisor 1024
echo -e "LAN DONE"
echo -e ""

flow classifier

Как было сказано выше - для SFQ рекомендуется включить классификатор Flow Classifier в ядре (модуль CLS_FLOW), который позволяет выбрать метод классификации потока (flow). Выбор метода привязывается к дисциплине (qdisc) и имеет ее же id. В классификаторе указывается как именно разделять потоки. К примеру можно выбрать разделение потоков по dst - адрес получателя и решить тем самым проблему многопоточных закачек от одного пользователя. Строка выглядит как:

$TC filter add dev $DEV_LAN parent 1127: protocol all handle 1127 flow hash keys dst divisor 1024

и ее можно наблюдать в двух последних дисциплинах вышеописанного скрипта (web и default для пользователей). Так мы по честному раздаем им http и прочие (в том числе торрент) соединения так, что один пользователь не забьет весь канал, а количество потоков будет делиться внутри его полосы только для него.


Все! Описали все устройство DEV_LAN. Весь трафик, который идет в локальную сеть - классифицирован.

Теперь опишем второе устройство DEV_INET - это трафик, идущий в Интернет:

корневая дисциплина:

echo -e "INET"
# INET
$TC qdisc add dev $DEV_INET root handle 1: htb default 127
$TC class add dev $DEV_INET parent 1: classid 1:1 htb rate $RATE_INET
   

серверный трафик:

   # INET_SERVER
   echo -e " INET_SERVER"
   $TC class add dev $DEV_INET parent 1:1 classid 1:11 htb rate $RATE_INET_SERVER prio 1
       $TC qdisc add dev $DEV_INET parent 1:11 handle 11: sfq perturb 10
       $TC filter add dev $DEV_INET parent 1:0 prio 1 $U32 $SRC $IP_SERVER classid 1:11

пользовательский класс:

   # INET_USER
   echo -e " INET_USER"
   $TC class add dev $DEV_INET parent 1:1 classid 1:12 htb rate $RATE_INET_USER prio 2

также разобьем его на классы, чтобы выставить приоритет по типу трафика, как в примере для DEV_LAN

       # INET_USER_SHELL
       echo -e "   * shell"
       $TC class add dev $DEV_INET parent 1:12 classid 1:121 htb rate $RATE_INET_USER_MIN ceil $RATE_INET_USER_MAX prio 1
       $TC qdisc add dev $DEV_INET parent 1:121 handle 121: sfq perturb 10
           $TC filter add dev $DEV_INET parent 1:0 prio 1 $U32 $TCP $DPORT 22 0xfffe classid 1:121   # to remote shell

Обратите внимание, тут мы используем $DPORT - т.е. удаленный порт 22. Т.к. трафик уходит в интернет на порт 22.

       # INET_USER_PING
       echo -e "   * ping"
       $TC class add dev $DEV_INET parent 1:12 classid 1:122 htb rate $RATE_INET_USER_MAX prio 2
       $TC qdisc add dev $DEV_INET parent 1:122 handle 122: sfq perturb 10
           $TC filter add dev $DEV_INET parent 1:0 prio 1 $U32 $ICMP classid 1:122            # ping
           $TC filter add dev $DEV_INET parent 1:0 prio 2 $U32 $SPORT 53 0xffff classid 1:122 # from our DNS
           $TC filter add dev $DEV_INET parent 1:0 prio 2 $U32 $DPORT 53 0xffff classid 1:122 # to other DNS
       # INET_USER_IM
       echo -e "   * im"
       $TC class add dev $DEV_INET parent 1:12 classid 1:123 htb rate $RATE_INET_USER_MIN ceil $RATE_INET_USER_MAX prio 3
       $TC qdisc add dev $DEV_INET parent 1:123 handle 123: sfq perturb 10
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 5222 0xfffe classid 1:123 # to remote jabber
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $SPORT 5222 0xffff classid 1:123 # from our jabber
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $SPORT 5556 0xffff classid 1:123 # from our icq-t
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 5269 0xffff classid 1:123 # from our jabber-connector
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 5190 0xffff classid 1:123 # icq
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 13279 0xffff classid 1:123 # Skype


       # INET_USER_MAIL
       echo -e "   * mail"
       $TC class add dev $DEV_INET parent 1:12 classid 1:124 htb rate $RATE_INET_USER_MIN ceil $RATE_INET_USER_MAX prio 4
       $TC qdisc add dev $DEV_INET parent 1:124 handle 124: sfq perturb 10
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 3389 0xffff classid 1:124 # remote desktop
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 443 0xffff classid 1:124  # to https
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $SPORT 443 0xffff classid 1:124  # our https
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 110 0xffff classid 1:124  # to pop3
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 25 0xffff classid 1:124   # sendmail
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $SPORT 25 0xffff classid 1:124    # from our sendmail
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 143 0xffff classid 1:124  # imap
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 220 0xffff classid 1:124  # imap3
       # INET_USER_WEB
       echo -e "   * web"
       $TC class add dev $DEV_INET parent 1:12 classid 1:125 htb rate $RATE_INET_USER_MIN ceil $RATE_INET_USER prio 5 # can use all RATE
       $TC qdisc add dev $DEV_INET parent 1:125 handle 125: sfq perturb 10
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 80 0xffff classid 1:125    # http
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $SPORT 80 0xffff classid 1:125    # from our http
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 443 0xffff classid 1:125   # https
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $SPORT 443 0xffff classid 1:125   # from our https
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 8080 0xffff classid 1:125  # http proxy
           $TC filter add dev $DEV_INET parent 1:0 prio 3 $U32 $TCP $DPORT 8081 0xffff classid 1:125  # http proxy

       # INET_USER_DEFAULT
       echo -e "   * default"
       $TC class add dev $DEV_INET parent 1:12 classid 1:127 htb rate $RATE_INET_USER_MIN ceil $RATE_INET_USER prio 7 # can use all RATE
           $TC qdisc add dev $DEV_INET parent 1:127 handle 127: sfq perturb 10


echo -e "INET DONE"
echo -e ""


Осталось всего одно устройство - DEV_BE. И у него все в одной трубе:

echo -e "BE"
# BE
$TC qdisc add dev $DEV_BE root handle 1: htb default 11
$TC class add dev $DEV_BE parent 1: classid 1:1 htb rate $RATE_BE
   # BE_ALL
   echo -e "  BE_ALL"
   $TC class add dev $DEV_BE parent 1:1 classid 1:11 htb rate $RATE_BE
   $TC qdisc add dev $DEV_BE parent 1:11 handle 11: sfq perturb 10
       $TC filter add dev $DEV_BE parent 1:0 prio 1 $U32 $DST $IP_BE classid 1:11
echo -e "BE DONE"
echo -e ""
#
######
echo "All Done."


Все. Шейпер готов и написан для каждого устройства.

Очистка правил

Скрипт очистки правил:

#!/bin/bash
TC=/sbin/tc
DEV_INET=eth1
DEV_LAN=eth2
DEV_BE=eth0
$TC qdisc del dev $DEV_INET root
$TC qdisc del dev $DEV_LAN root
$TC qdisc del dev $DEV_BE root


Просмотр результатов

Посмотреть результаты можно командами:

tc -s -d qdisc ls dev eth1
tc -s -d qdisc ls dev eth2

Каждая команда выведет информацию о дисциплинах и сколько трафика прошло по этим дисциплинам. В идеале в каждой дисциплине должен быть трафик, нолей с Sent быть не должно. Если есть 0 - значит трафик не пошел по этому правилу, а это значит либо не было подобного трафика, либо неверно работает filter

Еще советую tc-viewer - удобный скрипт на perl. Даже с подстветкой. К примеру для DEV_LAN в конфиг его надо будет внести следующие изменения:

$unit = "kbit";
$iface = "eth2";
%names = (
       '1:1' => 'LAN',
   '1:11' => 'LAN_INET',
   '1:12' => 'LAN_BE',
   '1:13' => 'LAN_LAN',
   '1:111' => 'LAN_INET_SERVER',
   '1:112' => 'LAN_INET_USER',
       '1:1121' => 'LAN_INET_USER_SHELL',
       '1:1122' => 'LAN_INET_USER_PING',
       '1:1123' => 'LAN_INET_USER_IM',
       '1:1124' => 'LAN_INET_USER_MAIL',
       '1:1125' => 'LAN_INET_USER_WEB',
       '1:1126' => 'reserved',
       '1:1127' => 'LAN_INET_USER_DEFAULT',
);


Фильтрация с помощью iptables

Частным случаем фильтрации можно назвать фильтрацию маркированных пакетов посредством iptables. Тут можно добиться большой гибкости. Так фильтр шейпера для маркированного трафика будет иметь вид:

$TC filter add dev $DEV_IN parent 1:0 prio 1 protocol ip u32 match mark 20 0xffff classid 1:20 

либо используя макросы, к примеру комбинирую с правилом направления порта:

$TC filter add dev $DEV_INET parent 1:0 prio 1 $U32 $MARK 20 0xffff $TCP $DPORT 22 0xfffe classid 1:112 

тут описано, что пакеты помеченные как 20 будут направлены в класс 20, для пометки пакета будем использовать IPTABLES, маркирующий пакет по какому-либо правилу, к примеру:

$iptables -A PREROUTING -t mangle  -s 172.32.0.0/24 -m mark --mark 20


  • Внимание !

Особенно полезна фильтрация iptables-ом для шейпера при использовании NAT! Т.е. к примеру исходящий трафик от локальных пользователей в интернет маскируется как внешний. И правило $SRC $LOCAL_IP для исходящего в интернет интерфейса $DEV_INET работать не будет! Для сортировки пакетов в таком случаи рекомендуется использовать как раз маркировку iptables (до NAT-а). А описание правила шейпера уже делать через $MARK, как показано в этом примере.

ESFQ

Скачать патч esfq для ядра (2.6.26 на текущий момент) от сюда:

https://dev.openwrt.org/browser/trunk/target/linux/generic-2.6/patches-2.6.26/200-sched_esfq.patch

Патч для iproute2 (последний был 2.6.25)

https://dev.openwrt.org/browser/trunk/package/iproute2/patches/006-iproute2-tc_esfq.patch


еще можно поискать тут:

http://fatooh.org/esfq-2.6/current/
http://fatooh.org/esfq-2.6/

применить:

patch -p1 200-sched_esfq.patch

Собрать, выставив в

QoS and/or fair queueing -> Enhanced Stochastic Fairness Queueing

В правилах вместо sfq писать:

tc qdisc add dev eth1 parent 1:2 handle 200: esfq hash dst

Тип hash-а читайте тут:

http://fatooh.org/esfq-2.6/current/README


Заметки

Замечу, что поднять шейпер на VLAN корректно мне не удалось. Я даже поднял тему по поводу этого: http://www.linux.org.ru/view-message.jsp?msgid=2993319 Как выяснилось на данный момент (ядро 2.6.26) - поднятие шейпера на vlan является проблемным, и советуется использовать для этих целей IMQ интерфейсы.

Ссылки

Личные инструменты