Статическая маршрутизация в Linux. Iproute2. Часть 2::Журнал СА 6.2003
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Архив номеров
Контакты
   

  Опросы
  Статьи

Электронный документооборот  

5 способов повысить безопасность электронной подписи

Область применения технологий электронной подписи с каждым годом расширяется. Все больше задач

 Читать далее...

Рынок труда  

Системные администраторы по-прежнему востребованы и незаменимы

Системные администраторы, практически, есть везде. Порой их не видно и не слышно,

 Читать далее...

Учебные центры  

Карьерные мечты нужно воплощать! А мы поможем

Школа Bell Integrator открывает свои двери для всех, кто хочет освоить перспективную

 Читать далее...

Гость номера  

Дмитрий Галов: «Нельзя сказать, что люди становятся доверчивее, скорее эволюционирует ландшафт киберугроз»

Использование мобильных устройств растет. А вместе с ними быстро растет количество мобильных

 Читать далее...

Прошу слова  

Твердая рука в бархатной перчатке: принципы soft skills

Лауреат Нобелевской премии, специалист по рынку труда, профессор Лондонской школы экономики Кристофер

 Читать далее...

1001 и 1 книга  
19.03.2018г.
Просмотров: 9956
Комментарии: 0
Потоковая обработка данных

 Читать далее...

19.03.2018г.
Просмотров: 8163
Комментарии: 0
Релевантный поиск с использованием Elasticsearch и Solr

 Читать далее...

19.03.2018г.
Просмотров: 8264
Комментарии: 0
Конкурентное программирование на SCALA

 Читать далее...

19.03.2018г.
Просмотров: 5232
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 5919
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

Друзья сайта  

 Статическая маршрутизация в Linux. Iproute2. Часть 2

Архив номеров / 2003 / Выпуск №6 (7) / Статическая маршрутизация в Linux. Iproute2. Часть 2

Рубрика: Администрирование /  Продукты и решения

ВСЕВОЛОД СТАХОВ

Статическая маршрутизация в Linux. Iproute2

Часть 2

Во второй части описаны принципы управления сетевым трафиком посредством очередей. Эта тема зачастую не упоминается в руководствах, но на самом деле с помощью очередей сетевых пакетов можно выполнять широкий круг исключительно полезных задач. Приведу несколько примеров, которые часто встречаются на практике. Итак, очереди способны контролировать скорость передачи пакетов, ограничивая нежелательный сетевой трафик по скорости (позволяет избежать выход из строя сервера или отдельных демонов в результате DoS- и даже DDoS-атак). Позволяет осуществлять распределение нагрузки между несколькими сетевыми интерфейсами. С помощью очередей также можно добиться существенного увеличения производительности сети в целом при помощи разделения различных видов трафика (например, интерактивные данные должны обрабатываться быстрее) на основе поля ToS (type of service – тип услуг). Iproute2 также дает возможность ограничения SYN-flood и ICMP-dDoS атак. Кроме этого, можно устанавливать свой предел скорости на основе различных фильтров.

Я начну с некоторых пояснений терминологии. Что есть очередь в данном контексте? Очередь – это механизм, позволяющий управлять приемом и передачей пакетов. Очередь является физическим объектом в памяти и содержит пакеты, поступившие на сетевой интерфейс. Чтобы понять принцип работы очередей (а именно возможности ограничения скорости), рассмотрим такой пример: пакет поступает в пустую очередь и передается на обработку, следующий пакет поступает в очередь и ждет обработки первого пакета (здесь под словом обработка подразумевается доставка пакета ядром на сокет-приемник), если в очередь поступает много пакетов, то они не успевают обрабатываться, и очередь растет. Если размер очереди ограничен (а он ограничен всегда), то следующие пакеты не попадают в очередь, а просто отбрасываются. Таким образом, регулируя длину очереди и зная время обработки одного пакета, можно вычислить наибольшую скорость передачи пакетов. Замечу, что утилита tc пакета iproute2, выполняющая контроль над очередями, осуществляет преобразование скорость –> длина очереди автоматически, и вам необходимо указывать не непосредственно длину очереди, а лимит скорости в формате:

  • mbit – мегабиты в секунду;
  • kbit – килобиты в секунду;
  • mbps – мегабайты в секунду;
  • kbps – килобайты в секунду.

На деле реализация очередей несколько отличается от вышеприведенной, т.к. отбрасывание пакетов из конца очереди приводит к неприятным последствиям для TCP-протокола: например, когда сообщение потеряно, приложение-отправитель может рассматривать это как сигнал о том, что оно посылает пакеты слишком быстро. TCP реагирует на такой сигнал замедлением отправки сообщений. Но когда очередь полна, то часто несколько сообщений отбрасываются друг за другом – в результате целый ряд приложений решает замедлить передачу. После этого приложения зондируют сеть для определения ее загруженности и буквально через несколько секунд возобновляют передачу с прежним темпом, что опять приводит к перегрузке. Поэтому обычно очередь просто отбрасывает лишние пакеты от различных источников (обычно используется алгоритм генерации случайных чисел), если за определенный промежуток времени её размер превзошёл некий лимит. Такой механизм называется «случайное раннее обнаружение» (Random Early Detection, RED).

Вторым краеугольным камнем системы управления трафиком iproute2 является многополосность очередей. Представим себе, что у сетевого интерфейса 2 независимые очереди, имеющие различный приоритет. То есть пакеты, поступившие в очередь 2, будут обрабатываться, только если в очереди 1 пакетов, ждущих обработки, нет. Что же дает такая схема? С помощью механизма полос можно выделять трафик, требующий быстрой обработки, и помещать его в 1-ю полосу, в то время как остальной трафик, не требующий особенно высокой скорости (например, ftp, smtp, pop) помещается в полосу с меньшим приоритетом (большим порядковым номером). Нечто подобное предлагают сети ATM или Frame Relay, но iproute2 позволяет организовать подобное поведение на любом интерфейсе. Необходимость в такой системе возникает при передаче интерактивных данных, например, видео или голосовых (ip-телефония). Существует следующий подход к выделению интерактивного трафика: поле ToS – метка, присваиваемая пакету с помощью netfilter (--tos) или присваиваемая непосредственно сетевой службой (ftp, smtp...).

Поле ToS является ранней попыткой реализации сервиса QoS, обеспечивающей достаточно небольшой набор значений, приведу таблицу наиболее популярных протоколов и значения ToS для них:

  • минимальная задержка – telnet, ftp (команды), ssh, smtp (команды), DNS (udp);
  • максимальная полоса пропускания – ftp (данные), smtp (данные);
  • максимальная надежность;
  • минимальная стоимость.

В настоящее время в основном используются значения «минимальная задержка» и «максимальная полоса пропускания» (хотя я не исключаю других вариантов).

Очереди, имеющие возможность фильтрации трафика по полосам, называют CBQ, (Class-Based Queuing – очередь, классифицирующая пакеты), хотя это и не совсем правильно, т.к. существует отдельный тип очереди cbq, поэтому очереди такого типа я в дальнейшем буду обозначать как classfull. Команда tc имеет возможность управления множеством типов очередей, поэтому я бы хотел несколько подробнее остановиться на описании синтаксиса этой команды (тем более, что зачастую длинные цепочки команд tc зачастую выглядят весьма устрашающе).

Для корректной работы traffic control необходимо включить поддержку нужных типов очередей на уровне ядра. При конфигурации ядра необходимо зайти на страницу Networking Options, войти в пункт QoS and/or fair queueing, после чего в выбранном подразделе выделить галочкой опцию QoS and/or fair queueing. В открывшемся списке параметров отметьте нужные типы очередей и опции traffic control. В принципе можно включить все опции, т.к. это может пригодиться в будущем при расширении схемы контроля трафика.

Как и ip, команда tc может управлять несколькими сетевыми объектами. Их, собственно, три:

  • qdisc – управление очередями;
  • class – управление определёнными частями очереди, например, полосами;
  • filters – управление фильтрами (фильтр определяет, в какую полосу очереди попадет тот или иной пакет на основании определенных параметров пакета, например, протокола, порта, метки, и т. д.).

Синтаксис команд следующий (нагло содрано мною из мануала):

tc  qdisc [add | change | replace | del | link] dev DEV [parent qdisc-id | root] [handle qdisc-id] queue-type [qdisc specific parameters]

tc  class [add | change | replace | del] dev DEV parent qdisc-id [classid class-id] queue-type [qdisc specific parameters]

tc  filter [add | change | replace | del] dev DEV [parent qdisc-id | root] protocol protocol prio priority

filter-type [filtertype specific parameters] flowid flow-id

  • tc qdisc show [ dev DEV ]показ очередей интерфейса;
  • tc class show dev DEV – показ классов сетевого устройства;
  • tc filter show dev DEV – показ фильтров интерфейса DEV. 

В основном отметьте последние 3 команды, т.к. назначение первых будет подробно описано далее на конкретных примерах.

Параметров у различных объектов очень много, чтобы описать их все, поэтому сейчас я бы хотел пояснить значение параметров parent qdisc-id и handle (classid) qdisc-id. На самом деле при добавлении новой очереди к очереди-контейнеру (classfull-очереди, понятие будет рассмотрено далее) производится построение дерева объектов очередей (на самом деле дерево содержится не в ядре, которое ничего не подозревает о наличии подочередей, а выбор подочереди для извлечения из неё пакета осуществляет очередь-контейнер). Например:

tc qdisc add dev eth0 root handle 1: classful-queue [parameters]

Дерево выглядит так:

Рисунок 1

Добавляем далее:

tc qdisc add dev eth0 parent 1: handle 10: queue-type [parameters]

Рисунок 2

Для примера добавлю ещё одну очередь:

tc qdisc add dev eth0 parent 1: handle 20: queue-type [parameters]

Рисунок 3

Как происходит извлечение пакета из очереди ядром? Ядро запрашивает очередь самого верхнего уровня (root) о необходимости извлечения пакета. Очередь верхнего уровня проверяет очереди нижнего уровня и выбирает ту, которая подходит по определенным параметрам (это зависит от типа очереди-контейнера и её настроек), после осуществления выбора очередь верхнего уровня извлекает пакет и передает его выше (очереди, находящейся выше по иерархии, или ядру, если это очередь root). Таким образом, сама иерархия построена по принципу минимальных знаний о нижних уровнях. Каждый уровень знает о существовании только одного нижнего уровня и осуществляет выбор только в рамках одного уровня. Так примерно можно представить схему уровней очередей:

Рисунок 4

По моему мнению, построение дерева очередей – предмет большинства ошибок в скриптах, т.к. очень легко перепутать идентификаторы очередей. Для тех, кто собрался продумывать систему контроля трафика, необходимо заранее спланировать (например, нарисовать схему на бумаге) будущее дерево, дабы избежать досадных промахов. Особенно важным становится этот аспект при построении сложных очередей, состоящих из нескольких простых очередей. Замена (удаление) очередей производится командой tc qdisc change (del) с указанием значений parent и handle:

# tc qdisc del dev eth0 root handle 1: queue-type

После некоторого чисто теоретического вступления настало время поговорить о типах очередей и о применении их на практике. Таковых типов существует 6. Среди них 3 типа являются classfull (возможность разделять трафик по полосам): prio, cbq, htb, а 3 являются обычными (classless): tbf, pfifo, sfq. Classless-очереди являются более простыми объектами, чем classfull, и способны лишь устанавливать определённые ограничения на передачу трафика. Основное отличие 2 семейств очередей в том, что classfull-очереди содержат внутри себя classless-«подочереди», отличающиеся приоритетом. Таким образом, classfull-очереди являются контейнерными объектами и позволяют выполнять разделение трафика по другим очередям (в терминологии traffic control – классами). Для начала я рассмотрю classless-очереди, а потом перейду к более сложным (но, несомненно, более интересным) classfull-очередям.

TBF(token bucket filter) – простой тип очереди, не выполняющий разделения пакетов, который удерживает скорость передачи пакетов на примерно постоянном уровне (меньшем, чем реальная скорость интерфейса). При этом, если скорость передачи меньше заданного значения, то пакеты кладутся в очередь, пока не будет достигнут определённый размер очереди, который затем передается на обработку (но происходит некоторая задержка данных, т.к. происходит ожидание необходимого количества пакетов для поддержания постоянной скорости передачи). Если же скорость превышает заданную, то «лишние» пакеты просто отбрасываются. Такой тип очереди нельзя порекомендовать для сетей, где очень резко меняется загруженность, т.к. могут возникнуть неоправданные задержки при слабой загрузке сети и снижение пропускной способности интерфейса при большой загрузке. Основные параметры tbf:

  • limit или latency – число байт, которые могут быть помещены в очередь для ожидания, фактически – максимальное время, которое может провести пакет в очереди (чем больше limit, тем сильнее увеличивается задержка данных при низкой загрузке интерфейса);
  • burst (buffer или maxburst) – длина буфера очереди в байтах, чем больше заданная скорость передачи, тем больше должен быть буфер данных, например, для скорости 10 mbit необходим буфер не менее 10 Кб, а для скорости 256 kbit – не менее 1 Кб. В общем, рассчитывается как rate_in_bytes/100. Для 100 Мбит – 104857600/800 = 1310172 байт. При указании этого параметра учтите, что все указанные значения – минимальные, ничто не мешает вам увеличить этот параметр для гарантии того, что пакеты не будут отбрасываться очередью по причине переполнения последней. Фактически размер буфера ограничен только размером физической памяти системы (т.к. буфер очереди обязан находиться в памяти);
  • mpu – минимальный размер пакета для помещения в очередь, пакеты меньшей длины отбрасываются, для сетей Ethernet каждый пакет должен быть больше 64 байт;
  • rate – заданый уровень скорости;
  • peakrate – максимально возможная скорость передачи пакетов из очереди в интерфейс, по умолчанию 1 Мбит/с.

С одной стороны, tbf снижает в общем пропускную способность интерфейса, но существуют ситуации, когда это оказывается полезным (хотя, честно говоря, я с такими ситуациями не встречался, но, может быть, кому-то это окажется полезным). Например, имеется ADSL-модем, который имеет очень большую очередь на upload. Поэтому при попытке передачи данных в сеть, скорость загрузки данных сильно падает, т.к. на модеме заполняется длинная очередь, контролировать которую не представляется возможным. Одним из вариантов решения проблемы является размещение tbf-очереди на Linux-маршрутизаторе и установке максимального времени ожидания пакета в очереди. Пример конфигурации:

# tc qdisc add dev ppp0 root tbf rate 220kbit latency 50ms burst 1500

Скорость rate выбрана исходя из максимальной скорости интерфейса (в данном примере 256 Кбит/с) минус несколько процентов. Размер очереди burst выбран также соответственно скорости. Особо отметьте параметр latency: чем меньше этот параметр, тем меньше пакеты будут задерживаться в очереди – меньшая задержка увеличивает интерактивность данных. Заметьте, tbf не спасет вас от флуда (см. замечание далее в описании sfq-очередей).

Следующий тип classless-очереди – sfq (stochastic fairness queueing – очередь равномерного случайного распределения пакетов). Алгоритм работы этого типа таков: данные, поступающие в очередь, разделяются на достаточно большое количество «виртуальных» подочередей (виртуальных, т.к. в памяти существует одна очередь, но она представляется совокупностью многих подочередей), из подочередей данные извлекаются по очереди. Т.е. это напоминает сети Token Ring с передачей маркера по кольцу. Подочередь, получившая маркер, передает один пакет данных, а маркер переходит к следующей подочереди. Случайность передачи обеспечивается тем, что размер подочередей обычно не фиксируется жестко и данные могут попадать в различные подочереди. Такая очередь весьма полезна при сильной загрузке сетевого интерфейса многими клиентами. SFQ не позволяет захватить одному клиенту все ресурсы обработки пакетов, поэтому обеспечивается примерно одинаковая скорость поступления данных от различных клиентов. Учтите, это не спасет от DoS или банального флуда, т.к. любая очередь управляет скоростью обработки данных, т.е. фактически скоростью передачи ответных пакетов, а при наводнении пакетами интерфейса, последний не сможет принимать другие пакеты, и очередь в этом случае не помогает, т.к. нарушается работа самого сетевого устройства. Таким образом, sfq-очереди эффективны для регулирования скорости передачи пакетов различным клиентам. Никогда не забывайте об этом факте (хотя далее будет приведен пример, позволяющий «спасти» систему от DoS-атак при помощи очередей, но при этом, если наводняется сам интерфейс, то работать он все равно не будет). Параметры sfq-очередей:

  • pertrub – число секунд, после которого происходит перерасчет длины подочередей, по умолчанию – 0, т.е. переконфигурирования не происходит, рекомендуется устанавливать этот параметр, равным примерно 10 секундам;
  • quantum – число байт, которые может передать подочередь, получившая маркер, значение по умолчанию – MTU интерфейса – подходит для большинства случаев, т.к. это гарантирует, что очередь сможет передать хотя бы один пакет.

Sfq-очереди удобно применять для балансирования нагрузки сервера:

# tc qdisc add dev eth0 root sfq pertrub 10

Наконец, самый простой тип classless-очереди – pfifo (лимит пакетов) и bfifo (лимит байт). Эти очереди являются простыми очередями определённой длины, не выполняющими никаких действий над поступающими в них пакетами. Единственный параметр таких очередей – limit, означающий размер очереди в пакетах (pfifo) и в байтах (bfifo).

# tc qdisc add dev eth0 root pfifo limit 500

Создает очередь длиной в 500 пакетов.

Прежде чем приступать к чтению следующего раздела, полезно вспомнить о принципах построения дерева объектов очередей, т.к. далее будет рассказываться о classfull-очередях, при создании которых необходимо добавлять очереди (или классы) в очереди-контейнеры.

Итак, classfull-очереди. Как работает такая очередь? Гм... строго говоря, classfull-очередь – это обычно некий контейнер, содержащий другие очереди. Решение, куда отправить тот или иной пакет, принимается на основании фильтров (о них будет рассказано далее) или на основании приоритета очереди-потомка. Решение, откуда отправлять пакет на обработку верхнему уровню, принимается на основании приоритета и настроек очереди-контейнера. Очереди же внутри контейнера не знают о существовании родителя непосредственно, т.е. они продолжают вести себя обычным образом, но все операции осуществляются не с ядром, а с очередью верхнего уровня. Итак, далее я бы хотел рассказать о некоторых типах classfull-очередей, которые являются наиболее полезными на практике.

Первый тип – prio-очереди. Очередь такого типа может разделять трафик между тремя полосами, которые являются очередями любого типа. Разделение осуществляется на основании фильтров (см. далее). Каждая подочередь, входящая в prio, имеет свой приоритет, определяемый значением handle. При этом больший приоритет имеет та подочередь, которая относится к классу 1:. Т.е. при извлечении пакета из очереди вначале исследуется подочередь с большим приоритетом, если в последней нет пакетов для обработки, то выбирается очередь с более низким приоритетом и т. д. Каким же образом трафик разделяется между полосами? Есть два пути: использование фильтров и использование поля TOS. На основании поля TOS различный вид трафика помещается в подочереди с разным приоритетом (отметьте, что очереди с меньшим номером обладают большим приоритетом):

TOS                                                                      № полосы

Максимальная надежность                             –      1

Минимальная цена                                            –      2

Максимальная пропускная способность     –      2

Минимальная задержка (интерактивный)   –      1

Среди параметров prio-очереди можно отметить только 2: bands – число полос (по умолчанию – 3) и priomap – карта распределения трафика в зависимости от поля TOS. Эти параметры редко приходится менять, т.к. для сложных ситуаций лучше применять более сложные очереди.

Очередь prio очень полезна, если необходимо повысить общую пропускную способность интерфейса. Разделение на основе TOS особенно выгодно в сетях Unix, т.к. популярные сервера и приложения умеют грамотно устанавливать флаг TOS (например, SSH устанавливает TOS «Минимальная задержка», а scp – «Максимальная пропускная способность»). К сожалению, в сетях, где имеются клиенты M$, ситуация усложняется, т.к. по неизвестной причине M$ ставит свои ОС в привилегированные условия, устанавливая TOS «Интерактивный» по умолчанию. Prio еще полезна тем, что может содержать очереди различного типа, например, tbf или sfq. Это её основное отличие от очереди pfifo_fast, которая создает три полосы pfifo по умолчанию и не позволяет менять тип подочередей. Поэтому тип pfifo_fast относится к classless, хотя осуществляет разделение трафика, но подочереди являются неотделимой частью самой pfifo_fast. Приведу пример создания prio-очереди, содержащей 2 подочереди sfq и одну tbf:

Псевдо-дерево будет выглядеть так:

Рисунок 5

# tc qdisc add dev eth0 root handle 1: prio

Заметьте, что 1: добавляет автоматически три полосы – 1:1 1:2 1:3, т.к. по умолчанию количество полос равно трем.

# tc qdisc add dev eth0 parent 1:1 handle 10: sfq pertrub 10

# tc qdisc add dev eth0 parent 1:2 handle 20: tbf rate 1mbit buffer 15000 latency 10ms

# tc qdisc add dev eth0 parent 1:3 handle 30: sfq pertrub 10

Если вы попробуете передавать данные различного типа, например ssh и ftp, то заметите, что интерактивный трафик поступает в 1-ю и 2-ю полосы, а неинтерактивный – в 3-ю полосу. Если prio является достаточно простым типом очереди, то следующие типы не обладают этим свойством.

Прежде всего, это тип cbq (classfull based queueing). Этот тип позволяет добавлять множество классов и осуществлять весьма нетривиальную обработку трафика. Распределение трафика по классам осуществляется посредством фильтров. Но cbq-очереди слишком сложны и не всегда делают то, что от них требуется. По этой причине я буду рассматривать не cbq-очереди, а их ближайших родственников – htb (hierarchial token bucket), которые имеют те же возможности, что и cbq-очереди, но лишены недостатков последних – излишней громоздкости синтаксиса и непрозрачности (единственным недостатком является то, что для их использования необходимо патчить 2.2 ядро, но 2.4.20 ядро имеет встроенную поддержку этого типа очередей).

Итак, позвольте представить, htb-очереди. Для начала убедитесь, что вы имеете ядро версии 2.4.20 и выше. Если такового не имеется, необходимо сходить на сайт проекта htb – http://luxik.cdi.cz/~devik/qos/htb и скачать пакет htb3, содержащий патчи для ядер 2.2 и 2.4, а также исправленную версию tc. Находится этот пакет по адресу http://luxik.cdi.cz/~devik/qos/htb/v3/htb3.6-020525.tgz. Что же позволяют делать htb-очереди? Они позволяют регулировать полосу пропускания трафика и фактически создавать на базе одного интерфейса несколько более медленных, и разделять трафик по полосам, отличающимся по скорости передачи. На самом деле htb-очередь – очень полезная вещь.

Допустим, нам необходимо повысить полосу пропускания www- и smtp-трафика, но понизить скорость ftp-трафика. С помощью очереди prio можно лишь менять приоритет разного рода трафика, но если передается только ftp-данные, то каким бы ни был приоритет ftp, все равно этот трафик будет передаваться с максимальной скоростью. Единственной возможностью решения такой ситуации для prio-очереди является использование в качестве полос tbf-очередей. В то же время, если необходимо организовать весьма нетривиальное распределение трафика и ограничение полос пропускания, то лучше всего использовать htb-очередь. Для упрощения понимания принципов создания htb-очередей приведу простой пример. Допустим, к серверу подключено 2 клиента (А и Б). Эти клиенты пользуются только www- и smtp-сервисами (такое упрощение сделано для наглядности, далее будут приведены примеры реальных скриптов). Имеется 10-ти мегабитный интерфейс, полосы пропускания будут выглядеть таким образом:

Клиент

Сервис

Полоса пропускания

A

smtp

2mbit

A

www

3mbit

B

all

1mbit

other

all

2mbit

Команды, реализующие эту схему, будут выглядеть так:

# tc qdisc add dev eth0 root handle 1: htb default 13

Думаю, эта команда не вызывает трудностей в понимании. Параметр default 13 означает, что по умолчанию трафик будет направляться на полосу с идентификатором 13. Трафик «по умолчанию» означает тот трафик, который не был распределён при помощи фильтров.

Теперь необходимо добавить полосы – классы. Учтите, что классы – это объекты, которые являются составными частями classfull-очередей, но сами по себе не являются очередями. Таким образом, схема составления htb-очереди выглядит примерно так:

добавление самой очереди –> добавление классов –> добавление фильтров, распределяющих трафик по классам –> добавление очередей

(обычно classless, по умолчанию pfifo), реализующих классы

tc class add dev eth0 parent 1: classid 1:1 htb rate 9mbit ceil 9mbit burst 12500

tc class add dev eth0 parent 1:1 classid 1:10 htb rate 2mbit ceil 9mbit burst 12500

tc class add dev eth0 parent 1:1 classid 1:11 htb rate 3mbit ceil 9mbit burst 12500

tc class add dev eth0 parent 1:1 classid 1:12 htb rate 1mbit ceil 9mbit burst 12500

Псевдодерево очередей будет выглядеть так:

Рисунок 6

Сделаю некоторые пояснения относительно параметров классов htb. Параметр rate означает полосу пропускания, ceil означает максимальную скорость обмена класса с родительской очередью (или классом). Также есть возможность указания приоритета каждого класса аналогично prio-очереди при помощи параметра prio (аналогично меньшее значение параметра означает больший приоритет). Существуют 2 параметра burst и cburst, регулирующие параметры буфера очереди:

  • burst – размер в байтах буфера, для Intel-машин вычисляется по формуле (speed_in_bits)/(1008); для нашего случая минимальное значение – 12500 байт;
  • cburst – означает минимальный размер данных в байтах, передаваемых родительской очереди; обычно не меньше mtu-интерфейса.

После добавления классов логичным является добавление средств, распределяющих трафик по этим классам. Таким средством являются фильтры. Вернемся к нашему простому примеру. Для него фильтры будут добавляться так:

# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src A_ip match ip dport 25 0xfff flowid 1:11

Поясню все по порядку. Директива protocol означает протокол для фильтрации, parent – очередь, c которой работает фильтр (в данном случае корневая htb), prio – приоритет данного фильтра (чем меньше значение, тем больше приоритет), u32 значит, что ведется поиск совпадений в любой части пакета, match ... – правила совпадения определенных полей пакета с заданными, если этой директивы нет, то под этот фильтр подходят любые неотфильтрованные ранее пакеты (хотя необходимо установить более низкий приоритет для этого фильтра, иначе он будет обрабатывать весь трафик, что не есть хорошо):

# tc filter add dev eth0 protocol ip parent 1:0 prio 2 flowid some_band

Директива flowid обозначает идентификатор класса, куда фильтр будет отправлять трафик.

Как правильно строить правила совпадения? При построении учтите несколько общих правил:

  • если в одной команде tc filter add встретилось несколько директив match, то они объединяются операцией «И»;
  • если необходимо создать несколько правил, объединенных «ИЛИ», то необходимо создавать эти правила в различных командах tc filter add.
  • match ip src ip_addr – поиск совпадения IP-адреса отправителя;
  • match ip dst ip_addr – поиск совпадения IP-адреса назначения (полезно при маршрутизации);
  • match ip sport | dport port_num 0xffff – поиск совпадения порта источника или порта назначения, назначение символов 0xffff – установка маски совпадения.

Существует также возможность фильтрации на основе меток netfilter. Метки ставятся с помощью iptables следующим образом:

# iptables -A FORWARD -t mangle -i eth0 -j MARK --set-mark 6

где вместо 6-ки может быть любое число до 255.

Установка фильтра выглядит несколько необычно:

# tc filter add dev eth0 ptrotocol ip parent 1:0 prio 1 handle 6 fw classid 1:1

Обратите внимание на отсутствие директивы u32, директивы handle, fw и classid (вместо flowid).

Далее будет показано на примере, как выбирать пакеты с установленными флагами SYN, ASK, выбирать протокол (TCP, UDP, ICMP), что позволяет предотвращать некоторые виды DoS-атак.

Для понимания фильтров, которые будут рассмотрены чуть позже, разъясню назначение некоторых дополнительных параметров фильтров:

  • поиск «сырых» байт – match [u32|u16|u8] 0xXX 0xMASK at where, где маска – 16-ое значение маски соответствующего формата, например u32 – 32 бита, u16 – 16 бит, u8 – 8 бит соответственно. Параметр where – обычное число, означающее количество соответствующих элементов (u32, u16, u8), отсчитанных от начала пакета. Например, фильтр match u8 0x10 0xff at 33 означает выбор поля ToS «Минимальная задержка»;
  • поиск протокола – match ip protocol 6 0xff – поиск пакетов TCP-протокола;
  • непосредственный поиск поля ToS – match ip tos 0x10 0xff;
  • поиск совпадения области роутинга:

# ip route add some_network via some_gate dev eth0 realm 2

область обозначается директивой realm, и фильтр для пакетов, попадающих в эту область (т.е. тех, что направлены в сеть some_network):

# tc filter add dev eth0 parent 1:0 protocol ip prio 1 route to 2

Если нужно выбирать пакеты, приходящие из области маршрутизации, то нужно просто заменить to на from;

  • отбор трафика, превосходящего определенный лимит скорости (очень полезно): в команде tc filter можно применять те же директивы, что и в tbf-очереди при указании особого параметра – police:
    • buffer
    • mtu
    • mpu
    • rate

    Если трафик превышает установленный лимит, то сам фильтр может выполнять определённые действия:

    • drop – отбрасывание трафика, превысившего лимит;
    • pass – пропускание такого трафика.
  • существует также возможность создания хешей фильтров (т.е. групп фильтров, которые применяются при прохождении определённых других фильтров), но это применяется лишь в случаях тысяч правил, когда обработка всех фильтров занимает значительное процессорное время. Объем этой статьи не позволяет мне рассказать и об этой возможности, поэтому при возникновении этой проблемы нужно почитать HOWTO – www.lartc.org.

# tc qdisc add dev eth0 parent 1:10 handle 20: pfifo limit 500

# tc qdisc add dev eth0 parent 1:11 handle 30: pfifo limit 500

# tc qdisc add dev eth0 parent 1:12 handle 40: pfifo limit 500

# tc qdisc add dev eth0 parent 1:13 handle 50: sfq perturb 10

Обратите внимание, что родителями этим очередям являются классы, таким образом, псевдодерево будет выглядеть так:

Рисунок 7

Еще хотел бы остановить ваше внимание на классе 1:1. На самом деле может быть несколько классов верхнего уровня, что позволяет строить весьма сложную иерархию классов.

На этом я, пожалуй, закончу теоретическое рассмотрение очередей и приведу реальный пример использования очередей для управления трафиком. В этом примере я покажу основные возможности очередей: разделение трафика и создание полос пропускания, фильтрацию на основе поля ToS, защита сервера от SYN- и ICMP-флуда, разделение трафика между несколькими сетевыми интерфейсами (туннели высокой пропускной способности, аналог port link в Cisco Catalyst). Итак, конфигурация сети:

Рисунок 8

В нашем случае имеется сервер, соединяющий две локальные сети (Server A). Он соединен через две сетевые карты со 2-м сервером (Server B) для повышения эффективности обмена данными между двумя серверами (например, резервные копии одного сервера на другом). Я не буду подробно останавливаться на механизмах маршрутизации и разграничения доступа, а для начала просто поясню, как при помощи очередей реализовать распределение трафика по двум интерфейсам. Для этого существует специальный тип очереди – teql. Сначала необходимо добавить такую очередь к двум интерфейсам (в нашем случае – eth1 и eth2):

# tc qdisc add dev eth1 root teql0

# tc qdisc add dev eth2 root teql0

После этого появляется виртуальный интерфейс – teql0. Работа с ним не отличается от работы с любым другим интерфейсом:

# ip link set dev teql0 up

– включает интерфейс.

Учтите, что подобную операцию нужно проделать на обеих машинах (Server A и Server B), а после этого необходимо настроить первоначально маршрутизацию:

Server A

# ip addr add dev eth1 10.0.0.1/31

# ip addr add dev eth2 10.0.0.3/31

# ip addr add dev teql0 10.0.0.5/31

Server B

# ip addr add dev eth1 10.0.0.2/31

# ip addr add dev eth2 10.0.0.4/31

# ip addr add dev teql0 10.0.0.6/31

При этом серверы будут общаться через виртуальную подсеть 10.0.0.0/31 и будут видеть друг друга через адреса 10.0.0.5 и 10.0.0.6 соответственно. Необходимо также отключить «отброс» пакетов серверами:

# echo 0 > /proc/sys/net/ipv4/conf/eth1/rp_filter

# echo 0 > /proc/sys/net/ipv4/conf/eth2/rp_filter

т.к. это может вызвать нежелательные циклы пакетов между серверами (каждый сервер будет отбрасывать не предназначенные ему пакеты в петлю, и эти пакеты будут гулять, пока не истечет ttl).

На этом будем считать настройку интерфейсов eth1 и eth2 законченной (если Server B настроен правильно, то причин для беспокойства нет, иначе есть смысл настроить firewall и максимальную скорость связи). Главной «оптимизации» подвергнется интерфейс eth0, ведущий в локальную сеть. Eth0 – стомегабитная сетевая карта. В нашей сети имеется 2 «особых» клиента: 192.168.2.2 и 192.168.2.3. Им необходимо выделить отдельные полосы пропускания для ftp- и smb-трафика (по 10 Мбит каждому). Кроме этого, сеть состоит из рабочих станций под управлением Unix/Linux и очень много работают с сервером по ssh (10 Мбит дополнительно). Помимо всего прочего, в сети необходимо учитывать ToS-флаги и ограничивать icmp-трафик (защита от любителей делать ping -f). Задачка не из легких, не так ли? Особенно если учесть, что мы будем устанавливать для различных полос различный приоритет...

Для начала создаем htb-очередь для выделения полос пропускания:

# tc qdisc add dev eth0 root handle 1: default 14

и сами полосы:

# tc class add dev eth0 parent 1: handle 1:1 rate 10mbit burst 150000 ceil 100mbit

# tc class add dev eth0 parent 1:1 handle 1:11 rate 10mbit burst 15000 ceil 100mbit prio 2

# tc class add dev eth0 parent 1:1 handle 1:12 rate 10mbit burst 15000 ceil 100mbit prio 2

# tc class add dev eth0 parent 1:1 handle 1:13 rate 10mbit burst 15000 ceil 100mbit prio 2

# tc class add dev eth0 parent 1:1 handle 1:14 rate 69mbit burst 100000 ceil 100mbit prio 3

Одна полоса для ICMP-трафика:

# tc class add dev eth0 parent 1:1 handle 1:15 rate 100kbit burst 1000 ceil 100mbit prio 4

Ещё одна полоса для TCP-AСK пакетов (эти пакеты должны иметь максимальный приоритет, т.к. это существенно повышает скорость загрузки данных с сервера к клиенту):

# tc class add dev eth0 parent 1:1 handle 1:16 rate 900kbit burst 2500 ceil 100mbit prio 1

Создаем фильтры:

  • создаем фильтры для особых клиентов:

Для клиента А:

# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 192.168.2.2 match ip dport 110 0xffff flowid 1:11

# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 192.168.2.2 match ip dport 138 0xfff flowid 1:11

Через 138-й порт в протоколе NetBios передаются данные.

Для клиента Б:

# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 192.168.2.3 match ip dport 110 0xffff flowid 1:12

# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 192.168.2.3 match ip dport 138 0xfffflowid 1:12

  • создаем фильтрацию ssh-трафика:

# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dport 22 0xffff flowid 1:13

  • задаем предел для ICMP и SYN-TCP трафика:

SYN:

Для начала установим правила отбора SYN-пакетов при помощи iptables (это намного проще, чем использовать сырой разбор пакетов и маскирование битов):

# iptables -A PREROUTING -i eth0 -t mangle -p tcp --syn -j MARK --set-mark 1

После этого необходимо добавить особый тип очереди – ingress, которая пропускает трафик не выше заданной скорости, при этом заметьте, что очередь не имеет размещения (всегда root), что позволяет добавлять к интерфейсу очередь ingress, не мешая htb (или любой другой очереди), подключенной к этому же интерфесу:

# tc qdisc add dev eth0 handle ffff: ingress

# tc filter add dev eth0 parent ffff: protocol ip prio 50 handle 1 fw police rate 100kbit burst 1500 mtu 9k drop flowid :1

Мы установили лимит SYN-пакетов до 320 пакетов в секунду (пакет с SYN-флагом занимает 320 бит, но 320 пакетов в секунду несколько многовато, хотя точно должно обеспечить обработку всех запросов на соединение).

    ICMP:

Тут мы имеем дело с обычным выделением полосы пропускания и фильтрацией протокола:

# tc filter add dev eth0 parent 10:0 protocol ip prio 100 u32 match ip protocol 1 0xff flowid 1:15

Значение protocol 1 соответствует ICMP.

  • добавляем фильтр TCP-AСK пакетов:

# tc filter add dev eth0 parent 1: protocol ip prio 10 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match

u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid 1:15        

Эта строчка выглядит несколько сложно, но здесь используется сырой разбор заголовков пакетов, поэтому весьма сложно понять, что к чему.

На этом настройки фильтрации заканчиваются. Приступим к добавлению очередей, реализующих классы:

  • для особых клиентов подойдут обычные pfifo-очереди:

# tc qdisc add dev eth0 parent 1:11 handle 20: pfifo limit 500

# tc qdisc add dev eth0 parent 1:12 handle 30: pfifo limit 500

для ssh-полосы лучше всего использовать sfq для равномерности распределения клиентов:

# tc qdisc add dev eth0 parent 1:13 handle 40: sfq pertrub 10

  • для остальной части подсети лучше всего использовать pfifo_fast очередь, т.к. необходимо выделять приоритетный трафик. pfifo_fast было выбрано вместо prio только по причине простоты использования (думаю, скрипт, реализующий установку всех очередей, и так способен смутить любого):

# tc qdisc add dev eth0 parent 1:14 handle 50: pfifo_fast

  • для AСK-пакетов подойдет также pfifo-очередь:

# tc qdisc add dev eth0 parent 1:15 handle 60: pfifo limit 15

Вот и все, я завершаю свой пример и рассказ о traffic control в GNU/Linux. Надеюсь, что моя статья окажется вам полезной. На этом я и прощаюсь с вами.

Приведу ряд полезных ссылок:

  1. http://www.lartc.org – Linux Advanced Routing and Traffic Control HOWTO.
  2. http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm – руководство по работе с htb-очередями (приведены графики производительности и описаны все основные параметры очередей этого типа).
  3. RFC795 – документ, посвященный использованию поля ToS.

Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru