"Правильный" шейпер исходящего траффика, проба №2. Хеши

Установка, настройка, поддержка
Ответить
NiTr0
Сообщения: 767
Зарегистрирован: Пт фев 08, 2008 4:46 pm

"Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение NiTr0 »

Итак, выкладываю для тестирования скрипт для инициализации хеш-таблиц и классов с дефолтными значениями; он же умеет и менять скорость для указанных классов. Глубоко не тестировал, на первый взгляд - работает корректно.
Думаю, в комментариях не нуждается, если не прав, поправьте. Прикрутить к действующему шейперу - думаю, тоже проблемы не составит; один совет - не забывать устанавливать скорость и для клиентов без ограничений по скорости, иначе - клиент внезапно может узнать, что у него скорость при каждом коннекте разная, в зависимости от того, насколько жадный абонент пользовал этот ип ранее...

Итак, hsh.conf (где хранятся все дефолтные настройки):

Код: Выделить всё

tc="/sbin/tc"
ip="/sbin/ip"
sed="/bin/sed"
awk="/usr/bin/awk"

POOLS="10.255.0.64/30 10.255.0.0/28"
UPDEV=ifb0
UPDEVINIT="$ip l s $UPDEV up 2>/dev/null"
UPDEVSTOP="$tc q d dev $UPDEV 2>/dev/null; $ip l s $UPDEV down 2>/dev/null"
URATE=1000Mbit
UHRATE=500Mbit
UCEIL=1000Mbit
UBURST="burst 512k"

И сам скрипт hsh.sh:

Код: Выделить всё

#!/bin/sh

. /etc/hsh.conf

#convert ip to integer
ip2int() {
    echo $@|$sed 's/\(\.\|\/\)/ /g'|$awk '{if ((NF==4)||(NF==5)) {print $1*2^24+$2*2^16+$3*2^8+$4}}'
}

#convert integer to ip
int2ip() {
    echo $@|$awk '{if (NF==1) {s1=$1%2^24; s2=$1%2^16; \
    print int($1/2^24)"."int(s1/2^16)"."int(s2/2^8)"."$1%256}}'
}

#get subnet addr for ip/mask
subnet() {
    echo $@|$sed 's/\(\.\|\/\)/ /g'|$awk '{if (NF==5) {ip=$1*2^24+$2*2^16+$3*2^8+$4; \
    subnet=ip-ip%2^(32-$5); s1=subnet%2^24; s2=subnet%2^16; \
    print int(subnet/2^24)"."int(s1/2^16)"."int(s2/2^8)"."subnet%256"/"$5}}'
}

#get width of subnet
netwidth() {
    echo $@|$sed 's/\(\.\|\/\)/ /g'|$awk '{if (NF==5) {print 2^(32-$5)}}'
}

#get # of 256-byte subnets of network
subcnt() {
    echo $@|$sed 's/\(\.\|\/\)/ /g'|$awk '{if (NF==5) {width=2^(32-$5); i=(width%256>0); print int(width/256)+i }}'
}

#return max of 2 ints
max() {
    echo $@|$awk '{if (NF==2) {if ($1>$2) {print $1} else {print $2}}}'
}

#print as hex
hex() {
    echo $@|$awk '{if (NF==1) {printf "%x", $1}}'
}

#sequence from $1 to $1+$2-1
lseq() {
    echo $@|$awk '{if (NF==2) {for (i=0;i<$2;i++) {print $1+i}}}'
}

#init
initdev() {
    echo $UPDEVINIT|sh
    $tc q d root dev $UPDEV 2>/dev/null
    $tc q a root dev $UPDEV handle 1: htb default 2
    $tc c a dev $UPDEV parent 1: classid 1:1 htb rate $URATE ceil $UCEIL $UBURST prio 1 quantum 1514
    $tc c a dev $UPDEV parent 1: classid 1:2 htb rate $URATE ceil $UCEIL $UBURST prio 2 quantum 1514
    $tc q a dev $UPDEV parent 1:2 handle 2: sfq perturb 10 quantum 1514
    $tc f a dev $UPDEV parent 1: prio 10 protocol ip u32
}

#add rule $1 with addr $2 for table $3 with default speed
addrule() {
    ar_id=$(($POOLWIDTH*($3+1)+$1))
    ar_id1=$(($RULECOUNT+$ar_id))
    ar_id2=$(($RULECOUNT*2+$ar_id))
    ar_h1=$(hex $1)
    ar_h3=$(hex $3)
    $tc c a dev $UPDEV parent 1:1 classid 1:$ar_id htb rate $URATE ceil $UCEIL $UBURST prio 1 quantum 1514
    $tc c a dev $UPDEV parent 1:$ar_id classid 1:$ar_id1 htb rate $UHRATE ceil $UCEIL $UBURST prio 2 quantum 1514
    $tc c a dev $UPDEV parent 1:$ar_id classid 1:$ar_id2 htb rate $UHRATE ceil $UCEIL $UBURST prio 1 quantum 1514
    $tc q a dev $UPDEV parent 1:$ar_id1 handle $ar_id1: sfq perturb 10 quantum 1514
    $tc q a dev $UPDEV parent 1:$ar_id2 handle $ar_id2: sfq perturb 10 quantum 1514
    $tc f a dev $UPDEV parent 1: protocol ip prio 1 u32 ht $ar_h3:$ar_h1: \
        match ip tos 0x10 0xff flowid 1:$ar_id2
    $tc f a dev $UPDEV parent 1: protocol ip prio 2 u32 ht $ar_h3:$ar_h1: \
        match ip protocol 6 0xff match u8 0x45 0xff at 0 match u16 0x0000 0xffc0 at 2 \
        match u8 0x10 0xff at 33 flowid 1:$ar_id2
    $tc f a dev $UPDEV parent 1: protocol ip prio 3 u32 ht $ar_h3:$ar_h1: \
        match ip protocol 1 0xff flowid 1:$ar_id2
    $tc f a dev $UPDEV parent 1: protocol ip prio 4 u32 ht $ar_h3:$ar_h1: match ip src $2 \
        flowid 1:$ar_id1
}

#set speed for rule $1 in table $2 with rate $3 kbit
setspeed() {
    ss_id=$(($POOLWIDTH*($2+1)+$1))
    ss_id1=$(($RULECOUNT+$ss_id))
    ss_id2=$(($RULECOUNT*2+$ss_id))
    ss_hr=$(($3/2))
    $tc c r dev $UPDEV parent 1:1 classid 1:$ss_id htb rate ${3}kbit prio 1 quantum 1514
    $tc c r dev $UPDEV parent 1:$ss_id classid 1:$ss_id1 htb rate ${ss_hr}kbit ceil ${3}kbit $UBURST prio 2 quantum 1514
    $tc c r dev $UPDEV parent 1:$ss_id classid 1:$ss_id2 htb rate ${ss_hr}kbit ceil ${3}kbit $UBURST prio 1 quantum 1514
}

#fill table for subnet addr $1, number $2
addtable() {
    nwidth=$(netwidth $1)
    $tc f a dev $UPDEV parent 1:1 prio 10 handle $tctr: protocol ip u32 divisor $nwidth
    $tc f a dev $UPDEV parent 1: protocol ip prio 10 u32 ht 800:: \
        match ip src $(subnet $1) \
        hashkey mask 0x$(hex $(($nwidth-1))) at 12 \
        link $tctr:
    at_t=0
    for at_i in $(lseq $(ip2int $1) $nwidth); do
        addrule $at_t $(int2ip $at_i) $tctr
        at_t=$(($at_t+1))
    done
}

#divide network $1 by subnets
divnet() {
    for dn_net in $@; do
        dn_count=$(subcnt $dn_net)
        if [ $dn_count -gt 1 ]; then
            dn_subnet=$(ip2int $(subnet $dn_net))
            for dn_i in $(lseq 0 $dn_count); do
                echo $(int2ip $((dn_subnet+dn_i*256)))/24
            done
        else
            echo $dn_net
        fi
    done
}

#is ip $2 in subnet $1?
chkip() {
    echo $(ip2int $1) $(ip2int $2) $(netwidth $1)|awk '{print (($2>=$1)&&($2<=$1+$3))}'
}

POOLWIDTH=0
NPOOLS=$(divnet $POOLS)
NETCOUNT=$(echo $NPOOLS|wc -w)
for i in $NPOOLS; do
    POOLWIDTH=$(max $POOLWIDTH $(netwidth $i))
done
RULECOUNT=$(($POOLWIDTH*$NETCOUNT))

tctr=1
case "$1" in
    init)
        initdev
        for i in $NPOOLS; do
            addtable $i
            tctr=$(($tctr+1))
        done;;
    set)
        for i in $NPOOLS; do
            if [ $(chkip $i $2) -eq 1 ]; then
                rulenum=$(($(ip2int $2)-$(ip2int $i)))
                setspeed $rulenum $tctr $3
                exit 0
            fi
            tctr=$(($tctr+1))
        done
        echo "This IP isn't in pool!"
        exit 1;;
    cstat)
        for i in $NPOOLS; do
            if [ $(chkip $i $2) -eq 1 ]; then
                cl1=$(($POOLWIDTH*($tctr+1)+$(ip2int $2)-$(ip2int $i)))
                $tc -s c s dev $UPDEV|grep -A 4 "1:$cl1"
                exit 0
            fi
            tctr=$(($tctr+1))
        done
        echo "This IP isn't in pool!"
        exit 1;;
    stop)
        echo $UPDEVSTOP|sh;;
    *)
        echo Usage: "$0 (init|stop|set <ip> <speed in kbit>|cstat <ip>)";;
esac
Буду на днях прикручивать к тестовому насу для проверки в работе, о результатах отпишусь.

P.S. Не стоит удивляться, если при инициализации правил для подсети /22 скрипт задумается надолго - 1к ип-адресов обрабатываются довольно долго, одних только классов прийдется создать 3к, +4к правил фильтра ;) На тестовом тазике (атлон 900) скрипт создавал 2к правил секунд 30-40. Изменение скорости класса будет происходить заметно быстрее :)

UPD:
На тестовом тазике вроде как красиво работает, пару дней понаблюдаю - и буду потихоньку на основных пппое поднимать, а потом - и на пптп...
Последний раз редактировалось NiTr0 Вт янв 04, 2011 3:37 pm, всего редактировалось 1 раз.

Ali
Сообщения: 24
Зарегистрирован: Чт июл 19, 2007 11:37 am

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение Ali »

пришли плиз данные сколько на одном тазике при таком шейпере у тебя юзеров и сколько внешних каналов кушается с него?
у меня помнится ифб отлично шейпил только до 70мбит..... NAS на accel-pptp

NiTr0
Сообщения: 767
Зарегистрирован: Пт фев 08, 2008 4:46 pm

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение NiTr0 »

Исходящий траффик до 120 мбит доходил; пользователи - до 160 человек на него вешалось... Проц - весьма чахлый (какой-то пентиум Е2ххх); сетевушка - i82576. Тянет легко, с солидным запасом. Ядро 2.6.32.10.

Ali
Сообщения: 24
Зарегистрирован: Чт июл 19, 2007 11:37 am

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение Ali »

NiTr0 писал(а):Исходящий траффик до 120 мбит доходил; пользователи - до 160 человек на него вешалось... Проц - весьма чахлый (какой-то пентиум Е2ххх); сетевушка - i82576. Тянет легко, с солидным запасом. Ядро 2.6.32.10.
а количество килопакетов пришли плиз???
и с аксель-пптп как шуршит? буду пробовать ставить но хочется услышать как оно....

vitaliych
Сообщения: 8
Зарегистрирован: Чт июл 02, 2009 7:43 am

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение vitaliych »

Да, видимо, тема нормального исходящего шейпера все-таки остается актуальной (AsmodeuS-у на заметку, к слову).
Вообще для использования IFB уже вся логика присутсвует в linkupdown, достаточно только правила для входящего траффика применить к IFB, соотвественно поменяв speed_in на speed_out.
И все. Вот что у меня получилось:
Скрипт, инициализирующий инетрфейсы при старте системы:
# cat /usr/abills/libexec/shaper.sh
#!/bin/sh
INT='eth1'
MIRRINT='ifb0'
/sbin/modprobe ifb
/sbin/ip link set dev ${MIRRINT} up
TC="/sbin/tc"
${TC} qdisc del dev ${INT} root &>/dev/null
${TC} qdisc del dev ${INT} ingress &>/dev/null
${TC} qdisc add dev ${INT} root handle 1: htb
${TC} qdisc add dev ${INT} handle ffff: ingress
${TC} qdisc del dev ${MIRRINT} root &>/dev/null
${TC} qdisc add dev ${MIRRINT} root handle 1: htb
${TC} filter add dev ${INT} parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ${MIRRINT}
echo "Shaper UP"
###################

А вот патч к linkupdown:
--- /root/abills/libexec/linkupdown 2010-05-31 16:02:15.000000000 +0300
+++ /usr/abills/libexec/linkupdown 2010-06-15 12:09:58.000000000 +0300
@@ -428,17 +428,20 @@
}
#Linux shaper
elsif ($OS eq 'Linux') {
+ my $MIRRINT = "ifb0";
my $tc = "/sbin/tc";
my $flowid = $speed_in > 0 ? sprintf("%x",$fw_num_out) : '';
my $drop = $speed_out > 0 ? "police rate $speed_out"."Kibit burst 12k drop" : '';

push @FW_ACTIONS, "$tc filter del dev $INTERFACE protocol ip parent 1: prio $fw_num_out &>/dev/null";
- push @FW_ACTIONS, "$tc filter del dev $INTERFACE protocol ip parent ffff: prio $fw_num_in &>/dev/null";
+ push @FW_ACTIONS, "$tc filter del dev $MIRRINT protocol ip parent 1: prio $fw_num_out &>/dev/null";

if ($flowid) {
push @FW_ACTIONS, "$tc class del dev $INTERFACE parent 1: classid 1:$flowid &>/dev/null";
+ push @FW_ACTIONS, "$tc class del dev $MIRRINT parent 1: classid 1:$flowid &>/dev/null";
if ($ACTION eq 'up') {
push @FW_ACTIONS, "$tc class add dev $INTERFACE parent 1: classid 1:$flowid htb rate $speed_in"."Kibit";
+ push @FW_ACTIONS, "$tc class add dev $MIRRINT parent 1: classid 1:$flowid htb rate $speed_out"."Kibit";
}
}

@@ -446,7 +449,7 @@
foreach my $ip_full (@nets_arr) {
if ($ip_full =~ /([!]{0,1})(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/{0,1}(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\d{1,2}):{0,1}(\S{0,200})/ ) {
push @FW_ACTIONS, "$tc filter add dev $INTERFACE protocol ip parent 1: prio $fw_num_out u32 match ip src $2/$3 match ip dst $CLIENT_IP flowid 1:$flowid";
- push @FW_ACTIONS, "$tc filter add dev $INTERFACE protocol ip parent ffff: prio $fw_num_in u32 match ip src $CLIENT_IP match ip dst $2/$3 $drop flowid 1:";
+ push @FW_ACTIONS, "$tc filter add dev $MIRRINT protocol ip parent 1: prio $fw_num_out u32 match ip src $CLIENT_IP match ip dst $2/$3 flowid 1:$flowid";
}
}
}
Все. Для IPN достаточно. Я, правда, не запускаю напрямую linkupdown из биллинга. Сделал так:
debian:~/abills-scripts# cat /usr/abills/libexec/config.pl | grep linkupdown
$conf{IPN_FW_START_RULE}="/usr/bin/sudo /usr/abills/libexec/linkupdown.sh ipn up eth1 %LOGIN %IP";
$conf{IPN_FW_STOP_RULE}="/usr/bin/sudo /usr/abills/libexec/linkupdown.sh ipn down eth1 %LOGIN %IP";

# cat /usr/abills/libexec/linkupdown.sh
#!/bin/bash
EXTIF="eth0"
case "$2" in
up)
/sbin/iptables -I FORWARD -s $5 -i $3 -o $EXTIF -j ACCEPT
/sbin/iptables -I FORWARD -d $5 -o $3 -i $EXTIF -j ACCEPT
;;
down)
_IFS=$IFS;IFS=$'\n'
for rule in `/sbin/iptables-save|grep "^-A.*FORWARD.*$5.*-i.*[$3|${EXTIF}].*-o.*[$3|${EXTIF}].*-j.*ACCEPT"|sed s/-A/-D/`; do
IFS=$_IFS
/sbin/iptables $rule
IFS=$IFS;IFS=$'\n'
done
IFS=$_IFS
;;
esac
/usr/abills/libexec/linkupdown $1 $2 $3 $4 $5

Ну и до кучи для VPN:
# cat /etc/ppp/ip-up.local
#!/bin/bash

MIRRINT="ifb0"
TC="/sbin/tc"
${TC} qdisc del dev ${IFNAME} root &>/dev/null
${TC} qdisc del dev ${IFNAME} ingress &>/dev/null
${TC} qdisc add dev ${IFNAME} root handle 1: htb
${TC} qdisc add dev ${IFNAME} handle ffff: ingress
${TC} filter add dev ${IFNAME} parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ${MIRRINT}
/usr/abills/libexec/linkupdown.sh pppd up $IFNAME $PEERNAME $IPREMOTE

# cat /etc/ppp/ip-down.local
#!/bin/bash
/usr/abills/libexec/linkupdown.sh pppd down $IFNAME $PEERNAME $IPREMOTE

Тесты (с помощью iperf):
VPN, тариф 4Мб/2Мб:
Client connecting to 192.168.0.2, TCP port 5001
TCP window size: 16.0 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.0.1 port 43740 connected with 192.168.0.2 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 4.73 MBytes 3.97 Mbits/sec
debian:~# iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[ 4] local 192.168.56.101 port 5001 connected with 192.168.0.2 port 1520
[ ID] Interval Transfer Bandwidth
[ 4] 0.0-10.0 sec 2.41 MBytes 2.02 Mbits/sec

IPN, тариф 2Мб/1Мб
Client connecting to 10.0.1.2, TCP port 5001
TCP window size: 16.0 KByte (default)
------------------------------------------------------------
[ 3] local 10.0.1.1 port 59725 connected with 10.0.1.2 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.2 sec 2.52 MBytes 2.08 Mbits/sec
debian:~/iperf-2.0.4# iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[ 4] local 10.0.2.15 port 5001 connected with 10.0.1.2 port 1493
[ ID] Interval Transfer Bandwidth
[ 4] 0.0-10.1 sec 1.21 MBytes 1.00 Mbits/sec

Как видим - изменения в биллинге минимальные, все уже сделано до нас AsmodeuS :)

P.S. Система Debian Lenny
# uname -a
Linux debian.local 2.6.26-2-686 #1 SMP Wed May 12 21:56:10 UTC 2010 i686 GNU/Linux
eth0 - мир, eth1 - локалка.
Тестировал на 3 одновременных коннектах к одному серверу. Сферический конь в вакууме, конечно, но на первый взгляд все работает как надо.

NiTr0
Сообщения: 767
Зарегистрирован: Пт фев 08, 2008 4:46 pm

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение NiTr0 »

vitaliych писал(а):Как видим - изменения в биллинге минимальные, все уже сделано до нас AsmodeuS :)
Апну старую тему, не видел каммент в свое время...
В приведенном примере с пользованием шейпера абиллса - шейпер получается без использования хеш-таблиц. Что совсем не хорошо скажется на производительности при 500+ клиентов (в среднем 250+ правил против 4-6 при пользовании таблиц).

vitaliych
Сообщения: 8
Зарегистрирован: Чт июл 02, 2009 7:43 am

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение vitaliych »

Ну насчет производительности не скажу. Я ставил задачу внести минимальные изменения в логику биллинга. Это получилось. Насколько изменится нагрузка при 500+ - не знаю, даже не представляю себе методику моделирования такой нагрузки.

NiTr0
Сообщения: 767
Зарегистрирован: Пт фев 08, 2008 4:46 pm

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение NiTr0 »

vitaliych писал(а):Я ставил задачу внести минимальные изменения в логику биллинга.
Ну в моем случае в логику биллинга вообще изменений не вносилось :) Ибо шейпер-то к биллингу вообще не прикручен, он прикручен напрямую к pppd, причем - на удаленной машине...

sergei_kzn
Сообщения: 2
Зарегистрирован: Сб сен 03, 2011 5:05 am

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение sergei_kzn »

прив, сорри что древнюю тему поднимаю :) но есть по ней вопросы.
почему в корневом классе указывается тоже $URATE? разве он не должен быть больше суммы $URATE дочерних классов?
и еще я пытаюсь повесить этот шейпер на интерфейс, который смотрит в локалку, то бишь все src становятся dst. я тупо сделал s/src/dst/g и там где указывается hashkey изменил 12 на 16 (вроде dst должен быть). не работает, моб я что-то просмотрел?

~AsmodeuS~
Site Admin
Сообщения: 5746
Зарегистрирован: Пт янв 28, 2005 3:11 pm
Контактная информация:

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение ~AsmodeuS~ »

уже все включено в linkupdown давно

sergei_kzn
Сообщения: 2
Зарегистрирован: Сб сен 03, 2011 5:05 am

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение sergei_kzn »

Я сейчас не использую билинговую систему, меня интересует сам шейпер. Хочу разобраться с ним

NiTr0
Сообщения: 767
Зарегистрирован: Пт фев 08, 2008 4:46 pm

Re: "Правильный" шейпер исходящего траффика, проба №2. Хеши

Сообщение NiTr0 »

sergei_kzn писал(а):прив, сорри что древнюю тему поднимаю :) но есть по ней вопросы.
почему в корневом классе указывается тоже $URATE? разве он не должен быть больше суммы $URATE дочерних классов?
rate не может быть больше исходящего канала принципиально. Теряется смысл шейпера. Это во-первых.
Во-вторых - да, правильнее было бы зарезать rate до некоего минимума на подклассах, и ограничивать ceil. Но это имеет смысл только если есть только 1 мощный тазик-шейпер. У меня же все крутится на нескольких насах, смысла делать "правильно" особо нет. При оверкоммите все равно приоритезироваться/ограничиваться будет на бордюре.
В третьих, при такой схеме вполне получается то, что мне нужно - клиенту выделяется полоса не более rate.
sergei_kzn писал(а):и еще я пытаюсь повесить этот шейпер на интерфейс, который смотрит в локалку, то бишь все src становятся dst. я тупо сделал s/src/dst/g и там где указывается hashkey изменил 12 на 16 (вроде dst должен быть). не работает, моб я что-то просмотрел?
Может что-то пропустили. Читайте LARTC, смотрите что у вас вышло.

P.S. обновленная/правленая версия входит в [urlhttp://leaf.sourceforge.net]LEAF[/url] - поправил кое-какие мелочи, подточил инициализацию на нескольких интерфейсах и т.п.

Ответить