2007-03-15 22:55:22 +01:00
#
2007-06-11 21:39:30 +02:00
# Shorewall-perl 4.0 -- /usr/share/shorewall-perl/Shorewall/Tc.pm
2007-03-15 22:55:22 +01:00
#
# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
# (c) 2007 - Tom Eastep (teastep@shorewall.net)
#
2007-03-20 00:29:22 +01:00
# Traffic Control is from tc4shorewall Version 0.5
# (c) 2005 Arne Bernin <arne@ucbering.de>
# Modified by Tom Eastep for integration into the Shorewall distribution
# published under GPL Version 2#
#
2007-03-15 22:55:22 +01:00
# Complete documentation is available at http://shorewall.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of Version 2 of the GNU General Public License
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
2007-04-19 01:55:25 +02:00
# This module deals with Traffic Shaping and the tcrules file.
2007-03-15 22:55:22 +01:00
#
2007-03-15 02:10:56 +01:00
package Shorewall::Tc ;
require Exporter ;
use Shorewall::Common ;
use Shorewall::Config ;
use Shorewall::Zones ;
use Shorewall::Chains ;
2007-03-20 01:06:56 +01:00
use Shorewall::Interfaces ;
2007-03-21 00:13:17 +01:00
use Shorewall::Providers ;
2007-03-15 02:10:56 +01:00
use strict ;
our @ ISA = qw( Exporter ) ;
2007-03-21 00:13:17 +01:00
our @ EXPORT = qw( setup_tc ) ;
2007-06-14 01:02:39 +02:00
our @ EXPORT_OK = qw( process_tc_rule initialize ) ;
2007-07-01 02:08:23 +02:00
our $ VERSION = 4.00 ;
2007-03-15 02:10:56 +01:00
2007-06-05 18:49:13 +02:00
our % tcs = ( T = > { chain = > 'tcpost' ,
connmark = > 0 ,
fw = > 1
} ,
2007-04-30 19:55:43 +02:00
CT = > { chain = > 'tcpost' ,
2007-03-15 02:10:56 +01:00
target = > 'CONNMARK --set-mark' ,
connmark = > 1 ,
2007-03-27 01:17:46 +02:00
fw = > 1
2007-03-15 02:10:56 +01:00
} ,
2007-04-30 19:55:43 +02:00
C = > { target = > 'CONNMARK --set-mark' ,
2007-03-15 02:10:56 +01:00
connmark = > 1 ,
2007-04-08 16:42:26 +02:00
fw = > 1
2007-03-15 02:10:56 +01:00
} ,
2007-04-30 19:55:43 +02:00
P = > { chain = > 'tcpre' ,
2007-03-15 02:10:56 +01:00
connmark = > 0 ,
fw = > 0
} ,
2007-04-30 19:55:43 +02:00
CP = > { chain = > 'tcpre' ,
2007-03-15 02:10:56 +01:00
target = > 'CONNMARK --set-mark' ,
connmark = > 1 ,
fw = > 0
} ,
2007-04-30 19:55:43 +02:00
F = > { chain = > 'tcfor' ,
2007-03-15 02:10:56 +01:00
connmark = > 0 ,
fw = > 0
} ,
2007-04-30 19:55:43 +02:00
CF = > { chain = > 'tcfor' ,
2007-03-15 02:10:56 +01:00
fw = > 0 ,
connmark = > 1 ,
} ,
2007-04-30 19:55:43 +02:00
T = > { chain = > 'tcpost' ,
2007-03-15 02:10:56 +01:00
connmark = > 0 ,
fw = > 0
} ,
2007-04-30 19:55:43 +02:00
CT = > { chain = > 'tcpost' ,
2007-03-15 02:10:56 +01:00
target = > 'CONNMARK --set-mark' ,
connmark = > 1 ,
fw = > 0
} ,
2007-04-30 19:55:43 +02:00
C = > { target = > 'CONNMARK --set-mark' ,
2007-03-15 02:10:56 +01:00
connmark = > 1 ,
fw = > 0
}
) ;
use constant { NOMARK = > 0 ,
SMALLMARK = > 1 ,
2007-04-08 16:42:26 +02:00
HIGHMARK = > 2
2007-03-15 02:10:56 +01:00
} ;
2007-03-27 01:17:46 +02:00
2007-06-05 18:49:13 +02:00
our @ tccmd = ( { match = > sub ( $ ) { $ _ [ 0 ] eq 'SAVE' } ,
target = > 'CONNMARK --save-mark --mask' ,
mark = > SMALLMARK ,
mask = > '0xFF'
} ,
2007-03-31 00:38:09 +02:00
{ match = > sub ( $ ) { $ _ [ 0 ] eq 'RESTORE' } ,
target = > 'CONNMARK --restore-mark --mask' ,
2007-03-15 02:10:56 +01:00
mark = > SMALLMARK ,
mask = > '0xFF'
} ,
2007-03-31 00:38:09 +02:00
{ match = > sub ( $ ) { $ _ [ 0 ] eq 'CONTINUE' } ,
2007-03-15 02:10:56 +01:00
target = > 'RETURN' ,
mark = > NOMARK ,
2007-04-08 16:42:26 +02:00
mask = > ''
2007-03-15 02:10:56 +01:00
} ,
2007-03-31 00:38:09 +02:00
{ match = > sub ( $ ) { $ _ [ 0 ] =~ '\|.*' } ,
2007-03-15 02:10:56 +01:00
target = > 'MARK --or-mark' ,
mark = > HIGHMARK ,
mask = > '' } ,
2007-03-31 00:38:09 +02:00
{ match = > sub ( $ ) { $ _ [ 0 ] =~ '&.*' } ,
2007-03-15 02:10:56 +01:00
target = > 'MARK --and-mark ' ,
mark = > HIGHMARK ,
2007-04-08 16:42:26 +02:00
mask = > ''
2007-03-15 02:10:56 +01:00
}
) ;
2007-06-13 20:40:31 +02:00
our % classids ;
2007-06-13 20:56:27 +02:00
our @ deferred_rules ;
2007-06-23 18:06:16 +02:00
#
# Perl version of Arn Bernin's 'tc4shorewall'.
#
# TCDevices Table
#
# %tcdevices { <interface> -> {in_bandwidth => <value> ,
# out_bandwidth => <value>
# number => <ordinal>
# default => <default class mark value> }
#
our @ tcdevices ;
our % tcdevices ;
#
# TCClasses Table
#
# %tcclasses { device => <device> ,
# mark => <mark> ,
# rate => <rate> ,
# ceiling => <ceiling> ,
# priority => <priority> ,
# options => { tos => [ <value1> , <value2> , ... ];
# tcp_ack => 1 ,
# ...
#
our @ tcclasses ;
our % tcclasses ;
our $ prefix = '1' ;
2007-06-15 00:07:45 +02:00
#
# Initialize globals -- we take this novel approach to globals initialization to allow
# the compiler to run multiple times in the same process. The
# initialize() function does globals initialization for this
# module and is called from an INIT block below. The function is
# also called by Shorewall::Compiler::compiler at the beginning of
# the second and subsequent calls to that function.
#
2007-06-14 01:02:39 +02:00
sub initialize () {
% classids = ( ) ;
@ deferred_rules = ( ) ;
2007-06-23 18:06:16 +02:00
@ tcdevices = ( ) ;
% tcdevices = ( ) ;
@ tcclasses = ( ) ;
% tcclasses = ( ) ;
2007-06-14 01:02:39 +02:00
}
2007-03-15 02:10:56 +01:00
sub process_tc_rule ( $$$$$$$$$$ ) {
2007-03-25 18:38:00 +02:00
my ( $ mark , $ source , $ dest , $ proto , $ ports , $ sports , $ user , $ testval , $ length , $ tos ) = @ _ ;
2007-03-15 02:10:56 +01:00
my $ original_mark = $ mark ;
2007-05-17 16:10:46 +02:00
( $ mark , my ( $ designator , $ remainder ) ) = split ( /:/ , $ mark , 3 ) ;
fatal_error "Invalid MARK" if defined $ remainder ;
2007-03-15 02:10:56 +01:00
2007-03-31 19:44:16 +02:00
my $ chain = $ globals { MARKING_CHAIN } ;
2007-03-15 02:10:56 +01:00
my $ target = 'MARK --set-mark' ;
my $ tcsref ;
my $ connmark = 0 ;
my $ classid = 0 ;
2007-06-13 20:56:27 +02:00
my $ device ;
2007-03-15 02:10:56 +01:00
if ( $ source ) {
if ( $ source eq $ firewall_zone ) {
$ chain = 'tcout' ;
$ source = '' ;
} else {
$ chain = 'tcout' if $ source =~ s/^($firewall_zone):// ;
}
}
if ( $ designator ) {
$ tcsref = $ tcs { $ designator } ;
2007-03-27 01:17:46 +02:00
2007-03-15 02:10:56 +01:00
if ( $ tcsref ) {
if ( $ chain eq 'tcout' ) {
2007-03-30 04:05:11 +02:00
fatal_error "Invalid chain designator for source $firewall_zone" unless $ tcsref - > { fw } ;
2007-03-15 02:10:56 +01:00
}
$ chain = $ tcsref - > { chain } if $ tcsref - > { chain } ;
$ target = $ tcsref - > { target } if $ tcsref - > { target } ;
$ mark = "$mark/0xFF" if $ connmark = $ tcsref - > { connmark } ;
2007-03-27 01:17:46 +02:00
2007-03-15 02:10:56 +01:00
} else {
2007-06-13 20:40:31 +02:00
fatal_error "Invalid MARK ($original_mark)" unless $ mark =~ /^([0-9]+|0x[0-9a-f]+)$/ and $ designator =~ /^([0-9]+|0x[0-9a-f]+)$/ ;
if ( $ config { TC_ENABLED } eq 'Internal' ) {
2007-06-13 20:56:27 +02:00
fatal_error "Unknown Class ($original_mark)}" unless ( $ device = $ classids { $ original_mark } ) ;
2007-06-13 20:40:31 +02:00
}
2007-03-15 02:10:56 +01:00
$ chain = 'tcpost' ;
$ classid = 1 ;
$ mark = $ original_mark ;
$ target = 'CLASSIFY --set-class' ;
}
}
my $ mask = 0xffff ;
2007-05-17 16:10:46 +02:00
my ( $ cmd , $ rest ) = split ( '/' , $ mark , 2 ) ;
2007-03-15 02:10:56 +01:00
2007-04-30 19:55:43 +02:00
unless ( $ classid ) {
MARK:
2007-03-15 02:10:56 +01:00
{
2007-04-30 19:55:43 +02:00
for my $ tccmd ( @ tccmd ) {
if ( $ tccmd - > { match } ( $ cmd ) ) {
2007-05-01 00:00:07 +02:00
fatal_error "$mark not valid with :C[FPT]" if $ connmark ;
2007-04-30 19:55:43 +02:00
$ target = "$tccmd->{target} " ;
my $ marktype = $ tccmd - > { mark } ;
2007-05-01 17:55:41 +02:00
if ( $ marktype == NOMARK ) {
$ mark = ''
} else {
$ mark =~ s/^[|&]// ;
}
2007-04-30 19:55:43 +02:00
if ( $ rest ) {
fatal_error "Invalid MARK ($original_mark)" if $ marktype == NOMARK ;
$ mark = $ rest if $ tccmd - > { mask } ;
if ( $ marktype == SMALLMARK ) {
verify_small_mark $ mark ;
} else {
validate_mark $ mark ;
2007-03-15 02:10:56 +01:00
}
2007-04-30 19:55:43 +02:00
} elsif ( $ tccmd - > { mask } ) {
$ mark = $ tccmd - > { mask } ;
2007-03-15 02:10:56 +01:00
}
2007-04-30 19:55:43 +02:00
last MARK ;
2007-03-15 02:10:56 +01:00
}
}
2007-03-27 01:17:46 +02:00
2007-03-15 02:10:56 +01:00
validate_mark $ mark ;
2007-04-08 16:42:26 +02:00
fatal_error 'Marks < 256 may not be set in the PREROUTING chain when HIGH_ROUTE_MARKS=Yes'
2007-05-02 17:03:44 +02:00
if $ cmd && $ chain eq 'tcpre' && numeric_value ( $ cmd ) <= 0xFF && $ config { HIGH_ROUTE_MARKS } ;
$ target =~ s/set-mark/or-mark/ if numeric_value ( $ cmd ) > 0xFF && ( $ chain eq 'tcpre' || $ chain eq 'tcout' ) ;
2007-03-15 02:10:56 +01:00
}
2007-04-30 19:55:43 +02:00
}
2007-03-15 02:10:56 +01:00
2007-06-13 20:56:27 +02:00
if ( my $ result = expand_rule (
ensure_chain ( 'mangle' , $ chain ) ,
NO_RESTRICT ,
do_proto ( $ proto , $ ports , $ sports ) . do_test ( $ testval , $ mask ) . do_tos ( $ tos ) ,
$ source ,
$ dest ,
'' ,
"-j $target $mark" ,
'' ,
'' ,
'' ) ) {
#
# expand_rule() returns destination device if any
#
fatal_error "Class Id $original_mark is not associated with device $result" if $ classid && $ device ne $ result ;
}
2007-03-27 01:17:46 +02:00
2007-07-07 18:34:38 +02:00
progress_message " TC Rule \"$currentline\" $done" ;
2007-03-27 01:17:46 +02:00
2007-03-15 02:10:56 +01:00
}
2007-03-27 01:17:46 +02:00
2007-03-20 00:29:22 +01:00
sub rate_to_kbit ( $ ) {
my $ rate = $ _ [ 0 ] ;
return $ 1 if $ rate =~ /^(\d+)kbit$/i ;
2007-04-10 02:12:27 +02:00
return $ 1 * 1000 if $ rate =~ /^(\d+)mbit$/i ;
return $ 1 * 8000 if $ rate =~ /^(\d+)mbps$/i ;
2007-03-20 00:29:22 +01:00
return $ 1 * 8 if $ rate =~ /^(\d+)kbps$/i ;
2007-04-10 02:12:27 +02:00
return $ rate / 125 if $rate =~ / ^ \ d + $/ ;
2007-06-16 23:08:12 +02:00
fatal_error "Invalid Rate ($rate)" ;
2007-03-20 00:29:22 +01:00
}
2007-04-10 02:12:27 +02:00
sub calculate_r2q ( $ ) {
2007-03-20 02:16:51 +01:00
my $ rate = rate_to_kbit $ _ [ 0 ] ;
2007-04-10 02:12:27 +02:00
my $ r2q = $ rate / 200 ;
$ r2q <= 5 ? 5 : $ r2q ;
}
sub calculate_quantum ( $$ ) {
my ( $ rate , $ r2q ) = @ _ ;
$ rate = rate_to_kbit $ rate ;
eval "int( ( $rate * 125 ) / $r2q )" ;
2007-03-27 01:17:46 +02:00
}
2007-03-20 00:29:22 +01:00
sub validate_tc_device ( $$$ ) {
my ( $ device , $ inband , $ outband ) = @ _ ;
2007-06-16 23:08:12 +02:00
fatal_error "Duplicate device ($device)" if $ tcdevices { $ device } ;
fatal_error "Invalid device name ($device)" if $ device =~ /[:+]/ ;
2007-03-20 00:29:22 +01:00
2007-03-20 02:16:51 +01:00
rate_to_kbit $ inband ;
rate_to_kbit $ outband ;
2007-03-20 00:29:22 +01:00
$ tcdevices { $ device } = { } ;
2007-03-20 02:16:51 +01:00
$ tcdevices { $ device } { in_bandwidth } = $ inband ;
$ tcdevices { $ device } { out_bandwidth } = $ outband ;
2007-03-21 00:13:17 +01:00
2007-03-20 00:29:22 +01:00
push @ tcdevices , $ device ;
2007-07-11 01:09:33 +02:00
progress_message " Tcdevice \"$currentline\" $done." ;
2007-03-20 00:29:22 +01:00
}
sub convert_rate ( $$ ) {
my ( $ full , $ rate ) = @ _ ;
2007-04-16 22:11:09 +02:00
if ( $ rate =~ /\bfull\b/ ) {
$ rate =~ s/\bfull\b/$full/g ;
$ rate = eval "int( $rate )" ;
} else {
$ rate = rate_to_kbit $ rate
}
2007-03-20 02:16:51 +01:00
"${rate}kbit" ;
2007-03-20 00:29:22 +01:00
}
sub validate_tc_class ( $$$$$$ ) {
my ( $ device , $ mark , $ rate , $ ceil , $ prio , $ options ) = @ _ ;
my % tosoptions = ( 'tos-minimize-delay' = > 'tos=0x10/0x10' ,
'tos-maximize-throughput' = > 'tos=0x08/0x08' ,
'tos-maximize-reliability' = > 'tos=0x04/0x04' ,
'tos-minimize-cost' = > 'tos=0x02/0x02' ,
'tos-normal-service' = > 'tos=0x00/0x1e' ) ;
2007-03-27 01:17:46 +02:00
2007-03-20 00:29:22 +01:00
my $ devref = $ tcdevices { $ device } ;
2007-06-16 23:08:12 +02:00
fatal_error "Unknown Device ($device)" unless $ devref ;
2007-03-20 02:16:51 +01:00
my $ full = rate_to_kbit $ devref - > { out_bandwidth } ;
2007-03-20 00:29:22 +01:00
$ tcclasses { $ device } = { } unless $ tcclasses { $ device } ;
my $ tcref = $ tcclasses { $ device } ;
2007-06-16 23:08:12 +02:00
fatal_error "Invalid Mark ($mark)" unless $ mark =~ /^([0-9]+|0x[0-9a-f]+)$/ && numeric_value ( $ mark ) < 0xff ;
2007-03-20 00:29:22 +01:00
my $ markval = numeric_value ( $ mark ) ;
2007-06-16 23:08:12 +02:00
fatal_error "Duplicate Mark ($mark)" if $ tcref - > { $ markval } ;
2007-03-20 00:29:22 +01:00
$ tcref - > { $ markval } = { } ;
$ tcref = $ tcref - > { $ markval } ;
$ tcref - > { tos } = [] ;
$ tcref - > { rate } = convert_rate $ full , $ rate ;
$ tcref - > { ceiling } = convert_rate $ full , $ ceil ;
2007-04-01 17:38:05 +02:00
$ tcref - > { priority } = $ prio eq '-' ? 1 : $ prio ;
2007-03-20 00:29:22 +01:00
2007-03-25 18:53:33 +02:00
unless ( $ options eq '-' ) {
for my $ option ( split /,/ , "\L$options" ) {
my $ optval = $ tosoptions { $ option } ;
2007-03-27 01:17:46 +02:00
2007-03-25 18:53:33 +02:00
$ option = $ optval if $ optval ;
2007-03-27 01:17:46 +02:00
2007-03-25 18:53:33 +02:00
if ( $ option eq 'default' ) {
fatal_error "Only one default class may be specified for device $device" if $ devref - > { default } ;
$ devref - > { default } = $ markval ;
} elsif ( $ option eq 'tcp-ack' ) {
$ tcref - > { tcp_ack } = 1 ;
} elsif ( $ option =~ /^tos=0x[0-9a-f]{2}$/ ) {
( undef , $ option ) = split /=/ , $ option ;
push @ { $ tcref - > { tos } } , "$option/0xff" ;
} elsif ( $ option =~ /^tos=0x[0-9a-f]{2}\/0x[0-9a-f]{2}$/ ) {
( undef , $ option ) = split /=/ , $ option ;
push @ { $ tcref - > { tos } } , $ option ;
} else {
2007-06-16 23:08:12 +02:00
fatal_error "Unknown option ($option)" ;
2007-03-25 18:53:33 +02:00
}
2007-03-20 00:29:22 +01:00
}
}
push @ tcclasses , "$device:$markval" ;
2007-07-11 01:09:33 +02:00
progress_message " Tcclass \"$currentline\" $done." ;
2007-03-27 01:17:46 +02:00
}
2007-03-20 00:29:22 +01:00
2007-03-15 02:38:04 +01:00
sub setup_traffic_shaping () {
2007-03-29 19:02:13 +02:00
save_progress_message "Setting up Traffic Control..." ;
2007-03-30 17:57:08 +02:00
my $ fn = open_file 'tcdevices' ;
2007-03-20 00:29:22 +01:00
2007-03-30 17:57:08 +02:00
if ( $ fn ) {
my $ first_entry = 1 ;
2007-03-20 00:29:22 +01:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-20 00:29:22 +01:00
2007-03-29 20:57:53 +02:00
if ( $ first_entry ) {
progress_message2 "$doing $fn..." ;
$ first_entry = 0 ;
}
2007-05-09 21:03:09 +02:00
my ( $ device , $ inband , $ outband ) = split_line 3 , 3 , 'tcdevices' ;
2007-04-01 01:53:17 +02:00
fatal_error "Invalid tcdevices entry" if $ outband eq '-' ;
2007-03-20 00:29:22 +01:00
validate_tc_device ( $ device , $ inband , $ outband ) ;
}
}
2007-03-30 17:57:08 +02:00
$ fn = open_file 'tcclasses' ;
2007-03-20 00:29:22 +01:00
2007-03-30 17:57:08 +02:00
if ( $ fn ) {
my $ first_entry = 1 ;
2007-03-20 00:29:22 +01:00
2007-03-29 19:02:13 +02:00
while ( read_a_line ) {
2007-03-20 00:29:22 +01:00
2007-03-29 20:57:53 +02:00
if ( $ first_entry ) {
progress_message2 "$doing $fn..." ;
$ first_entry = 0 ;
}
2007-04-01 17:38:05 +02:00
my ( $ device , $ mark , $ rate , $ ceil , $ prio , $ options ) = split_line 4 , 6 , 'tcclasses file' ;
2007-03-27 01:17:46 +02:00
2007-03-20 00:29:22 +01:00
validate_tc_class ( $ device , $ mark , $ rate , $ ceil , $ prio , $ options ) ;
}
}
2007-03-20 02:34:59 +01:00
my $ devnum = 1 ;
2007-03-20 00:29:22 +01:00
$ prefix = '10' if @ tcdevices > 10 ;
for my $ device ( @ tcdevices ) {
my $ dev = chain_base ( $ device ) ;
my $ devref = $ tcdevices { $ device } ;
2007-04-10 02:12:27 +02:00
my $ defmark = $ devref - > { default } || 0 ;
2007-03-20 00:29:22 +01:00
2007-04-10 02:12:27 +02:00
$ defmark = "${prefix}${defmark}" if $ defmark ;
2007-03-20 00:29:22 +01:00
emit "if interface_is_usable $device; then" ;
2007-03-27 01:17:46 +02:00
2007-03-20 00:29:22 +01:00
push_indent ;
2007-03-27 06:02:58 +02:00
emitj ( "${dev}_exists=Yes" ,
"qt tc qdisc del dev $device root" ,
"qt tc qdisc del dev $device ingress" ,
2007-04-10 02:12:27 +02:00
"run_tc qdisc add dev $device root handle $devnum: htb default $defmark" ,
2007-03-27 06:02:58 +02:00
"${dev}_mtu=\$(get_device_mtu $device)" ,
2007-04-11 19:55:18 +02:00
"${dev}_mtu1=\$(get_device_mtu1 $device)" ,
"run_tc class add dev $device parent $devnum: classid $devnum:1 htb rate $devref->{out_bandwidth} \$${dev}_mtu1"
2007-03-27 06:02:58 +02:00
) ;
2007-03-27 01:17:46 +02:00
2007-03-20 01:06:56 +01:00
my $ inband = rate_to_kbit $ devref - > { in_bandwidth } ;
2007-03-20 00:29:22 +01:00
if ( $ inband ) {
2007-03-27 06:02:58 +02:00
emitj ( "run_tc qdisc add dev $device handle ffff: ingress" ,
"run_tc filter add dev $device parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate ${inband}kbit burst 10k drop flowid :1"
) ;
2007-03-20 00:29:22 +01:00
}
2007-04-08 16:42:26 +02:00
$ devref - > { number } = $ devnum + + ;
2007-03-20 00:29:22 +01:00
save_progress_message_short " TC Device $device defined." ;
pop_indent ;
emit 'else' ;
push_indent ;
2007-04-12 01:55:08 +02:00
emit qq( error_message "WARNING: Device $device not up and configured -- traffic-shaping configuration skipped" ) ;
2007-03-20 00:29:22 +01:00
emit "${dev}_exists=" ;
pop_indent ;
emit "fi\n" ;
}
2007-03-20 01:27:50 +01:00
my $ lastdevice = '' ;
2007-03-20 00:29:22 +01:00
for my $ class ( @ tcclasses ) {
my ( $ device , $ mark ) = split /:/ , $ class ;
my $ devref = $ tcdevices { $ device } ;
my $ tcref = $ tcclasses { $ device } { $ mark } ;
2007-03-21 00:13:17 +01:00
my $ devnum = $ devref - > { number } ;
my $ classid = "$devnum:${prefix}${mark}" ;
2007-03-20 00:29:22 +01:00
my $ rate = $ tcref - > { rate } ;
2007-04-12 19:40:22 +02:00
my $ quantum = calculate_quantum $ rate , calculate_r2q ( $ devref - > { out_bandwidth } ) ;
2007-03-20 00:29:22 +01:00
my $ dev = chain_base $ device ;
2007-03-20 01:27:50 +01:00
2007-06-13 20:40:31 +02:00
$ classids { $ classid } = $ device ;
2007-03-20 01:27:50 +01:00
if ( $ lastdevice ne $ device ) {
if ( $ lastdevice ) {
pop_indent ;
emit "fi\n" ;
}
emit qq( if [ -n "\$${dev}_exists" ]; then ) ;
push_indent ;
$ lastdevice = $ device ;
}
2007-03-27 01:17:46 +02:00
2007-03-27 06:02:58 +02:00
emitj ( "[ \$${dev}_mtu -gt $quantum ] && quantum=\$${dev}_mtu || quantum=$quantum" ,
2007-04-11 19:55:18 +02:00
"run_tc class add dev $device parent $devref->{number}:1 classid $classid htb rate $rate ceil $tcref->{ceiling} prio $tcref->{priority} \$${dev}_mtu1 quantum \$quantum" ,
2007-03-27 06:02:58 +02:00
"run_tc qdisc add dev $device parent $classid handle ${prefix}${mark}: sfq perturb 10"
) ;
2007-03-20 00:29:22 +01:00
#
# add filters
#
if ( "$capabilities{CLASSIFY_TARGET}" && known_interface $ device ) {
2007-06-13 23:42:17 +02:00
push @ deferred_rules , match_dest_dev ( $ device ) . "-m mark --mark $mark/0xFF -j CLASSIFY --set-class $classid" ;
2007-03-20 00:29:22 +01:00
} else {
emit "run_tc filter add dev $device protocol ip parent $devnum:0 prio 1 handle $mark fw classid $classid" ;
}
#
#options
#
emit "run_tc filter add dev $device parent $devref->{number}:0 protocol ip prio 10 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid $classid" if $ tcref - > { tcp_ack } ;
2007-03-20 22:47:13 +01:00
for my $ tospair ( @ { $ tcref - > { tos } } ) {
2007-03-21 00:13:17 +01:00
my ( $ tos , $ mask ) = split q( / ) , $ tospair ;
2007-03-20 00:29:22 +01:00
emit "run_tc filter add dev $device parent $devnum:0 protocol ip prio 10 u32 match ip tos $tos $mask flowid $classid" ;
}
2007-03-20 01:27:50 +01:00
save_progress_message_short qq( " TC Class $class defined." ) ;
emit '' ;
}
2007-03-27 01:17:46 +02:00
2007-03-20 01:27:50 +01:00
if ( $ lastdevice ) {
pop_indent ;
emit "fi\n" ;
2007-03-20 00:29:22 +01:00
}
2007-03-15 02:38:04 +01:00
}
2007-03-21 00:13:17 +01:00
#
# Process the tcrules file and setup traffic shaping
#
sub setup_tc () {
2007-04-08 16:42:26 +02:00
my $ first_entry = 1 ;
2007-03-29 20:57:53 +02:00
if ( $ capabilities { MANGLE_ENABLED } ) {
ensure_mangle_chain 'tcpre' ;
2007-04-09 17:34:30 +02:00
ensure_mangle_chain 'tcout' ;
2007-03-21 00:13:17 +01:00
2007-03-29 20:57:53 +02:00
if ( $ capabilities { MANGLE_FORWARD } ) {
ensure_mangle_chain 'tcfor' ;
ensure_mangle_chain 'tcpost' ;
}
2007-03-21 00:13:17 +01:00
2007-03-29 03:14:13 +02:00
my $ mark_part = '' ;
2007-03-21 00:13:17 +01:00
2007-03-29 03:14:13 +02:00
if ( @ routemarked_interfaces && ! $ config { TC_EXPERT } ) {
2007-05-18 20:13:46 +02:00
$ mark_part = $ config { HIGH_ROUTE_MARKS } ? '-m mark --mark 0/0xFF00' : '-m mark --mark 0/0xFF' ;
2007-03-27 01:17:46 +02:00
2007-03-29 03:14:13 +02:00
for my $ interface ( @ routemarked_interfaces ) {
add_rule $ mangle_table - > { PREROUTING } , "-i $interface -j tcpre" ;
}
2007-03-21 00:13:17 +01:00
}
2007-03-29 03:14:13 +02:00
add_rule $ mangle_table - > { PREROUTING } , "$mark_part -j tcpre" ;
2007-04-09 17:31:46 +02:00
add_rule $ mangle_table - > { OUTPUT } , "$mark_part -j tcout" ;
2007-03-21 00:13:17 +01:00
2007-03-29 03:14:13 +02:00
if ( $ capabilities { MANGLE_FORWARD } ) {
add_rule $ mangle_table - > { FORWARD } , '-j tcfor' ;
add_rule $ mangle_table - > { POSTROUTING } , '-j tcpost' ;
}
2007-03-21 00:13:17 +01:00
2007-03-29 03:14:13 +02:00
if ( $ config { HIGH_ROUTE_MARKS } ) {
for my $ chain qw( INPUT FORWARD POSTROUTING ) {
2007-04-09 17:50:34 +02:00
insert_rule $ mangle_table - > { $ chain } , 1 , '-j MARK --and-mark 0xFF' ;
2007-03-29 03:14:13 +02:00
}
2007-03-21 00:13:17 +01:00
}
}
2007-04-23 21:49:02 +02:00
if ( $ globals { TC_SCRIPT } ) {
2007-03-21 00:13:17 +01:00
save_progress_message 'Setting up Traffic Control...' ;
2007-04-23 21:49:02 +02:00
append_file $ globals { TC_SCRIPT } ;
2007-03-21 00:13:17 +01:00
} elsif ( $ config { TC_ENABLED } eq 'Internal' ) {
2007-03-29 19:02:13 +02:00
setup_traffic_shaping ;
2007-03-21 00:13:17 +01:00
}
2007-06-13 20:40:31 +02:00
if ( my $ fn = open_file 'tcrules' ) {
while ( read_a_line ) {
if ( $ first_entry ) {
progress_message2 "$doing $fn..." ;
require_capability ( 'MANGLE_ENABLED' , 'a non-empty tcrules file' , 's' ) ;
$ first_entry = 0 ;
}
my ( $ mark , $ source , $ dest , $ proto , $ ports , $ sports , $ user , $ testval , $ length , $ tos ) = split_line1 2 , 10 , 'tcrules file' ;
if ( $ mark eq 'COMMENT' ) {
process_comment ;
} else {
process_tc_rule $ mark , $ source , $ dest , $ proto , $ ports , $ sports , $ user , $ testval , $ length , $ tos
}
}
$ comment = '' ;
}
2007-06-13 20:56:27 +02:00
for ( @ deferred_rules ) {
add_rule ensure_chain ( 'mangle' , 'tcpost' ) , $ _ ;
}
2007-03-21 00:13:17 +01:00
}
2007-03-15 02:10:56 +01:00
1 ;