Primeiramente, vamos criar o script que será utilizado na inicialização do CBQ.
# vi /sbin/script_cbq.sh
#!/bin/sh
PATH="/bin:/sbin:/usr/bin:/usr/sbin"
CBQ_PATH="/etc/cbq"
### Uncomment for debugging
#LOG_FILE="/var/run/cbq-$1"
if [ -n "$LOG_FILE" ]; then
### Initialize log file
echo "# `date`" > $LOG_FILE
### Logging equivalent of "ip" command
ip () {
[ -z "$LOG_FILE" ] && { /sbin/ip "$@"; return; }
echo -e "
ip $@
" >> $LOG_FILE
/sbin/ip "$@" 2>&1 | tee -a $LOG_FILE
} # ip
### Logging equivalent of "tc" command
tc () {
[ -z "$LOG_FILE" ] && { /sbin/tc "$@"; return; }
echo -e "
tc $@
" >> $LOG_FILE
/sbin/tc "$@" 2>&1 | tee -a $LOG_FILE
} # tc
fi # command logging
### Remove CBQ from all devices
cbq_off () {
# for dev in `ip link| sed -n '/^[0-9]/ { s/^[0-9]+:
#([a-z0-9]+)[:@].*/1/; p; }'`; do
# cbq_device_off $dev
for dev in `ifconfig |grep eth1 |awk {'print$1'}`; do
cbq_device_off $dev
done
return
} # cbq_off
### Remove root class from device $1
cbq_device_off () {
tc qdisc del dev $1 root &>/dev/null
return
} # cbq_device_off
### Display CBQ setup
cbq_show () {
for dev in $DEVICES; do
echo ---[ $dev: configured classes ]---------------------------
echo; tc $1 class show dev $dev; echo
echo ---[ $dev: queueing disciplines ]-------------------------
echo; tc $1 qdisc show dev $dev; echo
done
} # cbq_show
### Check configuration and load DEVFIELDS/CLASSLIST
cbq_init () {
### Check configuration in $CBQ_PATH directory and get CLASSLIST
CLASSLIST=`find $CBQ_PATH -name 'cbq-*' -maxdepth 1 -printf "%f
"| sort`
if [ -z "$CLASSLIST" ]; then
echo "**CBQ: not configured in $CBQ_PATH!"
exit
fi
### Collect all DEVICE fields from $CBQ_PATH/cbq-*
DEVFIELDS=`find $CBQ_PATH -name 'cbq-*' -maxdepth 1 -exec sed -ne
's/#.*//; s/ //g; /^DEVICE=.*,.*,.*/ { s/.*=//; p; q; };
/^DEVICE=/ q' {} ;| sort -u`
### Check if there are any devices to set up
if [ -z "$DEVFIELDS" ]; then
echo "**CBQ: can't find any DEVICE field in $CBQ_PATH/cbq-*!"
exit
fi
### Extract all device names from DEVICE fields in $CBQ_PATH/cbq-*
DEVICES=`echo "$DEVFIELDS"| sed 's/,.*//'| sort -u`
### Check for multiple devices with different DEVICE fields
if [ `echo "$DEVICES"| wc -l` -ne `echo "$DEVFIELDS"| wc -l` ]; then
echo "**CBQ: multiple different DEVICE fields for one device found!"
echo "$DEVFIELDS"
exit
fi
} # cbq_init
### Load class configuration from file $1
cbq_load_class () {
CNAME="$CBQ_PATH/$1"
CFILE=`sed -e 's/#.*//; s/ //g; /^$/ d' $CNAME`
CLASS=`echo $1| sed 's/^cbq-0*//; s/..*//'`
if [ `/usr/bin/printf "%d" 0x$CLASS` -le 1 ]; then
echo "**CBQ: class ID of $1 must be > 1!"
cbq_off
exit
fi
### Device parameters
DEVICE=`echo "$CFILE"| sed -n '/^DEVICE=/ { s/.*=//; s/,.*//; p; q; }'`
BANDWIDTH=`echo "$DEVFIELDS"| sed -n "/^$DEVICE,/ { s/.*,(.*),.*/1/; p; q; }"`
### Class parameters
CLASSID="1:$CLASS"
PARENT=`echo "$CFILE"| sed -n '/^PARENT=/ { s/.*=0*//; p; q; }'`
[ -z "$PARENT" ] && PARENT="1:1" || PARENT="1:$PARENT"
LEAF=`echo "$CFILE"| sed -n '/^LEAF=/ { s/.*=//; p; q; }'`
[ -z "$LEAF" ] && LEAF="tbf"
BOUNDED=`echo "$CFILE"| sed -n '/^BOUNDED=/ { s/.*=//; p; q; }'`
[ "$BOUNDED" = "no" ] && BOUNDED="" || BOUNDED="bounded"
ISOLATED=`echo "$CFILE"| sed -n '/^ISOLATED=/ { s/.*=//; p; q; }'`
[ "$ISOLATED" = "yes" ] && ISOLATED="isolated" || ISOLATED=""
PRIO=`echo "$CFILE"| sed -n '/^PRIO=/ { s/.*=//; p; q; }'`
RATE=`echo "$CFILE"| sed -n '/^RATE=/ { s/.*=//; p; q; }'`
WEIGHT=`echo "$CFILE"| sed -n '/^WEIGHT=/ { s/.*=//; p; q; }'`
if [ -z "$RATE" -o -z "$WEIGHT" -o -z "$PRIO" ]; then
echo "**CBQ: missing RATE, WEIGHT or PRIO field(s) in $1!"
cbq_off
exit
fi
### Leaf qdisc parameters for TBF
if [ "$LEAF" = "tbf" ]; then
BUFFER=`echo "$CFILE"| sed -n '/^BUFFER=/ { s/.*=//; p; q; }'`
[ -z "$BUFFER" ] && BUFFER="10Kb/8"
LIMIT=`echo "$CFILE"| sed -n '/^LIMIT=/ { s/.*=//; p; q; }'`
[ -z "$LIMIT" ] && LIMIT="15Kb"
PEAK=`echo "$CFILE"| sed -n '/^PEAK=/ { s/.*=//; p; q; }'`
[ -n "$PEAK" ] && PEAK="peakrate $PEAK"
MTU=`echo "$CFILE"| sed -n '/^MTU=/ { s/.*=//; p; q; }'`
[ -z "$MTU" ] && MTU="1500"
elif [ "$LEAF" = "sfq" ]; then
PERTURB=`echo "$CFILE"| sed -n '/^PERTURB=/ { s/.*=//; p; q; }'`
[ -n "$PERTURB" ] && PERTURB="perturb $PERTURB"
QUANTUM=`echo "$CFILE"| sed -n '/^QUANTUM=/ { s/.*=//; p; q; }'`
[ -n "$QUANTUM" ] && QUANTUM="quantum $QUANTUM"
elif [ "$LEAF" = "cbq" ]; then
echo "**CBQ: class $1, leaf qdisc CBQ not yet supported!"
fi
return 0
} # cbq_load_class
### Check if ip-route is installed
if [ ! -f /sbin/tc -o ! -f /sbin/ip ]; then
echo "**CBQ: ip-route2 utilities not installed!"
exit
fi
###########################
# See how were we called #
###########################
case "$1" in
### START ###
start)
### If you have cbq, tbf and u32 compiled into kernel, comment it out
for module in sch_cbq sch_tbf sch_sfq sch_prio cls_u32; do
if ! modprobe $module; then
echo "**CBQ: could not load module $module"
exit
fi
done
###################################
# Get all devices from configuration files $CBQ_PATH/cbq-*#
# and setup CBQ root classes for them (if it is possible).#
###################################
### Load DEVICES, DEVFIELDS and CLASSLIST
cbq_init
### Try to discover interface bandwidth from DEVICE
### field and if OK - setup root class for this one
for dev in $DEVICES; do
### Retrieve device bandwidth and weight
DEVTEMP=`echo "$DEVFIELDS"| sed -n "/^$dev,/ { s/.*,(.*),(.*)/1,2/; p;
q; }"`
DEVBWDT=${DEVTEMP%%,*}
DEVWGHT=${DEVTEMP##*,}
### If correctly set and the device is up, setup root class
if [ -n "$DEVBWDT" -a -n "$DEVWGHT" ]; then
if ! ip link | grep -q "$dev[:@].*UP"; then
echo "**CBQ: could not find device $dev! CBQ turned off."
cbq_off
exit
fi
### Remove old root class from device
cbq_device_off $dev
### Setup root class (queueing discipline) for device
tc qdisc add dev $dev root handle 1:0 cbq
bandwidth $DEVBWDT avpkt 1000 cell 8
### Create parent class :1. Every shaper will use it as
### parent unless specified otherwise using PARENT=xxxx
tc class add dev $dev parent 1:0 classid 1:1 cbq
bandwidth $DEVBWDT rate $DEVBWDT weight $DEVWGHT
prio 8 allot 1514 cell 8 maxburst 20 avpkt 1000
else
echo "**CBQ: could not determine bandwidth or weight for device $dev!"
echo "**CBQ: setup DEVICE field properly!"
exit
fi
done # device
##################################################
# Set up all classes described in $CBQ_PATH/cbq-*#
##################################################
for classfile in $CLASSLIST; do
cbq_load_class $classfile
### Create class and setup leaf qdisc
tc class add dev $DEVICE parent $PARENT classid $CLASSID cbq
bandwidth $BANDWIDTH rate $RATE weight $WEIGHT prio $PRIO
allot 1514 cell 8 maxburst 20 avpkt 1000 $BOUNDED $ISOLATED
### Setup leaf queueing discipline
if [ "$LEAF" = "tbf" ]; then
tc qdisc add dev $DEVICE parent $CLASSID tbf
rate $RATE buffer $BUFFER limit $LIMIT mtu $MTU $PEAK
elif [ "$LEAF" = "sfq" ]; then
tc qdisc add dev $DEVICE parent $CLASSID sfq
$PERTURB $QUANTUM
elif [ "$LEAF" = "cbq" ]; then
:
fi
### Create u32 filter for addresses specified by RULE fields
RULESET=`echo "$CFILE"| sed -n '/^RULE/ { s/.*=//; p; }'`
[ -z "$RULESET" ] && continue
### Rules present, parse them
for rule in $RULESET; do
u32_s=""; u32_d=""
SADDR=""; SPORT=""
### Split up destination
DST=${rule##*,}
DADDR=${DST%%:*}
[ "$DADDR" != "$DST" ] && DPORT=${DST##*:} || DPORT=""
[ "$DADDR" = "*" ] && DADDR=""
### Split up source (if specified)
if [ "$DST" != "$rule" ]; then
SRC=${rule%%,*}
SADDR=${SRC%%:*}
[ "$SADDR" != "$SRC" ] && SPORT=${SRC##*:}
[ "$SADDR" = "*" ] && SADDR=""
fi
### Compose the u32 filter rules
[ -n "$SPORT" ] && u32_s="match ip sport $SPORT 0xffff"
[ -n "$SADDR" ] && u32_s="match ip src $SADDR $u32_s"
[ -n "$DPORT" ] && u32_d="match ip dport $DPORT 0xffff"
[ -n "$DADDR" ] && u32_d="match ip dst $DADDR $u32_d"
### Uncomment the following if you want to see parsed rules
# echo "$rule: $u32_s $u32_d"
### Attach u32 filter to the appropriate class
tc filter add dev $DEVICE parent 1:0 protocol ip
prio 100 u32 $u32_s $u32_d flowid $CLASSID
done ### rule
done ### class file
;;
### TIMECHECK ###
timecheck)
### Load DEVICES, DEVFIELDS and CLASSLIST
cbq_init
### Current time in hh:mm format
TIME_NOW=`date +%k:%M`
TIME_ABS=$[${TIME_NOW%%:*}*60 + ${TIME_NOW##*:}]
### Check every config file for TIME parameter
for classfile in $CLASSLIST; do
TIMERATES=`sed -ne 's/#.*//; s/ //g; /^TIME/ { s/.*=//; p; }'
$CBQ_PATH/$classfile`
[ -z "$TIMERATES" ] && continue
MATCH=0; CHANGE=0;
for timerate in $TIMERATES; do
### Split up TIME parameter
INTERVAL=${timerate%%;*}; PARAMS=${timerate##*;}
BEG_TIME=${INTERVAL%%-*}; END_TIME=${INTERVAL##*-}
### Compute interval boundaries
BEG_ABS=$[${BEG_TIME%%:*}*60 + ${BEG_TIME##*:}]
END_ABS=$[${END_TIME%%:*}*60 + ${END_TIME##*:}]
### Midnight wrap fixup
if [ $BEG_ABS -gt $END_ABS ]; then
[ $TIME_ABS -le $END_ABS ] && TIME_ABS=$[TIME_ABS + 24*60]
END_ABS=$[END_ABS + 24*60]
fi
### If the time matches, remembers params and set flag
if [ $TIME_ABS -ge $BEG_ABS -a $TIME_ABS -lt $END_ABS ]; then
TMP_RATE=${PARAMS%%/*}
TMP_WGHT=${PARAMS#*/}
TMP_PEAK=${TMP_WGHT#*/}
[ "$TMP_PEAK" = "$TMP_WGHT" ] && TMP_PEAK="" || TMP_WGHT={$TMP_WGHT%%/*}
[ -n "$TMP_PEAK" ] && TMP_PEAK="peakrate $TMP_PEAK"
MATCH=1
fi
done ### timerate
cbq_load_class $classfile
### Get current RATE of CBQ class
RATE_NOW=`tc class show dev $DEVICE| sed -n
"/cbq $CLASSID / { s/.*rate //; s/ .*//; p; q; }"`
[ -z "$RATE_NOW" ] && continue
### Time interval match is found
if [ $MATCH -ne 0 ]; then
### Check if there is any change in class RATE
if [ "$RATE_NOW" != "$TMP_RATE" ]; then
NEW_RATE="$TMP_RATE"
NEW_WGHT="$TMP_WGHT"
NEW_PEAK="$TMP_PEAK"
CHANGE=1
fi
### Match not found, reset to default RATE if necessary
elif [ "$RATE_NOW" != "$RATE" ]; then
NEW_WGHT="$WEIGHT"
NEW_RATE="$RATE"
NEW_PEAK="$PEAK"
CHANGE=1
fi
### If there's a change, replace CBQ class and leaf qdisc
[ $CHANGE -ne 1 ] && continue
### Get leaf qdisc handle
LEAF_HND=`tc class show dev $DEVICE| sed -n
"/cbq $CLASSID .* leaf / { s/.*leaf //; s/ .*//; p; q; }"`
[ -z "$LEAF_HND" ] && continue
### Replace CBQ class
tc class replace dev $DEVICE classid $CLASSID cbq
bandwidth $BANDWIDTH rate $NEW_RATE weight $NEW_WGHT prio $PRIO
allot 1514 cell 8 maxburst 20 avpkt 1000 $BOUNDED $ISOLATED
### Replace leaf qdisc
if [ "$LEAF" = "tbf" ]; then
tc qdisc replace dev $DEVICE handle $LEAF_HND tbf
rate $NEW_RATE buffer $BUFFER limit $LIMIT mtu $MTU $NEW_PEAK
elif [ "$LEAF" = "sfq" ]; then
### SFQ does not support parameter changes
### yet so it does not need replacing
#tc qdisc replace dev $DEVICE handle $LEAF_HND sfq
#$PERTURB $QUANTUM
:
elif [ "$LEAF" = "cbq" ]; then
:
fi
echo "**CBQ: $TIME_NOW: class $CLASS on $DEVICE changed rate ($RATE_NOW ->
$NEW_RATE)"
done ### class file
5
;;
### STOP ###
stop)
cbq_off
;;
### RESTART ###
restart)
$0 stop
$0 start
;;
### LIST ###
list)
cbq_init
cbq_show
;;
### STATS ###
stats)
cbq_init
cbq_show -s
;;
### default ###
*)
echo "Usage: " `basename $0` "{start|stop|restart|timecheck|list|stats}"
esac
Para que o CBQ funcione, é preciso criar os arquivos que serão lidos para verificar a banda. Faça assim:
# mkdir /etc/cbq
Dentro dele crie os seguintes arquivos que facilitarão a criação dos arquivos de banda:
# vi geracbq.sh
#!/bin/bash
# x = número da seqüência
x=1
for i in `cat rede.txt`; do
# Regra de Limite Interface Eth1 ( INTERNA )
x=`expr ${x} + 1`
#info="`echo "${i}" | sed -e 's///_/g'`_64Kbits_eth1"
info="`echo "${i}" | sed -e 's///_/g'`"
echo "DEVICE=eth1,10Mbit,1Mbit
RATE=64Kbit
WEIGHT=6Kbit
PRIO=5
RULE=${i}
BOUNDED=yes
ISOLATED=yes
" >> cbq-0${x}.${info}
# Regra de Limite Interface Eth1.out
x=`expr ${x} + 1`
info="`echo "${i}" | sed -e 's///_/g'`_out"
echo "DEVICE=eth0,10Mbit,1Mbit
RATE=64Kbit
WEIGHT=6Kbit
PRIO=5
MARK=${x}
BOUNDED=yes
ISOLATED=no
" >> cbq-0${x}.${info}
echo "iptables -t mangle -A FORWARD -s ${i} -j MARK --set-mark ${x}" >> /etc/cbq/mangle
echo "iptables -t mangle -A FORWARD -s ${i} -j ACCEPT" >> /etc/cbq/mangle
echo "iptables -t mangle -A FORWARD -d ${i} -j MARK --set-mark ${x}" >> /etc/cbq/mangle
echo "iptables -t mangle -A FORWARD -d ${i} -j ACCEPT" >> /etc/cbq/mangle
done
Onde:
- eth0 - Rede externa
- eth1 - Rede interna
O segredo aqui está no MARK juntamente com o mangle, onde amarro o for feito pelo {x} nos dois.
Crie o arquivo rede.txt (altere para sua estrutura)
192.168.0.2
192.168.0.3
192.168.0.4
192.168.0.5
192.168.0.6
192.168.0.7
192.168.0.8
192.168.0.9
192.168.0.10
No exemplo acima ele criará 20 arquivos, 10 para download e 10 para upload.
Feito isto, agora apague ou mova para outro diretório os arquivos rede.txt e o geracbq.sh.