1#!/bin/bash 2# 3# cbq.init v0.7.3 4# Copyright (C) 1999 Pavel Golubev <pg@ksi-linux.com> 5# Copyright (C) 2001-2004 Lubomir Bulej <pallas@kadan.cz> 6# 7# chkconfig: 2345 11 89 8# description: sets up CBQ-based traffic control 9# 10# This program is free software; you can redistribute it and/or modify 11# it under the terms of the GNU General Public License as published by 12# the Free Software Foundation; either version 2 of the License, or 13# (at your option) any later version. 14# 15# This program is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU General Public License for more details. 19# 20# You should have received a copy of the GNU General Public License 21# along with this program; if not, see <http://www.gnu.org/licenses/>. 22# 23# To get the latest version, check on Freshmeat for actual location: 24# 25# http://freshmeat.net/projects/cbq.init 26# 27# 28# VERSION HISTORY 29# --------------- 30# v0.7.3- Deepak Singhal <singhal at users.sourceforge.net> 31# - fix timecheck to not ignore regular TIME rules after 32# encountering a TIME rule that spans over midnight 33# - Nathan Shafer <nicodemus at users.sourceforge.net> 34# - allow symlinks to class files 35# - Seth J. Blank <antifreeze at users.sourceforge.net> 36# - replace hardcoded ip/tc location with variables 37# - Mark Davis <mark.davis at gmx.de> 38# - allow setting of PRIO_{MARK,RULE,REALM} in class file 39# - Fernando Sanch <toptnc at users.sourceforge.net> 40# - allow underscores in interface names 41# v0.7.2- Paulo Sedrez 42# - fix time2abs to allow hours with leading zero in TIME rules 43# - Svetlin Simeonov <zvero at yahoo.com> 44# - fix cbq_device_list to allow VLAN interfaces 45# - Mark Davis <mark.davis at gmx.de> 46# - ignore *~ backup files when looking for classes 47# - Mike Boyer <boyer at administrative.com> 48# - fix to allow arguments to be passed to "restart" command 49# v0.7.1- Lubomir Bulej <pallas at kadan.cz> 50# - default value for PERTURB 51# - fixed small bug in RULE parser to correctly parse rules with 52# identical source and destination fields 53# - faster initial scanning of DEVICE fields 54# v0.7 - Lubomir Bulej <pallas at kadan.cz> 55# - lots of various cleanups and reorganizations; the parsing is now 56# some 40% faster, but the class ID must be in range 0x0002-0xffff 57# (again). Because of the number of internal changes and the above 58# class ID restriction, I bumped the version to 0.7 to indicate 59# something might have got broken :) 60# - changed PRIO_{U32,FW,ROUTE} to PRIO_{RULE,MARK,REALM} 61# for consistency with filter keywords 62# - exposed "compile" command 63# - Catalin Petrescu <taz at dntis.ro> 64# - support for port masks in RULE (u32) filter 65# - Jordan Vrtanoski <obeliks at mt.net.mk> 66# - support for week days in TIME rules 67# v0.6.4- Lubomir Bulej <pallas at kadan.cz> 68# - added PRIO_* variables to allow easy control of filter priorities 69# - added caching to speed up CBQ start, the cache is invalidated 70# whenever any of the configuration files changes 71# - updated the readme section + some cosmetic fixes 72# v0.6.3- Lubomir Bulej <pallas at kadan.cz> 73# - removed setup of (unnecessary) class 1:1 - all classes 74# now use qdisc's default class 1:0 as their parent 75# - minor fix in the timecheck branch - classes 76# without leaf qdisc were not updated 77# - minor fix to avoid timecheck failure when run 78# at time with minutes equal to 08 or 09 79# - respect CBQ_PATH setting in environment 80# - made PRIO=5 default, rendering it optional in configs 81# - added support for route filter, see notes about REALM keyword 82# - added support for fw filter, see notes about MARK keyword 83# - added filter display to "list" and "stats" commands 84# - readme section update + various cosmetic fixes 85# v0.6.2- Catalin Petrescu <taz at dntis.ro> 86# - added tunnels interface handling 87# v0.6.1- Pavel Golubev <pg at ksi-linux.com> 88# - added sch_prio module loading 89# (thanks johan at iglo.virtual.or.id for reminding) 90# - resolved errors resulting from stricter syntax checking in bash2 91# - Lubomir Bulej <pallas at kadan.cz> 92# - various cosmetic fixes 93# v0.6 - Lubomir Bulej <pallas at kadan.cz> 94# - attempt to limit number of spawned processes by utilizing 95# more of sed power (use sed instead of grep+cut) 96# - simplified TIME parser, using bash builtins 97# - added initial support for SFQ as leaf qdisc 98# - reworked the documentation part a little 99# - incorporated pending patches and ideas submitted by 100# following people for versions 0.3 into version 0.6 101# - Miguel Freitas <miguel at cetuc.puc-rio.br> 102# - in case of overlapping TIME parameters, the last match is taken 103# - Juanjo Ciarlante <jjo at mendoza.gov.ar> 104# - chkconfig tags, list + stats startup parameters 105# - optional tc & ip command logging (into /var/run/cbq-*) 106# - Rafal Maszkowski <rzm at icm.edu.pl> 107# - PEAK parameter for setting TBF's burst peak rate 108# - fix for many config files (use find instead of ls) 109# v0.5.1- Lubomir Bulej <pallas at kadan.cz> 110# - fixed little but serious bug in RULE parser 111# v0.5 - Lubomir Bulej <pallas at kadan.cz> 112# - added options PARENT, LEAF, ISOLATED and BOUNDED. This allows 113# (with some attention to config file ordering) for creating 114# hierarchical structures of shapers with classes able (or unable) 115# to borrow bandwidth from their parents. 116# - class ID check allows hexadecimal numbers 117# - rewritten & simplified RULE parser 118# - cosmetic changes to improve readability 119# - reorganization to avoid duplicate code (timecheck etc.) 120# - timecheck doesn't check classes without TIME fields anymore 121# v0.4 - Lubomir Bulej <pallas at kadan.cz> 122# - small bugfix in RULE parsing code 123# - simplified configuration parsing code 124# - several small cosmetic changes 125# - TIME parameter can be now specified more than once allowing you to 126# differentiate RATE throughout the whole day. Time overlapping is 127# not checked, first match is taken. Midnight wrap (eg. 20:00-6:00) 128# is allowed and taken care of. 129# v0.3a4- fixed small bug in IF operator. Thanks to 130# Rafal Maszkowski <rzm at icm.edu.pl> 131# v0.3a3- fixed grep bug when using more than 10 eth devices. Thanks to David 132# Trcka <trcka at poda.cz>. 133# v0.3a2- fixed bug in "if" operator. Thanks kad at dgtu.donetsk.ua. 134# v0.3a - added TIME parameter. Example: TIME=00:00-19:00;64Kbit/6Kbit 135# So, between 00:00 and 19:00 the RATE will be 64Kbit. 136# Just start "cbq.init timecheck" periodically from cron 137# (every 10 minutes for example). DON'T FORGET though, to run 138# "cbq.init start" for CBQ to initialize. 139# v0.2 - Some cosmetic changes. Now it is more compatible with old bash 140# version. Thanks to Stanislav V. Voronyi <stas at cnti.uanet.kharkov.ua>. 141# v0.1 - First public release 142# 143# 144# README 145# ------ 146# 147# First of all - this is just a SIMPLE EXAMPLE of CBQ power. 148# Don't ask me "why" and "how" :) 149# 150# This script is meant to simplify setup and management of relatively simple 151# CBQ-based traffic control on Linux. Access to advanced networking features 152# of Linux kernel is provided by "ip" and "tc" utilities from A. Kuznetsov's 153# iproute2 package, available at ftp://ftp.inr.ac.ru/ip-routing. Because the 154# utilities serve primarily to translate user wishes to RTNETLINK commands, 155# their interface is rather spartan, intolerant and requires quite a lot of 156# typing. And typing is what this script attempts to reduce :) 157# 158# The advanced networking stuff in Linux is pretty flexible and this script 159# aims to bring some of its features to the not-so-hard-core Linux users. Of 160# course, there is a tradeoff between simplicity and flexibility and you may 161# realize that the flexibility suffered too much for your needs -- time to 162# face "ip" and "tc" interface. 163# 164# To speed up the "start" command, simple caching was introduced in version 165# 0.6.4. The caching works so that the sequence of "tc" commands for given 166# configuration is stored in a file (/var/cache/cbq.init by default) which 167# is used next time the "start" command is run to avoid repeated parsing of 168# configuration files. This cache is invalidated whenever any of the CBQ 169# configuration files changes. If you want to run "cbq.init start" without 170# caching, run it as "cbq.init start nocache". If you want to force cache 171# invalidation, run it as "cbq.init start invalidate". Caching is disabled 172# if you have logging enabled (ie. CBQ_DEBUG is not empty). 173# 174# If you only want cqb.init to translate your configuration to "tc" commands, 175# use "compile" command which will output "tc" commands required to build 176# your configuration. Bear in mind that "compile" does not check if the "tc" 177# commands were successful - this is done (in certain places) only when the 178# "start nocache" command is used, which is also useful when creating the 179# configuration to check whether it is completely valid. 180# 181# All CBQ parameters are valid for Ethernet interfaces only, The script was 182# tested on various Linux kernel versions from series 2.1 to 2.4 and several 183# distributions with KSI Linux (Nostromo version) as the premier one. 184# 185# 186# HOW DOES IT WORK? 187# ----------------- 188# 189# Every traffic class must be described by a file in the $CBQ_PATH directory 190# (/etc/sysconfig/cbq by default) - one file per class. 191# 192# The config file names must obey mandatory format: cbq-<clsid>.<name> where 193# <clsid> is two-byte hexadecimal number in range <0002-FFFF> (which in fact 194# is a CBQ class ID) and <name> is the name of the class -- anything to help 195# you distinguish the configuration files. For small amount of classes it is 196# often possible (and convenient) to let <clsid> resemble bandwidth of the 197# class. 198# 199# Example of valid config name: 200# cbq-1280.My_first_shaper 201# 202# 203# The configuration file may contain the following parameters: 204# 205### Device parameters 206# 207# DEVICE=<ifname>,<bandwidth>[,<weight>] mandatory 208# DEVICE=eth0,10Mbit,1Mbit 209# 210# <ifname> is the name of the interface you want to control 211# traffic on, e.g. eth0 212# <bandwidth> is the physical bandwidth of the device, e.g. for 213# ethernet 10Mbit or 100Mbit, for arcnet 2Mbit 214# <weight> is tuning parameter that should be proportional to 215# <bandwidth>. As a rule of thumb: <weight> = <bandwidth> / 10 216# 217# When you have more classes on one interface, it is enough to specify 218# <bandwidth> [and <weight>] only once, therefore in other files you only 219# need to set DEVICE=<ifname>. 220# 221### Class parameters 222# 223# RATE=<speed> mandatory 224# RATE=5Mbit 225# 226# Bandwidth allocated to the class. Traffic going through the class is 227# shaped to conform to specified rate. You can use Kbit, Mbit or bps, 228# Kbps and Mbps as suffices. If you don't specify any unit, bits/sec 229# are used. Also note that "bps" means "bytes per second", not bits. 230# 231# WEIGHT=<speed> mandatory 232# WEIGHT=500Kbit 233# 234# Tuning parameter that should be proportional to RATE. As a rule 235# of thumb, use WEIGHT ~= RATE / 10. 236# 237# PRIO=<1-8> optional, default 5 238# PRIO=5 239# 240# Priority of class traffic. The higher the number, the lesser 241# the priority. Priority of 5 is just fine. 242# 243# PARENT=<clsid> optional, default not set 244# PARENT=1280 245# 246# Specifies ID of the parent class to which you want this class be 247# attached. You might want to use LEAF=none for the parent class as 248# mentioned below. By using this parameter and carefully ordering the 249# configuration files, it is possible to create simple hierarchical 250# structures of CBQ classes. The ordering is important so that parent 251# classes are constructed prior to their children. 252# 253# LEAF=none|tbf|sfq optional, default "tbf" 254# 255# Tells the script to attach specified leaf queueing discipline to CBQ 256# class. By default, TBF is used. Note that attaching TBF to CBQ class 257# shapes the traffic to conform to TBF parameters and prevents the class 258# from borrowing bandwidth from its parent even if you have BOUNDED set 259# to "no". To allow the class to borrow bandwith (provided it is not 260# bounded), you must set LEAF to "none" or "sfq". 261# 262# If you want to ensure (approximately) fair sharing of bandwidth among 263# several hosts in the same class, you might want to specify LEAF=sfq to 264# attach SFQ as leaf queueing discipline to that class. 265# 266# BOUNDED=yes|no optional, default "yes" 267# 268# If set to "yes", the class is not allowed to borrow bandwidth from 269# its parent class in overlimit situation. If set to "no", the class 270# will be allowed to borrow bandwidth from its parent. 271# 272# Note: Don't forget to set LEAF to "none" or "sfq", otherwise the class will 273# have TBF attached to itself and will not be able to borrow unused 274# bandwith from its parent. 275# 276# ISOLATED=yes|no optional, default "no" 277# 278# If set to "yes", the class will not lend unused bandwidth to 279# its children. 280# 281### TBF qdisc parameters 282# 283# BUFFER=<bytes>[/<bytes>] optional, default "10Kb/8" 284# 285# This parameter controls the depth of the token bucket. In other 286# words it represents the maximal burst size the class can send. 287# The optional part of parameter is used to determine the length 288# of intervals in packet sizes, for which the transmission times 289# are kept. 290# 291# LIMIT=<bytes> optional, default "15Kb" 292# 293# This parameter determines the maximal length of backlog. If 294# the queue contains more data than specified by LIMIT, the 295# newly arriving packets are dropped. The length of backlog 296# determines queue latency in case of congestion. 297# 298# PEAK=<speed> optional, default not set 299# 300# Maximal peak rate for short-term burst traffic. This allows you 301# to control the absolute peak rate the class can send at, because 302# single TBF that allows 256Kbit/s would of course allow rate of 303# 512Kbit for half a second or 1Mbit for a quarter of second. 304# 305# MTU=<bytes> optional, default "1500" 306# 307# Maximum number of bytes that can be sent at once over the 308# physical medium. This parameter is required when you specify 309# PEAK parameter. It defaults to MTU of ethernet - for other 310# media types you might want to change it. 311# 312# Note: Setting TBF as leaf qdisc will effectively prevent the class from 313# borrowing bandwidth from the ancestor class, because even if the 314# class allows more traffic to pass through, it is then shaped to 315# conform to TBF. 316# 317### SFQ qdisc parameters 318# 319# The SFQ queueing discipline is a cheap way for sharing class bandwidth 320# among several hosts. As it is stochastic, the fairness is approximate but 321# it will do the job in most cases. If you want real fairness, you should 322# probably use WRR (weighted round robin) or WFQ queueing disciplines. Note 323# that SFQ does not do any traffic shaping - the shaping is done by the CBQ 324# class the SFQ is attached to. 325# 326# QUANTUM=<bytes> optional, default not set 327# 328# This parameter should not be set lower than link MTU, for ethernet 329# it is 1500b, or (with MAC header) 1514b which is the value used 330# in Alexey Kuznetsov's examples. 331# 332# PERTURB=<seconds> optional, default "10" 333# 334# Period of hash function perturbation. If unset, hash reconfiguration 335# will never take place which is what you probably don't want. The 336# default value of 10 seconds is probably a good one. 337# 338### Filter parameters 339# 340# RULE=[[saddr[/prefix]][:port[/mask]],][daddr[/prefix]][:port[/mask]] 341# 342# These parameters make up "u32" filter rules that select traffic for 343# each of the classes. You can use multiple RULE fields per config. 344# 345# The optional port mask should only be used by advanced users who 346# understand how the u32 filter works. 347# 348# Some examples: 349# 350# RULE=10.1.1.0/24:80 351# selects traffic going to port 80 in network 10.1.1.0 352# 353# RULE=10.2.2.5 354# selects traffic going to any port on single host 10.2.2.5 355# 356# RULE=10.2.2.5:20/0xfffe 357# selects traffic going to ports 20 and 21 on host 10.2.2.5 358# 359# RULE=:25,10.2.2.128/26:5000 360# selects traffic going from anywhere on port 50 to 361# port 5000 in network 10.2.2.128 362# 363# RULE=10.5.5.5:80, 364# selects traffic going from port 80 of single host 10.5.5.5 365# 366# 367# 368# REALM=[srealm,][drealm] 369# 370# These parameters make up "route" filter rules that classify traffic 371# according to packet source/destination realms. For information about 372# realms, see Alexey Kuznetsov's IP Command Reference. This script 373# does not define any realms, it justs builds "tc filter" commands 374# for you if you need to classify traffic this way. 375# 376# Realm is either a decimal number or a string referencing entry in 377# /etc/iproute2/rt_realms (usually). 378# 379# Some examples: 380# 381# REALM=russia,internet 382# selects traffic going from realm "russia" to realm "internet" 383# 384# REALM=freenet, 385# selects traffic going from realm "freenet" 386# 387# REALM=10 388# selects traffic going to realm 10 389# 390# 391# 392# MARK=<mark> 393# 394# These parameters make up "fw" filter rules that select traffic for 395# each of the classes accoring to firewall "mark". Mark is a decimal 396# number packets are tagged with if firewall rules say so. You can 397# use multiple MARK fields per config. 398# 399# 400# Note: Rules for different filter types can be combined. Attention must be 401# paid to the priority of filter rules, which can be set below using 402# PRIO_{RULE,MARK,REALM} variables. 403# 404### Time ranging parameters 405# 406# TIME=[<dow>,<dow>, ...,<dow>/]<from>-<till>;<rate>/<weight>[/<peak>] 407# TIME=0,1,2,5/18:00-06:00;256Kbit/25Kbit 408# TIME=60123/18:00-06:00;256Kbit/25Kbit 409# TIME=18:00-06:00;256Kbit/25Kbit 410# 411# This parameter allows you to differentiate the class bandwidth 412# throughout the day. You can specify multiple TIME parameters, if 413# the times overlap, last match is taken. The fields <rate>, <weight> 414# and <peak> correspond to parameters RATE, WEIGHT and PEAK (which 415# is optional and applies to TBF leaf qdisc only). 416# 417# You can also specify days of week when the TIME rule applies. <dow> 418# is numeric, 0 corresponds to sunday, 1 corresponds to monday, etc. 419# 420### 421# 422# Sample configuration file: cbq-1280.My_first_shaper 423# 424# -------------------------------------------------------------------------- 425# DEVICE=eth0,10Mbit,1Mbit 426# RATE=128Kbit 427# WEIGHT=10Kbit 428# PRIO=5 429# RULE=192.128.1.0/24 430# -------------------------------------------------------------------------- 431# 432# The configuration says that we will control traffic on 10Mbit ethernet 433# device eth0 and the traffic going to network 192.168.1.0 will be 434# processed with priority 5 and shaped to rate of 128Kbit. 435# 436# Note that you can control outgoing traffic only. If you want to control 437# traffic in both directions, you must set up CBQ for both interfaces. 438# 439# Consider the following example: 440# 441# +---------+ 192.168.1.1 442# BACKBONE -----eth0-| linux |-eth1------*-[client] 443# +---------+ 444# 445# Imagine you want to shape traffic from backbone to the client to 28Kbit 446# and traffic in the opposite direction to 128Kbit. You need to setup CBQ 447# on both eth0 and eth1 interfaces, thus you need two config files: 448# 449# cbq-028.backbone-client 450# -------------------------------------------------------------------------- 451# DEVICE=eth1,10Mbit,1Mbit 452# RATE=28Kbit 453# WEIGHT=2Kbit 454# PRIO=5 455# RULE=192.168.1.1 456# -------------------------------------------------------------------------- 457# 458# cbq-128.client-backbone 459# -------------------------------------------------------------------------- 460# DEVICE=eth0,10Mbit,1Mbit 461# RATE=128Kbit 462# WEIGHT=10Kbit 463# PRIO=5 464# RULE=192.168.1.1, 465# -------------------------------------------------------------------------- 466# 467# Pay attention to comma "," in the RULE field - it denotes source address! 468# 469# Enjoy. 470# 471############################################################################# 472 473export LC_ALL=C 474 475### Command locations 476TC=/sbin/tc 477IP=/sbin/ip 478MP=/sbin/modprobe 479 480### Default filter priorities (must be different) 481PRIO_RULE_DEFAULT=${PRIO_RULE:-100} 482PRIO_MARK_DEFAULT=${PRIO_MARK:-200} 483PRIO_REALM_DEFAULT=${PRIO_REALM:-300} 484 485### Default CBQ_PATH & CBQ_CACHE settings 486CBQ_PATH=${CBQ_PATH:-/etc/sysconfig/cbq} 487CBQ_CACHE=${CBQ_CACHE:-/var/cache/cbq.init} 488 489### Uncomment to enable logfile for debugging 490#CBQ_DEBUG="/var/run/cbq-$1" 491 492### Modules to probe for. Uncomment the last CBQ_PROBE 493### line if you have QoS support compiled into kernel 494CBQ_PROBE="sch_cbq sch_tbf sch_sfq sch_prio" 495CBQ_PROBE="$CBQ_PROBE cls_fw cls_u32 cls_route" 496#CBQ_PROBE="" 497 498### Keywords required for qdisc & class configuration 499CBQ_WORDS="DEVICE|RATE|WEIGHT|PRIO|PARENT|LEAF|BOUNDED|ISOLATED" 500CBQ_WORDS="$CBQ_WORDS|PRIO_MARK|PRIO_RULE|PRIO_REALM|BUFFER" 501CBQ_WORDS="$CBQ_WORDS|LIMIT|PEAK|MTU|QUANTUM|PERTURB" 502 503### Source AVPKT if it exists 504[ -r /etc/sysconfig/cbq/avpkt ] && . /etc/sysconfig/cbq/avpkt 505AVPKT=${AVPKT:-3000} 506 507 508############################################################################# 509############################# SUPPORT FUNCTIONS ############################# 510############################################################################# 511 512### Get list of network devices 513cbq_device_list () { 514 ip link show| sed -n "/^[0-9]/ \ 515 { s/^[0-9]\+: \([a-z0-9._]\+\)[:@].*/\1/; p; }" 516} # cbq_device_list 517 518 519### Remove root class from device $1 520cbq_device_off () { 521 tc qdisc del dev $1 root 2> /dev/null 522} # cbq_device_off 523 524 525### Remove CBQ from all devices 526cbq_off () { 527 for dev in `cbq_device_list`; do 528 cbq_device_off $dev 529 done 530} # cbq_off 531 532 533### Prefixed message 534cbq_message () { 535 echo -e "**CBQ: $*" 536} # cbq_message 537 538### Failure message 539cbq_failure () { 540 cbq_message "$@" 541 exit 1 542} # cbq_failure 543 544### Failure w/ cbq-off 545cbq_fail_off () { 546 cbq_message "$@" 547 cbq_off 548 exit 1 549} # cbq_fail_off 550 551 552### Convert time to absolute value 553cbq_time2abs () { 554 local min=${1##*:}; min=${min##0} 555 local hrs=${1%%:*}; hrs=${hrs##0} 556 echo $[hrs*60 + min] 557} # cbq_time2abs 558 559 560### Display CBQ setup 561cbq_show () { 562 for dev in `cbq_device_list`; do 563 [ "`tc qdisc show dev $dev| wc -l`" -eq 0 ] && continue 564 echo -e "### $dev: queueing disciplines\n" 565 tc $1 qdisc show dev $dev; echo 566 567 [ "`tc class show dev $dev| wc -l`" -eq 0 ] && continue 568 echo -e "### $dev: traffic classes\n" 569 tc $1 class show dev $dev; echo 570 571 [ "`tc filter show dev $dev| wc -l`" -eq 0 ] && continue 572 echo -e "### $dev: filtering rules\n" 573 tc $1 filter show dev $dev; echo 574 done 575} # cbq_show 576 577 578### Check configuration and load DEVICES, DEVFIELDS and CLASSLIST from $1 579cbq_init () { 580 ### Get a list of configured classes 581 CLASSLIST=`find $1 -maxdepth 1 \( -type f -or -type l \) -name 'cbq-*' \ 582 -not -name '*~' -printf "%f\n"| sort` 583 [ -z "$CLASSLIST" ] && 584 cbq_failure "no configuration files found in $1!" 585 586 ### Gather all DEVICE fields from $1/cbq-* 587 DEVFIELDS=`find $1 -maxdepth 1 \( -type f -or -type l \) -name 'cbq-*' \ 588 -not -name '*~' -print0 | xargs -0 sed -n 's/#.*//; \ 589 s/[[:space:]]//g; /^DEVICE=[^,]*,[^,]*\(,[^,]*\)\?/ \ 590 { s/.*=//; p; }'| sort -u` 591 [ -z "$DEVFIELDS" ] && 592 cbq_failure "no DEVICE field found in $1/cbq-*!" 593 594 ### Check for different DEVICE fields for the same device 595 DEVICES=`echo "$DEVFIELDS"| sed 's/,.*//'| sort -u` 596 [ "`echo "$DEVICES"| wc -l`" -ne "`echo "$DEVFIELDS"| wc -l`" ] && 597 cbq_failure "different DEVICE fields for single device!\n$DEVFIELDS" 598} # cbq_init 599 600 601### Load class configuration from $1/$2 602cbq_load_class () { 603 CLASS=`echo $2| sed 's/^cbq-0*//; s/^\([0-9a-fA-F]\+\).*/\1/'` 604 CFILE=`sed -n 's/#.*//; s/[[:space:]]//g; /^[[:alnum:]_]\+=[[:alnum:].,:;/*@-_]\+$/ p' $1/$2` 605 606 ### Check class number 607 IDVAL=`/usr/bin/printf "%d" 0x$CLASS 2> /dev/null` 608 [ $? -ne 0 -o $IDVAL -lt 2 -o $IDVAL -gt 65535 ] && 609 cbq_fail_off "class ID of $2 must be in range <0002-FFFF>!" 610 611 ### Set defaults & load class 612 RATE=""; WEIGHT=""; PARENT=""; PRIO=5 613 LEAF=tbf; BOUNDED=yes; ISOLATED=no 614 BUFFER=10Kb/8; LIMIT=15Kb; MTU=1500 615 PEAK=""; PERTURB=10; QUANTUM="" 616 617 PRIO_RULE=$PRIO_RULE_DEFAULT 618 PRIO_MARK=$PRIO_MARK_DEFAULT 619 PRIO_REALM=$PRIO_REALM_DEFAULT 620 621 eval "`echo "$CFILE"| grep -E "^($CBQ_WORDS)="`" 622 623 ### Require RATE/WEIGHT 624 [ -z "$RATE" -o -z "$WEIGHT" ] && 625 cbq_fail_off "missing RATE or WEIGHT in $2!" 626 627 ### Class device 628 DEVICE=${DEVICE%%,*} 629 [ -z "$DEVICE" ] && cbq_fail_off "missing DEVICE field in $2!" 630 631 BANDWIDTH=`echo "$DEVFIELDS"| sed -n "/^$DEVICE,/ \ 632 { s/[^,]*,\([^,]*\).*/\1/; p; q; }"` 633 634 ### Convert to "tc" options 635 PEAK=${PEAK:+peakrate $PEAK} 636 PERTURB=${PERTURB:+perturb $PERTURB} 637 QUANTUM=${QUANTUM:+quantum $QUANTUM} 638 639 [ "$BOUNDED" = "no" ] && BOUNDED="" || BOUNDED="bounded" 640 [ "$ISOLATED" = "yes" ] && ISOLATED="isolated" || ISOLATED="" 641} # cbq_load_class 642 643 644############################################################################# 645#################################### INIT ################################### 646############################################################################# 647 648### Check for presence of ip-route2 in usual place 649[ -x $TC -a -x $IP ] || 650 cbq_failure "ip-route2 utilities not installed or executable!" 651 652 653### ip/tc wrappers 654if [ "$1" = "compile" ]; then 655 ### no module probing 656 CBQ_PROBE="" 657 658 ip () { 659 $IP "$@" 660 } # ip 661 662 ### echo-only version of "tc" command 663 tc () { 664 echo "$TC $*" 665 } # tc 666 667elif [ -n "$CBQ_DEBUG" ]; then 668 echo -e "# `date`" > $CBQ_DEBUG 669 670 ### Logging version of "ip" command 671 ip () { 672 echo -e "\n# ip $*" >> $CBQ_DEBUG 673 $IP "$@" 2>&1 | tee -a $CBQ_DEBUG 674 } # ip 675 676 ### Logging version of "tc" command 677 tc () { 678 echo -e "\n# tc $*" >> $CBQ_DEBUG 679 $TC "$@" 2>&1 | tee -a $CBQ_DEBUG 680 } # tc 681else 682 ### Default wrappers 683 684 ip () { 685 $IP "$@" 686 } # ip 687 688 tc () { 689 $TC "$@" 690 } # tc 691fi # ip/tc wrappers 692 693 694case "$1" in 695 696############################################################################# 697############################### START/COMPILE ############################### 698############################################################################# 699 700start|compile) 701 702### Probe QoS modules (start only) 703for module in $CBQ_PROBE; do 704 $MP $module || cbq_failure "failed to load module $module" 705done 706 707### If we are in compile/nocache/logging mode, don't bother with cache 708if [ "$1" != "compile" -a "$2" != "nocache" -a -z "$CBQ_DEBUG" ]; then 709 VALID=1 710 711 ### validate the cache 712 [ "$2" = "invalidate" -o ! -f $CBQ_CACHE ] && VALID=0 713 if [ $VALID -eq 1 ]; then 714 [ "`find $CBQ_PATH -maxdepth 1 -newer $CBQ_CACHE| \ 715 wc -l`" -gt 0 ] && VALID=0 716 fi 717 718 ### compile the config if the cache is invalid 719 if [ $VALID -ne 1 ]; then 720 $0 compile > $CBQ_CACHE || 721 cbq_fail_off "failed to compile CBQ configuration!" 722 fi 723 724 ### run the cached commands 725 exec /bin/sh $CBQ_CACHE 2> /dev/null 726fi 727 728### Load DEVICES, DEVFIELDS and CLASSLIST 729cbq_init $CBQ_PATH 730 731 732### Setup root qdisc on all configured devices 733for dev in $DEVICES; do 734 ### Retrieve device bandwidth and, optionally, weight 735 DEVTEMP=`echo "$DEVFIELDS"| sed -n "/^$dev,/ { s/$dev,//; p; q; }"` 736 DEVBWDT=${DEVTEMP%%,*}; DEVWGHT=${DEVTEMP##*,} 737 [ "$DEVBWDT" = "$DEVWGHT" ] && DEVWGHT="" 738 739 ### Device bandwidth is required 740 if [ -z "$DEVBWDT" ]; then 741 cbq_message "could not determine bandwidth for device $dev!" 742 cbq_failure "please set up the DEVICE fields properly!" 743 fi 744 745 ### Check if the device is there 746 ip link show $dev &> /dev/null || 747 cbq_fail_off "device $dev not found!" 748 749 ### Remove old root qdisc from device 750 cbq_device_off $dev 751 752 753 ### Setup root qdisc + class for device 754 tc qdisc add dev $dev root handle 1 cbq \ 755 bandwidth $DEVBWDT avpkt $AVPKT cell 8 756 757 ### Set weight of the root class if set 758 [ -n "$DEVWGHT" ] && 759 tc class change dev $dev root cbq weight $DEVWGHT allot 1514 760 761 [ "$1" = "compile" ] && echo 762done # dev 763 764 765### Setup traffic classes 766for classfile in $CLASSLIST; do 767 cbq_load_class $CBQ_PATH $classfile 768 769 ### Create the class 770 tc class add dev $DEVICE parent 1:$PARENT classid 1:$CLASS cbq \ 771 bandwidth $BANDWIDTH rate $RATE weight $WEIGHT prio $PRIO \ 772 allot 1514 cell 8 maxburst 20 avpkt $AVPKT $BOUNDED $ISOLATED || 773 cbq_fail_off "failed to add class $CLASS with parent $PARENT on $DEVICE!" 774 775 ### Create leaf qdisc if set 776 if [ "$LEAF" = "tbf" ]; then 777 tc qdisc add dev $DEVICE parent 1:$CLASS handle $CLASS tbf \ 778 rate $RATE buffer $BUFFER limit $LIMIT mtu $MTU $PEAK 779 elif [ "$LEAF" = "sfq" ]; then 780 tc qdisc add dev $DEVICE parent 1:$CLASS handle $CLASS sfq \ 781 $PERTURB $QUANTUM 782 fi 783 784 785 ### Create fw filter for MARK fields 786 for mark in `echo "$CFILE"| sed -n '/^MARK/ { s/.*=//; p; }'`; do 787 ### Attach fw filter to root class 788 tc filter add dev $DEVICE parent 1:0 protocol ip \ 789 prio $PRIO_MARK handle $mark fw classid 1:$CLASS 790 done ### mark 791 792 ### Create route filter for REALM fields 793 for realm in `echo "$CFILE"| sed -n '/^REALM/ { s/.*=//; p; }'`; do 794 ### Split realm into source & destination realms 795 SREALM=${realm%%,*}; DREALM=${realm##*,} 796 [ "$SREALM" = "$DREALM" ] && SREALM="" 797 798 ### Convert asterisks to empty strings 799 SREALM=${SREALM#\*}; DREALM=${DREALM#\*} 800 801 ### Attach route filter to the root class 802 tc filter add dev $DEVICE parent 1:0 protocol ip \ 803 prio $PRIO_REALM route ${SREALM:+from $SREALM} \ 804 ${DREALM:+to $DREALM} classid 1:$CLASS 805 done ### realm 806 807 ### Create u32 filter for RULE fields 808 for rule in `echo "$CFILE"| sed -n '/^RULE/ { s/.*=//; p; }'`; do 809 ### Split rule into source & destination 810 SRC=${rule%%,*}; DST=${rule##*,} 811 [ "$SRC" = "$rule" ] && SRC="" 812 813 814 ### Split destination into address, port & mask fields 815 DADDR=${DST%%:*}; DTEMP=${DST##*:} 816 [ "$DADDR" = "$DST" ] && DTEMP="" 817 818 DPORT=${DTEMP%%/*}; DMASK=${DTEMP##*/} 819 [ "$DPORT" = "$DTEMP" ] && DMASK="0xffff" 820 821 822 ### Split up source (if specified) 823 SADDR=""; SPORT="" 824 if [ -n "$SRC" ]; then 825 SADDR=${SRC%%:*}; STEMP=${SRC##*:} 826 [ "$SADDR" = "$SRC" ] && STEMP="" 827 828 SPORT=${STEMP%%/*}; SMASK=${STEMP##*/} 829 [ "$SPORT" = "$STEMP" ] && SMASK="0xffff" 830 fi 831 832 833 ### Convert asterisks to empty strings 834 SADDR=${SADDR#\*}; DADDR=${DADDR#\*} 835 836 ### Compose u32 filter rules 837 u32_s="${SPORT:+match ip sport $SPORT $SMASK}" 838 u32_s="${SADDR:+match ip src $SADDR} $u32_s" 839 u32_d="${DPORT:+match ip dport $DPORT $DMASK}" 840 u32_d="${DADDR:+match ip dst $DADDR} $u32_d" 841 842 ### Uncomment the following if you want to see parsed rules 843 #echo "$rule: $u32_s $u32_d" 844 845 ### Attach u32 filter to the appropriate class 846 tc filter add dev $DEVICE parent 1:0 protocol ip \ 847 prio $PRIO_RULE u32 $u32_s $u32_d classid 1:$CLASS 848 done ### rule 849 850 [ "$1" = "compile" ] && echo 851done ### classfile 852;; 853 854 855############################################################################# 856################################# TIME CHECK ################################ 857############################################################################# 858 859timecheck) 860 861### Get time + weekday 862TIME_TMP=`date +%w/%k:%M` 863TIME_DOW=${TIME_TMP%%/*} 864TIME_NOW=${TIME_TMP##*/} 865 866### Load DEVICES, DEVFIELDS and CLASSLIST 867cbq_init $CBQ_PATH 868 869### Run through all classes 870for classfile in $CLASSLIST; do 871 ### Gather all TIME rules from class config 872 TIMESET=`sed -n 's/#.*//; s/[[:space:]]//g; /^TIME/ { s/.*=//; p; }' \ 873 $CBQ_PATH/$classfile` 874 [ -z "$TIMESET" ] && continue 875 876 MATCH=0; CHANGE=0 877 for timerule in $TIMESET; do 878 TIME_ABS=`cbq_time2abs $TIME_NOW` 879 880 ### Split TIME rule to pieces 881 TIMESPEC=${timerule%%;*}; PARAMS=${timerule##*;} 882 WEEKDAYS=${TIMESPEC%%/*}; INTERVAL=${TIMESPEC##*/} 883 BEG_TIME=${INTERVAL%%-*}; END_TIME=${INTERVAL##*-} 884 885 ### Check the day-of-week (if present) 886 [ "$WEEKDAYS" != "$INTERVAL" -a \ 887 -n "${WEEKDAYS##*$TIME_DOW*}" ] && continue 888 889 ### Compute interval boundaries 890 BEG_ABS=`cbq_time2abs $BEG_TIME` 891 END_ABS=`cbq_time2abs $END_TIME` 892 893 ### Midnight wrap fixup 894 if [ $BEG_ABS -gt $END_ABS ]; then 895 [ $TIME_ABS -le $END_ABS ] && 896 TIME_ABS=$[TIME_ABS + 24*60] 897 898 END_ABS=$[END_ABS + 24*60] 899 fi 900 901 ### If the time matches, remember params and set MATCH flag 902 if [ $TIME_ABS -ge $BEG_ABS -a $TIME_ABS -lt $END_ABS ]; then 903 TMP_RATE=${PARAMS%%/*}; PARAMS=${PARAMS#*/} 904 TMP_WGHT=${PARAMS%%/*}; TMP_PEAK=${PARAMS##*/} 905 906 [ "$TMP_PEAK" = "$TMP_WGHT" ] && TMP_PEAK="" 907 TMP_PEAK=${TMP_PEAK:+peakrate $TMP_PEAK} 908 909 MATCH=1 910 fi 911 done ### timerule 912 913 914 cbq_load_class $CBQ_PATH $classfile 915 916 ### Get current RATE of CBQ class 917 RATE_NOW=`tc class show dev $DEVICE| sed -n \ 918 "/cbq 1:$CLASS / { s/.*rate //; s/ .*//; p; q; }"` 919 [ -z "$RATE_NOW" ] && continue 920 921 ### Time interval matched 922 if [ $MATCH -ne 0 ]; then 923 924 ### Check if there is any change in class RATE 925 if [ "$RATE_NOW" != "$TMP_RATE" ]; then 926 NEW_RATE="$TMP_RATE" 927 NEW_WGHT="$TMP_WGHT" 928 NEW_PEAK="$TMP_PEAK" 929 CHANGE=1 930 fi 931 932 ### Match not found, reset to default RATE if necessary 933 elif [ "$RATE_NOW" != "$RATE" ]; then 934 NEW_WGHT="$WEIGHT" 935 NEW_RATE="$RATE" 936 NEW_PEAK="$PEAK" 937 CHANGE=1 938 fi 939 940 ### If there are no changes, go for next class 941 [ $CHANGE -eq 0 ] && continue 942 943 ### Replace CBQ class 944 tc class replace dev $DEVICE classid 1:$CLASS cbq \ 945 bandwidth $BANDWIDTH rate $NEW_RATE weight $NEW_WGHT prio $PRIO \ 946 allot 1514 cell 8 maxburst 20 avpkt $AVPKT $BOUNDED $ISOLATED 947 948 ### Replace leaf qdisc (if any) 949 if [ "$LEAF" = "tbf" ]; then 950 tc qdisc replace dev $DEVICE handle $CLASS tbf \ 951 rate $NEW_RATE buffer $BUFFER limit $LIMIT mtu $MTU $NEW_PEAK 952 fi 953 954 cbq_message "$TIME_NOW: class $CLASS on $DEVICE changed rate ($RATE_NOW -> $NEW_RATE)" 955done ### class file 956;; 957 958 959############################################################################# 960################################## THE REST ################################# 961############################################################################# 962 963stop) 964 cbq_off 965 ;; 966 967list) 968 cbq_show 969 ;; 970 971stats) 972 cbq_show -s 973 ;; 974 975restart) 976 shift 977 $0 stop 978 $0 start "$@" 979 ;; 980 981*) 982 echo "Usage: `basename $0` {start|compile|stop|restart|timecheck|list|stats}" 983esac 984