Каждому по потребностям. Ограничение полосы пропускания на Linux'овом шлюзеЕвгений Зобнин (j1m@synack.ru) Разделение, ограничение и управление трафиком - актуальная и сложная задача, которую обычно возлагают на дорогостоящее специальное сетевое оборудование. Но решить ее можно и с помощью подсистемы Linux-ядра Traffic Control, не уступающей по возможностям Cisco IOS.
Допустим, существует офис некой компании X, и в нем числится около ста сотрудников, каждый из которых может выходить в интернет через шлюз. Скорость внешнего канала составляет 100 Мбит. Системный администратор справился с настройкой шлюза в силу своих способностей – что и посчитал достаточным для правильного функционирования сети. К чему это привело? К увольнению недальновидного (или ленивого) админа. Со временем большинство сотрудников начали жаловаться на «тормоза» интернета, а другие, наоборот, заметили, что могут качать торренты в рабочее время на очень внушительных скоростях. Говоря админским языком, в сети образовались заторы, вызванные теми, кто в тот момент активно ее использовал. Стомегабитный канал распределялся неравномерно между пользователями, и каждый мог занять его весь. Остальным приходилось ждать. Краткий сценарийРешение проблемы: разделение канала между сотрудниками с ограничением скорости! Сеть будет функционировать на «5+», если каждый сотрудник получит в распоряжение отдельный канал, скорость которого будет составлять 1 Мбит. Тогда отдельно взятый интернет-пользователь не сможет занять больше причитающейся ему доли и отобрать часть канала у других. С точки зрения компании, это еще и отличный способ экономии (после разделения канала оказывается, что его суммарная пропускная способность даже излишне высока) и ведения статистики по трафику для отдельно взятого сотрудника. Обычно для разделения канала с ограничением скорости используются возможности операционной системы IOS, на которой функционирует сетевое оборудование Cisco (дешевые решения от других производителей, таких, как Dlink, Trendnet и Netgear, вообще не обладают такой возможностью). Однако особой необходимости тратить баснословные суммы на аппаратные шлюзы от Cisco нет. Ядро Linux уже более пяти лет как содержит в себе код сложной и весьма функциональной подсистемы управления трафиком Traffic Control, которая по некоторым параметрам даже обходит IOS. Подсистема Traffic ControlПодсистема Traffic Control поддерживает множество методов классификации, приоритезации, разделения и ограничения трафика (как исходящего, так и входящего). Она очень гибка в настройке, но сложна в понимании. Поэтому мы уделим значительную часть статьи теоретическому материалу и лишь затем приступим к решению задачи с помощью HTB – одной из наиболее гибких и популярных дисциплин Traffic Control. Подсистема управления трафиком Linux позволяет делать следующее:
В обработке трафика участвуют три ключевых сущности: дисциплины обработки пакетов, классы и фильтры. Настройка эффективной системы ограничения трафика невозможна без понимания механизмов их работы, роли и связи друг с другом. Мы в подробностях рассмотрим каждую из них:
В простейшем варианте путь пакета от приложения до удаленного конца соединения выглядит так. Приложение генерирует (пользуясь услугами библиотек и ядра) сетевой пакет и отдает его ядру, которое помещает пакет в очередь FIFO (первым пришел, первым ушел). Драйвер сетевой карты время от времени обращается к специальному алгоритму ядра, который извлекает из очереди пакет и отдает его драйверу. Далее пакет уходит к адресату. Все просто. Linux действует таким же образом. Но формат представления очереди и алгоритм ее обработки, в совокупности называемые дисциплиной обработки пакетов, в нем заменяемы! По умолчанию используется дисциплина pfifo_fast, реализующая очередь FIFO. Пользуясь утилитой tc, администратор может заменить ее на другую дисциплину, которая будет переупорядочивать пакеты (планирование), задерживать их на определенное время (шейпинг) или выполнять другие действия. Дисциплины классовTraffic Control не был бы столь гибким, если бы не позволял разбивать трафик на классы с помощью классовой дисциплины и набора ее подклассов. Схематически классовая дисциплина очень похожа на файловую систему, c тем лишь исключением, что ее корень или классы (каталоги) могут содержать либо дисциплину (файл), либо подклассы (подкаталоги). Одно из двух. Классовые дисциплины и классы предназначены для построения дерева выбора. Сначала весь трафик разбивается на несколько общих классов (например, трафик до Отдела-1, трафик до специализированных внутренних серверов и т.д.), а затем каждый из них разбивается на несколько подклассов (например, трафик до DNS-сервера Отдела-1), за которыми уже могут быть закреплены дисциплины. Чтобы управлять тем, дисциплиной какого класса будет обработан определенный тип трафика, классовые дисциплины позволяют подключать к себе фильтры. Это дает возможность «завернуть» определенный трафик в один из ее подклассов. Фильтры используют классификаторы для идентификации пакетов нужного типа и как бы говорят ядру: «Этот вид трафика должен обрабатываться с помощью дисциплины вот этого класса». Существует несколько разных классификаторов. Самыми популярными являются u32 и fw. Первый позволяет выделять пакеты по исходящим адресам и адресам назначения, портам, парам «хост:порт», типам протокола и типу сервиса. Второй классифицирует пакеты путем чтения маркировок, записанных брандмауэром iptables/netfilter (цель MARK). За каждым сетевым интерфейсом должны быть закреплены две особые дисциплины: корневая дисциплина (root qdisc) и входящая дисциплина (ingress qdisc). В первую помещается весь исходящий трафик (по умолчанию используется дисциплина pfifo_fast). Во вторую - входящий. Для идентификации дисциплин и классов используются дескрипторы. Они состоят из старшего и младшего номеров. Первый - это произвольное число, однако все классы, имеющие общего родителя, должны иметь одинаковый старший номер. Младший номер используется либо для произвольной идентификации классов, либо для указания на то, что объект является дисциплиной (номер 0). Специальный дескриптор ffff:0 зарезервирован для входящей дисциплины. Утилита tcДля конфигурирования подсистемы управления трафиком предназначена утилита tc из пакета iproute2. Она принимает набор команд в качестве аргументов, с помощью которых можно создавать классы, привязывать к ним дисциплины и добавлять фильтры. Синтаксис ее похож на синтаксис команды ipfw из операционной системы FreeBSD, так что знакомые с ним быстро освоятся. Для примера рассмотрим простейший вариант использования:
Эта команда устанавливает ограничение для всего исходящего трафика в 256 Кбит/с. Разберем подробнее все аргументы tc:
Проще говоря, команда подключает дисциплину tbf в качестве корневой на интерфейсе eth0 и задает ей несколько параметров. Token Bucket Filter (TBF) - это бесклассовая дисциплина, которая передает поступающие пакеты с заданной скоростью. Способ указания скоростей и других величин в утилите tc несколько отличается от общепринятого, поэтому следующую табличку придется запомнить: Формат указания скорости в утилите tc Заменить стандартную корневую дисциплину на любую бесклассовую совсем несложно, но на таком коне далеко не уедешь. Для создания разветвленной системы управления трафиком нужны классовые дисциплины, классы, фильтры и целое дерево дисциплин. Чтобы настроить все это, может понадобиться не один десяток команд. Мы рассмотрим несколько вводных примеров, перед тем как перейти к обсуждению дисциплины HTB. Пример дерева дисциплинКлассовая дисциплина prio предназначена для классификации трафика с помощью фильтров или приоритезации. По умолчанию prio содержит три класса, в каждом из которых находится обычная дисциплина FIFO. Когда сетевая карта обращается за очередным пакетом, проверяется класс :1. Если он не содержит пакетов, проверяется класс :2 и только в последнюю очередь - :3. Получается, что пакеты класса :1 получают наивысший приоритет, а :3 - наименьший. Решение о том, в какой класс направить трафик, дисциплина prio принимает на основе поля TOS сетевого пакета. Подключим дисциплину prio в качестве корневой и назначим ей имя (дескриптор) «1:0»:
Результат этой команды: дисциплина prio, подключенная в качестве корня, и три класса (1:1, 1:2 и 1:3) внутри нее, к каждому из которых подключена дисциплина FIFO.
Это обеспечит справедливое разделение канала между интерактивными приложениями (они имеют наивысший приоритет). Чтобы остальные приложения, такие как менеджеры закачек и torrent-клиенты (которые обычно шлют пакеты с меньшим приоритетом в поле TOS), не мешали интерактивным, ограничим для них скорость:
Такая схема будет плохо работать в реальной жизни, но для примера вполне годится. Теперь сделаем так, чтобы весь SSH-трафик имел наивысший приоритет. Для этого закрепим за корневой дисциплиной prio фильтр, который будет перенаправлять пакеты с портом назначения 22 в дисциплину класса «1:1».
Рассмотрим подробнее механизм подключения фильтров.
Это все. Мы получили разветвленную систему управления трафиком, выполнив всего пять команд. Классовая дисциплина HTBЕще в первый релиз системы Traffic Control была включена классовая дисциплина CBQ (Class-Based Queue), предназначенная для реализации сложных систем управления и ограничения трафика. CBQ завоевала большую популярность благодаря своей гибкости, но была очень сложна, запутана и обладала рядом ограничений (тут и необходимость заранее указывать максимальную пропускную способность канала, и неэффективный алгоритм шейпинга). Поэтому в скором времени появилась более эффективная и простая в использовании альтернатива под названием HTB (Hierarchical Token Bucket). Классовая дисциплина HTB предназначена для разделения полосы пропускания между различными видами трафика, каждому из которых может быть предоставлена полоса гарантированной ширины. Она не обладает гибкостью CBQ, но гораздо более проста в настройке и лишена ее недостатков. Именно на HTB сегодня принято строить сложные и эффективные системы ограничения трафика. Рассмотрим применение HTB на примере, представленном в начале статьи, но более усложненном. Допустим, у нас есть шлюз на Linux, интерфейс eth1 которого смотрит наружу, а eth0 - во внутреннюю сеть. Ширина канала - 100 Мбит. Задача: разделить канал между сотрудниками компании так, чтобы директор и сотрудники IT-отдела могли выходить в интернет без скоростных ограничений, маркетологи получили ограничение в 2 Мбит/c каждый, менеджеры - 1 Мбит/c, секретари - 512 Кбит/c, а все остальные - 256 Кбит/c. Мы же создадим симбиоз этих двух систем, когда трафик сначала будет разбиваться на подклассы, соответствующие подсетям, а уже потом на отдельные классы для каждого пользователя. Для начала создадим работоспособную систему, основанную только на втором варианте решения задачи. Подключим дисциплину HTB в качестве корневой:
Опция «default 15» говорит о том, что весь неклассифицированный фильтрами трафик должен быть обработан с помощью дисциплин класса «1:15». Создадим корневой класс, под который будет попадать весь трафик (это нужно для реализации заимствования):
Создадим в нем пять подклассов для пяти наших подсетей. Директору и IT-отделу выделим 30-мегабитный канал с возможностью его расширения (заимствования) вплоть до 100 Мбит в случаях, когда остальные каналы не заняты:
Для маркетологов выделим 20-мегабитный канал:
Менеджерам – 10 Мбит/с:
Секретарям – 5 Мбит/с:
И – 40 Мбит/с на всех остальных:
По умолчанию к вновь созданным классам подключены дисциплины, реализующие очередь FIFO. Это нам не подходит. Чтобы канал равномерно распределялся между всеми участниками подсети, мы должны подключить к ним дисциплину sfq:
Теперь подключим фильтры, которые будут классифицировать трафик:
Для «всех остальных» фильтр не нужен, потому как мы уже указали дефолтовый класс неклассифицированного трафика. Все, система будет работать, но не обеспечит жесткого ограничения для каждого пользователя (если, например, в определенный момент времени интернетом будет пользоваться только один менеджер, ему достанутся все 10 Мбит, отведенные для всех менеджеров). Жесткое ограничение можно реализовать, если вместо дисциплин подключить к классам другие классы HTB, по одному на каждого пользователя, и создать соответствующие фильтры. Для примера, установим ограничение в 256 Кбит/с для пользователя, находящегося в подсети «все остальные». Сначала добавим к «классу-подсети» новый «класс-пользователь»:
А затем фильтр:
Подключать к классу дисциплину нет необходимости, так как по умолчанию к нему уже подключена дисциплина FIFO. Подобные команды придется выполнить в отношении каждого пользователя, не забывая давать им уникальные дескрипторы класса. При желании все это нетрудно упаковать в простой скрипт, который будет проходить по списку IP-адресов и выполнять связку команд для каждого адреса. Наиболее используемые дисциплины
|