Jump to content

Recommended Posts

Posted

Утилита для генерации правил массового шейпинга.

Модули для биллинга (UTM4, UTM5, ...) и для шлюза (FreeBSD+ipfw+Dummynet).

 

Брать/читать - здесь:

http://sources.homelink.ru/shaping/

 

В том виде, в котором выложены, не тестировались (тот вид, в котором они фактически используются,

для выкладывания решительно не подходит), поэтому могут быть ошибки, но вряд ли серьёзные.

Posted (edited)

Выкладываю свой вариант :) подходит для утм5, а вообще можно к любому биллингу и любому шейперу прикрутить

 

#!/usr/local/bin/php
<?php
#скрипт генерации правил шейперов. должен запускаться по крону
include ("/netup/wkbill/config.php");
###функции
######################################################правила для IPFW
#создание файла для ipfw
function ipfw_create_file($ipfw_commands_file) {
    $file=fopen($ipfw_commands_file,"w+");
    fwrite($file, "#!/bin/sh\n");
    fwrite($file, "fwcmd=\"/sbin/ipfw -q\"\n");
    fclose($file);
}
#создание пайпа dummynet
function ipfw_create_pipe($ipfw_commands_file,$pipe_id,$in_speed,$out_speed,$tariff_id,$tariff_name) {
    $file=fopen($ipfw_commands_file,"a+");
    fwrite($file,"#Tariff ID: $tariff_id Name: $tariff_name\n");
    fwrite($file,"\${fwcmd} pipe $pipe_id config mask dst-ip 0xffffffff bw ".$in_speed."Kbit/s\n");
    $pipe_id=$pipe_id+1000;
    fwrite($file,"\${fwcmd} pipe $pipe_id config mask src-ip 0xffffffff bw ".$out_speed."Kbit/s\n");
    fclose($file);
}
function ipfw_table_add($ipfw_commands_file,$ip,$pipe) {
    $file=fopen($ipfw_commands_file,"a+");
    fwrite($file,"\${fwcmd} table 1 add $ip $pipe\n");
    $pipe=$pipe+1000;
    fwrite($file,"\${fwcmd} table 2 add $ip $pipe\n");
    fclose($file);
}

function ipfw_table_del($ipfw_commands_file,$ip) {
    $file=fopen($ipfw_commands_file,"a+");
    fwrite($file,"\${fwcmd} table 1 delete $ip\n");
    fwrite($file,"\${fwcmd} table 2 delete $ip\n");
    fclose($file);
}


#################################################основной скрипт
$utm5dblink = mysql_connect($utm5dbhost , $utm5dbuser, $utm5dbpass)or die ("Возникла ошибка при подключении к базе данных\n"); //подключаемся к базе
mysql_select_db($utm5dbname) or die ("Возникла ошибка при выборе базы данных\n");
#узнаем текущий час
$cur_hour=date("H:i:s");
$current_time=strtotime(now);
#создаем файл в который будем писать комманды для шейперов
ipfw_create_file($ipfw_commands_file);

#запрос на получение существующих правил для тарифов
$query = mysql_query("SELECT * FROM tariffs, wk_shaper_rules WHERE wk_shaper_rules.tariff_id = tariffs.id AND tariffs.is_deleted=0") or die("не удается получить список правил для тарифов");
$numresults = mysql_num_rows($query);
for ($i=0; $i <$numresults; $i++){
    $row=mysql_fetch_array($query);
    $id=$row[0]; //id пользователя
    $tariff_id=iconv("cp1251", "KOI8-R", $row['tariff_id']); //ID тарифа $comment = iconv("cp1251", "UTF-8", $comment);
    $tariff_name=$row['name']; //Название тарифа
    $day_mode_start=$row['day_mode_start']; //начало дневного режима
    $day_mode_end=$row['day_mode_end']; //конец дневного режима
    $day_in_speed=$row['day_in_speed']; //входящая скорость дневного режима
    $day_out_speed=$row['day_out_speed']; //исходящая скорость дневного режима
    $night_mode_start=$row['night_mode_start']; //начало ночного режима
    $night_mode_end=$row['night_mode_end']; //конец дневного режима
    $night_in_speed=$row['night_in_speed']; //входящая скорость ночного режима
    $night_out_speed=$row['night_out_speed']; //исходящая скорость ночного режима
    $boost_in_speed=$row['boost_in_speed']; //входящая скорость n2o boost
    $boost_out_speed=$row['boost_out_speed']; //исходящая скорость n2o boost
    $night_boost_in_speed=$row['night_boost_in_speed']; //входящая скорость n2o boost ночь
    $night_boost_out_speed=$row['night_boost_out_speed']; //исходящая скорость n2o boost ночь

    #создаем правила шейпинга ночного режима
    if ($cur_hour>=$night_mode_start AND $cur_hour<=$night_mode_end) {
        ipfw_create_pipe($ipfw_commands_file,$tariff_id,$night_in_speed,$night_out_speed,$tariff_id,$tariff_name);
    }
    #в остальном случае создаем стандартные правила шейпинга
    else {
        ipfw_create_pipe($ipfw_commands_file,$tariff_id,$day_in_speed,$day_out_speed,$tariff_id,$tariff_name);
    }
    #если на тарифе есть услуга N2O Boost то создаем отдельные правила
    if ($boost_in_speed>0 AND $boost_out_speed>0) {
        $boost_id=$tariff_id+2000;
        #n2o boost ночной режим
        if ($cur_hour>=$night_mode_start AND $cur_hour<=$night_mode_end) {
            ipfw_create_pipe($ipfw_commands_file,$boost_id,$night_boost_in_speed,$night_boost_out_speed,$tariff_id,$tariff_name);
        }
        #в остальном случае стандартные правила для n2o boost
        else {
            ipfw_create_pipe($ipfw_commands_file,$boost_id,$boost_in_speed,$boost_out_speed,$tariff_id,$tariff_name);
        }
    }
}
#пишем комманды на удаление/добавление правил в созданные пайпы
$query = mysql_query ("SELECT sl.account_id, INET_NTOA(ip & 4294967295) AS ip, tsl.tariff_id, ac.int_status FROM ip_groups ig, iptraffic_service_links il, service_links sl, tariffs_services_link tsl, accounts ac WHERE ig.is_deleted =0 AND il.is_deleted =0 AND sl.is_deleted =0 AND ig.ip_group_id = il.ip_group_id AND sl.id = il.id AND sl.service_id = tsl.service_id AND ac.id = sl.account_id AND ig.ip_type =1") or die("Запрос к бд на получение правил доступа не прошел");
$numresults = mysql_num_rows($query);
for ($i=0; $i <$numresults; $i++){
    $row=mysql_fetch_array($query);
    $tariff_id_out=0;
    $id = $row[0]; //ID пользователя
    $ip=$row['ip']; //IP адрес
    $tariff_id=$row['tariff_id']; //ID тарифа
    $tariff_id_out=$tariff_id+1000;
    $int_status=$row['int_status']; //статус интернета (1 - включен 0 - выключен)
    #проверяем. сменился ли тариф с предыдущего запуска или нет. если изменился то сначала удаляем старые правила
    $c_query = mysql_query("SELECT id FROM wk_shaper_rules_cache WHERE ip='$ip' AND tariff_id='$tariff_id'") or die("не удалось проверить правила с предыдущего запуска");
    if (mysql_num_rows($c_query)==0) {
        print("У пользователя $id сменился тарифф\n");
        ipfw_table_del($ipfw_commands_file,$ip);
        mysql_query("DELETE FROM wk_shaper_rules_cache WHERE ip='$ip'") or die("не удается удалить старую запись из кеша");
        mysql_query("INSERT INTO wk_shaper_rules_cache (ip, tariff_id) VALUES ('$ip', '$tariff_id')");
    }
    #если интернет включен то даем разрешающее правило
    if ($int_status==1) {
        #сначала проверяем услугу N2O Boost
        #проверяем не требуется ли подключить услугу
        $n2o_query = mysql_query ("SELECT * FROM wk_service_n2o_boost WHERE boost_start_time<=$current_time AND boost_end_time>=$current_time AND ip_addr='$ip'") or die("не удается провертить подключение услуги N2O Boost");
        if (mysql_num_rows($n2o_query)!=0) {
            $n2o_row=mysql_fetch_array($n2o_query);
            if ($n2o_row['status']==0) {
                mysql_query("UPDATE wk_service_n2o_boost SET status=1 WHERE id=$n2o_row[0]");
                ipfw_table_del($ipfw_commands_file,$ip);
            }
            $boost_id=$tariff_id+2000;
            ipfw_table_add($ipfw_commands_file,$ip,$boost_id);
        }
        #проверяем не требуется ли отключить услугу
        $n2o_query = mysql_query("SELECT * FROM wk_service_n2o_boost WHERE boost_end_time<=$current_time AND status=1 AND ip_addr='$ip'") or die ("Запрос не прошел");
        if (mysql_num_rows($n2o_query)!=0) {
            $n2o_row=mysql_fetch_array($n2o_query);
            if ($n2o_row['status']==1) {
                mysql_query("UPDATE wk_service_n2o_boost SET status=0 WHERE id=$n2o_row[0]");
                ipfw_table_del($ipfw_commands_file,$ip);
            }
            ipfw_table_add($ipfw_commands_file,$ip,$tariff_id);
        }
        #если допуслуг нету то просто разрешаем доступ в интернет
        else {
                ipfw_table_add($ipfw_commands_file,$ip,$tariff_id);
        }
    }
    #если интернет выключен то даем команду на удаление из таблиц
    if ($int_status==0) {
        ipfw_table_del($ipfw_commands_file,$ip);
    }
}

mysql_close($utm5dblink);
?>

 

все данные по тарифам храняться и скоростям и форсажу храняться в таблицах

wk_shaper_rules

CREATE TABLE IF NOT EXISTS `wk_shaper_rules` (
  `id` int(11) NOT NULL auto_increment,
  `tariff_id` int(11) NOT NULL,
  `day_in_speed` int(11) NOT NULL,
  `day_out_speed` int(11) NOT NULL,
  `night_mode_start` time NOT NULL,
  `night_mode_end` time NOT NULL,
  `night_in_speed` int(11) NOT NULL,
  `night_out_speed` int(11) NOT NULL,
  `boost_in_speed` int(11) NOT NULL,
  `boost_out_speed` int(11) NOT NULL,
  `night_boost_in_speed` int(11) NOT NULL default '0',
  `night_boost_out_speed` int(11) NOT NULL default '0',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=koi8r;

 

таблица wk_service_n2o_boost

CREATE TABLE IF NOT EXISTS `wk_service_n2o_boost` (
  `id` int(11) NOT NULL auto_increment,
  `account_id` int(11) NOT NULL,
  `boost_id` int(11) NOT NULL,
  `boost_start_time` int(11) NOT NULL,
  `boost_end_time` int(11) NOT NULL,
  `tariff_id` int(11) NOT NULL,
  `status` int(11) NOT NULL default '0',
  `ip_addr` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=koi8r;

 

и для отслеживания изменившихся тарифных планов у пользователей мы еще держим кеш в таблице wk_shaper_rules_cache

CREATE TABLE IF NOT EXISTS `wk_shaper_rules_cache` (
  `id` int(11) NOT NULL auto_increment,
  `ip` varchar(255) NOT NULL,
  `tariff_id` int(11) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=koi8r;

 

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

#Правила шейпера
*/5 * * * * /netup/wkbill/shaper_rules.php 2>&1

 

на каждом из маршрутизаторов раз в 3 минуты по крону пускается другой скрипт (который скачивает файл по ftp и запускает его), get_shaper_rules.sh

#!/bin/sh
#скрипт для скачивания правил шейпирования с маршрутизатора и их применения
ftp_login="xxxxx"
ftp_passw="xxxx"
/usr/bin/fetch -o /usr/local/wkt_scripts/ipfw_commands ftp://$ftp_login:$ftp_passw@195.93.xxx.xxx/ipfw_commands
/bin/sh /usr/local/wkt_scripts/ipfw_commands

 

ко всему этому прикручивается вебморда для управления скоростями и временем, код не дам а покажу только скриншот :) можете сделать подобное или написать мне в приват если интересно :)

 

чуть позже выложу статейку по настройке маршрутизатора под FreeBSD 7.2 + пример использования tablearg

shaper.jpg

Edited by Denis Samsonov
  • 2 weeks later...
Posted

Мы не стали заморачиваться с вводом дополнительных boost-параметров, просто сделали по крону замену файлов с шаблонами скоростей:

# Night tariffs
59      23      *       *       *       root    /bin/ln -sf /root/utmscripts/utm2shaper.night /root/utmscripts/utm2shaper.utm
# Day tariffs
59      13      *       *       *       root    /bin/ln -sf /root/utmscripts/utm2shaper.day /root/utmscripts/utm2shaper.utm

Это существенно проще. ;)

Posted
Мы не стали заморачиваться с вводом дополнительных boost-параметров, просто сделали по крону замену файлов с шаблонами скоростей:
Смотри в shapers_generate, как правильно. Без крона, без дублирования общих строк,

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

 

Fullspeed - не замена ночному режиму, а дополнение к нему.

Posted (edited)

Смотрю.

Ты ещё и delay для пайпов задаёшь?? ИМХО, извращение. Лучше уж [для низкоскоростных тарифов] задавать параметры queue и burst. У нас неплохие результаты показывает такое сочетание:

    my $speed_in = int($actual_limit*1.1);
    my $speed_out = $speed_in;
    my $queue_size = int($speed_in / 8);
    my $burst_size = int($speed_in * 1.5 / 8);          # http://www.cisco.com/en/US/docs/ios/12_2/qos/configuration/guide/qcfpolsh.html
                                                        # normal burst = configured rate * (1 byte)/(8 bits) * 1.5 seconds
                                                        # extended burst = 2 * normal burst

 

 

P.S.

 downloaded ".($total_bytes/1000000)." Mbytes" : ""

Скажем нет маркетологически-метрологическим мегабайтам! Даёшь 1 048 576 байт в мегабайте! :)

 

Edited by Dyr
Posted
Смотрю.

Ты ещё и delay для пайпов задаёшь?? ИМХО, извращение.

Это настраиваемый параметр, который по умолчанию не используется.

Оставлен исключительно для экспериментов с delay -1ms.

 

 downloaded ".($total_bytes/1000000)." Mbytes" : ""

Скажем нет маркетологически-метрологическим мегабайтам! Даёшь 1 048 576 байт в мегабайте! :)

Это комментарий для проверки "на всякий случай".
Posted
Это настраиваемый параметр, который по умолчанию не используется.

Оставлен исключительно для экспериментов с delay -1ms.

Тогда на всякий случай предупрежу - "delay -1ms" на 8.0 практически моментально приводит к перезагрузке, так что если будешь переходить на восьмёрку, экспериментировать с ним не советую ;)
Posted
 downloaded ".($total_bytes/1000000)." Mbytes" : ""

Скажем нет маркетологически-метрологическим мегабайтам! Даёшь 1 048 576 байт в мегабайте! :)

 

Если заглянуть унутрь dumminet, то можно заметить, что в килобайте у нее 1000 байт, а в мегабайте 1000 кил. Тоже самое про биты. Хорошо что хоть в байте 8 бит. (при задании скоростей в командной строчке они приводятся к байт/сек. При выводе обратно к К и М). 1000 там в исходниках.

Posted (edited)

st_re, увы, я не знаю С, но всё же - где это там? В /usr/src/sys/netinet/ip_dummynet.c чётко видно, что 1024:

# grep -w 1000 /usr/src/sys/netinet/ip_dummynet.*

ip_dummynet.c:  p->delay = (p->delay * hz) / 1000;
ip_dummynet.c:          pipe_bp->delay = (pipe_bp->delay * 1000) / hz;
ip_dummynet.h: *    If we limit to max 1000 flows and a max weight of 100, then
ip_dummynet.h: * XXX With this scaling, max 1000 flows, max weight 100, 1Gbit/s, the

# grep -w 1024 /usr/src/sys/netinet/ip_dummynet.*

ip_dummynet.c:static long pipe_byte_limit = 1024 * 1024;
ip_dummynet.c:                  x->qsize = 1024 * 1024;

# grep -w 1024 /usr/src/sbin/ipfw/dummynet.c

                        sprintf(qs, "%d KB", l / 1024);
                                p.fs.qsize *= 1024;
                                p.fs.min_th *= 1024;
                                p.fs.max_th *= 1024;
                        limit = 1024*1024;

Правда, есть ещё и такое:

# grep -w 1000 /usr/src/sbin/ipfw/dummynet.c

                else if (b >= 1000)
                        sprintf(buf, "%7.3f Kbit/s", b/1000);
                                p.bandwidth *= 1000;

Edited by Dyr
Posted

Похоже, действительно в десятичных единицах считает. Ото ж блин, а...

                            if (*end == 'K' || *end == 'k') {
                                end++;
                                p.bandwidth *= 1000;
                            } else if (*end == 'M') {
                                end++;
                                p.bandwidth *= 1000000;
                            }

И что за извращение такое, выпендриться и строго соответствовать букве вместо общепринятого обозначения. Идиётизм.

Posted

uname -a

FreeBSD xxx.xxx.ru 7.3-PRERELEASE FreeBSD 7.3-PRERELEASE #0: Fri Feb 12 20:13:07 MSK 2010 root@xxx.xxx.ru:/usr/obj/usr/src/sys/xxx amd64

 

/usr/src/sbin/ipfw/dummynet.c

                /*
                 * Print rate (or clocking interface)
                 */
                if (p->if_name[0] != '\0')
                        sprintf(buf, "%s", p->if_name);
                else if (b == 0)
                        sprintf(buf, "unlimited");
                else if (b >= 1000000)
                        sprintf(buf, "%7.3f Mbit/s", b/1000000);
                else if (b >= 1000)
                        sprintf(buf, "%7.3f Kbit/s", b/1000);
                else
                        sprintf(buf, "%7.3f bit/s ", b);

 

Если в 8 они поправили, то могет быть. Насколько лет назад я пытался накатать sendpr... но потом себе биллинг поправил, чтобы всегда в бит/с писал.

Posted

И что за извращение такое, выпендриться и строго соответствовать букве вместо общепринятого обозначения. Идиётизм.

это вы про что? общеприниятое всмысле "bit/s" ?

Posted

Кстати, хозяйке на заметку.Задание в конфиге mbyte/s приведет к очень интересному для многих фифекту... Как и mBite. Бит от байта оно отличает строго по высоте буквы B. если нижний регистр - биты, верхний - байты. Что там дальше за буквы - пофиг.

Posted
И что за извращение такое, выпендриться и строго соответствовать букве вместо общепринятого обозначения. Идиётизм.
это вы про что? общеприниятое всмысле "bit/s" ?

Это я про то, что вместо общепринятых, хоть и строго говоря, неправильных приравниваний СИшных приставок кило- и мега- как 1024 и 1024^2 разработчики dummynet решили пойти своим путём и написать всё строго "по бумажке", где кило это 1000 а мега это 1000^2.

 

Кстати, хозяйке на заметку.Задание в конфиге mbyte/s приведет к очень интересному для многих фифекту... Как и mBite. Бит от байта оно отличает строго по высоте буквы B. если нижний регистр - биты, верхний - байты. Что там дальше за буквы - пофиг.
Прэлестно, просто прэлестно. Сегодня не успею, а вот завтра постараюсь отправить соотсветствующее письмецо разработчикам.
Posted (edited)

Dyr, AFAIK, в телекоме редко кто информацию меряет в двоичных мегабитах. Неудобно. Собственно, разрабочики стандарта Fast Ethernet свои 100Mbit/s сделали десятичными.

С двоичными обозначеними только проблем больше будет. Сможешь с ходу сказать, сколько нужно довичных мегабит, чтобы сэмулировать 100Mbit/s интерфейс?

Edited by littlesavage
Posted (edited)

Да не сказал бы. Просто если на скорость ещё не очень обращают внимание, то трафик как раз считают обычно в двоичных приставках.

 

Собственно, разрабочики стандарта Fast Ethernet свои 100Mbit/s сделали десятичными.
Кстати, тоже пытался найти сходу упоминание о битовой скорости для данного стандарта и не нашёл. :) Edited by Dyr
Posted (edited)

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

http://en.wikipedia.org/wiki/Binary_prefix...andard_prefixes

http://physics.nist.gov/cuu/Units/binary.html

Kibit = 1024 bit, Mibit = 1024*1024 bit и т.д. В линуксовом tc они используются и там такой путаницы не возникает.

Edited by photon
Posted

Уговорили :)

Но с тем, что байты меряются с двоичными приставками (и в том же dummynet тоже, кстати), согласны? ;)

Posted
собственно статейка с примером по tablearg

http://denjs.habrahabr.ru/blog/82656/

 

Опубликуйте в статьях на сайте кто может или кому интересно :)

а сколько kpps бегает через шейпер ?

и сколько интерфейсов в онлайне ?

 

у меня похожая конфигурация, но , что то нет нормальных показателей по производительности, может дел в железе ?! мать intel dq35joe + core2duo 3Ghz

Posted

Комменты к статье, кстати, жгут. Советы заменить pf nat на ng_nat хороши ровно до тех пор, пока не вспоминаешь, что pf умеет натить на диапазон адресов.

  • 1 month later...
Posted
Ilya Evseev, ограничение скорости на более чем один ip на пользователя не делается я так понимаю? То есть если к абоненту в сервисной связке привязано с десяток ip'шников то ограничение будет только у одного - первого?
Posted
Ilya Evseev, ограничение скорости на более чем один ip на пользователя не делается я так понимаю? То есть если к абоненту в сервисной связке привязано с десяток ip'шников то ограничение будет только у одного - первого?
Для UTM5 не проверялось.

Для UTM4 каждый IP получит отдельный пайп.

  • 1 month later...
Posted (edited)

У меня вопрос по шейперам на FreeBSD, если можно. Почитал Ваши,Ivan Rostovikov и Denis Samsonov, примеры и появилось несколько вопросов.

 

1) Как правильно реализовать алгоритм переброса абонента из одной полосы в другую? Скорее всего перемещением его в другую ipfw table, но как лучше: занулением всех таблиц и раскладкой юзеров по новым заново или вычислением текущего состония ipfw tables, сравнением с новым нужным состоянием и генераций новых ipfw table N add и ipfw table N delete листингов, которые будут делать изменения.

 

Если, по каким-то причинам, нужно делать полный ipfw table N flush и заполнять их заново, я так понимаю что сервер выкинет всех из пайпов\очередей и на момент (1,2-10 секунд, минута и т.п.) и пропустит их на wirespeed (что в принципе не сильная проблема). Вопрос: отдадутся ли оставшиеся пакеты (если были) в существующих очередях\пайпах юзерам при сбросе\переключении таблиц. Не будет ли потерь пакетов в этот момент?

 

2) Как я понял, в обоих примерах пайпы создаются динамически, через определенный промежуток времени конфигурация пайпов\очередей грохается и пересоздается заново в зависимости от текущих тарифов\ночного времени и прочего. Вопрос: насколько "плохо" становится серверу при пересоздании большого количества пайпов (ведь с масками типа 0xffffffff отдельный пайп, вернее 2 по одному на вход и выход, создается для каждого адреса)? Теряются ли пакеты при сбросе конфигурации пайпов (ipfw pipe flush, ipfw queue flush) и насколько это ощущает абонент?

 

Коллеги, мне нужно понять правильный алгоритм действий (или возможные варианты) в плане динамики изменения пайпов\ipfw таблиц, а потом уже реализовать его не проблема для связки с нашим билингом.

 

Заранее спасибо за ответы.

Edited by Sergey_Taurus

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...
На сайте используются файлы cookie и сервисы аналитики для корректной работы форума и улучшения качества обслуживания. Продолжая использовать сайт, вы соглашаетесь с использованием файлов cookie и с Политикой конфиденциальности.