Построение высокопроизводительного шейпера на FreeBSD + dummynet

У любого провайдера рано или поздно появляеться необходимость настраивать шейпер. У некоторых провайдеров это просто полисеры для абонентов (если внешний канал резиновый), а у других это достаточно сложный шейпер, который делит канал на основе приоритетов.
Однажды мне потребовалось создать шейпер на 200-300 мегабит, 50k pps и 15k пользователей.
Начнем с железа. Особо важным моментом при построении роутеров являеться выбор сетевого адаптера, на мой взгляд в настоящее время самым оптимальным вариантом являеться сетевухи Intel серии PRO/1000 и интерфейсом PCI-X или PCI-E, причем экономия на сетевухах, на мой взгляд, в таких местах абсолютно неуместна.
Операционная система: мне нравится FreeBSD, для сервера, думаю, она самая оптимальная.
Шейпер: сначала я хотел заюзать ALTQ, но в процессе исследования я очень быстро понял что счаться при таком варианте не видать (ну не для этого ALTQ предназначен, хотя в линуксе люди умудряются на tc стоить относительно большие шейперы, судя по всему это от отсутствия выбора). Поэтому выбор пал на dummynet.
Как известно при высоком pps на роутере надо хорошо оптимизировать фаервол, на предмет количества правил, которые будет проходить пакет, и вариант создать на каждого пользователя правило тут явно неуместен. И тут я стал думать, как-же это все красиво построить, да еще так, чтобы было легко синхронизировать правила с биллингом. В результате было написано 3 версии скриптов синхронизации, 3я версия оказалась, на мой взгляд, достаточно удачной, но об этом позже.
Начнем с построения рулесета. Сразу договоримся, что сетевуха em0 у нас смотрит на роутер в интернет, а em1 на роутер в сторону абонентов.
во первых нам необходимо иметь возможность отключить пользователю доступ в глобальную сеть. Во вторых нам необходимо пользователю резать скорость согласно тарифному плану(так-же желательно иметь возможность некоторым пользователям устанавливать скорость отличную от тарифного плана), и наконец нам нужно чтобы безлимитчики, которые качают терабайты вареза и порно не мешали честным пользователям на тарифах по трафику.
Начнем с блокировок: создадим в фаерволе таблицу в которой будет находиться список разрешенных пользователей:

ipfw add 501 deny ip from not table\(15\) to any out recv em1 xmit em0

Таким образом мы закрываем доступ всем исходящем пакетам от абонента
Если у Вас за шейпером стоит NAT то обычно этого достаточно, если абоненты имеют внешние IP адреса, то будет не лишнем добавить строку

ipfw add 502 deny ip from any to not table\(15\) out recv em0 xmit em1

таким образом мы отрежем абонентам входящий трафик.
Теперь нам для открытия доступа абоненту достаточно добавить его в таблицу 15, для закрытия – удалить из таблицы
Режем скорость согласно тарифному плану:
Для каждого тарифного плана создадим 2 пайпа с нужной скоростью, один на вход, второй на выход, и укажем у входящего dst-mask 0xffffffff а у исходящего src-mask 0xffffffff

ipfw pipe 212 config bw 1152Kbit/s mask src-ip 0xffffffff noerror
ipfw pipe 213 config bw 1152Kbit/s mask dst-ip 0xffffffff noerror

и добавим такие правила в фаервол:

ipfw add 1001 pipe tablearg ip from table\(12\) to any out recv em1 xmit em0
ipfw add 1002 pipe tablearg ip from any to table\(13\) out recv em0 xmit em1
теперь нам надо добавить в таблицу 12 и 13 всех пользователей указав им в качестве аргумента номер пайпа их тарифного плана, например:
ipfw table 12 add 10.0.172.138/32 212
ipfw table 13 add 10.0.172.138/32 213
при этом у каждого пользователя этого тарифного плана будет скорость резаться независимо от других пользователей.
Но, как обычно бывает, сумма скоростей всех пользователей у нас намного больше чем пропускная способность наших каналов, поэтому надо-бы сделать чтобы когда скорости не хватает пользователи “поджимались”, для этого существует такая замечательная шиука как WFQ (честные взвешенные очереди).
Их смысл заключается в том, что ими можно разделить полосу поровну между всеми членами этого пайпа, или можно и не поровну, если разным юзерам задать не равные “веса”, но, на мой взгляд, в ISP это лишнее. Поэтому делить будем поровну.
В связи с чем у нас появляется понятие “классов”, классами я называю группу пользователей, которые разделяют общую полосу.
создаем класс следующим образом:
ipfw pipe 121 config bw 260Mbit/s noerror
ipfw pipe 122 config bw 260Mbit/s noerror
ipfw queue 121 config pipe 121 weight 30 mask src-ip 0xffffffff noerror
ipfw queue 122 config pipe 122 weight 30 mask dst-ip 0xffffffff noerror

и добавляем правила фаервола:

ipfw add 1010 queue tablearg ip from table\(10\) to any out recv em1 xmit em0
ipfw add 1011 queue tablearg ip from any to table\(11\) out recv em0 xmit em1

и теперь мы можем добавить в таблицу 10 и 11 записи о пользователях, и они будут разделять общую полосу соответствующего класса.
Я это использую для того, чтобы разделить пользователей на тарифных планах по трафику, безлимитных и пользователей, которые много качают и мешают другим на безлимитных тарифах.
в результате у нас получается очень короткий, но очень эффективный рулесет:

00501 deny ip from not table(15) to any out recv em1 xmit em0
01001 pipe tablearg ip from table(12) to any out recv em1 xmit em0
01002 pipe tablearg ip from any to table(13) out recv em0 xmit em1
01010 queue tablearg ip from table(10) to any out recv em1 xmit em0
01011 queue tablearg ip from any to table(11) out recv em0 xmit em1

Думаю с фаерволом разобрались. Теперь перейдем к управлению этим хозяйством.
Совершенно очевидно что в ручную тарифами рулить неудобно, юзерами тем более.
Для этого я разработал специальный скрипт – генератор фаервола.
На вход ему подается файл, описывающий конфигурацию шейпера, и пользователей.
Начнем с описания классов:
class 1 bw 65Mbit userbw 8192Kb name planTraf
class 2 bw 160Mbit name planUlim
class 3 bw 80Mbit name planBadUlim

Тут мы создали 3 класса:
1. Лимитчики, им выделили 65 мегабит, и указали скорость пользователя 8 мегабит (т.е. у нас один пользователь не сможет занять больше, если иное не указано в описании тарифного плана, об этом ниже)
2. Безлимитчики, им выделили 160 мегабит, лимитов на пользователя мы тут не ставим, они будут в тарифных планах
3. Еще одни безлимитчики – которые много качают имешают другим им дадим 80 мегабит.
Дальше описываем тарифные планы:
plan 12 userbw 32Kbit
plan 13 userbw 64Kbit
plan 14 userbw 96Kbit
plan 16 userbw 384Kbit
plan 26 userbw 256Kbit
plan 27 userbw 512Kbit
plan 5 userbw 1024Kbit
plan 31 userbw 2048Kbit
Тут, думаю, все понятно, указываем лимит скорости на пользователя. номера тарифов нужны обязательно, мне удобно брать их из биллинга.
Теперь нам небходимо написать список пользователей, кому нужно разрешить выход в глобальную сеть, пишем их так
open 10.1.12.227
open 10.2.13.228
open 10.3.14.229
эти пользователи появятся в таблице 15, а которых тут не перечислено, будут удалены из таблицы
И наконец описание пользователей:

user 12133 ip4 10.0.172.138 class 3 plan 11
user 12170 ip4 10.0.172.139 class 2 plan 17
user 12270 ip4 10.0.172.140 class 1
user 12177 ip4 10.0.172.141 class 2 plan 17 bw 1Mbit
user 12178 ip4 10.0.172.142 class 2 bw 1Mbit

Тут мы создаем 5 пользователей, указываем их номера (они нужны для создания пайпов, если задана персональная скорость, у меня это номер пользователя по биллингу) IPv4 адреса (IPv6 думаю скоро прикручу), их класс(обязательно), тарифный план(не обязательно) и скорость(тоже не обязательно).
Скорость пользователя выбирается и класса, затем она переопределяется тарифным планом, затем скоростью указанной у пользователя. Однако не стоит злоупотреблять указанием скоростей у пользователей, лучше их указывать у классов или тарифов, т.к. при большом количестве пайпов (несколько десятков тысяч) есть шанс получить нехватку памяти ядра, а это очень-очень плохо.
Вот и все, теперь перейдем к скрипту.
Пользоваться скриптом достаточно просто, первый раз его необходимо запускать вот так, он перед настройкой всего предварительно убивает пайпы и очереди, на всякий случай:
cat config.txt | gencfg.pl
А потом:
cat config.txt | gencfg.pl -t
Тут он ничего не убивает, а просто их “мягко” переконфигуривает, у меня он выполняеться каждые 10 минут. Он так-же “мягко” синхронизирует таблицы. “Мягко” это значит что скрипт строит дифф из таблиц ipfw и данными из конфига и изменяет только несовпадающие данные. Так-же он при этом переконфигурирует все пайпы.
Вот и сам скрипт: gencfg.pl

И наконец надо не забыть сделать sysctl net.inet.ip.fw.one_pass=0
Да, кстати, тюнинг ядра для больших нагрузок обязателен, но об этом есть куча статей в интернете, может и я когда-нибудь напишу свою.

Сейчас этот шейпер в пике пропускает 350 мегабит 50k pps при этом его загрузка доходит до 80%. Когда станет не хватать, придется разрабатывать механизм балансировки нагрузки между несколькими. Тогда напишу еще одну статью.
Источник http://viruzzz.org/2009/03/03/highperformanceshaper/

Запись опубликована в рубрике *Lan&Wan, *Unix,*Linux, FreeBSD. Добавьте в закладки постоянную ссылку.

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Я не спамер This plugin created by Alexei91