Это предварительный вариант статьи для обсуждения и дополнений (скоро в DocBook CVS). Окончательный вариант войдет в очередной дистрибутив AltLinux.
1. Введение.
Алексей Кузнецов и 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 (см. пакет nag).
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-ом. Маркируются-ли пакеты, мы можем узнать, вызывая 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 -> qdisks), классов (classes) и фильтров (filters). Это все в терминологии "tc". На самом деле, дисциплина и очередь, а так-же фильтр и классификатор - это _практически_ синонимы.
Небольшое объяснение дальнейшей терминологии поможет вникнуть в процесс классификации трафика:
Qdisk - алгоритм, который управляет очередью пакетов на интерфейсе, входящей (ingress), или исходящей (egress). В qdisk "сидят" пакеты, если их например нужно задержать.
Classless Qdisc - qdisk, который не может иметь подклассов, и, следовательно, разветвленную систему обработки пакетов.
Classfull Qdisk - дисциплина, которая может содержать подклассы, которые, в свою очередь, могут содержать вложенные дисциплины и т.д. Обычно HTB или CBQ.
Classes - логические контейнеры. Интерфейс с развитой системой классов, организует их в виде дерева. В нем конечные классы становятся листьями этой структуры (leaves), и всегда содержат qdisk.
Classifier - каждая, полная классов дисциплина, определяет с помощью классификации в какой класс отсылать пакет.
Filter - классификация происходит с использованием фильтров. Если значения фильтра совпали (например IP адрес), пакет следует согласно этому фильтру в нужный класс.
Sheduling - как уже было указано выше, это механизм, позволяющий определить, какому пакету в какой последовательности покидать qdisk.
Shaping - процесс задержки пакетов в qdisk или их отбрасывания. Shaping с постановкой в очередь (т.е. с задержкой) может выполняться только в egress qdisks.
Важно понимать, что реально ограничивать мы можем только исходящий трафик. И в самом деле, как можно заставить кого-то слать нам пакеты медленнее? Однако есть несколько механизмов, позволяющих делать это.
1)Бесклассовый qdisk Ingress поможет вам, если вы хотите просто убивать трафик, превышающий установленную величину, и предназначенный только этому вашему единственному хосту. Это полезно для вынесения буфера из модема в ядро, для того чтобы повысить интерактив;
2)Использование внутреннего для ядра устройства IMQ, тоже позволит решить эту задачу, и даже будет возможно строить классы, и ставить входящие пакеты в очередь! Только для этого надо патчить ядро. Остальную информацию можно найти по адресу: http://luxik.cdi.cz/~patrick/imq/
3)Изменение параметра TCP WIN может позволить управлять входящим трафиком "удаленно", но для этого снова надо патчить ядро. Кроме того, у этого решения нет возможности sheduling. Есть еще недостатки, связанные с не совсем верной реализацией стека TCP/IP на некоторых платформах. Поэтому "удаленное" управление работает не всегда. Хотя это самый корректный, с точки зрения RFC метод. Патчи редки, и не всегда грамотны. Официальных вариантов нет.
4)Самый распространенный и реальный в реализации вариант - рассматривать два интерфейса, через которые проходит транзитный трафик, как исходящие. Предположим, есть клиент, подключенный по ppp к хосту. Для ограничения входящего для клиента трафика вешаем qdisk на ppp, а для ограничения исходящего от клиента - на eth из хоста во внутреннюю сеть. С точки зрения хоста - оба интерфейса исходящие.
Если вы продолжаете понимать, что здесь написано, то рассмотрим непосредственно алгоритмы управления трафиком.
6.Queque Disciplines.
Даже если вы не установили пакет iproute2, ядро уже использует по умолчанию classless qdisc! Он называется pfifo_fast, и представляет собой трех-полосный буфер, приоритезация в котором может производиться с помощью поля Type Of Service (TOS), т.е. с помощью iptables. Однако это работает не очень хорошо, особенно в условиях высокой загрузки канала. Кроме того, бесклассовая архитектура этой дисциплины работает только для всего интерфейса в целом. Подробнее про управление с помощью TOS можно прочитать в man iptables.
Наиболее часто используется Token Bucket Filter qdisk. Это типичный шейпер, который вы должны использовать, если вам нужно просто ограничить полосу пропускания по какому-нибудь критерию. В кратце, механизм действия TBF такой: существует корзина (bucket), в которую собираются жетоны (tokens). Как только корзина наполняется, qdisk отдает трафик. Если скорость трафика превышает размер корзины (скорости ее наполнения), то трафик задерживается, или даже теряется.
Другой, часто используемый бесклассовый qdisk - SFQ (Stochastic Fairness Queueing). Принцип его действия таков, что поступающий в qdisk трафик разбивается на множество FIFO очередей, каждой из которых в случайном порядке отдается приоритет. Таким образом, каждая TCP сессия находится в равных условиях, по сравнению с остальными, и не может "забить" канал. Подводя итог, надо заметить, что:
1)Для простого ограничения трафика лучше использовать TBF;
2)Если ваш канал реально загружен, лучшим решением будет SFQ;
3)Если ваш канал очень большой, обратите внимание на RED (man 8 tc-red);
4)Если вам нужна только приоритезация без шейпинга, почитайте про PRIO qdisk. Он очень похож на pfifo_fast, только условно классовый (man 8 tc-prio);
5)Для ограничения входящего трафика, который не будет форвардиться дальше, используйте Ingress qdisk. Только не забывайте, что он либо только пропускает пакеты, либо убивает(drop) их.
До сих пор, я не отмечал преимуществ классового подхода для управления трафиком. Все просто - бесклассовый qdisk, установленный сразу на устройство, действует вообще на все это устройство. Бесклассовые дисциплины обычно используются как листья в дереве классовой дисциплины. Это позволяет реализовывать наследование и приоритезацию сразу по нескольким классам. Классовость дает гибкость в построении нашего дерева правил, в которых в качестве классификаторов может быть множество параметров. Например адреса или типы трафика по портам. Таким образом, в классовой структуре, классы расположены в root qdisk, который в свою очередь привязан к интерфейсу; и классы тоже могут содержать подклассы и qdisks в качестве листьев.
Существует два широко используемых Classfull Qdisks. Первый, весьма сложный в своей семантике, но поэтому гибкий - CBQ (Class Based Queue). Эта дисциплина была разработана одной из первых, и на ее основе строятся все наиболее популярные схемы распределения трафика. Она позволяет выполнять все возможные манипуляции по построению классов, приоритезации трафика и его ограничения. При разделении полосы с помощью шейперов, например TBF или SFQ, возможно наследование свободного канала как вниз по дереву классов, так и вверх. CBQ позволяет приоритезировать трафик наподобие PRIO qdisk. При разделении канала на полосы, дисциплина работает в целом относительно точно, так как использует его временные характеристики. Но не всегда точно в рамках отдельного класса. Основной рекомендацией при использовании CBQ является то, что надо следить, чтобы сумма полос нижележащих классов была меньше или равна каналу родительского класса. Из этого правила могут быть исключения, например если ваш канал никогда полностью не загружен по всем классам (когда трафик минимален). Но в любом случае описание структуры CBQ начинается с указания точной максимальной полосы пропускания вашего канала. Что не всегда известно. Сложность при использовании CBQ заключается в его не простом синтаксисе, а так-же довольно не интуитивной системе построения классов в целом.
Второй Classfull Qdisk - HTB (Hierachical Token Bucket). Эта дисциплина появилась в официальном ядре начиная с версии 2.4.20. Алгоритм ее работы во многом схож с TBF и CBQ одновременно. HTB отличает такая-же гибкость, как у CBQ, но в отличие от последнего, HTB гораздо более интуитивно понятен, имеет упрощенную конфигурацию, более точен в механизмах шейпинга. Кроме того, для HTB не нужно описывать пропускную способность всего канала. Подробнее о HTB я изложил в отдельном материале ниже.
7. Классификаторы.
Для того, чтобы определить в какой класс направить пакет, используются фильтры-классификаторы. Они описываются так-же как классы и дисциплины, с помощью утилиты "tc". Детальный синтаксис команды можно найти в "man 8 tc". Наиболее часто используются два классификатора - u32 и fw. Первый более гибок и популярен. Он позволяет выделять пакеты по адресам src/dst, портам src/dst, парам "хост:порт", типам протокола, типу сервиса. Второй классифицирует пакеты, отмеченные fwmark с помощью iptables. Рассмотрим простейший пример приоритезации. Например, мы создали PRIO qdisk, называемый "10:", который содержит три класса, и мы хотим назначить всему трафику ssh самую приоритетную полосу в qdisk. Тогда фильтры могут быть приблизительно такие:
Код:
# 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 уже реализовано для наиболее распространенного варианта. Вам даже не придется разбираться с семантикой каждого отдельного qdisks и классификаторов. В пакет iproute2 включен скрипт, выполненный как сервис, который достаточно просто позволяет создать разветвленную систему классов. Скрипт называется /etc/rc.d/init.d/cbq, и представляет собой наполовину комментарий, с инструкцией по его использованию. При запуске, он анализирует содержимое каталога /etc/sysconfig/cbq, в котором, кстати, уже лежит заготовленный пример класса. Далее он многократно запускает утилиту tc (на каждый новый класс), и создает таким образом древовидную систему. В корне этой системы - СBQ qdisk (с описанием вашего максимума пропускной способности в канале), от которого будут ответвляться TBF (по умолчанию) листья ваших классов. О формате файлов классов, и значение опций смотрите в содержимое самого скрипта. Статистику по классам и дисциплинам можно просмотреть, используя команду:
Код:
# service cbq list
# service cbq stats
С помощью второй команды можно просмотреть значения счетчиков байт в дисциплинах, и убедиться, что ваше дерево работает. Серьезный недостаток подобного подхода - скрипт не понимает ppp интерфейсов. А в остальном, если не вдаваться в продвинутые способы использования tc, очень даже полезен.
9.Дополнительная информация.
1) http://www.docum.org/ - Хорошая страничка с описаниями qdisks и примерами.
2) http://lartc.org/ - "Штаб-квартира" Linux Advanced Routing & Traffic Control. Огромный HOWTO, и все возможности для управления трафиком.
3) https://www.opennet.ru/docs/RUS/iptables/ - Наиболее полное руководство по iptables на русском языке.
После некоторого обсуждения статьи, стало ясно, что не хватает скриптов практической реализации использования CBQ. К сожалению, скрипт cbq.init пока не работает с HTB, однако существует простой скрипт для HTB, который вы можете найти ... у себя на винчестере! Вот он: [url]file:///usr/share/doc/HOWTO/HTML/en/ADSL-Bandwidth-Management-HOWTO/implementation.html.
Как видно из ссылки, нужен пакет howto-html-en. Скрипт сопровождает довольно краткое, но очень конкретное описание "как это работает". Он использует не только HTB для upstream, но и IMQ для downstream. Напомню, что IMQ требует патчения ядра. Другие примеры можно найти по ссылкам, которые я давал выше.
PS В последнюю сборку iproute2 для Сизифа я включил скрипты wondershaper от lartc.org Они работают и с HTB.
PPS На самом деле, есть htb.init. Про него читайте "Сагу о HTB" ниже.
После посещения местной Линуксовки, натолкнулся на статью моего коллеги, где будет больше конкретики. http://linuxadmin.chat.ru/pulsar/QoS.txt От себя добавлю лишь то, что не рассмотренный RED занимается "предварительным случайным дропанием" пакетов, "вылезающих" за рамки. Подробнее с RED можно ознакомится в исходниках ядра.
Сага по Hierarchical Token Bucket.
Выше я упоминал полноклассовую дисциплину HTB. Так как она в последнее время набирает обороты, хочу поделиться некоторой информацией, которая может оказаться полезной при ее использовании.
Начну с краткого описания HTB. Я думаю, есть хотя-бы приблизительное представление о том, как работает classfull шэйпинг, поэтому в описание терминологии вдаваться не нужно. Кроме того, все заинтересованные прочитали статью выше.
Итак, что дает нам HTB:
1) Последняя реализация так-же быстра, как CBQ и гораздо точнее ее.
2) Это более понятно и легко конфигурируемо, чем CBQ.
3) HTB работает похоже на CBQ, но не расчитывает на временнЫе параметры в канале в момент простоев (idle time) для разграничения полос. Вместо этого, он работает как полноклассовый Token Bucket Filter. У НТВ небольшое число параметров, которые неплохо документированы.
4) Вытекает из предыдущего - классы уже работают как TBF-шэйперы и мы можем выбирать qdisk, который будет не резать, а распределять трафик. У CBQ.init в листьях был только TBF.
5) Решение на основе НТВ более расширяемо. Нужно мало действий, чтобы создать минимальную конфигурацию. Про CBQ так не скажешь.
6) HTB может наследовать неиспользуемую полосу пропускания как по горизонтали, так и по вертикали в дереве классов.
7) Наследование происходит на основе приоритетов классов и фильтров, а так-же установленного rate.
8) Вводится понятие CIR(знакомое по frame-relay), т.е. минимально-допустимой полосы пропускания.
9) В HTB работают все настройки, в отличие от параметров bounded совместно с isolated в CBQ.
10) Разработка этого добра продолжается, тогда как про CBQ Кузнецов почему-то забыл. Приходится даже патчить его утилиту tc на предмет совместимости с HTB. В ядре HTB начиная с 2.4.20.
11) Наконец существует довольно оживленный список рассылки по вопросам конфигурирования и функционирования HTB.
12) И последнее - при описании дерева классов, не обязательно точно описывать родительский RATE.
1) Напоминаю, что управлять можно только исходящим трафиком.
2) kbps = kilobyte/s; kb = kilobyte; kbit = kilobit/s or kilobit - это нужно для понимания вывода статистики по сконфигурированным классам и дальнейших рассуждений.
3) Не нужно добавлять htb/cbq/tbf qdisks в классы - это вносит задержки и жрет CPU. Мы будем использовать sfq и pbfifo.
4) Чем больше prio в классе, тем ниже приоритет при наследовании полосы. Допустимый диапазон - 0-7.
5) rate <= ceil в классе.
6) sum (rate of childs) <= parent rate & ceil; в идеальном случае sum (rate of childs) = parent rate. Если правило не соблюдается, то на parent параметры HTB кладет (см. п.12 выше). Это допустимо но не желательно. Реальный пример - имеем около 80 классов, которые имеют rate=32kbit. При их суммировании, имеем почти в три раза большее значение, чем имеющийся у нас мегабит исходящего канала, который описан как rate родительского класса. Таким образом HTB сохраняет свою целостность (в отличие от CBQ, у которого съезжает крыша, и он начинает ошибаться и врать в статистике). В случае, если предположить, что все классы будут забиты трафиком (почти невероятно), то никто не получит свой CIR, но будет очень близок к нему тот класс, у которого выше prio и больше quantum.
7) Если ceil не указан, то он = rate.
8) Если класс промежуточный (не leaf и не root), в него _не_ нужно вводить qdisk и фильтры(rules) соответственно. Приоритеты в таких классах так-же не работают, они нужны только для leafs.
9) Приоритет класса описывает то, какие классы в каком порядке наследуют полосу. Приоритет фильтра описывает порядок просмотра списка фильтров при классификации.
10) HTB обычно подразумевается как класс, но так-же существует и как qdisk, который присоединяется _только_ непосредственно к интерфейсу (до root-class)
11) Численное значение любого класса лежит в пределах 2-ffff и должно быть уникально в пределах интерфейса.
Теперь сообщу приятную новость. Есть-таки скрипт htb.init, который был переписан с cbq.init и работает очень похоже. Найти его можно по адресу http://freshmeat.net/projects/htb.init. Ниже я расскажу о нюансах в примерной реализации дерева классов на его основе. А сейчас рассмотрим параметры НТВ, которые, кстати, вместе с примером описаны в этом скрипте. Все слова написанные ЗАГЛАВНЫМИ буквами распознаются htb.init как параметры. Однако, никто не запрещает писать plain-script с вызовами tc, и для него эти параметры такие-же.
1) Для HTB qdisk устройства мы описываем DEFAULT класс по умолчанию, в который попадает трафик не подпавший ни под одно rule, обычно это самый низко-приоритетный трафик. Если DEFAULT не указывать, то весь не склассифицированный трафик пойдет напрямую с наивысшим 0 приоритетом.
2) Второй параметр R2Q используется для квантования трафика в классах. В случае малых скоростей рекомендуется значение = 1, что позволяет шейпать с точностью до 4kbit. Если не указан, то =10, что для скоростей >120Kbit.
3) В скриптах допустим параметр QUANTUM, но он используется только как параметр для sfq, а не htb. При использовании htb.init для htb-классов вычисляется автоматически, исходя из глобального параметра R2Q и rate класса. На что влияет quantum в HTB, можно попробовать понять из http://www.docum.org/stef.coene/qos/faq/#11b. На что влияет QUANTUM(как параметр в скриптах) для sfq - читать http://lartc.org/howto/lartc.qdisc.classless.html#LARTC.SFQ.
4) CEIL используется для ограничения полосы в leaf(оконечном) классе, точно как RATE в cbq.init. RATE в HTB используется для управления тем, как полоса разбивается между дочерними классами, и гарантирует минимальную полосу, если есть у кого наследовать. То есть своего рода CIR.
5) Про PRIO я уже писал. Обычно у классов хостов он где-то в середине, у интерактивного трафика - повыше, у остального (или по умолчанию) - пониже.
6) Классификация трафика происходит так-же как и в cbq.init с помощью параметра RULE, которых допустимо несколько. Пакет попавший под любое rule будет отправлен в этот класс. Обращать внимание на запятые! rule=addres, - это source; rule=addres - это destination. Допустимы звездочки, при описании портов. При введении rules, классификация происходит с использованием u32 классификатора, про который читать http://lartc.org/howto/lartc.adv-filter.html#LARTC.ADV-FILTER.U32.
7) Классификация например в интерактивный класс может происходит так-же при помощи параметра MARK. Тут в дело вступает классификатор fw, который выбирает fwmarked пакеты. Отмечаем пакеты в фаерволе. Приоритет при просмотре фильтров у fw в скрипте ниже, что IMHO не правильно. Для того, чтобы ловить сначала мелкие пакеты (ACKs), а только потом по адресам-портам, надо подправить приоритеты фильтрам прямо в htb.init. При генерации plain-script-а, htb.init назначит всем фильтрам один приоритет, который отличется численно для u32, fw и route классификаторов.
8) Параметры листьев: для sfq - QUANTUM и PERTURB, можно оставить по умолчанию. Для pfifo - LIMIT. О смысле расскажу ниже в реализации. Других qdisks, кроме как еще bfifo, для HTB не предусмотрено (RED не рассматриваем, да он и не поддерживается скриптом).
9) BURST и CBURST можно не описывать, они вычисляются автоматически. Смысл - как и PEAK в CBQ (который вы тоже скорее всего не описывали).
Наконец примерная реализация.
В двух словах, htb.init парсит каталог /etc/sysconfig/htb, генерит plain-script, состоящий из множества вызовов tc и запускает его. Точно также как и cbq.init. Одно из существенных изменений - именование файлов классов. Оно детально расписано внутри самого скрипта, так-что приведу только пример:
eth0-2:20:0101:A101.client_out
1) Здесь до минуса - интерфейс, в его соответствующем файле описываем параметры htb qdisk (R2Q и DEFAULT).
2) Далее id корнвого класса, в его файле только rate, для eth = 10/100Mbit.
3) Далее id=20 Это класс, объединяющий всех клиентов. Про него ниже.
4) Далее 0101 - parent класс клиента (только в случае запроса на prio, и соответственно разделение трафика по подклассам), обычно это просто листовой класс клиента. Чтобы разобраться, зачем это нужно, смотрите http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm#hsharing.
5) A101 - leaf класс клиента, в котором описывается qdisk.
6) Все, что после точки - смысловое описание класса.
В нашей примерной реализации я создал три класса сразу под корневым - 10: 20: и 30: Их функции таковы:
1) 10: - интерактивный трафик. Сюда попадает ssh трафик, SMB в офисе, http на превилегированный хост - чтобы интранет работал без ограничений. С помощью марки в таблице mangle в этот-же класс завернуты пакеты < 64 байт, в основном это ACK-и. Нужно для обеспечения клиентам upload-а во время download-а. А так-же завернуты udp запросы к DNS - они маленькие, а пользы прибавят. ICMP в этот класс не попадает, для возможности актуальной проверки канала на загрузку. Вы конечно можете это делать по своему. Подчеркну, что клиентский трафик, попавший в этот класс, не шейпается его классом (если не забыли сделать fw фильтру бОльший приоритет, чем u32 в htb.init, см. п.7 параметров), и более приоритетен, чем его остальной трафик. Правда он достаточно мал, чтобы слишком выбиваться на картинках клинтского трафика. Служит для того, чтобы клиент не "убивал" одной закачкой весь свой канал. К классу прикреплена дисциплина sfq, так как в него попадают пакеты с разных адресов. SFQ работает по принципу русской рулетки и не дает одному tcp виртуальному каналу все время владеть полосой. Подробнее я писал выше. Параметр QUANTUM оставлен по умолчанию, параметр PERTURB, так-же по умолчанию и равен 10 сек. Это время перестройки хэша "русской рулетки", т.е. частота рандомизации хэша. Подробно про SFQ читать - см. п.3 в параметрах HTB. Полоса класса например 10Мбит, ceil = 100Mbit.
2) 20: - клиентский трафик. Ниже лежат все классы-листья клиентов. Ничего особого - rate(который может быть overlimit) согласно нашему внешнему каналу, ceil = 100Mbit(как-раз для таких случаев). Особо остановлюсь на клиентских классах. В общем ничего мудреного, все согласно правилам выше. Про прио я уже упоминал. В качестве qdisk в листьях используется pfifo (packet фифо), который еще проще, чем pfifo_fast, используемый в случае отсутствия какого-либо qdisk. Напомню, что pfifo_fast трехполосный, и распределение по полосам плохенько, но производилось с помощью TOS. TOS умеют выставлять некоторые "умные" приложения, типа openssh. Используемый pfifo имеет одну полосу, поэтому весь трафик в ней эквивалентен, однако в отличие от pfifo_fast имеет механизм статистики. Единственный параметр LIMIT указывает на размер очереди для pfifo в пакетах, для bfifo в байтах. Стандартная очередь pfifo{_fast} = 100. Чем больше очередь, тем беспрерывнее и "быстрее" работает одна tcp сессия, например закачка. Но и не дает никому другому кислорода, поэтому больше задержка в канале. При значениях по умолчанию qlen*MTU/sec=100*1500=150kbps, то есть к примеру на идеально забитом канале в 128kbit мы можем ожидать ответа например на пинг 150/16kbps больше 9 секунд! Исходя из этих соображений выбирайте значения поменьше, если хотите обеспечить интерактив.
3) 30: - класс по умолчанию, он же "опущенный". Сюда попадает весь не описанный фильтрами в классах трафик. Qdisk = SFQ, т.к. разномастный трафик от разных адресов.
Так-как рулеса в скриптах работают только с tcp и портами, udp и мелкие ACK-и придется маркировать в iptables. Для этого создаем соответствующую цепочку в mangle. Любопытных милости просим сходить по треттей ссылке из генеральных.
Напоследок замечу, что это действительно работает :-)
Спасибо за внимание, надеюсь на Ваше понимание. Советую перечитать сначала =)