Думаю, в комментариях не нуждается, если не прав, поправьте. Прикрутить к действующему шейперу - думаю, тоже проблемы не составит; один совет - не забывать устанавливать скорость и для клиентов без ограничений по скорости, иначе - клиент внезапно может узнать, что у него скорость при каждом коннекте разная, в зависимости от того, насколько жадный абонент пользовал этот ип ранее...
Итак, 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:
На тестовом тазике вроде как красиво работает, пару дней понаблюдаю - и буду потихоньку на основных пппое поднимать, а потом - и на пптп...