Динамический шейпинг во FreeBSD на основе PF
Как и обещал, расскажу об устройстве собственно NAT`а на шлюзе. Но не простого NAT`а, а ещё и с шейпингом. Но не с простым шейпингом, а динамическим. То есть, при отсутствии активности одного из компутеров в интернете, весь канал будет отдаваться другим.
Отступление по поводу шейпнга вообще и PF в частности:
- Шейпить физически можно только исходящий траффик. Так как входящий физически шейпить невозможно. Даже если и ограничить скорость его получения, то канал это не отчистит. Так как пакеты на сетевуху уже пришли. Но тут нам в помощь вступает принцип действия протокола TCP. Если отправитель не будет получать уведомления о получении пакетов, то он будет отсылать их медленнее. Так вот шейпер просто, если пакеты не влазят в нужные ограничения, их дропает, ну а отправитель их соответственно начинает посылать медленнее. Но это происходит не моментально..
Отсюда вытекает то, что шейпить можно только TCP пакеты. К счастью, большинство протоколов использует именно TCP - PF, насколько я знаю, умеет шейпить только исходящий траффик, но, как мы уже выяснили, нам это не помешает
- Шейпить пакеты (делить их по юзерам) можно только на внутреннем интерфейсе, так как на внешнем интерфейсе они будут уже отначенные. PF умеет обрабатывать пакеты по пользователям, независимо от того на каком интерфейсе мы их шейпим.
- Во FreeBSD Начиная с 7.0 и в OpenBSD с 4.1 (если не ошибаюсь) ко всем правилам по умолчанию используется опция keep-state. Что она делает: кто-то изнутри отправляет пакет наружу. Пакет соответствует какому-либо разрешающему правилу с такой опцией. Ответ на этот пакет снаружи будет также попадать под это правило. Плюс такой схемы, что можно тупо закрыть все входящие соединения снаружи — нужные пакеты ходить будут. НО! В таком случае ответные пакеты на исходяшие соединения, попадающие в какую-либо исходящую очередь, будут попадать в неё же. А нам это, не нужно. Поэтому для правил «деления по юзерам» обязательно нужна опция no state. И чтобы не возникло недоразумений, советую её указаывать всегда, независимо от версии ОС.
Теперь от слов к делу. Имеется FreeBSD 7.0. Изначально PF подгружается модулем (что не есть тру для файрвола), да и нету в ядре поддержки механизма ALTQ (очереди пакетов. Именно через этот механизм работает шейпинг в PF). Так что первое, чем надо заняться, это пересобрать ядро:
- Если не установлены, то устанавливаем исходники ядра
- Идём в директорию /usr/src/sys/i386/conf
- Копируем файлик GENERIC в какой-нибудь другой. Например у меня это KERNELTUUPIC
- Редактируем его: меняем сроку ident на то что охота видеть при uname -i; дописываем следующие девайсы:
device pf device pflog device pfsync
и следующие опции:
options ALTQ options ALTQ_CBQ # Class Bases Queuing (CBQ) options ALTQ_RED # Random Early Detection (RED) options ALTQ_RIO # RED In/Out options ALTQ_HFSC # Hierarchical Packet Scheduler (HFSC) options ALTQ_PRIQ # Priority Queuing (PRIQ) options ALTQ_NOPCC # Required for SMP build
Кому скучно, может ещё и всякие лишние девайсы убрать или ещё что-нить включить 🙂
- Собираем ядро
cd /usr/src && make buildkernel KERNCONF="KERNELTUUPIC"
и ставим его
cd /usr/src && make installkernel KERNCONF="KERNELTUUPIC"
- Для работы NAT`а необходимо включить форвардинг в ядре. В /etc/rc.conf добавим
gateway_enable="YES"
Для включения форвардинга без перезагрузки можно выполнить
sysctl net.inet.ip.forwarding=1
- reboot
Теперь можно работать с PF. Для начала добавим его в автозагрузку.
Правим /etc/rc.conf:
В OpenBSD так:
pf="YES"
Во FreeBSD так:
pf_enable="YES"
Укажем где лежит конфиг файл с правилами
pf_rules="/etc/pf.conf" #можно указать другой файл
Включим логирование если охота.
(аналогично как и включение PF)
Лог PF пишет в бинарном виде. И читать его надо tcpdump`ом.
Примерно вот так
tcpdump -n -e -ttt -r /var/log/pflog
Также можно смотреть что идёт в логи на интерфейсе
tcpdump -n -e -ttt -i pflog0
.
В логи пишется только те пакеты, которые попадают в правила, у которых включено логгирование.
pflogd="YES" #OpenBSD
pflog_enable="YES" #FreeBSD
Также PF можно управлять через утилиту pfctl. Например, включить pf можно так pfctl -e, а выключить pfctl -d.
При включении через pfctl правила автоматически не загружаются. Для того, чтобы их загрузить надо выполнить команду pfctl -f /etc/pf.conf (вместо имени файла указать своё). Эта команда ещё пригодится много раз. Если в конфиг файле что-то изменили, то чтобы эти изменения вступили в силу надо их применить pfctl -f /etc/pf.conf.
Прочие команды писать не буду — их можно посмотреть в документации.
Теперь приступим собственно к составлению конфига.
Отмечу очень важное отличие pf от прочих файрволов (ipfw, iptables): в тех файрволах действует первое правило в списке под которое попадает пакет. И остальными правилами уже не обрабатывается. В pf же наоборот. Действует последнее правило. Но можно отменить дальнейшее рассмотрение правил если для правила применить параметр quick. Так что pf очень гибок в этом.
Вот как выглядит конфиг:
Макросы. Что-то типа переменных
ext_if="dc0" #собственно внешний интерфейс
int_if="vr0" # внутренний интерфейс
lan="192.168.0.0/24" #локальная внутренняя сеть
Собираем статистику на внешнем интефейсе. Полезно знать сколько ушло, сколько пришло и т.п. Статистику можно так собирать только на одном интефейсе. Для просмотра статистики используется команда pfctl -s info или pfctl -si
set loginterface $ext_if
Таблицы. Удобная штука. Если адресов много, их можно запихать в таблицу. Есть ещё списки, но они работают медленнее. Далее в конфиге будет пример списка. По сути та же таблица, но таблица определяется заранее, а список во время работы.
table <my> { 192.168.0.0/24, рабочая_сеть } #сети откуда я могу заходить на ssh и ftp.
table <me> { 192.168.1.10, мой_внешний_ip, 192.168.0.1 } #собственно IP адреса, при обращени к которым считается, что обратились к самому серваку.
table <user1_ips> { 192.168.0.2, 192.168.0.5 } #IP адреса первого пользователя
table <user2_ips> { 192.168.0.3 } #IP второго пользователя
Нормализация всех входящих пакетов. Желательно.
scrub in all
Собственно определения очередей. Синтаксис такой:
altq on interface scheduler bandwidth bw qlimit qlim \
tbrsize size queue { queue_list }
interface — собсно интерфейс
scheduler — планировщик очереди. Нас интересует cbq (есть ещё priq, но он не годится для наших целей)
bw — ширина очереди, и чаще всего ширина канала.
qlim — максимальное число пакетов в очереди. Необязательный параметр. По умолчанию — 50
size — размер token bucket regulator в байтах. Если не определен, то устанавливается на основе ширины полосы пропускания. (понятия не имею что и зачем)
queue_list — список дочерних очередей.
queue name [on interface] bandwidth bw [priority pri] [qlimit qlim] \
scheduler ( sched_options ) { queue_list }
interface — интерфейс. Так как у нас пакеты в очередь должны пихаться независимо от того на каком они интерфейсе, то параметр надо опустить.
bw — ширина очереди. Можно указывать в %, можно в бит/с.
scheduler — уже сказано.
pri — приоритет. Для cbq приоритет изменяется от 0 до 7, для priq диапазон от 0 до 15. Приоритет 0 считается самым низким. Если этот параметр не определён, ему назначается 1.
sched_option — опции планироващика. Нас интересует опция borrow. Позволяет занимать в случае необходимости и наличиния возможности ширину канала у родительской очереди и у очередей, также являющихся для данной родительской дочерними. (ппц замутил). Данная опция работает только с планировщиком cbq.
Также желательна опция red. Позволяет не допускать полного забивания канала. То есть начинает потихоньку дропать пакеты, задолго до полной забитости очереди. Чем более забита очередь, тем больше дропает.
queue_list — список дочерних очередей. То есть для основной они уже будут «внуками». Возможно использовать только с планировщиком cbq.
altq on $int_if cbq bandwidth 100Mb queue { inet_in, default_in }
queue inet_in bandwidth 240Kb { user1_in, user2_in }
queue user1_in bandwidth 50% cbq(red, borrow)
queue user2_in bandwidth 50% cbq(red, borrow)
queue default_in bandwidth 99% cbq(default) #обязательно должна быть очередь default на интерфейсе. В неё будут пихаться все пакеты не вошедшие в остальные очереди.
Очередь на внутреннем интерфейсе для входящего траффика (хотя для внутреннего интерфейса он будет исходящим 🙂 ) Ширина канала 256Кбит/с. На всякий случай сделал запас и определил как 240. Юзерам идёт пополам. Сама сетевуха на 100 мегабит. Для очереди дефаулт ширина 99мегабит. Чтобы с нормально скоростью работать с шарой на сервере.
altq on $ext_if cbq bandwidth 100Mb queue { inet_out, default_out }
queue inet_out bandwidth 400Kb { user1_out, user2_out }
queue user1_out bandwidth 50% cbq(red, borrow)
queue user2_out bandwidth 50% cbq(red, borrow)
queue default_out bandwidth 99% cbq(default)
Аалогично для внешнего интерфейса.
Собственно NAT
nat on $ext_if from $lan to !$lan -> ($ext_if)
Думаю, всё понятно. Если пакет на внешнем интерфейсе идёт из локалки в мир, его отнатить, подставив адрес внешнего интерфейса. (у меня это всё потом ещё раз натится на модеме, каскад ната, блин 🙂 )
UPDATED: Если интерфейс в скобках, то в качестве обратного адреса подставляется любой адрес на интерфейсе. Сделано для динамических IP, чтобы не надо было перечитывать правила. Если без скобок, то при смене IP, надо будет выполнить pfctl -f путь_к_конфигу. Очень часто рекомендуют писать в скобках, но это может вызвать проблемы. Подробнее тут
Фильтры. Синтаксис скопировал из доки и немного подправил:
action [direction] [log] [quick] [on interface] [af] [proto protocol] \
[from src_addr [port src_port]] [to dst_addr [port dst_port]] \
[flags tcp_flags] [state]
action — Разрешить (pass) или послать (block)
direction — Направление пакета при прохождении через интерфейс: in или out.
log — Писать об этом в лог pflogd. Если указаны опции keep state, modulate state или synproxy state — в журнал попадают только пакеты открывшие соединение. Если надо, чтобы в журнал попали вообще все пакеты, применяйте правило log (all).
quick — Выполнить правило и больше не обрабатывать этот пакет следующими правилами
interface — интерфейс на котором применять это правило
af — тип адреса. inet (для Ipv4) и inet6 (для Ipv6). Обычно, нафиг не надо. PF и так догадается что за адрес
protocol — протокол транспортного уровня. Может быть tcp, udp, icmp, имя протокола из файла /etc/protocols, номер протокола от 0 до 255. Можно использовать список
src_addr, dst_addr — адрес исчтоника и назначения. Может быть одиночный адрес, сеть, таблица, доменное имя (в момент применения конфига оно заменится соответствующим IP), имя интерфейса (аналогично с доменным именем), any (любой адрес), all (то же самое, что и from any ti any). Можно использовать отрицание ( ! )
src_port, dst_port -порт источника или назначения в заголовке транспортного уровня. Может быть список. Можно использовать отрицание.
tcp_flags — Указывает какие флаги TCP должны быть выставлены в пакете, если указано proto tcp. Неинтресны для наших целей. Поэтому не буду описывать (тем более, что и сам не особо понимаю о чём)
state — Интересны только 2 варианта keep state (зачем это я уже писал), и no state. Для правил распихивающим пакеты в очереди, ОБЯЗАТЕЛЬНО no state
block in quick proto tcp from !<my> to port { 21, 22 } #закрываем ftp и ssh порты от злых хакеров с ботнетами и брутфорсом. Пример списка
block in quick from $lan to 192.168.1.10 #закрываю внешний IP от доступа из локалки. Мало ли какая винда дуркнет. Если качать с сервера по внешнему IP, то пакет считается, как пакет из интернета, и его скорость урезается.
Фильтров всего 2, так как всё остальное фильтрит модем. В нём всё закрыто, кроме ssh и ftp
И в самом конце правила, распихивающие пакеты по очередям. Думаю, тут должно быть всё понятно.
pass in on $int_if from <user1_ips> to !$lan queue user1_out no state
pass in on $int_if from <user2_ips> to !$lan queue user2_out no state
pass out on $int_if from !$lan to <user1_ips> queue user1_in no state
pass out on $int_if from !$lan to <user2_ips> queue user2_in no state
Чтобы посмотреть все правила фильрации используется команда pfctl -s rules или pfctl -sr.
Чтобы посмотреть подробно (сколько пакетов этот фильтр заблокировал, или послал в очередь) pfctl -sr -v
Чтобы посмотреть список очередей pfctl -s queue или pfctl -sq
Чтобы посмотреть сколько пакетов в какую очередь попало pfctl -sq -v
Чтобы наблюдать за очередями почти в реальном времени pfctl -sq -vv (две v, а не дубль ве). Выведет то же, что и pfctl -sq -v, но будет обновляться каждые 5 секунд и показывать текщие скорости пакетов в очереди.
Настраивал это всё я основываясь на следующих статьях:
BSDA в вопросах и ответах
Шлюз с авторизацией и динамическим распределением канала на базе pf+altq и authpf
Привет, а реально ли настроить распределение скорости между двумя компами, причем динамически, если к примеру на одном компе инет не используется, то на втором скорость была бы на весь выделенный канал, а если на обоих используется, то пополам делится?
Прошу прощения, уважаемый, был невнимательным, не прочитал первый абзац топика, спасибо за статью!
А как возможно реализовать данное на ipfw?
@ xander:
В своё время, когда я искал, как такое реализовать вообще, способов на ipfw не нашёл.
Вместо lan=»192.168.0.0/24″ лучше использовать таблицы, table { 192.168.0.0/24 }, в мануале описано, что они парсятся быстрее.
http://www.openbsd.org/faq/pf/ru/tables.html — вот здесь можно почитать
http://www.openbsd.org/faq/pf/ru/tables.html — вот здесь можно почитать. Спасибо за материал, кстати, поковыряю на досуге тогда 🙂
Сорри, не заметил, что ты об этом написал 🙂 Спасибо за материал )
в чем проблема появляеться такая ошибка
pfctl: the sum of the child bandwidth higher than parent «root_rl0»
pfctl: the sum of the child bandwidth higher than parent «root_rl1»
правила такие
altq on $int_if cbq bandwidth 100Mb queue { inet_in, default_in }
queue inet_in on $int_if bandwidth 4000Kb { user1_in,user2_in,user3_in,user4_in,user5_in,user6_in,user7_in, user8_in,user9_in,user10_in }
queue user1_in bandwidth 10% cbq(red, borrow)
queue user2_in bandwidth 10% cbq(red, borrow)
queue user3_in bandwidth 10% cbq(red, borrow)
queue user4_in bandwidth 10% cbq(red, borrow)
queue user5_in bandwidth 10% cbq(red, borrow)
queue user6_in bandwidth 10% cbq(red, borrow)
queue user7_in bandwidth 10% cbq(red, borrow)
queue user8_in bandwidth 10% cbq(red, borrow)
queue user9_in bandwidth 10% cbq(red, borrow)
queue user10_in bandwidth 10% cbq(red, borrow)
queue default_in bandwidth 99% cbq(default)
altq on $ext_if cbq bandwidth 100Mb queue { inet_out, default_out }
queue inet_out on $ext_if bandwidth 4000Kb { user1_out, user2_out, user3_out, user4_out, user5_out, user6_out, user7_out, user8_out, user9_out, user10_out }
queue user1_out bandwidth 10% cbq(red, borrow)
queue user2_out bandwidth 10% cbq(red, borrow)
queue user3_out bandwidth 10% cbq(red, borrow)
queue user4_out bandwidth 10% cbq(red, borrow)
queue user5_out bandwidth 10% cbq(red, borrow)
queue user6_out bandwidth 10% cbq(red, borrow)
queue user7_out bandwidth 10% cbq(red, borrow)
queue user8_out bandwidth 10% cbq(red, borrow)
queue user9_out bandwidth 10% cbq(red, borrow)
queue user10_out bandwidth 10% cbq(red, borrow)
queue default_out bandwidth 99% cbq(default)
Преведушию снима. 🙂 разобрался. default очередь и очереди инета были больше 100% в сумме. исправил на 90% запустилось
Спасибо огромное, данная статья очень помогла, т.к. до этого везде читал что pf не позволяет ограничивать исходящую скорость, но сделал как тут и всё получилось!!!
Lord_MorTis ты не мог бы отправит мне на почту пример твоего pf.conf? буду очень признателен! а то у меня не робит((((
вообще ребят, кому не лень отправть мне рабочий пример wolf4ara@gmail.com
@ Wolf4ara:
http://tuupic.org.ru/wp-content/uploads/pf.conf
а как быть, если клиенты подключатся по pppoe
ведь тогда для каждого клиента создается новый внутренний интерфейс tun0, tun1 и т.д.
и altq on $int_if уже здесь не катит.
Как правильно составить конфиг?
simer написал:
Не использовал раздачу интернета по ppp, поэтому даже не знаю досконально как оно работает. Поэтому, тут не могу ничего сказать, к сожалению.
Пока, единственное, что вертится в голове, это использовать какой-нибудь промежуточный виртуальный интерфейс. Но опять же, на практике я это не применял.
@ simer:
поднимай сессию ppp через mpd и использую интерфейс ng0
спасибо!
В чем может быть проблема не работает, пытаюсь ограничить трафик только для одного клиента только входящий к нему.
ext_if=»rl0″
int_if=»re0″
lan=»10.10.77.0/24″
set loginterface $int_if
table {10.10.77.3}
scrub in all
altq on $int_if cbq bandwidth 100Mb queue {inet_in, default_in}
queue inet_in bandwidth 128Kb {user_in}
queue user_in bandwidth 50% cbq(red,borrow)
queue default_in bandwidth 99% cbq(default)
pass out on $int_if from any to 10.10.77.3 queue user_in
извиняюсь забыл отключить keep-state
надо так
pass out on $int_if from any to 10.10.77.3 queue user_in no state
Прочитал Вашу статью о шейпинге, почитал мануал на оффсайте про pf. Хорошая статья, но:
есть грабли, которые бьют меня уже 4 дня
Имеется:
локалка — 10.0.0.0/8
VPN клиенты — 192.168.100.0/24
Канал в инет 16 Mbit/s
Нужно поделить его между пользователями локалки поровну,(канал в инет создается посредством подключение к вышестоящему интернет провайдеру тоже через VPN по локалке посредством mpd5) но не выходит! Шейпил на локальном интерфейсе, как в статье и сказано.
Фишка в том,что весь траффик заворачивается на default queue и как бы я не старался, трафик не хочет идти на приписаные к локальым адресам пользоваетелей очереди
Вот конфиг, если что то не правильно, подправте меня пожалуйста, а то хочется уже мне выспатся, поскольку и днем и ночью рою форумы по этой причине и безрезультатно
int_if=»em0″
ext_if=»ng0″
inet_users=»192.168.100.0/24″
allows=»{ 10.112.0.57 10.112.0.59 10.112.6.2 10.117.4.172 10.112.6.27 10.112.8.55 }»
admin=»10.112.6.2″
olya=»10.117.4.172″
test=»{10.112.0.57, 10.112.0.59}»
mig=»10.112.6.27″
localka=»10.0.0.0/8″
scrub in all
altq on $int_if cbq bandwidth 100Mb queue { inet_in, local }
queue local bandwidth 60Mb cbq (default)
queue inet_in bandwidth 40Mb qlimit 1000 cbq {admin, olya, test, mig}
queue admin bandwidth 512Kb cbq(red)
queue olya bandwidth 1.5Mb cbq(red)
queue mig bandwidth 1.5Mb cbq(red)
queue test bandwidth 1.5Mb cbq(red)
nat on ng0 from $inet_users to any -> $ext_if
nat on em0 from any to $ext_if -> $int_if
block in on $int_if all
block in on $ext_if all
pass out on $int_if from $int_if to $localka
pass out on $ext_if all
pass out quick on $int_if from !$localka to $admin queue admin no state
pass out quick on $int_if from !$localka to $mig queue mig no state
pass out quick on $int_if from !$localka to $olya queue olya no state
pass out quick on $int_if from !$localka to $test queue test no state
Скорее всего, когда пакет проходит наружу через
pass out on $ext_if all
он подпадает под keep-state и ответ идёт по тому же правилу.
Либо создайте правила и для исходящих, либо уберите
block in on $ext_if all
и
pass out on $ext_if all
int_if=»em0″
ext_if=»ng0″
inet_users=»192.168.100.0/24″
allows=»{ 10.112.0.57 10.112.0.59 10.112.6.2 10.117.4.172 10.112.6.27 10.112.8.55 }»
admin=»10.112.6.2″
olya=»10.117.4.172″
test=»{10.112.0.57, 10.112.0.59}»
mig=»10.112.6.27″
localka=»10.0.0.0/8″
scrub in all
altq on $int_if cbq bandwidth 100Mb queue { inet_in, local }
queue local bandwidth 60Mb cbq (default)
queue inet_in bandwidth 40Mb qlimit 1000 cbq {admin, olya, test, mig}
queue admin bandwidth 512Kb cbq(red)
queue olya bandwidth 1.5Mb cbq(red)
queue mig bandwidth 1.5Mb cbq(red)
queue test bandwidth 1.5Mb cbq(red)
altq on $ext_if cbq bandwidth 16Mb queue { admin_out, olya_out, mig_out, test_out }
queue admin_out bandwidth 512Kb cbq
queue olya_out bandwidth 1.5Mb cbq (default)
queue mig_out bandwidth 1.5Mb cbq(red)
queue test_out bandwidth 1.5Mb cbq(red)
nat on ng0 from $inet_users to any -> $ext_if
nat on em0 from any to $ext_if -> $int_if
block in on $int_if all
pass in on $int_if from $allows to $int_if
pass out on $int_if from $int_if to $localka
pass out quick on $int_if from !$localka to $admin queue admin no state
pass out quick on $int_if from !$localka to $mig queue mig no state
pass out quick on $int_if from !$localka to $olya queue olya no state
pass out quick on $int_if from !$localka to $test queue test no state
pass out quick on $ext_if from $admin to any queue admin_out no state
pass out quick on $ext_if from $olya to any queue olya_out no state
pass out quick on $ext_if from $mig to any queue mig_out no state
pass out quick on $ext_if from $test to any queue test_out no state
Один пень на дефолтный валятся :`(
Добросовесному и понимающему человеку могу дать логин и пароль к серваку, ибо эти грабли мне уже весь мозг выели((((
Вообще, для начала, убери все фильтрующие правила, оставь только те, что по очередям.
reonaydo дай свою асю пожалуйста, мне нужна квалифицированная помощь
Боюсь, у меня нету времени заниматься этим
есть задача:
1. есть канал в инет 20мб (естественно он динамически «проседает» — 25%).
2. есть 4 юзера, которые время от времени включают комп и юзают инет, им нужно обеспечить скорость до 10мб (не больше).
3. если вдруг они все полезут в инет и начнут интенсивно «качать» нужно разделить канал поровну, то есть по 5мб.
Грамотно ли будет решить задачу средствами РF and NAT ???
ЗЫ: буду благодарен за конфиг ))
SID, дай свою асю, помогу тебе … еле догнал
прекрасно режиться )) если что, пишите 497850700
Настроил как написано! Но динамического распределения нет! Канал режет как надо, но отдавать свободные куски канала вообще не хочет.
Нашел проблему ) ошибся, когда указывал ширину исходящего канала спасибо за материал
german
моя ася 578913334
жду, стучись)
я тут набросал простенький генератор конфига http://pf.serviscentr.net.ua/
Подскажите, почему если правило указано без no state:
pass in on $int_if from to !$lan queue user1_out — входящий трафик режет, но естественно исходящий нет,
а если:
pass in on $int_if from to !$lan queue user1_out no state, правило игнорируется и трафик идет через default?
Делайте два разных правила для входящего и исходящего трафика и используйте pass out на клиента, как входящий для него и pass out на провайдера, как исходящий от клиента.
Подскажите, пожалуйста, есть канал 10Мб, несколько пользователей, одному из них необходимо обеспечить скорость не ниже 300Кб, т.е. если канал свободен то он может и больше, но если загружен он стабильно отгрызает свои 300, для остальных канал просто делится. Реально ли такое реализовать?
Спасибо автору! Давно собирался реализовать шейпинг, да ни как руки не доходили, а тут как раз обстоятельства — один из 15 пользователей сети положил торентом весь канал, наткнулся на данную статью, пару часов экспериментов и готово! Еще раз спасибо.