1 #!/bin/bash 2 # 3 # cbq.init v0.7.3 4 # Copyright (C) 1999 Pavel Golubev <pg (at] ksi-linux.com> 5 # Copyright (C) 2001-2004 Lubomir Bulej <pallas (at] 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 473 export LC_ALL=C 474 475 ### Command locations 476 TC=/sbin/tc 477 IP=/sbin/ip 478 MP=/sbin/modprobe 479 480 ### Default filter priorities (must be different) 481 PRIO_RULE_DEFAULT=${PRIO_RULE:-100} 482 PRIO_MARK_DEFAULT=${PRIO_MARK:-200} 483 PRIO_REALM_DEFAULT=${PRIO_REALM:-300} 484 485 ### Default CBQ_PATH & CBQ_CACHE settings 486 CBQ_PATH=${CBQ_PATH:-/etc/sysconfig/cbq} 487 CBQ_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 494 CBQ_PROBE="sch_cbq sch_tbf sch_sfq sch_prio" 495 CBQ_PROBE="$CBQ_PROBE cls_fw cls_u32 cls_route" 496 #CBQ_PROBE="" 497 498 ### Keywords required for qdisc & class configuration 499 CBQ_WORDS="DEVICE|RATE|WEIGHT|PRIO|PARENT|LEAF|BOUNDED|ISOLATED" 500 CBQ_WORDS="$CBQ_WORDS|PRIO_MARK|PRIO_RULE|PRIO_REALM|BUFFER" 501 CBQ_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 505 AVPKT=${AVPKT:-3000} 506 507 508 ############################################################################# 509 ############################# SUPPORT FUNCTIONS ############################# 510 ############################################################################# 511 512 ### Get list of network devices 513 cbq_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 520 cbq_device_off () { 521 tc qdisc del dev $1 root 2> /dev/null 522 } # cbq_device_off 523 524 525 ### Remove CBQ from all devices 526 cbq_off () { 527 for dev in `cbq_device_list`; do 528 cbq_device_off $dev 529 done 530 } # cbq_off 531 532 533 ### Prefixed message 534 cbq_message () { 535 echo -e "**CBQ: $@" 536 } # cbq_message 537 538 ### Failure message 539 cbq_failure () { 540 cbq_message "$@" 541 exit 1 542 } # cbq_failure 543 544 ### Failure w/ cbq-off 545 cbq_fail_off () { 546 cbq_message "$@" 547 cbq_off 548 exit 1 549 } # cbq_fail_off 550 551 552 ### Convert time to absolute value 553 cbq_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 561 cbq_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 579 cbq_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 '*~' | xargs 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 602 cbq_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 654 if [ "$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 667 elif [ -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 681 else 682 ### Default wrappers 683 684 ip () { 685 $IP "$@" 686 } # ip 687 688 tc () { 689 $TC "$@" 690 } # tc 691 fi # ip/tc wrappers 692 693 694 case "$1" in 695 696 ############################################################################# 697 ############################### START/COMPILE ############################### 698 ############################################################################# 699 700 start|compile) 701 702 ### Probe QoS modules (start only) 703 for module in $CBQ_PROBE; do 704 $MP $module || cbq_failure "failed to load module $module" 705 done 706 707 ### If we are in compile/nocache/logging mode, don't bother with cache 708 if [ "$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 726 fi 727 728 ### Load DEVICES, DEVFIELDS and CLASSLIST 729 cbq_init $CBQ_PATH 730 731 732 ### Setup root qdisc on all configured devices 733 for 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 762 done # dev 763 764 765 ### Setup traffic classes 766 for 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 851 done ### classfile 852 ;; 853 854 855 ############################################################################# 856 ################################# TIME CHECK ################################ 857 ############################################################################# 858 859 timecheck) 860 861 ### Get time + weekday 862 TIME_TMP=`date +%w/%k:%M` 863 TIME_DOW=${TIME_TMP%%/*} 864 TIME_NOW=${TIME_TMP##*/} 865 866 ### Load DEVICES, DEVFIELDS and CLASSLIST 867 cbq_init $CBQ_PATH 868 869 ### Run through all classes 870 for 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)" 955 done ### class file 956 ;; 957 958 959 ############################################################################# 960 ################################## THE REST ################################# 961 ############################################################################# 962 963 stop) 964 cbq_off 965 ;; 966 967 list) 968 cbq_show 969 ;; 970 971 stats) 972 cbq_show -s 973 ;; 974 975 restart) 976 shift 977 $0 stop 978 $0 start "$@" 979 ;; 980 981 *) 982 echo "Usage: `basename $0` {start|compile|stop|restart|timecheck|list|stats}" 983 esac 984