From: Иван Песин <ipesin at post.Lviv.UA>
Newsgroups: Russian Linux Gazette
Date: Mon, 14 Jan 2004 14:31:37 +0000 (UTC)
Subject: Повесть о 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
традиционный элемент компонент 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
фильтров.
В состав операционной системы 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] }
Для каждого типа объекта существует свой набор опций, который частично
мы рассмотрим в этом документе.
1- Добавляем дисциплину обработки очереди. Для удаления
используется del вместо add.
2- Указываем устройство, к которому мы подключаем дисциплину
обработки очереди.
3- Указываем, что это корневая дисциплина (т.е. исходящая). К
этому же устройству можно подключить и входящую дисциплину
(ingress qdisc).
4- Задаем дескриптор в форме старший номер:младший номер. Младший
номер для любой дисциплины обработки очереди должен равняться
нулю. Допускается сокращение дескриптора дисциплины в виде
"1:". Тогда подразумевается, что младший номер равен нулю.
5- Указываем тип дисциплины обработки очереди, который мы хотим
подключить. После этого указываются параметры, специфические
для каждой дисциплины. В этом примере, мы не задаем никаких
параметров дисциплины.
Итак, мы рассмотрели простейший пример использования утилиты tc для
назначения дисциплины обработки очереди устройству. Теперь давайте
посмотрим, как добавлять классы к существующим классам.
1- Создаем класс. Удалить его можно с помощью команды del.
2- Указываем устройство, к которому мы подключаем новый класс.
3- Указываем дескриптор родителя, к которому мы подключаем данный
класс.
4- Задаем уникальный дескриптор для данного класса. Младший номер
должен быть отличным от нуля.
5- Классовые дисциплины обработки очередей требуют, чтобы все
подклассы были одного типа с родителями. Потому у дисциплины
обработки очереди HTB классы будут тоже HTB.
6, 7- Специфические параметры класса.
Команда tc filter
[root@eagle]# tc filter add (1)
> dev eth0 (2)
> parent 1:0 (3)
> protocol ip (4)
> prio 5 (5)
> u32 (6)
> match ip port 22 0xffff (7)
> match ip tos 0x10 0xff (8)
> flowid 1:6 (9)
> police (10)
> rate 32000bps (11)
> burst 10240 (12)
> mpu 0 (13)
> action drop/continue (14)
1- Добавляем фильтр. Удалить его можно командой del.
2- Указываем устройство, к которому мы подключаем фильтр.
3- Указываем дескриптор родителя, к которому мы подключаем фильтр.
4- Задаем протокол, с которым будет работать фильтр.
5- Параметр prio позволяет присвоить классифицированному этим
фильтром трафику приоритет. Параметр pref является синонимом.
6- Задаем классификатор, в данном случае это u32.
7- Параметры классификатора. Здесь выбираются пакеты
соответствующие 22-му порту и со значением 0x10 в поле TOS
(означающим интерактивный трафик).
8- Ключ flowid задает дескриптор класса, куда направляются пакеты
соответствующие заданному фильтру.
9- Подключаем к фильтру ограничитель.
11- Задаем граничную скорость для принятия решения: до этой
скорости ограничитель будет выполнять одно действие, сверх --
другое.
12- Размер буфера пакетов.
13- Минимальный размер обрабатываемого объекта. Для учета всего
трафика mpu должен равняться нулю.
14- Ключ action задает действия выполняемые ограничителем. Первый
аргумент ключа выполняется при превышении граничной скорости,
второй -- до превышения.
Как показано выше, команда tc имеет сложный синтаксис, даже для
описания простых операций. Однако, существуют проекты, позволяющие
упростить написание простых правил. Например: CBQ.init, HTB.init, tcng
Бесклассовые дисциплины обработки очередей
Все дисциплины этого типа могут использоваться и как основная
дисциплина для всего интерфейса, так и внутри краевого класса
классовой дисциплины обработки очереди. Это основные планировщики,
используемые в Linux. По умолчанию используется дисциплина pfifo_fast.
pfifo и bfifo
Алгоритм 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 10240pfifo:
[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 pfifo_fast,
дисциплина обработки очереди используемая по умолчанию
Данная дисциплина обработки очереди является основной для всех
интерфейсов Linux. Она основана на обычном алгоритме FIFO, но обладает
некоторыми функциями приоритезации. pfifo_fast содержит три отряда
(bands) для разделения трафика. Каждый отряд представляет собой
обычную очередь FIFO. Высокоприоритетный трафик помещается в нулевой
отряд и всегда обрабатывается в первую очередь. Аналогично, трафик,
попадающий в первый отряд, обрабатывается перед вторым отрядом.
Не путайте эту простую бесклассовую дисциплину с классовой PRIO. Не
смотря на то, что они используют аналогичные подходы, pfifo_fast
является бесклассовой и не может содержать подклассы и другие
дисциплины обработки очередей.
Вы не можете изменять настройки этой дисциплины, поскольку они
"зашиты" в коде.
SFQ, Stochastic Fair Queuing
Дисциплина обработки очереди 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 могут быть активны одновременно (больше пакетов не
помещается в очереди!). Один раз в десять секунд изменяется хэш.
ESFQ, Extended Stochastic Fair Queuing
Концептуально, эта дисциплина обработки очереди ничем не отличается от
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.
Random Early Detection (RED)
Дисциплина 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, Token Bucket Filter
Данная дисциплина обработки очереди основывается на токенах. Она
просто ограничивает скорость исходящего потока на интерфейсе и
представляет собой отличное решение в случаях, когда нужно ограничить
скорость передачи.
Пакеты передаются только в случае наличия достаточного количества
токенов. При недостатке токенов, пакеты начинают помещаться в буфер, а
после его заполнения -- уничтожаться.
limit или latency
Эти параметры позволяют настроить различными способами длину
очереди пакетов, ожидающих токены. Параметр limit задает размер
буфера в байтах, latency -- максимальное время нахождения
пакета в буфере TBF.
burst/buffer/maxburst
Размер буфера токенов, в байтах. Задает максимальное количество
байт данных, для которых доступны токены в один момент времени.
В общем, чем выше скорость ограничения, тем больше должен быть
буфер токенов. Например, при задании ограничения 10 мегабит на
платформе x86, вам будет нужен хотя бы 10-ти килобайтный буфер
токенов для поддержания такой скорости!
Если вы сконфигурируете слишком маленький буфер, начнут
теряться пакеты, так как количество генерируемых токенов за тик
будет больше, чем помещается в буфере токенов.
mpu
Пакет без полезных данных ("нулевой пакет") все равно занимает
часть полосы пропускания. В технологии ethernet минимальный
размер пакета равен 64 байтам. Параметр mpu (Minimum Packet
Unit) определяет минимальное количество токенов необходимых для
передачи пакета.
rate
Этот параметр задает ограничение полосы пропускания
Пример использования 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
Sent 80949 bytes 107 pkts (dropped 42, overlimits 112)
Этот пример очень полезен. Если у вас есть сетевое устройство с
большой очередью, например DSL или кабельный модем с высокоскоростным
интерфейсом типа ethernet, вы обнаружите что передача через него
большого объема данных совершенно нивелирует возможность работы с
интерактивными сетевыми приложениями, например ssh.
Это происходит из-за заполнения всей очереди модема при передаче, а
она, скорее всего, очень большая, так как это обеспечивает хорошую
производительность канала. Но это же означает, что любому пакету
требуется несколько секунд, чтобы пройти очередь. Это и мешает
нормальной работе интерактивных приложений.
Введя вышеприведенную строку, мы ограничиваем скорость передачи данных
и не позволяем заполняться очереди модема.
Классовые дисциплины обработки очереди
Классовые дисциплины обработки очереди обеспечивают Linux высокую
гибкость в управлении трафиком. Они позволяют определять фильтры
классифицирующие трафик и направляющие его в определенные подклассы.
Для обозначения классов, подключенных к корневой дисциплине и
оконечных классов используются специальные термины. Корневым классом
называют класс, подключенный к корневой дисциплине. Подклассы корневых
классов называются внутренними классами. Конечные классы называются
краевыми и не являются внутренними классами.
HTB, Hierarchical Token Bucket
Дисциплина 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 задает
дескриптор подкласса, куда направляется весь
неклассифицированный трафик. По умолчанию равен нулю,
следовательно, неклассифицированный трафик минует все классы и
обрабатывается с максимальной скоростью.
rate
Параметр, определяющий разрешенную скорость передачи. Можно
рассматривать как гарантированную полосу пропускания для
данного краевого класса.
ceil
Задает максимально доступную скорость передачи трафика. При
наличии незанятой полосы пропускания в других классах, будет
происходить ее заем до данного значения
burst
Определяет размер буфера токенов. Определяет максимальное число
байт, для которых могут быть доступны токены в один момент
времени.
prio
Данная дисциплина обработки очереди позволяет задавать
приоритеты классов. Меньшее значение параметра prio задает
больший приоритет. Классы с меньшим приоритетом не
обрабатываются, пока есть данные в классах с большим
приоритетом.
Поскольку HTB ограничивает скорость только в краевых классах, сумма
значений параметров rate краевых классов не должна превышать
значения ceil родительского класса. Лучше всего, когда эта
сумма равна параметру rate родительского класса.
Пример использования дисциплины 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]# tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil 100kbps
[root@eagle]# tc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil 100kbps
[root@eagle]# tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps
[root@eagle]# tc qdisc add dev eth0 parent 1:10 handle 20: pfifo limit 5
[root@eagle]# tc qdisc add dev eth0 parent 1:12 handle 40: sfq perturb 10
[root@eagle]# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32
match ip src 1.2.3.4 match ip dport 80 0xffff flowid 1:10
[root@eagle]# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32
match ip src 1.2.3.4 flowid 1:11
[root@eagle root]# tc -s qdisc
qdisc sfq 40: dev eth0 quantum 1514b perturb 10sec
Sent 0 bytes 0 pkts (dropped 0, overlimits 0)
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, Class-Based Queue
Дисциплина обработки очереди 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, позволяющие настроить
ограничение полосы пропускания:
avpkt
Усредненный размер пакета в байтах. Вместе с параметром
maxburst используется для вычисления значения maxidle.
bandwidth
Физическая пропускная способность нашего устройства; необходима
для вычисления времени простоя.
cell
Время, необходимое на передачу пакета через интерфейс может
ступенчато увеличиваться. Например, передача 800- и 806
байтовых пакетов займет одно и тоже время, а 810-байтовый пакет
-- немного больше. Данный параметр задает размер шага, с
которым увеличивается расчетное время передачи. Чаще всего
устанавливается значение '8'. Значение должно быть целой
степенью 2.
maxburst
Параметр используется для вычисления значения maxidle. Он
задает количество усредненных пакетов, которые будут обработаны
до того, как значение avgidle станет нулем. Параметр maxidle
задать прямо нельзя, только косвенно, с помощью этого
параметра.
minburst
Как уже говорилось, при перегрузке CBQ перестает передавать
данные. Идеальным решением является ожидание в течении
вычисленного промежутка времени, передача пакета и снова
ожидание. Однако, у ядер ОС Unix существует сложности с
планированием событий, короче 10 мс, потому лучше увеличить
промежуток ожидания, после которого передавать не один пакет, а
количество, определенное параметром minburst. Соответственно,
промежуток ожидания между передачей пакетов увеличивается
пропорционально значению данного параметра.
Промежуток времени между передачей пакетов называется offtime.
Большие значения параметра minburst позволяют более точно
ограничить полосу пропускания в длительных временных
интервалах, но в секундных масштабах трафик будет вести себя
скачкообразно.
minidle
Отрицательное значение avgidle указывает, что канал перегружен,
и нужно прекратить передачу на время, пока avgidle не
увеличится достаточно для передачи следующего пакета. Однако,
это может вызвать длительные периоды ожидания после интенсивных
всплесков трафика. В таких случаях avgidle присваивается
значение параметра minidle.
Minidle задается в отрицательных микросекундах, следовательно
значение "10" ограничивает параметр avgidle -10 микросекундами.
mpu
Минимальный размер пакета -- необходим, поскольку даже нулевой
пакет дополняется до 64 байт для передачи по сети ethernet, а
это уже занимает определенное время. Алгоритм CBQ должен знать
минимальный размер пакета для правильного вычисления времени
простоя.
rate
Параметр ограничивает скорость передачи трафика для данной
дисциплины.
Кроме ограничения полосы пропускания, CBQ также может распределять
трафик по классам с различными приоритетами и соответствующим образом
их обрабатывать.
Каждый раз, когда нужно передать пакет, инициируется циклический
процесс ('WRR') выбора, начиная с высокоприоритетного класса.
Следующие параметры управляют взвешенным циклическим процессом выбора:
allot
Когда производится выбор пакета для передачи, CBQ начинает
опрашивать свои подклассы в соответствии с их приоритетом.
Когда классу предоставляется возможность передачи, выбирается
определенный объем данных. Базовая единица этого объема
определяется параметром allot.
prio
Дисциплина CBQ может присваивать классам приоритеты. Меньшее
значение означает больший приоритет. Пока не будет обработан
трафик с высшим приоритетом, трафик с меньшим приоритетом не
обрабатывается.
weight
Каждый класс получает возможность передать данные. Если у вас
есть классы, значительно отличающиеся полосой пропускания,
имеет смысл разрешить классам с большой пропускной способностью
посылать за раз больше данных, чем классы с небольшой
пропускной способностью
Дисциплина CBQ присваивает классам вес и нормирует их, потому
можно использовать любые значения: важно отношение этих
значений. Обычно используется простое правило: вес = полоса
пропускания / 10. Для определения количества данных, посылаемых
классом за раз, нормированное значение умножается на allot.
Кроме простого ограничения полосы пропускания для определенных типов
трафика, можно давать возможность классам занимать часть полосы
пропускания у других классов.
isolated/sharing
У класса, созданного с параметром isolated, нельзя занимать
полосу пропускания. То есть другие классы, настроенные на заем
доступной полосы пропускания никогда не смогут занять полосу
этого класса, даже если она будет свободной.
Наоборот, класс созданный с параметром sharing будет
предоставлять свою свободную часть пропускающей способности
другим классам.
bounded/borrow
Эти два параметра определяют, может ли класс занимать
пропускающую способность других классов. Параметр bounded
запрещает, а borrow разрешает занимать свободную полосу
пропускания классов, сконфигурированных с параметром sharing.
Примером, когда используются bounded и isolated класс, может послужить
ситуация использования одного канала двумя организациями. В этом
случае они будут действительно ограничены сконфигурированной
скоростью.
Внутри каждого такого класса могут находиться подклассы с опциями
sharing и borrow. Заем будет выполняться в пределах родительского
класса для каждой организации.
Пример конфигурации дисциплины CBQ
Рассмотрим реализацию следующего сценария. Нам нужно ограничить полосу
пропускания веб-трафика до 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, Intermediate queueing device
Устройство IMQ не является дисциплиной обработки очереди, но тесно с
ними связано. В ОС Linux дисциплины обработки очередей подключаются к
сетевым устройствам и все, что помещается в очередь к устройству,
попадает сначала в очередь дисциплины обработки очереди. Из-за этого
подхода существуют два ограничения:
1. Ограничение пропускной возможности работает только для исходящего
трафика (дисциплина обработки входящего трафика существует, но ее
возможности мизерны по сравнению с классическими дисциплинами).
2. Дисциплина обработки очереди обрабатывает трафик только одного
интерфейса, нет возможности задавать глобальные ограничения.
Устройство IMQ пытается решить эти проблемы. С помощью подсистемы
фильтрации ОС Linux можно определенные пакеты направлять через этот
псевдо-интерфейс, к которому подключаются различные дисциплины
обработки очередей. Таким образом, можно управлять полосой
пропускания, как входящего, так и общего трафика.
Примеры использования IMQ
Пример, обеспечивающий гарантированную полосу пропускания для адреса
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 5k
[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 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. Она справляется с большими скоростями,
при соответствующем изменении размера буфера токенов.
* Для ограничения исходящего трафика, с делением по классам,
используйте дисциплину HTB. Это простая эффективная классовая
дисциплина подойдет в подавляющем большинстве случаев.
* Если канал или класс полностью загружены, и вы хотите не допустить
доминирование одних соединений над другими -- используйте SFQ или
ESFQ.
* При работе с магистральными каналами используйте дисциплину RED.
* Для ограничения входящего трафика используйте ограничитель
(Ingress Policer).
* Если вы форвардите входящий трафик, то лучше используйте
ограничение исходящего трафика на исходящих интерфейсах, или
устройство IMQ.
Ссылки
В данном документе были использованы материалы из следующих
источников:
* RFC3549 (http://www.faqs.org/rfcs/rfc3549.html)
* (http://www.tldp.org/HOWTO/Adv-Routing-HOWTO/index.html) Linux 2.4 Advanced Routing & Traffic Control HOWTO
* Traffic-Control-HOWTO (http://www.tldp.org/HOWTO/Traffic-Control-HOWTO/index.html)
* Traffic-Control-tcng-HTB-HOWTO (http://www.tldp.org/HOWTO/Traffic-Control-tcng-HTB-HOWTO/index.html)
* http://diffserv.sourceforge.net/
* http://www.docum.org/
* http://www.trash.net/~kaber/imq/
* http://luxik.cdi.cz/~devik/qos/htb/