Повесть о Linux и
управлении трафиком.
Автор: © Иван Песин
Управление трафиком состоит из набора механизмов и операций с помощью которых пакеты организовываются в очередь для приема/передачи через сетевой интерфейс. Цель данного документа -- описать возможности, реализацию и привести примеры использования системы управления трафиком в операционной системе Linux.
Документ предназначен для сетевых администраторов и других пользователей, которые желают получить представление об управлении трафиком в целом и утилитах, доступных для этой цели в ОС Linux.
Управление трафиком -- это термин, объединяющий системы обработки очередей и функции приема/передачи пакетов в маршрутизаторе. Они включают в себя механизмы принятия решений, какие пакеты принимать и с какой скоростью на входящем интерфейсе, определения пакетов для передачи, их порядка и скорость передачи на исходящем устройстве.
Термин "качество обслуживания" (Quality of Service, QoS) часто используется как синоним управления трафиком.
Данный документ основан на нескольких англоязычных документах, включая Linux Advanced Routing and Traffic Control HOWTO. Ссылки на источники приведены в конце документа.
Очереди образуют основу всего управления трафиком и являются неотъемлемой частью системы планирования. В сетях под очередью понимают буфер, где пакеты (или другие единицы данных) ожидают передачи устройством. В простейшей модели пакеты передаются по принципу "первым пришел -- первым ушел". Такой алгоритм (дисциплина) обработки очереди в области информационных технологий обозначается как FIFO.
У такой дисциплины обработки очереди нет никаких механизмов для управления трафиком. Все, что она умеет делать -- это помещать пакеты в очередь и извлекать их оттуда.
Намного интересней, когда алгоритм обработки очереди включает в себя механизмы задержки пакетов, изменения порядка, утери и приоритезации пакетов в нескольких очередях. У очередей могут существовать подочереди, которые повышают возможности системы планирования.
Поток -- это отдельное соединение или диалог между двумя хостами. Любой уникальный набор пакетов передающихся между двумя хостами может рассматриваться как поток. В протоколе TCP понятие соединения с исходящими, целевыми адресами и портами представляет собой поток. Аналогично может быть определено понятие потока для протокола UDP.
Понятие потока очень важно, когда полоса пропускания делится на равные части между конкурирующими соединениями, особенно если некоторые приложения сознательно создают большое количество соединений.
Еще два фундаментальных понятия системы управления трафиком -- это токены и буферы.
Для того, чтобы контролировать скорость вывода объектов из очереди, алгоритм обработки может считать число пакетов или байт в извлекаемом объекте. Но этот метод требует сложной реализации. Другой метод, получивший широкое распространение в управлении трафиком -- это генерация токенов с определенной скоростью, а извлечение объектов из очереди производится только в случае наличия токенов.
Максимальное количество токенов задается размером буфера, в который помещаются сгенерированные токены. Все имеющиеся в буфере токены, могут быть использованы без ожидания генерации следующих токенов.
Ограничение исходящего трафика (shaping) -- это механизм, с помощью которого пакеты задерживаются перед передачей с тем, чтобы скорость передачи соответствовала желаемой. Это один из самых часто используемых механизмов управления трафиком. Как побочный эффект, данный механизм позволяет сглаживать взрывообразный трафик.
Планирование (scheduling) -- это механизм, который позволяет упорядочивать или переупорядочивать объекты между входом и выходом конкретной очереди. Примерами планировщика могут послужить алгоритмы FIFO, SQF, WRR и другие.
Классификация (classifying) -- механизм, разделяющий пакеты для различной обработки, возможно в разные очереди. В процессе приема, маршрутизации и передачи пакетов, сетевое устройство может по-разному их классифицировать. Это может быть маркирование, которое обычно происходит на границе сети с единым администрированием, или же может выполняться индивидуально на каждом промежуточном узле.
Ограничение входящего трафика (policing) -- очередной элемент системы качества обслуживания, ограничивающий трафик. Этот механизм принимает пакеты до определенной скорости, а над частью трафика превысившей заданный порог выполняется определенное действие. Например, можно уничтожать трафик, или переклассифицировать его. Не смотря на то, что в данном случае тоже используется концепция буфера токенов, он не поддерживает возможность задержки пакетов в отличие от механизма ограничения исходящего трафика
Уничтожение (dropping) -- механизм, уничтожающий данные. Например, он используется при переполнении буфера данных ограничителя исходящего трафика.
Маркирование (marking) -- механизм изменения пакета. Обратите внимание, что это не fwmark. Цели MARK и --mark утилит iptables и ipchains соответственно, модифицируют метаданные пакета, а не сам пакет.
Таблица соответствия между классическими механизмами управления трафиком и их реализацией в ОС Linux
традиционный элемент | компонент Linux |
---|---|
ограничение исходящего трафика (shaping) | класс (class) обладает возможностями по ограничению исходящего трафика. |
планирование (scheduling) | алгоритм обработки очереди (qdisc) выполняет планирование. Планирование может быть простым, например FIFO, и сложным, с классами и другими дисциплина обработки очереди, например HTB. |
классификация (classifying) | фильтр (filter) выполняет классификацию с помощью классификаторов (classifier). Строго говоря, классификаторы Linux не могут существовать вне фильтра. |
ограничение входящего трафика (policing) | ограничитель в Linux-реализации контроля трафика существует только как составная часть фильтра. |
уничтожение (dropping) | для уничтожения трафика необходим фильтр с ограничителем, который использует действие "drop". |
маркирование (marking) | для маркирования используется алгоритм обработки очереди dsmark. |
Алгоритм (дисциплина) обработки очереди -- это механизм планирования. Каждому исходящему интерфейсу необходим тот или иной механизм планирования. В Linux по умолчанию используется pfifo_fast, более сложный, чем классический алгоритм FIFO. Другие доступные дисциплины обработки очереди переупорядочивают пакеты, поступающие в очередь в соответствии с заданными правилами.
Алгоритмы обработки очереди -- это основные блоки, из которых строится управление трафиком в Linux.
Классовые дисциплины обработки очереди могут содержать классы и определять дескрипторы, к которым подключаются фильтры. Можно использовать классовые дисциплины, не задавая классов, однако это будет бесполезным использованием системных ресурсов.
Бесклассовые дисциплины обработки очереди не могут содержать классы и к ним нельзя подключить фильтры. Следовательно, при использовании бесклассовых дисциплин классификация не имеет смысла.
Источником путаницы является использование терминов "корневая дисциплина" (root qdisc) и "входящая дисциплина" (ingress qdisc). На самом деле это не дисциплины обработки очереди, а точки, к которым подключаются конструкции управления трафиком для управления исходящим (egress) и входящим (ingress) трафиком.
Каждый интерфейс имеет эти две точки. Основной и наиболее используемой точкой является egress, для исходящего трафика, которая часто и называется "корневой дисциплиной обработки очереди" (root qdisc). Она может содержать любую настоящую дисциплину обработки очереди (qdisc). В подавляющем большинстве документации используется термин "корневая дисциплина" и ее потомки. Весь исходящий трафик интерфейса проходит через корневую дисциплину.
Входящий трафик, в свою очередь, проходит через входящую дисциплину (ingress qdisc). Из-за ограниченных возможностей, у нее нельзя создавать классы, можно лишь подключить к ней фильтр. На практике, входящая дисциплина представляет собой объект, к которому подключается ограничитель для регулирования скорости входящего трафика на интерфейсе.
Классы существуют только внутри классовых дисциплин обработки очереди. Классы -- это очень гибкие структуры, которые всегда могут содержать либо несколько подклассов, либо дисциплину обработки очереди. Класс может даже содержать классовую дисциплину обработки очереди, что позволяет создавать очень сложные решения по управлению трафиком.
К каждому классу может быть подключено произвольное количество фильтров, которые позволяют распределять по подклассам, переклассифицировать и уничтожать трафик, проходящий через данный класс.
Краевой класс (leaf class) -- это завершающий класс дисциплины обработки очереди. Краевой класс не может содержать подклассы, только дисциплину обработки очереди (по умолчанию pfifo_fast). Любой класс, содержащий подклассы, называется внутренним (или корневым, если нет родительского класса).
Фильтры (filters) представляют наиболее сложную компоненту системы управления трафиком в Linux. Они обеспечивают удобный механизм связывания нескольких элементов управления трафиком в единое целое. В простейшем случае фильтр используется для классификации пакетов. Фильтры подключаются либо к классовым дисциплинам обработки очередей, либо к классам, однако в любом случае пакет, проходящий через интерфейс, всегда проходит корневую дисциплину и лишь после этого может быть классифицирован и направлен в подкласс.
Классификаторы (classifiers) -- это инструменты, используемые фильтрами для идентификации характеристик пакета или метаданных пакета. В Linux существуют разные механизмы классификации, но наиболее используемым классификатором является u32. Он позволяет классифицировать пакеты на основании их атрибутов.
Ограничитель (policer) используется в системе управления трафиком Linux как часть фильтра. Ограничитель выполняет одно из двух заданных действий в зависимости от скорости трафика, превышает она заданное значение или нет. Не смотря на то, что и класс, и ограничитель являются элементами, ограничивающими полосу пропускания, ограничитель никогда не задерживает трафик, он лишь выполняет заданное действие.
Механизм уничтожения (drop) пакетов может применяться в Linux-реализации системы управления трафиком только как часть ограничителя. Любой ограничитель, подключенный к фильтру, может выполнять уничтожение. Ограничитель -- это единственный компонент системы управления трафиком Linux, для которого вы явно можете задать уничтожение пакетов. Например, ограничитель может уничтожать весь трафик соответствующий определенному шаблону.
Однако, в других случаях пакеты тоже могут уничтожаться. При использовании алгоритма обработки очереди GRED уничтожение пакетов является механизмом управления полосой пропускания. Также пакеты будут уничтожаться при переполнении буферов планировщиков, что может происходить в периоды особенного повышения трафика.
Каждый класс и классовая дисциплина требуют уникального идентификатора в конструкциях управления трафиком. Этот идентификатор называется дескриптором и состоит из двух чисел, старшего и младшего номера. Это могут быть произвольные числа, заданные пользователем в соответствии со следующими правилами:
Нумерация дескрипторов классов и дисциплин обработки очередей
Это совершенно произвольное число, которое выбирает пользователь в соответствии со своей схемой нумерации. Однако, все объекты структуры управления трафиком у которых есть общий родитель должны иметь одинаковый старший номер дескриптора.
Если младший номер дескриптора равен нулю, то это однозначно идентифицирует объект, как дисциплину обработки очереди. Другие значения обозначают классы. У всех классов с общим родителем должны быть уникальные младшие номера
Специальный дескриптор ffff:0 зарезервирован для входящей дисциплины.
Дескриптор используется в качестве аргумента ключей classid и flowid фильтров.
Ниже приведена схема прохождения трафика:
____ +---------------+ +->-| FW |---> | TCP, UDP, ... | | +----+ +---------------+ | | ^ v | _|_ +----<----+ | FW | | +----+ ^ | | Y К IP-стеку От IP-стека хоста хоста ^ | |_____ | Входящее ^ Y Исходящее устройство ____ +----------+ +--|---|--+ ____ +----------+ устройство ---->------>| FW |-->|Управление|-->---->| Форвар- |->| FW |->|Управление|-->--> +----+ | входящим | | динг | +----+ |исходящим | | трафиком | +---------+ | трафиком | +----------+ +----------+ FW -- брандмауэр. |
А теперь схема блока управления исходящим трафиком:
++ ++ +-----+ +------------+ ++ ++ .++ || . || +------+ | |-->| Дисциплина |-->|| || || || ||---->|Фильтр|--->|Класс| +------------+ ||-+ || || || || | +------+ | +--------------------+| | || || || . || | +---------------------------+ | || .|| || . || | +------+ | || || || || +->|Фильтр|-_ +-----+ +------------+ ++ | || .|| || -->|| | +------+ ->| |-->| Дисциплина |-->|| | ||->|| || . || | |Класс| +------------+ ||-+-->|| .|| -->|| || | +------+ _->| +--------------------+| || || || || +->|Фильтр|- +---------------------------+ || .|| || || +------+ || .|| || . |+---------------------------------------------------+| || || | Внутренняя дисциплина обработки очереди | .|| || . +-----------------------------------------------------+ .|| || . . .. . . .. . . . .. .. .. . .. || |+-------------------------------------------------------------+| | Корневая дисциплина обработки очереди | | (подключена к исходящему устройству) | +---------------------------------------------------------------+ |
В состав операционной системы Linux входит пакет iproute2. Он содержит утилиты для работы со структурами ядра, относящимися к конфигурации сетевой поддержки. Нас будет интересовать всего одна утилита из этого пакета -- tc. Она предназначена для работы с системой управления трафика.
Поскольку данная утилита напрямую работает с ядром, для создания, удаления и изменения конструкций управления трафиком она должна содержать поддержку всех дисциплин обработки очередей, с которыми вы хотите работать. К сожалению, во многих современных дистрибутивах пакет iproute2 не имеет поддержки алгоритма HTB, потому если вы хотите с ним работать, вам, вероятно, придется наложить патч и пересобрать пакет iproute2.
Команда tc выполняет все необходимые действия при работе с системой управления трафика. Как следствие этого, синтаксис у данной команды достаточно сложный, но весьма функциональный. В качестве первого аргумента (не ключа) утилита ожидает один из трех компонент системы управления трафиком: дисциплину (qdisc), класс (class) или фильтр (filter).
Синтаксис команды tc:
[root@eagle]# tc Usage: tc [ OPTIONS ] OBJECT { COMMAND | help } where OBJECT := { qdisc | class | filter } OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] } |
Для каждого типа объекта существует свой набор опций, который частично мы рассмотрим в этом документе.
[root@eagle]# tc qdisc add \ > dev eth0 \ > root \ > handle 1:0 \ > htb |
Итак, мы рассмотрели простейший пример использования утилиты tc для назначения дисциплины обработки очереди устройству. Теперь давайте посмотрим, как добавлять классы к существующим классам.
Команда tc class:
[root@eagle]# tc class add \ > dev eth0 \ > parent 1:1 \ > classid 1:6 \ > htb \ > rate 256kbit \ > ceil 512kbit |
Команда tc filter
[root@eagle]# tc filter add \ > dev eth0 \ > parent 1:0 \ > protocol ip \ > prio 5 \ > u32 \ > match ip port 22 0xffff \ > match ip tos 0x10 0xff \ > flowid 1:6 \ > police \ > rate 32000bps \ (11) > burst 10240 \ (12) > mpu 0 \ (13) > action drop/continue (14) |
Как показано выше, команда tc имеет сложный синтаксис, даже для описания простых операций. Однако, существуют проекты, позволяющие упростить написание простых правил. Например: CBQ.init, HTB.init, tcng
Все дисциплины этого типа могут использоваться и как основная дисциплина для всего интерфейса, так и внутри краевого класса классовой дисциплины обработки очереди. Это основные планировщики, используемые в Linux. По умолчанию используется дисциплина pfifo_fast.
Алгоритм FIFO является прообразом дисциплины pfifo_fast, используемой по умолчанию на всех интерфейсах Linux. Он не выполняет ограничения входящего трафика и переупорядочивания пакетов. Он просто обрабатывает по принципу "первый пришел, первый ушел".
Однако, для очереди типа FIFO нужно задавать размер буфера для предотвращения переполнения, если пакеты поступают в очередь быстрее, чем выводятся из нее. В ОС Linux реализовано две основных дисциплины FIFO -- одна основана на байтах, другая -- на пакетах. Вне зависимости от того, какая очередь используется, для задания размера буфера применяется параметр limit. Для дисциплины pfifo под объектом понимают пакет, а для bfifo -- байт.
Указание размера буфера для очередей pfifo и bfifo
bfifo: [root@eagle]# tc qdisc add dev eth0 handle 1:0 root dsmark indices 1 default_index 0 [root@eagle]# tc qdisc add dev eth0 handle 2:0 parent 1:0 bfifo limit 10240 pfifo: [root@eagle]# tc qdisc add dev eth0 handle 1:0 root dsmark indices 1 default_index 0 [root@eagle]# tc qdisc add dev eth0 handle 2:0 parent 1:0 pfifo limit 30 |
Данная дисциплина обработки очереди является основной для всех интерфейсов Linux. Она основана на обычном алгоритме FIFO, но обладает некоторыми функциями приоритезации. pfifo_fast содержит три отряда (bands) для разделения трафика. Каждый отряд представляет собой обычную очередь FIFO. Высокоприоритетный трафик помещается в нулевой отряд и всегда обрабатывается в первую очередь. Аналогично, трафик, попадающий в первый отряд, обрабатывается перед вторым отрядом.
Не путайте эту простую бесклассовую дисциплину с классовой PRIO. Не смотря на то, что они используют аналогичные подходы, pfifo_fast является бесклассовой и не может содержать подклассы и другие дисциплины обработки очередей.
Вы не можете изменять настройки этой дисциплины, поскольку они "зашиты" в коде.
Дисциплина обработки очереди SFQ пытается распределить поровну возможность передавать данные между произвольным количеством потоков. Это достигается использованием хэш-функции для разделения трафика на отдельные очереди типа FIFO, которые потом циклически обрабатываются. Поскольку существует вероятность совпадения значения хэш-функции, она периодически изменяется. Параметр perturb задает интервал в секундах, через который происходит изменение функции. Если параметр не задан, функция изменяться не будет, что крайне не рекомендуется. Значение 10 подходит в большинстве случаев.
Количество данных обрабатываемых из одного потока за один прием задается параметром quantum. По умолчанию равен одному максимальному объекту передачи. Не устанавливайте размер меньше этого значения.
Пример использования SFQ
[root@eagle]# tc qdisc add dev ppp0 root sfq perturb 10 [root@eagle]# tc -s -d qdisc ls qdisc sfq 800c: dev ppp0 quantum 1514b limit 128p flows 128/1024 perturb 10sec Sent 4812 bytes 62 pkts (dropped 0, overlimits 0) |
Число 800c: -- это автоматически присвоенный дескриптор, limit означает, что в данной очереди может ожидать обработки 128 пакетов. Всего доступно 1024 "псевдоочереди" (на которые разбивается трафик), из которых 128 могут быть активны одновременно (больше пакетов не помещается в очереди!). Один раз в десять секунд изменяется хэш.
Концептуально, эта дисциплина обработки очереди ничем не отличается от SFQ, но дает больше возможностей по настройке очереди. Имея возможность управлять используемым алгоритмом хэша, пользователь может достичь более равномерного распределения полосы пропускания. Используя данную дисциплину, можно вручную задать параметры depth (максимально возможное количество "псевдо-очередей", в SFQ жестко задано значение 1024), limit (количество пакетов хранящихся в буфере, в SFQ равняется 128), hash (тип хэша: classic -- аналогичный используемому в SFQ, src -- по адресу источника и dst -- по адресу получателя) и divisor (длина хэша в битах, в SFQ равно 10).
Обычная дисциплина SFQ становится неэффективной, если пользователи работают с программами, поддерживающими несколько потоков. В этом случае SFQ не способен поровну разделить канал между пользователями. Однако доступ к дополнительным параметрам алгоритма, обеспечиваемый дисциплиной обработки очереди ESFQ позволяет решить описанную проблему использованием другого хэша, например, по исходному адресу.
Пример использования ESFQ
[root@eagle]# tc qdisc add dev eth0 root esfq limit 128 depth 128 divisor 10 hash classic perturb 15 [root@eagle]# tc qdisc add dev eth0 root esfq limit 64 depth 64 divisor 11 hash dst |
Применяя данную дисциплину, следует помнить, что значение параметра limit должно быть меньше или равно параметру depth, а значение параметра divisor -- меньше 15.
Дисциплина RED предоставляет механизм предотвращения перегрузок за счет уничтожения случайных пакетов из произвольных потоков. Применяется только для протоколов транспортного уровня, таких как TCP, которые обнаруживают утерю пакетов и соответствующим образом это обрабатывает -- уменьшает размер окна, переходит в режим медленного старта, прерывает на некоторое время передачу, после чего пытается повторно передать данные. Дисциплина RED контролирует размер очереди и при ее заполнении выбирает случайным образом отдельные TCP-потоки и начинает уничтожать пакеты, с целью замедлить скорость передачи.
Использование алгоритма RED целесообразна на высокоскоростных магистралях, где полосы пропускания составляют десятки мегабит, что требует других подходов, нежели относительно медленные выделенные каналы.
Обычная обработка очередей маршрутизаторов в Internet называется "обрубанием хвостов" (tail-drop). При этом данные до какого-то объема помещаются в очередь, после чего все остальные данные начинают уничтожаться. Такое поведение не является лучшим вариантом и ведет к повторным передачам и повторным установлениям соединений. Причем после повторного установления соединения предающий узел снова быстро заполнит очередь маршрутизатора и перегрузка повторится.
Во избежание таких ситуаций на магистральных маршрутизаторах часто делают огромные очереди. Это предотвращает ненужное уничтожение трафика и обеспечивает высокую пропускную способность, но при этом очень сильно увеличиваются задержки.
Проблемы с механизмом "обрубания хвостов" в Internet становятся все более серьезными из-за возросшего количества приложений агрессивно использующих Сеть. Linux предоставляет в пользование дисциплину обработки очереди RED, сокращение от Random Early Detect.
RED не представляет собой решения всех проблем; приложения, которые не реализуют правильную обработку перегрузок будут получать меньшую часть полосы пропускания, но они не будут отрицательно влиять на полосу пропускания и задержку других потоков.
RED статистически уничтожает пакеты из потоков до того, как скорость потока достигает жесткой границы. Это позволяет мягко понижать скорость передачи на перегруженном канале и избегать повторных соединений. Кроме того, при использовании такой технологии протокол TCP может "найти" оптимальную скорость, при которой пакеты еще не уничтожаются, а нагрузка находится в разумных приделах. Вероятность уничтожения пакета из определенного соединения пропорциональна загруженности полосы пропускания, а не количеству передаваемых пакетов
Дисциплина RED обычно применяется на высоконагруженных магистральных каналах, где по аппаратным и другим причинам нельзя позволить поддерживать статус соединений, необходимый для очередей типа SFQ.
Для использования дисциплины обработки очереди RED вам будет нужно определить три параметра: min, max и burst. Параметр min задает минимальную длину очереди в байтах, при которой начинается уничтожение пакетов, max -- это мягкая граница, в пределах которой алгоритм будет пытаться удержаться. Наконец burst задает максимальное число пакетов, которые могут "прорваться".
Для определения параметра min можно выполнить такие вычисления: взять максимально допустимую задержку и умножить ее на полосу пропускания. Например, если для 64 килобитного (8 килобайт) канала мы хотим достичь задержки в 200мс (0.2 с), параметр min установим равным 1600 байтам ( 8192 * 0.2 = 1638,4 ~ 1600 ). Маленькое значение параметра min уменьшает пропускную способность, большое -- увеличивает задержку. Задание маленького значение min не тоже самое, что уменьшение значения MTU на медленных каналах для увеличения интерактивности.
Параметр max должен быть как минимум в два раза больше параметра min для избежания повторных соединений. На медленных каналах с маленьким значением min может иметь смысл устанавливать значение max в четыре, пять раз больше.
Параметр burst контролирует реакцию алгоритма RED на резкое увеличение трафика. Значение данного параметра должно быть больше отношения min/avpkt. Есть рекомендации использовать формулу (min+min+max)/(3*avpkt).
Дополнительно можно указать параметры limit и avpkt. Параметр limit задает объем очереди, превращая RED в алгоритм "обрубания хвостов". Другой параметр -- avpkt определяет усредненный размер пакета. Для ethernet-интерфейсов можно считать его равным 1000 байт при MTU равном 1500 байтам.
Данная дисциплина обработки очереди основывается на токенах. Она просто ограничивает скорость исходящего потока на интерфейсе и представляет собой отличное решение в случаях, когда нужно ограничить скорость передачи.
Пакеты передаются только в случае наличия достаточного количества токенов. При недостатке токенов, пакеты начинают помещаться в буфер, а после его заполнения -- уничтожаться.
Давайте рассмотрим доступные конфигурационные параметры дисциплины TBF:
Эти параметры позволяют настроить различными способами длину очереди пакетов, ожидающих токены. Параметр limit задает размер буфера в байтах, latency -- максимальное время нахождения пакета в буфере TBF.
Размер буфера токенов, в байтах. Задает максимальное количество байт данных, для которых доступны токены в один момент времени. В общем, чем выше скорость ограничения, тем больше должен быть буфер токенов. Например, при задании ограничения 10 мегабит на платформе x86, вам будет нужен хотя бы 10-ти килобайтный буфер токенов для поддержания такой скорости!
Если вы сконфигурируете слишком маленький буфер, начнут теряться пакеты, так как количество генерируемых токенов за тик будет больше, чем помещается в буфере токенов.
Пакет без полезных данных ("нулевой пакет") все равно занимает часть полосы пропускания. В технологии ethernet минимальный размер пакета равен 64 байтам. Параметр mpu (Minimum Packet Unit) определяет минимальное количество токенов необходимых для передачи пакета.
Этот параметр задает ограничение полосы пропускания
Пример использования TBF
[root@eagle]# tc qdisc add dev eth0 root tbf rate 180kbit latency 20ms buffer 1540 [root@eagle]# tc -s qdisc ls qdisc tbf 8004: dev eth0 rate 180Kbit burst 1539b lat 24.4ms |
Этот пример очень полезен. Если у вас есть сетевое устройство с большой очередью, например DSL или кабельный модем с высокоскоростным интерфейсом типа ethernet, вы обнаружите что передача через него большого объема данных совершенно нивелирует возможность работы с интерактивными сетевыми приложениями, например ssh.
Это происходит из-за заполнения всей очереди модема при передаче, а она, скорее всего, очень большая, так как это обеспечивает хорошую производительность канала. Но это же означает, что любому пакету требуется несколько секунд, чтобы пройти очередь. Это и мешает нормальной работе интерактивных приложений.
Введя вышеприведенную строку, мы ограничиваем скорость передачи данных и не позволяем заполняться очереди модема.
Классовые дисциплины обработки очереди обеспечивают Linux высокую гибкость в управлении трафиком. Они позволяют определять фильтры классифицирующие трафик и направляющие его в определенные подклассы.
Для обозначения классов, подключенных к корневой дисциплине и оконечных классов используются специальные термины. Корневым классом называют класс, подключенный к корневой дисциплине. Подклассы корневых классов называются внутренними классами. Конечные классы называются краевыми и не являются внутренними классами.
Дисциплина HTB использует идею токенов. Благодаря классовости, поддержки технологии заема полосы пропускания, HTB позволяет организовывать сложное и тонкое управление трафиком. Одно из наиболее простых применений данной дисциплины -- это простое ограничение скорости.
Весь процесс ограничения полосы пропускания выполняется в краевых классах. Внутренние классы предназначены для реализации механизма заема пропускной способности.
Важной составляющей дисциплины HTB является механизм заема полосы пропускания. Подклассы начинают занимать часть полосы пропускания у своих родительских классов, только когда трафик превышает значение, заданное параметром rate. Подклассы могут занимать полосу пропускания, только если часть общей полосы пропускания свободна. Максимальное значение полосы пропускания, до которого может занимать класс, определяется параметром ceil, по достижении которого пакеты начинают помещаться в буфер, ожидая токены. Дисциплина HTB позволяет создавать классы двух типов, поведение которых при различных значениях параметра rate приведено ниже в таблице
Состояния классов HTB и соответствующие поведение дисциплины
тип класса | состояние класса | внутреннее состояние HTB | поведение |
---|---|---|---|
краевой | < rate | HTB_CAN_SEND | Краевой класс будет передавать данные, пока есть доступные токены |
краевой | > rate, < ceil | HTB_MAY_BORROW | Краевой класс попытается занять токены для передачи данных у родительского класса. |
краевой | > ceil | HTB_CANT_SEND | Пакеты не будут передаваться до тех пор, пока не будет достаточного количества токенов. |
внутренние, корневой | < rate | HTB_CAN_SEND | Внутренний класс будет передавать токены своим подклассам. |
внутренние, корневой | > rate, < ceil | HTB_MAY_BORROW | Внутренний класс попытается занять токены у родительского класса и передать их своим подклассам. |
внутренние, корневой | > ceil | HTB_CANT_SEND | Внутренний класс не будет пытаться занять токены у родительского класса и не будет передавать токены своим подклассам. |
Давайте теперь рассмотрим параметры дисциплины HTB
default
Необязательный параметр планировщика HTB default задает дескриптор подкласса, куда направляется весь неклассифицированный трафик. По умолчанию равен нулю, следовательно, неклассифицированный трафик минует все классы и обрабатывается с максимальной скоростью.
Параметр, определяющий разрешенную скорость передачи. Можно рассматривать как гарантированную полосу пропускания для данного краевого класса.
Задает максимально доступную скорость передачи трафика. При наличии незанятой полосы пропускания в других классах, будет происходить ее заем до данного значения
Определяет размер буфера токенов. Определяет максимальное число байт, для которых могут быть доступны токены в один момент времени.
Данная дисциплина обработки очереди позволяет задавать приоритеты классов. Меньшее значение параметра prio задает больший приоритет. Классы с меньшим приоритетом не обрабатываются, пока есть данные в классах с большим приоритетом.
Пример использования дисциплины HTB
[root@eagle]# tc qdisc add dev eth0 root handle 1: htb default 12 [root@eagle]# tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps [root@eagle root]# tc -s qdisc qdisc pfifo 30: dev eth0 limit 5p Sent 3910 bytes 32 pkts (dropped 0, overlimits 0) qdisc pfifo 20: dev eth0 limit 5p Sent 56664 bytes 264 pkts (dropped 0, overlimits 0) qdisc htb 1: dev eth0 r2q 10 default 12 direct_packets_stat 0 Sent 60574 bytes 296 pkts (dropped 0, overlimits 0) |
Дисциплина обработки очереди CBQ является наиболее сложной дисциплиной из всех доступных в ОС Linux, наиболее известной, наименее понятной и, наверно, самой сложной в настройке.
Кроме того, что CBQ является классовой дисциплиной, она может ограничивать пропускную способность. Причем использует для этого интересный подход. Например, если вы хотите ограничить 10 мегабитное соединение до 1 мегабита, канал должен простаивать 90% времени. Если это не так, то нужно сделать чтобы он был свободен 90% времени.
Поскольку это весьма сложно рассчитать, алгоритм CBQ получает время простоя по количеству микросекунд прошедших между запросами на передачу данных. На основании этих данных примерно рассчитывается загруженность канала.
Однако такой подход не всегда дает правильные результаты. Например, возможно, что интерфейс не может передавать данные на максимальной скорости в 100 мегабит из-за плохо реализованных драйверов. А сетевая карта PCMCIA тоже никогда не достигнет скорости передачи 100 мегабит из-за архитектурных решений.
Но, все-таки, в большинстве случаев CBQ работает вполне удовлетворительно.
Итак, идея работы CBQ заключается в обеспечении незанятости канала на время, соответствующее заданному ограничению полосы пропускания. Для этого дисциплина рассчитывает временные интервалы, которые должны выдерживаться между передачей пакетов.
В процессе работы реальное время простоя канала вычисляется алгоритмом экспоненциально взвешенного скользящего среднего (exponential weighted moving average, EWMA), который полагает, что важность пакета обратно-экспоненциально зависит возраста пакета. Значение средней загрузки UNIX (loadave) рассчитывается таким же образом.
Необходимое для соответствия заданному ограничению время простоя вычитается вычисленного реального (EWMA) времени простоя, полученное значение называется avgidle. При нулевом значении данного параметра достигается наибольшая эффективность работы -- пакеты поступают с интервалом, точно соответствующем расчетному. На практике такого практически никогда не бывает.
Перегруженный канал имеет отрицательное значение avgidle и когда она превышает определенный порог, CBQ останавливает передачу по каналу на некоторое время и выставляет значение overlimit.
Наоборот, после нескольких часов простоя, канал может накопить большое значение avgidle, которое приведет отсутствию ограничения полосы пропускания. Для избежания такой ситуации значение avgidle ограничено значением maxidle.
При перегрузке, теоретически CBQ должен приостанавливать передачу на вычисленный временной интервал, потом передать следующую порцию данных и снова остановиться. Но с реализацией такого алгоритма работы есть нюансы, смотрите ниже описание параметра minburst.
Давайте рассмотрим параметры дисциплины CBQ, позволяющие настроить ограничение полосы пропускания:
Усредненный размер пакета в байтах. Вместе с параметром maxburst используется для вычисления значения maxidle.
Физическая пропускная способность нашего устройства; необходима для вычисления времени простоя.
Время, необходимое на передачу пакета через интерфейс может ступенчато увеличиваться. Например, передача 800- и 806 байтовых пакетов займет одно и тоже время, а 810-байтовый пакет -- немного больше. Данный параметр задает размер шага, с которым увеличивается расчетное время передачи. Чаще всего устанавливается значение '8'. Значение должно быть целой степенью 2.
Параметр используется для вычисления значения maxidle. Он задает количество усредненных пакетов, которые будут обработаны до того, как значение avgidle станет нулем. Параметр maxidle задать прямо нельзя, только косвенно, с помощью этого параметра.
Как уже говорилось, при перегрузке CBQ перестает передавать данные. Идеальным решением является ожидание в течении вычисленного промежутка времени, передача пакета и снова ожидание. Однако, у ядер ОС Unix существует сложности с планированием событий, короче 10 мс, потому лучше увеличить промежуток ожидания, после которого передавать не один пакет, а количество, определенное параметром minburst. Соответственно, промежуток ожидания между передачей пакетов увеличивается пропорционально значению данного параметра.
Промежуток времени между передачей пакетов называется offtime. Большие значения параметра minburst позволяют более точно ограничить полосу пропускания в длительных временных интервалах, но в секундных масштабах трафик будет вести себя скачкообразно.
Отрицательное значение avgidle указывает, что канал перегружен, и нужно прекратить передачу на время, пока avgidle не увеличится достаточно для передачи следующего пакета. Однако, это может вызвать длительные периоды ожидания после интенсивных всплесков трафика. В таких случаях avgidle присваивается значение параметра minidle.
Minidle задается в отрицательных микросекундах, следовательно значение "10" ограничивает параметр avgidle -10 микросекундами.
Минимальный размер пакета -- необходим, поскольку даже нулевой пакет дополняется до 64 байт для передачи по сети ethernet, а это уже занимает определенное время. Алгоритм CBQ должен знать минимальный размер пакета для правильного вычисления времени простоя.
Параметр ограничивает скорость передачи трафика для данной дисциплины.
Кроме ограничения полосы пропускания, CBQ также может распределять трафик по классам с различными приоритетами и соответствующим образом их обрабатывать.
Каждый раз, когда нужно передать пакет, инициируется циклический процесс ('WRR') выбора, начиная с высокоприоритетного класса.
Следующие параметры управляют взвешенным циклическим процессом выбора:
Когда производится выбор пакета для передачи, CBQ начинает опрашивать свои подклассы в соответствии с их приоритетом. Когда классу предоставляется возможность передачи, выбирается определенный объем данных. Базовая единица этого объема определяется параметром allot.
Дисциплина CBQ может присваивать классам приоритеты. Меньшее значение означает больший приоритет. Пока не будет обработан трафик с высшим приоритетом, трафик с меньшим приоритетом не обрабатывается.
Каждый класс получает возможность передать данные. Если у вас есть классы, значительно отличающиеся полосой пропускания, имеет смысл разрешить классам с большой пропускной способностью посылать за раз больше данных, чем классы с небольшой пропускной способностью
Дисциплина CBQ присваивает классам вес и нормирует их, потому можно использовать любые значения: важно отношение этих значений. Обычно используется простое правило: вес = полоса пропускания / 10. Для определения количества данных, посылаемых классом за раз, нормированное значение умножается на allot.
Кроме простого ограничения полосы пропускания для определенных типов трафика, можно давать возможность классам занимать часть полосы пропускания у других классов.
isolated/sharing
У класса, созданного с параметром isolated, нельзя занимать полосу пропускания. То есть другие классы, настроенные на заем доступной полосы пропускания никогда не смогут занять полосу этого класса, даже если она будет свободной.
Наоборот, класс созданный с параметром sharing будет предоставлять свою свободную часть пропускающей способности другим классам.
Эти два параметра определяют, может ли класс занимать пропускающую способность других классов. Параметр bounded запрещает, а borrow разрешает занимать свободную полосу пропускания классов, сконфигурированных с параметром sharing.
Примером, когда используются bounded и isolated класс, может послужить ситуация использования одного канала двумя организациями. В этом случае они будут действительно ограничены сконфигурированной скоростью.
Внутри каждого такого класса могут находиться подклассы с опциями sharing и borrow. Заем будет выполняться в пределах родительского класса для каждой организации.
Рассмотрим реализацию следующего сценария. Нам нужно ограничить полосу пропускания веб-трафика до 5 мегабит, а SMTP -- до 3 мегабит. Суммарная полоса пропускания не должна превышать 6 мегабит. На сервере стоит 100-мегабитная сетевая карта, классы могут занимать пропускную способность друг у друга.
Создаем корневую дисциплину и класс, куда будет направляться HTTP и SMTP трафик. Параметр bounded запрещает этому классу занимать полосу пропускания.
[root@eagle]# tc qdisc add dev eth0 root handle 1:0 cbq bandwidth 100Mbit \ avpkt 1000 cell 8 [root@eagle]# tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 100Mbit \ rate 6Mbit weight 0.6Mbit prio 8 allot 1514 cell 8 maxburst 20 \ avpkt 1000 bounded |
Как видно, CBQ требует много больше настроек по сравнению с HTB.
Теперь задаем два класса непосредственно для веб и почтового трафика. Мы указали параметры borrow и sharing, что разрешает занимать классам полосу пропускания друг друга в пределах родительского класса 1:1.
[root@eagle]# tc class add dev eth0 parent 1:1 classid 1:3 cbq bandwidth 100Mbit \ rate 5Mbit weight 0.5Mbit prio 5 allot 1514 cell 8 maxburst 20 \ avpkt 1000 borrow sharing [root@eagle]# tc class add dev eth0 parent 1:1 classid 1:4 cbq bandwidth 100Mbit \ rate 3Mbit weight 0.3Mbit prio 5 allot 1514 cell 8 maxburst 20 \ avpkt 1000 borrow sharing |
Для более равномерного распределения пропускной способности между соединениями, присоединим к каждому классу дисциплину обработки очереди SFQ
[root@eagle]# tc qdisc add dev eth0 parent 1:3 handle 30: sfq perturb 10 [root@eagle]# tc qdisc add dev eth0 parent 1:4 handle 40: sfq perturb 10 |
Наконец, классифицируем трафик с помощью фильтров и направляем его в нужные классы:
[root@eagle]# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \ sport 80 0xffff flowid 1:3 [root@eagle]# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \ sport 25 0xffff flowid 1:4 |
Обратите внимание, что фильтры мы подключаем к корневой дисциплине обработки очереди. Трафик не соответствующий эти фильтрам будет обрабатываться с максимально возможной скоростью.
Если суммарно по классам будет необходима скорость больше, чем сконфигурированная, то 6 мегабит разделятся между классами в отношении 5/3, соответственно весу классов.
Следовательно, в такой конфигурации гарантированная полоса пропускания для веб-трафика будет равна 5/8 * 6 мбит= 3.75 мегабит.
Устройство IMQ не является дисциплиной обработки очереди, но тесно с ними связано. В ОС Linux дисциплины обработки очередей подключаются к сетевым устройствам и все, что помещается в очередь к устройству, попадает сначала в очередь дисциплины обработки очереди. Из-за этого подхода существуют два ограничения:
1. Ограничение пропускной возможности работает только для исходящего трафика (дисциплина обработки входящего трафика существует, но ее возможности мизерны по сравнению с классическими дисциплинами).
2. Дисциплина обработки очереди обрабатывает трафик только одного интерфейса, нет возможности задавать глобальные ограничения.
Устройство IMQ пытается решить эти проблемы. С помощью подсистемы фильтрации ОС Linux можно определенные пакеты направлять через этот псевдо-интерфейс, к которому подключаются различные дисциплины обработки очередей. Таким образом, можно управлять полосой пропускания, как входящего, так и общего трафика.
Пример, обеспечивающий гарантированную полосу пропускания для адреса 10.0.0.230:
[root@eagle]# tc qdisc add dev imq0 root handle 1: htb default 20 [root@eagle]# tc class add dev imq0 parent 1: classid 1:1 htb rate 2mbit burst 15k [root@eagle]# tc class add dev imq0 parent 1:1 classid 1:10 htb rate 1mbit [root@eagle]# tc class add dev imq0 parent 1:1 classid 1:20 htb rate 1mbit [root@eagle]# tc qdisc add dev imq0 parent 1:10 handle 10: pfifo [root@eagle]# tc qdisc add dev imq0 parent 1:20 handle 20: sfq [root@eagle]# tc filter add dev imq0 parent 10:0 protocol ip prio 1 u32 match \ ip dst 10.0.0.230/32 flowid 1:10 |
В этом примере использован классификатор u32, остальные классификаторы тоже могут применяться в работе с imq.
[root@eagle]# iptables -t mangle -A PREROUTING -i eth0 -j IMQ --todev 0 [root@eagle]# ip link set imq0 up |
Цель IMQ утилиты iptables доступна в цепочках PREROUTING и POSTROUTING таблицы mangle table. Синтаксис:
IMQ [ --todev n ], n -- номер устройства imq |
Есть поддержка и утилиты ip6tables.
Обратите, что трафик попадает на интерфейс не в тот момент, когда переходит к цели IMQ, а после обработки соответствующей цепочки. Точное место входа на устройство imq зависит от типа трафика (входящий/исходящий).
Для входящего трафика imq регистрируется с приоритетом NF_IP_PRI_MANGLE + 1. Это означает, что пакеты попадают на устройство сразу после прохождения цепочки PREROUTING.
Для исходящего трафика, imq использует приоритет NF_IP_PRI_LAST, который гарантирует, что пакеты, уничтоженные пакетным фильтром не будут занимать полосу пропускания устройства.
Еще один пример, общего ограничения исходящего веб трафика до 100кбит на суммарно, по всем интерфейсам:
[root@eagle]# modprobe imq numdevs=1 [root@eagle]# tc qdisc add dev imq0 handle 1: root htb default 1 [root@eagle]# tc cladd add dev imq0 parent 1: classid 1:1 htb rate 100kbit [root@eagle]# tc qdisc add dev imq0 parent 1:1 handle 10: htb default 10 [root@eagle]# tc class add dev imq0 parent 10: classid 10:10 htb rate 100kbit [root@eagle]# tc qdisc add dev imq0 parent 10:10 handle 20: sfq [root@eagle]# iptables -t mangle -A POSTROUTING -p tcp --dport 80 -j IMQ [root@eagle]# ip link set imq0 up |
Есть несколько общих правил системы управления трафиком в ОС Linux.
Маршрутизатор, выполняющий ограничение полосы пропускания должен быть узким местом канала и должен ограничивать поток до величины немного меньшей реальной пропускной способности канала. Это предотвращает образование очередей в других маршрутизаторах и обеспечивает максимальный контроль над потоком данных нашему маршрутизатору.
Ограничить скорость можно только у исходящего трафика. Входящий трафик уже пришел к нам, потому мы не можем непосредственно ограничить скорость его поступления. Традиционным решением является использование ограничителя, однако это не есть лучшее решение, ведь уничтожаются уже пришедшие данные, которые заняли часть нашей полосы пропускания. Лучшим решением является использование IMQ.
Каждый интерфейс должен иметь дисциплину обработки очереди. По умолчанию используется дисциплина pfifo_fast.
Использование классовых дисциплин обработки очередей без подклассов не имеет смысла и использует зря ресурсы системы.
Созданный класс по умолчанию содержит дисциплину fifo. Ее можно заменить любой другой дисциплиной обработки очереди.
Вот небольшие рекомендации, какой тип очереди использовать в различных ситуациях.
Для ограничения исходящего трафика, без деления по классам, используйте дисциплину TBF. Она справляется с большими скоростями, при соответствующем изменении размера буфера токенов.
Если канал или класс полностью загружены, и вы хотите не допустить доминирование одних соединений над другими -- используйте SFQ или ESFQ.
При работе с магистральными каналами используйте дисциплину RED.
Для ограничения входящего трафика используйте ограничитель (Ingress Policer).
Если вы форвардите входящий трафик, то лучше используйте ограничение исходящего трафика на исходящих интерфейсах, или устройство IMQ.
В данном документе были использованы материалы из следующих источников:
Copyright © 2003, Ivan Pesin