#!/bin/bash # # v0.1 (c) Pavel Golubev , 1998 # # README # ------ # # First of all - this is just a SIMPLE EXAMPLE of CBQ power. # Don't ask me "why" and "how" :) # # This is an example of using CBQ (Class Based Queueing) and policy-based # filter for building smart ethernet shapers. All CBQ parameters are # correct only for ethernet (eth0,1,2..) linux interfaces. It was tested # on 2.1.125-2.1.129 linux kernels (KSI linux, Nostromo version) and # ip-route utility by A.Kuznetsov (iproute2-ss981101 version). # You can download ip-route from ftp://ftp.inr.ac.ru/ip-routing or # get iproute2*.rpm (compiled with glibc) from ftp.ksi-linux.com/pub/Devel. # # # HOW IT WORKS # # Each shaper must be described by config file in $CBQ_PATH # (/etc/sysconfig/cbq/) directory - one config file for each CBQ shaper. # # Some words about config file name: # Each shaper has its personal ID - two byte HEX number. Really ID is # CBQ class. # So, filename looks like: # # cbq-1280.My_first_shaper # ^^^ ^^^ ^^^^^^^^^^^^^ # | | |______ Shaper name - any word # | |___________________ ID (0000-FFFF), let ID looks like shaper's rate # |______________________ Filename must begin from "cbq-" # # # Config file describes shaper parameters and source[destination] # address[port]. # For example let's prepare /etc/sysconfig/cbq/cbq-1280.My_first_shaper: # # ----------8<--------------------- # DEVICE=eth0,10Mbit,1Mbit # RATE=128Kbit # WEIGHT=10Kbit # PRIO=5 # RULE=192.168.1.0/24 # ----------8<--------------------- # # This is minimal configuration, where: # DEVICE: eth0 - device where we do control our traffic # 10Mbit - REAL ethernet card bandwidth # 1Mbit - "weight" of :1 class (parent for all shapers for eth0), # as a rule of thumb weight=batdwidth/10. # 100Mbit adapter's example: DEVICE=eth0,100Mbit,10Mbit # *** If you want to build more than one shaper per device it's # enough to describe bandwidth and weight once - cbq.init # is smart :) You can put only 'DEVICE=eth0' into cbq-* # config file for eth0. # # RATE: Shaper's speed - Kbit,Mbit or bps (bytes per second) # # WEIGHT: "weight" of shaper (CBQ class). Like for DEVICE - approx. RATE/10 # # PRIO: shaper's priority from 1 to 8 where 1 is the highest one. # I do always use "5" for all my shapers. # # RULE: [source addr][:source port],[dest addr][:dest port] # Some examples: # RULE=10.1.1.0/24:80 - all traffic for network 10.1.1.0 to port 80 # will be shaped. # RULE=10.2.2.5 - shaper works only for IP address 10.2.2.5 # RULE=:25,10.2.2.128/25:5000 - all traffic from any address and port 25 to # address 10.2.2.128 - 10.2.2.255 and port 5000 # will be shaped. # RULE=10.5.5.5:80, - shaper active only for traffic from port 80 of # address 10.5.5.5 # Multiple RULE fields per one config file are allowed. For example: # RULE=10.1.1.2:80 # RULE=10.1.1.2:25 # RULE=10.1.1.2:110 # # *** ATTENTION!!! # All shapers do work only for outgoing traffic! # So, if you want to build bidirectional shaper you must set it up for # both ethernet card. For example let's build shaper for our linux box like: # # --------- 192.168.1.1 # BACKBONE -----eth0-| linux |-eth1------*[our client] # --------- # # Let all traffic from backbone to client will be shaped at 28Kbit and # traffic from client to backbone - at 128Kbit. We need two config files: # # ---8<-----/etc/sysconfig/cbq/cbq-28.client-out---- # DEVICE=eth1,10Mbit,1Mbit # RATE=28Kbit # WEIGHT=2Kbit # PRIO=5 # RULE=192.168.1.1 # ---8<--------------------------------------------- # # ---8<-----/etc/sysconfig/cbq/cbq-128.client-in---- # DEVICE=eth0,10Mbit,1Mbit # RATE=128Kbit # WEIGHT=10Kbit # PRIO=5 # RULE=192.168.1.1, # ---8<--------------------------------------------- # ^pay attention to "," - this is source address! # # Enjoy. ############################################################################# PATH="/bin:/sbin:/usr/bin:/usr/sbin" CBQ_PATH="/etc/sysconfig/cbq" ### Function for remove CBQ from all devices cbq_off () { for dev in `ip link|grep ^[0-9]|cut -d ":" -f2`; do tc qdisc del dev $dev root done return } ### Function for remove root class from device $dev cbq_device_off () { tc qdisc del dev $dev root return } ### Check if ip-route is installed if [ ! -f /sbin/tc -o ! -f /sbin/ip ]; then echo "**CBQ: ip-route utilities is not installed!" exit fi case "$1" in start) #### START ##### ### Loading modules. ### If you have cbq,tbf,u32 compiled into kernel - comment it: for cbqmodule in sch_cbq sch_tbf cls_u32; do if ! modprobe $cbqmodule; then echo "**CBQ: can't load module $cbqmodule" exit fi done ######################################################################## # Get all devices from configuration files $CBQ_PATH/cbq-* # # and setup CBQ root classes for them (if it is possible). # ######################################################################## ### Collect all DEVICE fields from $CBQ_PATH/cbq-* if [ "`ls $CBQ_PATH/cbq-*`" != "" ]; then DEVFIELDS=`grep -h ^DEVICE $CBQ_PATH/cbq-*|cut -d "=" -f 2 |\ sed 's/ //g' | sort -u` else echo "**CBQ: not configured in $CBQ_PATH/ !" exit fi ### Extract all CBQ devices from DEVICE fields in $CBQ_PATH/cbq-* if [ "$DEVFIELDS" == "" ];then echo "**CBQ: can't find any DEVICE field in $CBQ_PATH/cbq-*!" exit else DEVICES=`grep -h ^DEVICE $CBQ_PATH/cbq-*|cut -d "=" -f 2 |\ sed 's/ //g' | cut -d "," -f 1 | sort -u` fi ### try to discover interface bandwidth from DEVICE field ### and if OK - setup root class for this one for dev in $DEVICES ; do ### Check for BANDWIDTH and WEIGHT and if correct - setup it BANDWIDTH=`echo "$DEVFIELDS"|grep $dev|grep ","|cut -d "," -f2` WEIGHT=`echo "$DEVFIELDS"|grep $dev|grep ","|cut -d "," -f3` if [ "$BANDWIDTH" != "" -a \ "`echo "$BANDWIDTH"|wc -l|sed 's/ //g'`" == "1" -a \ "$WEIGHT" != "" -a \ "`echo "$WEIGHT"|wc -l|sed 's/ //g'`" == "1" ]; then if [ "`ip link|grep "$dev"|grep UP`" == "" ]; then echo "**CBQ: can't find device $dev!" echo "**CBQ: turned OFF." cbq_off exit fi ### Delete old root class, if present cbq_device_off ### Setup root class for device $dev tc qdisc add dev $dev root handle 1`echo $dev|sed 's/[a-z,A-Z]//g'`: \ cbq bandwidth $BANDWIDTH avpkt 1000 cell 8 ### Make parent class :1. Every shaper will use it as parent. tc class add dev $dev parent 1`echo $dev|sed 's/[a-z,A-Z]//g'`:0 \ classid 1`echo $dev|sed 's/[a-z,A-Z]//g'`:1 cbq \ bandwidth $BANDWIDTH rate $BANDWIDTH allot 1514 cell 8 \ weight $WEIGHT prio 8 maxburst 20 avpkt 1000 else echo "**CBQ: can't discover $dev bandwidth or weight." echo "**CBQ: Setup DEVICE field!" exit fi done ####################################################################### # Setting up all classes described in $CBQ_PATH/cbq-* # ####################################################################### ### Extract all class ID from filenames in $CBQ_PATH/ CLASSLIST=`cd $CBQ_PATH;ls -1 cbq-*` for config_cbq in $CLASSLIST; do CLASS=`echo "$config_cbq"|cut -d "-" -f 2|cut -d "." -f 1` if [ "$CLASS" == "0" -o "$CLASS" == "1" ]; then echo "**CBQ: CLASS in $CBQ_PATH/$config_cbq must be >1 !" exit fi DEVICE=`grep ^DEVICE $CBQ_PATH/$config_cbq| \ cut -d "=" -f 2|cut -d "," -f 1` BANDWIDTH=`echo "$DEVFIELDS"|grep $dev|grep ","|cut -d "," -f2` ROOT="1`echo $DEVICE|sed 's/[a-z,A-Z]//g'`" CLASSID="$ROOT:$CLASS" RATE=`grep ^RATE $CBQ_PATH/$config_cbq| \ cut -d "=" -f 2` WEIGHT=`grep ^WEIGHT $CBQ_PATH/$config_cbq| \ cut -d "=" -f 2` PRIO=`grep ^PRIO $CBQ_PATH/$config_cbq| \ cut -d "=" -f 2` if [ "$RATE" == "" -o "$WEIGHT" == "" -o "$PRIO" == "" ]; then echo "**CBQ: Absent RATE,WEIGHT or PRIO field(s)" echo "**CBQ: in $CBQ_PATH/$config_cbq !" echo "**CBQ: CBQ terminated!" cbq_off exit fi BUFFER=`grep ^BUFFER $CBQ_PATH/$config_cbq|\ cut -d "=" -f 2` if [ "$BUFFER" == "" ]; then BUFFER="10Kb/8" fi LIMIT=`grep ^LIMIT $CBQ_PATH/$config_cbq|\ cut -d "=" -f 2` if [ "$LIMIT" == "" ]; then LIMIT="15Kb" fi RULE=`grep ^RULE $CBQ_PATH/$config_cbq| \ cut -d "=" -f 2` ### Create class and detup TBF tc class add dev $DEVICE parent $ROOT:1 classid $CLASSID cbq \ bandwidth $BANDWIDTH rate $RATE allot 1514 cell 8 weight $WEIGHT \ prio $PRIO maxburst 20 avpkt 1000 bounded tc qdisc add dev $DEVICE parent $CLASSID tbf rate $RATE \ buffer $BUFFER limit $LIMIT ### Create u32 filter for DEST/SOURCE address/port (if RULE field is present) if [ "$RULE" != "" ]; then for this_rule in $RULE; do if [ "`echo $this_rule|grep ","`" != "" ]; then SOURCE=`echo $this_rule|cut -d "," -f 1` DEST=`echo $this_rule|cut -d "," -f 2` if [ "`echo $SOURCE|grep ":"`" != "" ]; then SADDR=`echo $SOURCE|cut -d ":" -f 1` SPORT=`echo $SOURCE|cut -d ":" -f 2` if [ "$SADDR" != "" -a "$SADDR" != "*" ]; then if [ "$SPORT" != "" -a "$SPORT" != "*" ]; then u32_s="match ip src $SADDR match ip sport $SPORT 0xffff" else u32_s="match ip src $SADDR" fi else if [ "$SPORT" != "" -a "$SPORT" != "*" ]; then u32_s="match ip sport $SPORT 0xffff" fi fi else if [ "$SOURCE" != "" -a "$SOURCE" != "*" ]; then u32_s="match ip src $SOURCE" else u32_s="" fi fi if [ "`echo $DEST|grep ":"`" != "" ]; then DADDR=`echo $DEST|cut -d ":" -f 1` DPORT=`echo $DEST|cut -d ":" -f 2` if [ "$DADDR" != "" -a "$DADDR" != "*" ]; then if [ "$DPORT" != "" -a "$DPORT" != "*" ]; then u32_d="match ip dst $DADDR match ip dport $DPORT 0xffff" else u32_d="match ip src $DADDR" fi else if [ "$DPORT" != "" -a "$DPORT" != "*" ]; then u32_d="match ip sport $DPORT 0xffff" fi fi else if [ "$DEST" != "" -a "$DEST" != "*" ]; then u32_s="match ip dst $DEST" else u32_d="" fi fi else if [ "`echo $this_rule|grep ":"`" != "" ]; then DADDR=`echo $this_rule|cut -d ":" -f 1` DPORT=`echo $this_rule|cut -d ":" -f 2` if [ "$DADDR" != "" -a "$DADDR" != "*" ]; then if [ "$DPORT" != "" -a "$DPORT" != "*" ]; then u32_d="match ip dst $DADDR match ip dport $DPORT 0xffff" else u32_d="match ip dst $DADDR" fi else if [ "$DPORT" != "" -a "$DPORT" != "*" ]; then u32_d="match ip dport $DPORT 0xffff" fi fi else u32_d="match ip dst $this_rule" fi fi ### Uncomment this if you want to see all parsed rules # echo "$u32_s $u32_d" tc filter add dev $DEVICE parent $ROOT:0 protocol ip \ prio 100 u32 $u32_s $u32_d flowid $CLASSID u32_s="";u32_d="" done fi done ;; stop) cbq_off ;; *) echo "Usage: cbq.init {start|stop}" esac