Новичкам про управление шириной канала в Linux | ||
Сети > Для новичка Некоторое время назад меня попросили настроить в удаленном филиале простейшую балансировку трафика. Работают они, бедолаги, через ADSL, и отправка электронных писем большого объема (сканы документов) забивает им весь обратный канал, что приводит к проблемам в работе с офисными онлайн-программами через VPN.
В качестве шлюза у них используется Linux (Fedora). До этого я пару раз видел, как подобная балансировка настраивается через ipfw на FreeBSD, а так как знаю механизм iptables достаточно хорошо, неожидал особых проблем. Но поискав в Интернете, я был неприятно удивлен тем, что iptables мне тут совсем не помощник. И знания о порядке прохождения пакетов через его таблицы и правила мне почти не пригодятся. Нужно изучать tc из пакета iproute2. Неожиданно для себя, я потратил два дня, для того чтобы более-менее разобраться в балансировке трафика средствами iproute2. Поэтому и попытался собрать полученные мною знания в одну статью, а главное описать все на доступном для новичков уровне. Дело у меня как-то сразу пошло тяжело. Сначала попалась не самая лучшая для новичка статья про HTB(здесь). Различные примеры из Интернет порой вводили в ступор, так как в них часто не было описания смысла применения конкретных опций и приходилось собирать информацию в единое целое из разных источников. Даже немногочисленные статьи на Хабре по данному вопросу быстро скатывались к скриптам-“генерилкам” правил для tc, опять оставляя неясными ряд деталей. И хотя нужные правила на основе примеров я настроил в тот же день, явно чувствовался пробел в теории, как же это устроено. Сразу оговорюсь, “резать” будем только исходящий с сетевого интерфейса трафик. Входящий тоже можно регулировать, но это требует дополнительных хитростей. Бесклассовые дисциплины Итак, в Linux для управления трафиком каждому сетевому интерфейсу назначается дисциплина (qdisc). Именно из дисциплин и строится вся система управления трафиком. Но пугаться не стоит, на самом деле, дисциплина — это просто алгоритм обработки очереди сетевых пакетов. Дисциплин на одном интерфейсе может быть задействовано несколько, а непосредственно к интерфейсу крепится так называемая корневая дисциплина (root qdisc). При этом каждый интерфейс имеет свою собственную корневую дисциплину. prio_fast По-умолчанию после загрузки системы, root qdisc задает алгоритм обработки пакетов типа pfifo_fast. Проверяем: # tc qdisc qdisc pfifo_fast 0: dev eth0 root bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 pfifo_fast — это обычный алгоритм “First Input — First Output”, но с некоторой приоритизацией трафика. Дисциплина такого типа содержит внутри себя три очереди FIFO с разным приоритетом обработки пакетов. Пакеты раскладываются по ним на основе флага ToS (Type of Service) в каждом IP-пакете. Пакет попавший в FIFO0 имеет наивысший приоритет к обработке, в FIFO2 — наименьший. Сам ToS требует отдельного разговора, поэтому предлагаю ограничиться тем фактом, что операционная система сама знает какой ToS назначить отправленному IP-пакету. Например, в пакетах telnet и ping ToS будет иметь разные значения. 0: — дескриптор корневой дисциплины. Дескрипторы должны иметь вид старший_номер: младший_номер, но у дисциплин младший номер всегда должен быть 0, и поэтому его можно опускать. Параметр priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1, как раз задает побитовое соответствие поля ТоS каждой внутренней очереди (band). Например, при ToS=4, пакет обрабатывается в очереди 1, при ToS=7 в очереди 0. В ряде источников указано, что параметры дисциплины pfifo_fast нельзя изменить, поверим. TBF Теперь рассмотрим как можно ограничить скорость общего исходящего трафика. Для этого назначим корневой дисциплиной интерфейса дисциплину типа TBF (Token Bucket Filter). # tc qdisc add dev eth0 root tbf rate 180kbit latency 20ms buffer 1540 rate 180kbit — устанавливает порог скорости передачи на интерфейсе. latency 20ms — задает максимальное время нахождения пакета данных в ожидании токена. buffer 1540 — задаем размер буфера токенов в байтах. В примерах пишут, что для ограничения в 10Мbit/s достаточно буфера на 10Kbytes. Главное не сделать его слишком малого размера, больше можно. Примерная формула расчета: rate_in_Bytes/100. Дисциплина ТBF для своей работы использует механизм токенов. Токены генерируются системой с постоянной скоростью и помещаются в буфер(bucket). За каждый токен, вышедший из буфера с интерфейса уходит IP-пакет. Если скорости передачи пакетов и генерации токенов совпадает, процесс передачи данных идет без задержки. Если скорость передачи пакетов меньше чем скорость токенов, последние начинают накапливаться в буфере и затем могут использоваться для кратковременной передачи данных на скорости выше пороговой. Если скорость передачи пакетов выше — токенов начинает не хватать. Пакеты данных ожидают новых токенов некоторое время, а затем начинают отбрасываться. Описанные две дисциплины относятся к так называемым бесклассовым (classless) дисциплинам. Они имеют ряд функциональных ограничений: подключаются только к интерфейсу (либо краевому классу), плюс для них нельзя применять фильтры пакетов. И соответственно мою задачу по балансировке почтового трафика с их помощью решить не удастся. Кстати, полный набор бесклассовых дисциплин несколько шире: pfifo, bfifo, sqf (обеспечивает одинаковую скорость пакетов поступивших из разных потоков), esqf и т.д. Классовые дисциплины Если дисциплины можно представить как отрезки водопроводных труб, то классы — это соединители (фитинги). Это может быть простой фитинг-переходник, а может хитрый фитинг-разветвитель с десятком отводов. В качестве родителя класса может выступать либо дисциплина, либо другой класс. К классу могут присоединяться дочерние классы (состыковка нескольких фитингов). Класс, не имеющий дочерних классов, называется краевым (leaf class). Здесь пакеты данных, пробежав по нашему “водопроводу” покидают систему управления трафиком и отправляются сетевым интерфейсом. По-умолчанию, любой краевой класс имеет присоединенную дисциплину типа fifo, и именно она определяет порядок передачи пакетов для этого класса. Но вся прелесть в том, что мы можем поменять эту дисциплину на любую другую. В случае добавления дочернего класса данная дисциплина удаляется. prio Вернемся к задаче по балансировке почтового трафика и рассмотрим классовую дисциплину prio. Она очень похожа на уже описанную pfifo_fast. Но данная дисциплина особенная тем, что при ее назначении автоматически создается три класса (количество можно менять параметром bands). Заменим корневую дисциплину интерфейса на prio. # tc qdisc add dev eth0 root handle 1: prio handle 1: — задаем дескриптор данной root qdisc. В классовых дисциплинах его затем указывают при подключении классов. Проверяем дисциплину: # tc qdisc qdisc prio 1: dev eth0 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 Проверяем классы: # tc -d -s class show dev eth0 class prio 1:1 parent 1: Sent 734914 bytes 7875 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 class prio 1:2 parent 1: Sent 1555058583 bytes 8280199 pkt (dropped 124919, overlimits 26443 requeues 0) backlog 0b 0p requeues 0 class prio 1:3 parent 1: Sent 57934378 bytes 802213 pkt (dropped 70976, overlimits 284608 requeues 0) backlog 0b 0p requeues 0 Видим три класса с идентификаторами 1:1, 1:2 и 1:3, подключенные к родительской диcциплине 1: типа prio (классы обязаны иметь общий старший_номер идентификатора со своим родителем). Т.е на “трубу” root qdisc, которая разделяет потоки данных также как это делает pfifo_fast мы насадили тройник. Руководствуясь ToS, в класс 1:1 попадает высокоприоритетный трафик, в класс 1:2 обычный трафик, в класс 1:3 совсем уж «мусор». Допустим обратный канал ADSL выдает скорость 90Кбайт/c. Разделим его на 20Kбайт/c под почту и 70Kбайт/c на все остальное. Трафик из класса 1:1 специально ограничивать не будем. Его пакеты всегда смогут занять хоть всю ширину канала из-за высокого приоритета ToS, но объем трафика в этом классе у нас будет ничтожно мал по сравнению с остальными двумя классами. Поэтому отдельную полосу под него не резервируем. Стандартный трафик, как правило, попадает в класс 1:2. Подключаем ко второму выводу класса-тройника трубу-дисциплину на 70Kбайт/c: # tc qdisc add dev eth0 parent 1:2 handle 10: tfb rate 70kbps buffer 1500 latency 50ms На третий вывод тройника подключим трубу-дисциплину на 20Kбайт/c: # tc qdisc add dev eth0 parent 1:3 handle 20: tfb rate 20kbps buffer 1500 latency 50ms Все три этих класса краевые. И теперь осталось только направить почтовый трафик не в класс 1:2, как происходило раньше, а в класс 1:3. Это делается с помощью фильтров классовых дисциплин. # tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip dport 25 0xffff flowid 1:3 parent 1: — фильтр может крепиться только к дисциплине и вызывается из нее же. На основе срабатывания фильтра дисциплина решает в каком классе продолжится обработка пакета. protocol ip — определяем тип сетевого протокола prio 1 — параметр долго вводил меня в замешательство, так как он применяется в классах и фильтрах, плюс это название дисциплины. Здесь prio задает приоритет срабатывания фильтров, первыми задействуются фильтры с меньшим prio. u32 — так называемый классификатор трафика, который может выполнять отбор пакетов по любому его признаку: по ip-адресу отправителя/получателя, по порту источника/приeмника, по типу протокола. Эти условия, собственно, и указаны далее. match ip dport 25 0xffff — задает срабатывание фильтра при отправке пакетов на порт 25. 0xffff — это битовая маска для номера порта. flowid 1:3 — указываем в какой класс передаются пакеты при срабатывании данного фильтра. Сделано грубо, но задачу выполнит. Смотрим статистику прохождения пакетов: # tc -s -d qdisq show dev eth0 # tc -s -d class show dev eth0 # tc -s -d filter show dev eth0 Быстро удалить все классы, фильтры и вернуть root qdisc интерфейса в первоначальное состояние можно командой: # tc qdisc del dev eth0 root HTB С другой стороны, у нас и так слишком тонкий обратный канал, чтобы резервировать 20Kбайт/c только под отправку электронной почты. Поэтому здесь лучше использовать классовую дисциплину HTB (Hierarchical Token Bucket). Она позволяет производить заимствование полосы пропускания дочерним классами у родительского. # tc qdics add dev eth0 root handle 1: htb default 20 default 20 — задаем класс по-умолчанию. В нем будут обрабатываться пакеты, не попавшие в другие классы дисциплины htb. Если его не указать, то будет назначен “default 0” и весь неклассифицированный (непопавший под фильтры) трафик будет отправляться со скоростью интерфейса. # tc class add dev eth0 parent 1: classid 1:1 htb rate 90kbps ceil 90kbps прикрепляем к root qdisc класс с идентификатором 1:1. Тем самым ограничиваем скорость на интерфейсе до 90Кбайт/c. classid 1:1 — идентификатор класса. rate 90kbps — устанавливаем нижний порог пропускной способности для класса. ceil 90kbps — устанавливаем верхний порог пропускной способности для класса. # tc class add dev eth0 parent 1:1 classid 1:10 htb rate 20kbps ceil 70kbps создаем класс 1:10, дочерний классу 1:1. Затем в него фильтром будет направляться исходящий почтовый трафик. rate 20kbps — устанавливаем гарантированный нижний порог пропускной способности для класса. ceil 70kbps — устанавливаем верхний порог пропускной способности для класса. В случае если у родительского класса будет свободна полоса пропускания (наличие “лишних” токенов), class 1:10 сможет временно поднять скорость передачи данных, вплоть до указанного предела в 70Кбайт/c. # tc class add dev eth0 parent 1:1 classid 1:20 htb rate 70kbps ceil 90kbps Создаем класс по умолчанию. В него будет попадать весь остальной трафик. Точно также, параметрами rate и ceil, задаем расширение пропускной способности в случае отсутствия уже почтового трафика. # tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip dport 25 0xffff flowid 1:10 фильтр на базе u32, направляющий пакеты исходящие на 25й порт в класс 1:10. Кстати, в документации указано, что по факту в HTB шейпинг трафика происходит только в краевых классах, в нашем случае 1:10 и 1:20. Указание параметров ограничения полосы пропускания в остальных классах HTB нужно лишь для функционирования системы заимствования между классами. При добавлении класса также возможно указать параметр prio. Он задает приоритет класса (0 — макс.приоритет). Классы с меньшим приоритетом не обрабатываются пока есть данные в более приоритетных классах. Источник: http://habrahabr.ru/blogs/linux/133076/ |
||
Комментарии | ||