From: Andy Gorev <gorev@mail333.com>
Date: Mon, 13 Mar 2003 13:01:37 +0000 (UTC)
Subject: Advanced Routing и QoS в linux (пакеты iproute2, cbq.init и htb.init)
Алексей Кузнецов и NET core team уже давно и небезуспешно
разрабатывают сетевые функции ядра Линукс. Например вот что мы можем
делать, используя только ядро:
- Управлять полосой пропускания для разных компьютеров;
- Успешно разделять полосу пропускания между разными типами трафика;
- Приоритизировать трафик в полосе, согласно нашим нуждам (Quality
of Service);
- Защищаться от DoS атак;
- Фильтровать трафик (firewall);
- Использовать один реальный адрес, для работы всей локалки в Интернет (SNAT);
- Объединять несколько каналов в один, и балансировать нагрузкой;
- Ограничивать доступ к сервисам;
- Перебрасывать порты с одной машины на другую (DNAT);
- Ограничивать полосу пропускания как входящего, так и исходящего
трафика;
- Маршрутизировать по портам, адресам, MAC-адресу, TOS, времени дня,
и даже по имени пользователя;
- Туннелирование, multicast routing, IPSec, и многое другое.
Все эти вещи обычно не нужны для рабочей станции, однако если вы
управляете маршрутизатором (router), то это поможет в работе. Во
многом возможности ядра Линукс на уровне, или даже опережают
возможности Cisco IOS, но к сожалению отстают в этой области по
документированности, в силу большой занятости его разработчика. Однако
я подскажу, где искать дальнейшую информацию.
Половина из вышеперечисленного реализуется с использованием iptables,
но в данном обзоре все-же рассматриваются особенности ip/tc. Однако я
буду иногда ссылаться на iptables. Итак, вы должны хотя-бы в общих
чертах представлять себе как работает стек TCP/IP, фильтрация на базе
ipchains/iptables, и прочитали например Network Administration Guide.
2. Iproute2.
Для вас может оказаться сюрпризом, что вышеперечисленные свойства ядра
уже по умолчанию включены в него, и работают! Просто работают они все
по умолчанию, то есть, по сути ничего особенного не делают. Более
того, старинные команды типа route, arp, ifconfig просто ничего не
покажут вам из тех возможностей, которые уже давно, начиная с ядер
2.2, имеются на вооружении.
Для того, чтобы задействовать, настроить, или хотя-бы увидеть
дополнительные возможности ядра, необходимо установить пакет
iproute2. Автор утилит, вошедших в пакет, опять-же Алексей Кузнецов.
Центральная утилита пакета - "ip" заменяет собой все
вышеперечисленные "старинные" команды, помогает реализовать
возможности многотабличной маршрутизации (advanced routing),
туннелирования и multicast routing. Кроме того, Линукс имеет гибкую
систему управления трафиком, называемую Traffic Control. Эта система
поддерживает множество методов для классификации, приоритизации,
разделения, и ограничения (shaping) обоих типов трафика - входящего и
исходящего. Управлять всем этим позволяет вторая утилита пакета -
"tc". Подчеркну, что обе утилиты являются как-бы интерпретаторами
команд, а все функции выполняет ядро.
3. IP.
Даже если вы только установили пакет iproute2 и ничего не настраивали,
вы уже можете просматривать некоторую новую информацию о своей
системе. Например:
ip link list - покажет конфигурацию интерфейсов;
ip address show - покажет IP адреса на интерфейсах;
ip route show - покажет таблицу маршрутов main (сравните с route -n);
ip neigh show - покажет таблицу ARP;
ip rule list - покажет список правил, согласно которым принимается
решение о маршрутизации.
По умолчанию у ядра имеется три таблицы маршрутов - local, main и
default. Старая утилита route показывает содержимое только таблицы
main. Таблицы local и default - новые. Мы сами можем создавать свои
таблицы маршрутов, и с помощью правил (ip rules) указывать, какой
трафик маршрутизировать согласно какой таблице. Классический механизм
маршрутизации основан на destination addres пакета. Мы же можем, кроме
этого, управлять маршрутизацией, используя поле TOS пакета, или
согласно fwmark, который установил на пакет iptables. В этом и
заключается концепция Advanced Routing. Детальную семантику команды
ip, и настройку NAT на ее основе, можно найти в ip-cref Алексея
Кузнецова. Этот документ есть в составе пакета iproute2.
4. Advanced Routing.
Теперь я приведу несколько примеров, иллюстрирующих возможности
table-routing.
Для начала рассмотрим вариант простейшей маршрутизации по адресу src.
Предположим, у нас есть высокоскоростной и дорогой линк на провайдера
(xDSL) и медленный, но дешёвый линк по коммутируемуму доступу
(dial-up). Маршрут по умолчанию в основной таблице установлен на xDSL,
но мы хотим одну из машин внутренней сети направить в нашу медленную
связь, и освободить таким образом основной канал. Теперь мы создадим
для этой машины отдельную таблицу маршрутов, которую назовем Manager:
# echo 100 Manager >> /etc/iproute2/rt_tables
Далее создаем правило-селектор по адресу нашей выделенной машины,
чтобы маршрутизация для нее переходила в новую таблицу:
# ip rule add from 191.216.121.1 table Manager
Осталось добавить маршрут по умолчанию в таблицу Manager (там пока
пусто), и сбросить кэш маршрутов:
# ip route add default via 191.216.121.14 dev ppp2 table Manager
# ip route flush cache
Все готово. Здесь ppp2 - наш дешёвый линк. Конечно это можно было
сделать и не создавая отдельную таблицу маршрутов, это просто пример
работы с таблицами.
Теперь предположим, что нам надо направить в dial-up весь SMTP трафик,
идущий с внутреннего адреса. Используя iptables помечаем пакеты SMTP:
NAT-ить можно и на уровне iptables (чаще всего так и делают), но
заворачивать отмеченные маркой пакеты в другую таблицу, и
следовательно интерфейс, все-равно придется приблизительно
вышеприведенным правилом. Отмечу не очевидный факт, что ipfw-марка
существует только в пределах интерфейса. Поэтому она не передается за
ваш router, и даже не пытайтесь увидеть ее tcpdump-ом icon_eek.gif .
Маркируются-ли пакеты, мы можем узнать, вызывая iptables с ключем -v.
Возвращаясь к теме напомню, что и в этом случае таблицу Manager можно
было не создавать. По такому-же принципу строится маршрутизация на
основе TOS.
Сейчас рассмотрим ситуацию с разделением каналов. Предположим у нас
есть два линка на разных провайдеров. Создаем для каждого свою таблицу
маршрутов, и правила привязывающие соответственные сети провайдеров к
нужным интерфейсам. Это гарантирует нам возврат пакета, отправленного
через одного из провайдеров от него-же. Однако если маршрут по
умолчанию будет указывать только на один линк, мы получим
разбалансированную нагрузку по каналам. Для решения этой проблемы
маршрут по умолчанию укажем на два устройства сразу:
# ip route add default scope global nexthop via $ip_prov1 dev ppp0
weight 1 nexthop via $ip_prov2 dev ppp1 weight 1
Это сбалансирует маршруты через обоих провайдеров. Параметр weight
может быть настроен для перевеса нагрузки в ту или иную сторону.
Однако надо отметить, что в общем случае вы не сможете угадать, по
какому каналу приходит тот или иной пакет (не входящий, а исходящий).
Еще можно сконфигурировать EQL устройство, но это из другой темы.
Напомню, что существует "Ip Command Reference" в составе пакета
iproute2. Кроме всего прочего, "ip" корректно сосуществует и дополняет
пакеты динамической маршрутизации (zebra, gated).
5. Управление трафиком.
Для управления трафиком в составе iproute2 находится утилита "tc".
Там-же можно найти кое-какие man-pages для нее. Вообще говоря,
механизм управления трафиком с помощью классов и tc - довольно сложная
тема, поэтому если вы не хотите вникать в теорию, пролистывайте сразу
до реализации.
Итак, управление трафиком позволяет делать следующее:
SHAPING. Шейпинг - техника ограничения трафика в ту или иную сторону.
Может применяться не только для "нарезания" канала, но и для
сглаживания бросков при пиковых нагрузках.
SCHEDULING. Упорядочивание типов трафика в канале, позволяет избегать
задержек для критичных типов трафика. Например ICMP ping. Другими
словами, это приоритизация (QoS).
POLICING. Политика входящего трафика позволяет определить, когда
трафик пропускать, а когда уничтожать (drop). Например, частично
поможет от DDoS.
Процесс управления трафиком осуществляется с помощью трех
взаимосвязанных частей - дисциплин (Queue Disciplines -> qdiscs),
классов (classes) и фильтров (filters). Это все в терминологии "tc".
На самом деле, дисциплина и очередь, а так-же фильтр и классификатор -
это _практически_ синонимы.
Небольшое объяснение дальнейшей терминологии поможет вникнуть в
процесс классификации трафика:
Qdisc - алгоритм, который управляет очередью пакетов на интерфейсе,
входящей (ingress), или исходящей (egress). В qdisc "сидят"
пакеты, если их например нужно задержать.
Classless Qdisc - qdisc, который не может иметь подклассов, и,
следовательно, разветвленную систему обработки пакетов.
Classfull Qdisc - дисциплина, которая может содержать подклассы,
которые, в свою очередь, могут содержать вложенные дисциплины и
т.д. Обычно HTB или CBQ.
Classes - логические контейнеры. Интерфейс с развитой системой
классов, организует их в виде дерева. В нем конечные классы
становятся листьями этой структуры (leaves), и всегда содержат
qdisc.
Classifier - каждая, полная классов дисциплина, определяет с
помощью классификации в какой класс отсылать пакет.
Filter - классификация происходит с использованием фильтров. Если
значения фильтра совпали (например IP адрес), пакет следует
согласно этому фильтру в нужный класс.
Sheduling - как уже было указано выше, это механизм, позволяющий
определить, какому пакету в какой последовательности покидать
qdisc.
Shaping - процесс задержки пакетов в qdisc или их отбрасывания.
Shaping с постановкой в очередь (т.е. с задержкой) может
выполняться только в egress qdiscs.
Важно понимать, что реально ограничивать мы можем только исходящий
трафик. И в самом деле, как можно заставить кого-то слать нам пакеты
медленнее? Однако есть несколько механизмов, позволяющих делать это.
1)Бесклассовый qdisc Ingress поможет вам, если вы хотите просто
убивать трафик, превышающий установленную величину, и предназначенный
только этому вашему единственному хосту. Это полезно для вынесения
буфера из модема в ядро, для того чтобы повысить интерактив;
2)Использование внутреннего для ядра устройства IMQ, тоже позволит
решить эту задачу, и даже будет возможно строить классы, и ставить
входящие пакеты в очередь! Только для этого надо патчить ядро. Однако
эти патчи более официально не развиваются разработчиком. Остальную
информацию можно найти по адресу: http://trash.net/~kaber/imq/
3)Изменение параметра TCP WIN может позволить управлять входящим
трафиком "удаленно", но для этого снова надо патчить ядро. Кроме того,
у этого решения нет возможности sheduling. Есть еще недостатки,
связанные с не совсем верной реализацией стека TCP/IP на некоторых
платформах. Поэтому "удаленное" управление работает не всегда. Хотя
это самый корректный, с точки зрения RFC метод. Патчи редки, и не
всегда грамотны. Официальных вариантов нет.
4)Самый распространенный и реальный в реализации вариант -
рассматривать два интерфейса, через которые проходит транзитный
трафик, как исходящие. Предположим, есть клиент, подключенный по ppp к
хосту. Для ограничения входящего для клиента трафика вешаем qdisc на
ppp, а для ограничения исходящего от клиента - на eth из хоста во
внутреннюю сеть. С точки зрения хоста - оба интерфейса исходящие.
Если вы продолжаете понимать, что здесь написано icon_smile.gif , то
рассмотрим непосредственно алгоритмы управления трафиком.
6.Queque Disciplines.
Даже если вы не установили пакет iproute2, ядро уже использует по
умолчанию classless qdisc! Он называется pfifo_fast, и представляет
собой трех-полосный буфер, приоритизация в котором может производиться
с помощью поля Type Of Service (TOS), т.е. с помощью iptables. Однако
это работает не очень хорошо, особенно в условиях высокой загрузки
канала. Кроме того, бесклассовая архитектура этой дисциплины работает
только для всего интерфейса в целом. Подробнее про управление с
помощью TOS можно прочитать в man iptables.
Наиболее часто используется Token Bucket Filter qdisc. Это типичный
шейпер, который вы должны использовать, если вам нужно просто
ограничить полосу пропускания по какому-нибудь критерию. В кратце,
механизм действия TBF такой: существует корзина (bucket), в которую
собираются жетоны (tokens). Как только корзина наполняется, qdisc
отдает трафик. Если скорость трафика превышает размер корзины
(скорости ее наполнения), то трафик задерживается, или даже теряется.
Другой, часто используемый бесклассовый qdisc - SFQ (Stochastic
Fairness Queueing). Принцип его действия таков, что поступающий в
qdisc трафик разбивается на множество FIFO очередей, каждой из которых
в случайном порядке отдается приоритет. Таким образом, каждая TCP
сессия находится в равных условиях, по сравнению с остальными, и не
может "забить" канал. Подводя итог, надо заметить, что:
1)Для простого ограничения трафика лучше использовать TBF;
2)Если ваш канал реально загружен, лучшим решением будет SFQ;
3)Если ваш канал очень большой, обратите внимание на RED (man 8
tc-red). Пример реализации можно посмотреть у Пульсара
http://linuxadmin.chat.ru/pulsar/QoS.txt От себя добавлю лишь
то, что RED занимается "предварительным случайным дропанием"
пакетов, "вылезающих" за рамки. Подробнее с RED можно ознакомится
в исходниках ядра.
4)Если вам нужна только приоритизация без шейпинга, почитайте про
PRIO qdisc. Он очень похож на pfifo_fast, только условно классовый
(man 8 tc-prio);
5)Для ограничения входящего трафика, который не будет форвардиться
дальше, используйте Ingress qdisc. Только не забывайте, что он
либо только пропускает пакеты, либо убивает(drop) их.
Например для 115кбит канала на ppp0:
# tc qdisc del dev ppp0 ingress 2>/dev/null
# tc qdisc add dev ppp0 handle ffff: ingress
# tc filter add dev ppp0 parent ffff: protocol ip prio 10 u32 match ip
src 0.0.0.0/0 police rate 80kbit burst 4k drop flowid :1
До сих пор, я не отмечал преимуществ классового подхода для управления
трафиком. Все просто - бесклассовый qdisc, установленный сразу на
устройство, действует вообще на все это устройство. Бесклассовые
дисциплины обычно используются как листья в дереве классовой
дисциплины. Это позволяет реализовывать наследование и приоритизацию
сразу по нескольким классам. Классовость дает гибкость в построении
нашего дерева правил, в которых в качестве классификаторов может быть
множество параметров. Например адреса или типы трафика по портам.
Таким образом, в классовой структуре, классы расположены в root qdisс,
который в свою очередь привязан к интерфейсу; и классы тоже могут
содержать подклассы и qdisсs в качестве листьев.
Существует два широко используемых Classfull Qdisсs. Первый, весьма
сложный в своей семантике, но поэтому гибкий - CBQ (Class Based
Queue). Эта дисциплина была разработана одной из первых, и на ее
основе строятся все наиболее популярные схемы распределения трафика.
Она позволяет выполнять все возможные манипуляции по построению
классов, приоритизации трафика и его ограничения. При разделении
полосы с помощью шейперов, например TBF или SFQ, возможно наследование
свободного канала как вниз по дереву классов, так и вверх. CBQ
позволяет приоритизировать трафик наподобие PRIO qdisc. При разделении
канала на полосы, дисциплина работает в целом относительно точно, так
как использует его временнЫе характеристики. Но не всегда точно в
рамках отдельного класса. Основной рекомендацией при использовании CBQ
является то, что надо следить, чтобы сумма полос нижележащих классов
была меньше или равна каналу родительского класса. Из этого правила
могут быть исключения, например если ваш канал никогда полностью не
загружен по всем классам (когда трафик минимален). Но в любом случае
описание структуры CBQ начинается с указания точной максимальной
полосы пропускания вашего канала. Что не всегда известно. Сложность
при использовании CBQ заключается в его не простом синтаксисе, а
так-же довольно не интуитивной системе построения классов в целом.
Второй Classfull Qdisc - HTB (Hierachical Token Bucket). Эта
дисциплина появилась в официальном ядре начиная с версии 2.4.20.
Алгоритм ее работы во многом схож с TBF и CBQ одновременно. HTB
отличает такая-же гибкость, как у CBQ, но в отличие от последнего, HTB
гораздо более интуитивно понятен, имеет упрощенную конфигурацию, более
точен в механизмах шейпинга. Кроме того, для HTB не нужно описывать
пропускную способность всего канала. Подробнее о HTB я изложил в
отдельном материале - "Сага по Hierarchical Token Bucket"
(http://www.atmsk.ru/index.php?option=articles&task=viewarticle&artid=28).
7. Классификаторы.
Для того, чтобы определить в какой класс направить пакет, используются
фильтры-классификаторы. Они описываются так-же как классы и
дисциплины, с помощью утилиты "tc". Детальный синтаксис команды можно
найти в "man 8 tc". Наиболее часто используются два классификатора -
u32 и fw. Первый более гибок и популярен. Он позволяет выделять пакеты
по адресам src/dst, портам src/dst, парам "хост:порт", типам
протокола, типу сервиса. Второй классифицирует пакеты, отмеченные
fwmark с помощью iptables. Рассмотрим простейший пример приоритизации.
Например, мы создали PRIO qdisc, называемый "10:", который содержит
три класса, и мы хотим назначить всему трафику ssh самую приоритетную
полосу в qdisc. Тогда фильтры могут быть приблизительно такие:
# tc filter add dev eth0 protocol ip parent 10: prio 1 u32 match ip
dport 22 0xffff flowid 10:1
# tc filter add dev eth0 protocol ip parent 10: prio 1 u32 match ip
sport 80 0xffff flowid 10:1
# tc filter add dev eth0 protocol ip parent 10: prio 2 flowid 10:2
Мы сказали здесь: присоединить к eth0, узлу 10:, с приоритетом 1, u32
фильтр который совпадает точно по порту назначения ssh, и посылает в
этом случае пакет в полосу 10:1. Похожая ситуация происходит с
http-трафиком. Последнее правило понижает приоритет у всего
остального.
Как видите, ничего сложного. Однако когда дело дойдет до построения
классов с использованием CBQ, синтаксис заметно усложнится. В
следующем разделе я расскажу как облегчить себе задачу в достаточно
простом случае, если надо просто "нарезать" полосу, а в конце обзора
можно будет найти ссылки на ресурсы, более подробно описывающие
синтаксис ip/tc, и полные примеров.
8.Реализация.
Практическое использование всей мощи CBQ уже реализовано для наиболее
распространенного варианта. Вам даже не придется разбираться с
семантикой каждого отдельного qdiscs и классификаторов. В пакет
iproute2 включен скрипт, выполненный как сервис, который достаточно
просто позволяет создать разветвленную систему классов. Скрипт
называется "/etc/rc.d/init.d/cbq", и представляет собой наполовину
комментарий, с инструкцией по его использованию. icon_smile.gif
При запуске, он анализирует содержимое каталога /etc/sysconfig/cbq, в
котором, кстати, уже лежит заготовленный пример класса. Далее он
многократно запускает утилиту tc (на каждый новый класс), и создает
таким образом древовидную систему. В корне этой системы - СBQ qdisc (с
описанием вашего максимума пропускной способности в канале), от
которого будут ответвляться TBF (по умолчанию) листья ваших классов. О
формате файлов классов, и значение опций смотрите в содержимое самого
скрипта. Статистику по классам и дисциплинам можно просмотреть,
используя команду:
# service cbq list
# service cbq stats
С помощью второй команды можно просмотреть значения счетчиков байт в
дисциплинах, и убедиться, что ваше дерево работает. Серьезный
недостаток подобного подхода - скрипт не понимает ppp интерфейсов. А в
остальном, если не вдаваться в продвинутые способы использования tc,
очень даже полезен. Если у вас этого скрипта не оказалось, его можно
найти по адресу http://sourceforge.net/projects/cbqinit
9.Дополнительная информация.
1) http://www.docum.org/ - Хорошая страничка с описаниями qdiscs и
примерами.
2) http://lartc.org/ - "Штаб-квартира" Linux Advanced Routing &
Traffic Control. Огромный HOWTO, и все возможности для управления
трафиком.
3) http://gazette.linux.ru.net/rus/articles/index-iptables-tutorial.html
Наиболее полное руководство по iptables на русском языке.
4) http://lartc.org/manpages/ - Здесь находятся все man-pages на
которые были ссылки в статье.
5) http://mailman.ds9a.nl/pipermail/lartc/ - Архивы и
http://www.lartc.org/#mailinglist - подписка на список рассылки,
посвященный Linux Advanced Routing and TControl.
1235 Прочтений • [Advanced Routing и QoS в linux (пакеты iproute2, cbq.init и htb.init) (firewall nat iptables iproute2 traffic route bandwidth shaper qos cbq htb)] [08.05.2012] [Комментариев: 0]