201
sub process_tc_rule( ) {
202
my ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability , $dscp );
203
if ( $family == F_IPV4 ) {
204
( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $probability, $dscp ) =
205
split_line1 'tcrules file', { mark => 0, action => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, user => 6, test => 7, length => 8, tos => 9, connbytes => 10, helper => 11, probability => 12 , dscp => 13 }, { COMMENT => 0, FORMAT => 2 } , 14;
208
( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability, $dscp ) =
209
split_line1 'tcrules file', { mark => 0, action => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, user => 6, test => 7, length => 8, tos => 9, connbytes => 10, helper => 11, headers => 12, probability => 13 , dscp => 14 }, { COMMENT => 0, FORMAT => 2 }, 15;
207
sub process_tc_rule1( $$$$$$$$$$$$$$$$ ) {
208
my ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability , $dscp , $state ) = @_;
213
%tccmd = ( SAVE => { match => sub ( $ ) { $_[0] eq 'SAVE' } ,
214
target => 'CONNMARK --save-mark --mask' ,
215
mark => $config{TC_EXPERT} ? HIGHMARK : SMALLMARK,
216
mask => in_hex( $globals{TC_MASK} ) ,
219
RESTORE => { match => sub ( $ ) { $_[0] eq 'RESTORE' },
220
target => 'CONNMARK --restore-mark --mask' ,
221
mark => $config{TC_EXPERT} ? HIGHMARK : SMALLMARK ,
222
mask => in_hex( $globals{TC_MASK} ) ,
225
CONTINUE => { match => sub ( $ ) { $_[0] eq 'CONTINUE' },
231
SAME => { match => sub ( $ ) { $_[0] eq 'SAME' },
237
IPMARK => { match => sub ( $ ) { $_[0] =~ /^IPMARK/ },
243
'|' => { match => sub ( $ ) { $_[0] =~ '\|.*'} ,
244
target => 'MARK --or-mark' ,
248
'&' => { match => sub ( $ ) { $_[0] =~ '&.*' },
249
target => 'MARK --and-mark' ,
254
TPROXY => { match => sub ( $ ) { $_[0] =~ /^TPROXY/ },
260
DIVERT => { match => sub( $ ) { $_[0] =~ /^DIVERT/ },
266
TTL => { match => sub( $ ) { $_[0] =~ /^TTL/ },
272
HL => { match => sub( $ ) { $_[0] =~ /^HL/ },
278
IMQ => { match => sub( $ ) { $_[0] =~ /^IMQ\(\d+\)$/ },
284
DSCP => { match => sub( $ ) { $_[0] =~ /^DSCP\(\w+\)$/ },
290
TOS => { match => sub( $ ) { $_[0] =~ /^TOS\(.+\)$/ },
296
CHECKSUM => { match => sub( $ ) { $_[0] eq 'CHECKSUM' },
297
target => 'CHECKSUM' ,
302
INLINE => { match => sub( $ ) { $_[0] eq 'INLINE' },
216
311
fatal_error 'MARK must be specified' if $originalmark eq '-';
218
if ( $originalmark eq 'COMMENT' ) {
223
if ( $originalmark eq 'FORMAT' ) {
224
if ( $source =~ /^([12])$/ ) {
229
fatal_error "Invalid FORMAT ($source)";
232
313
my ( $mark, $designator, $remainder ) = split( /:/, $originalmark, 3 );
234
315
fatal_error "Invalid MARK ($originalmark)" unless supplied $mark;
558
676
} elsif ( $tccmd->{mask} ) {
559
677
$mark = $tccmd->{mask};
680
fatal_error "Invalid ACTION ($originalmark)";
568
if ( $config{PROVIDER_OFFSET} ) {
569
my $val = numeric_value( $cmd );
570
fatal_error "Invalid MARK/CLASSIFY ($cmd)" unless defined $val;
571
my $limit = $globals{TC_MASK};
572
unless ( have_capability 'FWMARK_RT_MASK' ) {
573
fatal_error "Marks <= $limit may not be set in the PREROUTING or OUTPUT chains when HIGH_ROUTE_MARKS=Yes"
574
if $cmd && ( $chain eq 'tcpre' || $chain eq 'tcout' ) && $val <= $limit;
682
} elsif ( $mark =~ /-/ ) {
683
( $mark, $mark1 ) = split /-/, $mark, 2;
685
fatal_error "Invalid mark range ($mark-$mark1)" if $mark =~ m'/';
686
validate_mark $mark1;
687
require_capability 'STATISTIC_MATCH', 'A mark range', 's';
691
if ( $config{PROVIDER_OFFSET} ) {
692
my $val = numeric_value( $cmd );
693
fatal_error "Invalid MARK/CLASSIFY ($cmd)" unless defined $val;
694
my $limit = $globals{TC_MASK};
695
unless ( have_capability 'FWMARK_RT_MASK' ) {
696
fatal_error "Marks <= $limit may not be set in the PREROUTING or OUTPUT chains when HIGH_ROUTE_MARKS=Yes"
697
if $cmd && ( $chain eq 'tcpre' || $chain eq 'tcout' ) && $val <= $limit;
580
704
fatal_error "USER/GROUP only allowed in the OUTPUT chain" unless ( $user eq '-' || ( $chain eq 'tcout' || $chain eq 'tcpost' ) );
582
if ( ( my $result = expand_rule( ensure_chain( 'mangle' , $chain ) ,
583
$restrictions{$chain} | $restriction,
584
do_proto( $proto, $ports, $sports) . $matches .
586
do_test( $testval, $globals{TC_MASK} ) .
587
do_length( $length ) .
589
do_connbytes( $connbytes ) .
590
do_helper( $helper ) .
591
do_headers( $headers ) .
592
do_probability( $probability ) .
597
$mark ? "$target $mark" : $target,
706
if ( $state ne '-' ) {
707
my @state = split_list( $state, 'state' );
708
my %state = %validstates;
711
fatal_error "Invalid STATE ($_)" unless exists $state{$_};
712
fatal_error "Duplicate STATE ($_)" if $state{$_};
722
my $chainref = ensure_chain( 'mangle', $chain );
724
( $mark1, my $mask ) = split( '/', $mark1 );
726
my ( $markval, $mark1val ) = ( numeric_value $mark, numeric_value $mark1 );
728
fatal_error "Invalid mark range ($mark-$mark1)" unless $markval < $mark1val;
730
$mask = $globals{TC_MASK} unless supplied $mask;
732
$mask = numeric_value $mask;
737
$increment <<= 1, $shift++ until $increment & $mask;
739
$mask = in_hex $mask;
741
my $marks = ( ( $mark1val - $markval ) >> $shift ) + 1;
743
for ( my $packet = 0; $packet < $marks; $packet++, $markval += $increment ) {
744
my $match = "-m statistic --mode nth --every $marks --packet $packet ";
746
expand_rule( $chainref,
747
$restrictions{$chain} | $restriction,
751
do_test( $testval, $globals{TC_MASK} ) .
752
do_test( $testval, $globals{TC_MASK} ) .
753
do_length( $length ) .
755
do_connbytes( $connbytes ) .
756
do_helper( $helper ) .
757
do_headers( $headers ) .
758
do_probability( $probability ) .
760
state_match( $state ) ,
764
"$target " . join( '/', in_hex( $markval ) , $mask ) ,
769
} elsif ( ( my $result = expand_rule( ensure_chain( 'mangle' , $chain ) ,
770
$restrictions{$chain} | $restriction,
772
do_proto( $proto, $ports, $sports) . $matches .
774
do_test( $testval, $globals{TC_MASK} ) .
775
do_length( $length ) .
777
do_connbytes( $connbytes ) .
778
do_helper( $helper ) .
779
do_headers( $headers ) .
780
do_probability( $probability ) .
782
state_match( $state ) ,
786
$mark ? "$target $mark" : $target,
603
792
# expand_rule() returns destination device if any
801
sub process_tc_rule( ) {
802
my ( $originalmark, $source, $dest, $protos, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability , $dscp , $state );
803
if ( $family == F_IPV4 ) {
804
( $originalmark, $source, $dest, $protos, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $probability, $dscp, $state ) =
805
split_line1 'tcrules file', { mark => 0, action => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, user => 6, test => 7, length => 8, tos => 9, connbytes => 10, helper => 11, probability => 12 , dscp => 13, state => 14 }, {}, 15;
808
( $originalmark, $source, $dest, $protos, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability, $dscp, $state ) =
809
split_line1 'tcrules file', { mark => 0, action => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, user => 6, test => 7, length => 8, tos => 9, connbytes => 10, helper => 11, headers => 12, probability => 13 , dscp => 14 , state => 15 }, {}, 16;
812
for my $proto (split_list( $protos, 'Protocol' ) ) {
813
process_tc_rule1( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability , $dscp , $state );
612
817
sub rate_to_kbit( $ ) {
613
818
my $rate = $_[0];
887
1095
fatal_error "Duplicate INTERFACE ($device)" if $tcdevices{$device};
888
1096
fatal_error "Invalid INTERFACE name ($device)" if $device =~ /[:+]/;
890
my ( $classify, $pfifo, $flow, $qdisc ) = (0, 0, '', 'htb' );
1098
my ( $classify, $pfifo, $flow, $qdisc, $linklayer, $overhead, $mtu, $mpu, $tsize ) =
1099
(0, 0, '', 'htb', '', 0, 0, 0, 0);
892
1101
if ( $options ne '-' ) {
893
1102
for my $option ( split_list1 $options, 'option' ) {
903
1112
$qdisc = 'hfsc';
904
1113
} elsif ( $option eq 'htb' ) {
1115
} elsif ( $option =~ /^linklayer=([a-z]+)$/ ) {
1117
fatal_error "Invalid linklayer ($linklayer)" unless $validlinklayer{ $linklayer };
1118
} elsif ( $option =~ /^overhead=(.+)$/ ) {
1119
$overhead = numeric_value( $1 );
1120
fatal_error "Invalid overhead ($1)" unless defined $overhead;
1121
fatal_error q('overhead' requires 'linklayer') unless $linklayer;
1122
} elsif ( $option =~ /^mtu=(.+)$/ ) {
1123
$mtu = numeric_value( $1 );
1124
fatal_error "Invalid mtu ($1)" unless defined $mtu;
1125
fatal_error q('mtu' requires 'linklayer') unless $linklayer;
1126
} elsif ( $option =~ /^mpu=(.+)$/ ) {
1127
$mpu = numeric_value( $1 );
1128
fatal_error "Invalid mpu ($1)" unless defined $mpu;
1129
fatal_error q('mpu' requires 'linklayer') unless $linklayer;
1130
} elsif ( $option =~ /^tsize=(.+)$/ ) {
1131
$tsize = numeric_value( $1 );
1132
fatal_error "Invalid tsize ($1)" unless defined $tsize;
1133
fatal_error q('tsize' requires 'linklayer') unless $linklayer;
907
1135
fatal_error "Unknown device option ($option)";
1004
1238
( $dev , $devref );
1241
use constant { RED_INTEGER => 1, RED_FLOAT => 2, RED_NONE => 3 };
1243
my %validredoptions = ( min => RED_INTEGER,
1245
limit => RED_INTEGER,
1246
burst => RED_INTEGER,
1247
avpkt => RED_INTEGER,
1248
bandwidth => RED_INTEGER,
1249
probability => RED_FLOAT,
1253
use constant { CODEL_INTEGER => 1, CODEL_INTERVAL => 2, CODEL_NONE => 3 };
1255
my %validcodeloptions = ( flows => CODEL_INTEGER,
1256
target => CODEL_INTERVAL,
1257
interval => CODEL_INTERVAL,
1258
limit => CODEL_INTEGER,
1260
noecn => CODEL_NONE,
1261
quantum => CODEL_INTEGER
1264
sub validate_filter_priority( $$ ) {
1265
my ( $priority, $kind ) = @_;
1267
my $pri = numeric_value( $priority );
1269
fatal_error "Invalid $kind priority ($priority)" unless defined $pri && $pri > 0 && $pri <= 65535;
1007
1274
sub validate_tc_class( ) {
1008
1275
my ( $devclass, $mark, $rate, $ceil, $prio, $options ) =
1009
1276
split_line 'tcclasses file', { interface => 0, mark => 1, rate => 2, ceil => 3, prio => 4, options => 5 };
1057
1325
my $tcref = $tcclasses{$device};
1327
if ( $devref->{qdisc} eq 'htb' ) {
1328
fatal_error "Invalid PRIO ($prio)" unless defined numeric_value $prio;
1061
1334
if ( $mark ne '-' ) {
1062
if ( $devref->{classify} ) {
1063
warning_message "INTERFACE $device has the 'classify' option - MARK value ($mark) ignored";
1065
fatal_error "MARK may not be specified when TC_BITS=0" unless $config{TC_BITS};
1067
$markval = numeric_value( $mark );
1068
fatal_error "Invalid MARK ($markval)" unless defined $markval;
1070
fatal_error "Invalid Mark ($mark)" unless $markval <= $globals{TC_MAX};
1072
if ( $classnumber ) {
1073
fatal_error "Duplicate Class NUMBER ($classnumber)" if $tcref->{$classnumber};
1075
$classnumber = $config{TC_BITS} >= 14 ? $devref->{nextclass}++ : hex_value( $devnum . $markval );
1076
fatal_error "Duplicate MARK ($mark)" if $tcref->{$classnumber};
1335
fatal_error "MARK may not be specified when TC_BITS=0" unless $config{TC_BITS};
1337
( $mark, my $priority ) = split/:/, $mark, 2;
1339
if ( supplied $priority ) {
1340
$markprio = validate_filter_priority( $priority, 'mark' );
1342
fatal_error "Missing mark priority" if $prio eq '-';
1343
$markprio = ( $prio << 8 ) | 20;
1344
progress_message2 " Priority of the $device packet mark $mark filter is $markprio";
1347
$markval = numeric_value( $mark );
1348
fatal_error "Invalid MARK ($markval)" unless defined $markval;
1350
fatal_error "Invalid Mark ($mark)" unless $markval <= $globals{TC_MAX};
1352
if ( $classnumber ) {
1353
fatal_error "Duplicate Class NUMBER ($classnumber)" if $tcref->{$classnumber};
1355
$classnumber = $config{TC_BITS} >= 14 ? $devref->{nextclass}++ : hex_value( $devnum . $markval );
1356
fatal_error "Duplicate MARK ($mark)" if $tcref->{$classnumber};
1080
1359
fatal_error "Duplicate Class NUMBER ($classnumber)" if $tcref->{$classnumber};
1101
1382
my ( $umax, $dmax ) = ( '', '' );
1384
if ( $ceil =~ /^(.+):(.+)/ ) {
1385
fatal_error "An LS rate may only be specified for HFSC classes" unless $devref->{qdisc} eq 'hfsc';
1103
1390
if ( $devref->{qdisc} eq 'hfsc' ) {
1104
( my $trate , $dmax, $umax , my $rest ) = split ':', $rate , 4;
1106
fatal_error "Invalid RATE ($rate)" if defined $rest;
1108
$rate = convert_rate ( $ratemax, $trate, 'RATE', $ratename );
1109
$dmax = convert_delay( $dmax );
1110
$umax = convert_size( $umax );
1111
fatal_error "DMAX must be specified when UMAX is specified" if $umax && ! $dmax;
1391
if ( $rate eq '-' ) {
1392
fatal_error 'A RATE must be supplied' unless $lsceil;
1395
( my $trate , $dmax, $umax , my $rest ) = split ':', $rate , 4;
1397
fatal_error "Invalid RATE ($rate)" if defined $rest;
1399
$rate = convert_rate ( $ratemax, $trate, 'RATE', $ratename );
1400
$dmax = convert_delay( $dmax );
1401
$umax = convert_size( $umax );
1402
fatal_error "DMAX must be specified when UMAX is specified" if $umax && ! $dmax;
1114
1406
$rate = convert_rate ( $ratemax, $rate, 'RATE' , $ratename );
1141
1433
$tcref = $tcref->{$classnumber};
1143
fatal_error "RATE ($tcref->{rate}) exceeds CEIL ($tcref->{ceiling})" if $tcref->{rate} > $tcref->{ceiling};
1435
fatal_error "RATE ($rate) exceeds CEIL ($ceil)" if $rate && $ceil && $rate > $ceil;
1437
my ( $red, %redopts ) = ( 0, ( avpkt => 1000 ) );
1438
my ( $codel, %codelopts ) = ( 0, ( ) );
1145
1440
unless ( $options eq '-' ) {
1146
1441
for my $option ( split_list1 "\L$options", 'option' ) {
1147
my $optval = $tosoptions{$option};
1149
$option = "tos=$optval" if $optval;
1445
( $option, my $pri ) = split /:/, $option, 2;
1447
if ( $option =~ /^tos=(.+)/ || ( $optval = $tosoptions{$option} ) ) {
1449
if ( supplied $pri ) {
1450
$priority = validate_filter_priority( $pri, 'mark' );
1452
fatal_error "Missing TOS priority" if $prio eq '-';
1453
$priority = ( $prio << 8 ) | 15;
1454
progress_message2 " Priority of the $device $option filter is $priority";
1457
$option = "tos=$optval" if $optval;
1458
} elsif ( supplied $pri ) {
1459
$option = join ':', $option, $pri;
1151
1462
if ( $option eq 'default' ) {
1152
1463
fatal_error "Only one default class may be specified for device $device" if $devref->{default};
1153
1464
fatal_error "The $option option is not valid with 'occurs" if $tcref->{occurs} > 1;
1154
1465
$devref->{default} = $classnumber;
1155
} elsif ( $option eq 'tcp-ack' ) {
1466
} elsif ( $option =~ /tcp-ack(:(\d+|0x[0-0a-fA-F]))?$/ ) {
1156
1467
fatal_error "The $option option is not valid with 'occurs" if $tcref->{occurs} > 1;
1157
$tcref->{tcp_ack} = 1;
1469
$tcref->{tcp_ack} = validate_filter_priority( $2, 'tcp-ack' );
1471
fatal_error "Missing tcp-ack priority" if $prio eq '-';
1472
my $ackpri = $tcref->{tcp_ack} = ( $prio << 8 ) | 10;
1473
progress_message2 " Priority of the $device tcp-ack filter is $ackpri";
1158
1475
} elsif ( $option =~ /^tos=0x[0-9a-f]{2}$/ ) {
1159
1476
fatal_error "The $option option is not valid with 'occurs" if $tcref->{occurs} > 1;
1160
1477
( undef, $option ) = split /=/, $option;
1161
push @{$tcref->{tos}}, "$option/0xff";
1478
push @{$tcref->{tos}}, "$option/0xff:$priority";
1162
1479
} elsif ( $option =~ /^tos=0x[0-9a-f]{2}\/0x[0-9a-f]{2}$/ ) {
1163
1480
fatal_error "The $option option is not valid with 'occurs" if $tcref->{occurs} > 1;
1164
1481
( undef, $option ) = split /=/, $option;
1165
push @{$tcref->{tos}}, $option;
1482
push @{$tcref->{tos}}, "$option:$priority";
1166
1483
} elsif ( $option =~ /^flow=(.*)$/ ) {
1167
1484
fatal_error "The 'flow' option is not allowed with 'pfifo'" if $tcref->{pfifo};
1485
fatal_error "The 'flow' option is not allowed with 'red'" if $tcref->{red};
1168
1486
$tcref->{flow} = process_flow $1;
1169
1487
} elsif ( $option eq 'pfifo' ) {
1170
fatal_error "The 'pfifo'' option is not allowed with 'flow='" if $tcref->{flow};
1488
fatal_error "The 'pfifo' option is not allowed with 'flow='" if $tcref->{flow};
1489
fatal_error "The 'pfifo' option is not allowed with 'red='" if $tcref->{red};
1490
fatal_error "The 'pfifo' option is not allowed with 'fq_codel='" if $tcref->{fq_codel};
1171
1491
$tcref->{pfifo} = 1;
1172
1492
} elsif ( $option =~ /^occurs=(\d+)$/ ) {
1188
1508
warning_message "limit ignored with pfifo queuing" if $tcref->{pfifo};
1189
1509
fatal_error "Invalid limit ($1)" if $1 < 3 || $1 > 128;
1190
1510
$tcref->{limit} = $1;
1511
} elsif ( $option =~ s/^red=// ) {
1512
fatal_error "The 'red=' option is not allowed with 'flow='" if $tcref->{flow};
1513
fatal_error "The 'red=' option is not allowed with 'pfifo'" if $tcref->{pfifo};
1514
fatal_error "The 'pfifo' option is not allowed with 'fq_codel='" if $tcref->{fq_codel};
1518
for my $redopt ( split_list( $option , q('red' option list) ) ) {
1520
# $2 ----------------------
1521
# $1 ------ | $3 ------- |
1523
if ( $redopt =~ /^([a-z]+) (?:= ( ([01]?\.)?(\d{1,8})) )?$/x ) {
1524
fatal_error "Invalid RED option ($1)" unless $opttype = $validredoptions{$1};
1527
# '=<value>' supplied
1529
fatal_error "The $1 option does not take a value" if $opttype == RED_NONE;
1534
fatal_error "The $1 option requires an integer value" if $opttype == RED_INTEGER;
1535
fatal_error "The value of $1 must be <= 1" if $2 > 1;
1540
fatal_error "The $1 option requires a value 0 <= value <= 1" if $opttype == RED_FLOAT;
1546
fatal_error "The $1 option requires a value" unless $opttype == RED_NONE;
1551
fatal_error "Invalid RED option specification ($redopt)";
1555
for ( qw/ limit min max avpkt burst probability / ) {
1556
fatal_error "The $_ 'red' option is required" unless $redopts{$_};
1559
fatal_error "The 'max' red option must be at least 2 * 'min'" unless $redopts{max} >= 2 * $redopts{min};
1560
fatal_error "The 'limit' red option must be at least 2 * 'max'" unless $redopts{limit} >= 2 * $redopts{min};
1561
$redopts{ecn} = 1 if exists $redopts{ecn};
1562
$tcref->{redopts} = \%redopts;
1563
} elsif ( $option =~ /^fq_codel(?:=.+)?$/ ) {
1564
fatal_error "The 'fq_codel' option is not allowed with 'red='" if $tcref->{red};
1565
fatal_error "The 'fq_codel' option is not allowed with 'pfifo'" if $tcref->{pfifo};
1566
$tcref->{fq_codel} = 1;
1569
$option =~ s/fq_codel=?//;
1571
for my $codelopt ( split_list( $option , q('fq_codel' option list) ) ) {
1573
# $1 ------ $2 --------------
1576
if ( $codelopt =~ /^([a-z]+) (?:= ((?:\d+)(ms)?))?$/x )
1578
fatal_error "Invalid CODEL option ($1)" unless $opttype = $validcodeloptions{$1};
1581
# '=<value>' supplied
1583
fatal_error "The $1 option does not take a value" if $opttype == CODEL_NONE;
1588
fatal_error "The $1 option requires an integer value" if $opttype == CODEL_INTEGER;
1593
fatal_error "The $1 option requires an interval value" if $opttype == CODEL_INTERVAL;
1599
fatal_error "The $1 option requires a value" unless $opttype == CODEL_NONE;
1602
$codelopts{$1} = $2;
1604
fatal_error "Invalid fq_codel option specification ($codelopt)";
1608
if ( exists $codelopts{ecn} ) {
1609
fatal_error "The 'ecn' and 'noecn' fq_codel options are mutually exclusive" if exists $codelopts{noecn};
1610
$codelopts{ecn} = 1;
1611
} elsif ( exists $codelopts{noecn} ) {
1612
$codelopts{noecn} = 1;
1614
$codelopts{ecn} = 1;
1617
$tcref->{codelopts} = \%codelopts;
1192
1619
fatal_error "Unknown option ($option)";
1209
1633
while ( --$occurs ) {
1210
1634
fatal_error "Duplicate class number ($classnumber)" if $tcclasses{$device}{++$classnumber};
1212
$tcclasses{$device}{$classnumber} = { tos => [] ,
1213
rate => $tcref->{rate} ,
1214
ceiling => $tcref->{ceiling} ,
1215
priority => $tcref->{priority} ,
1217
flow => $tcref->{flow} ,
1218
pfifo => $tcref->{pfifo},
1220
parent => $parentclass,
1221
limit => $tcref->{limit},
1636
$tcclasses{$device}{$classnumber} = { tos => [] ,
1637
rate => $tcref->{rate} ,
1638
ceiling => $tcref->{ceiling} ,
1639
priority => $tcref->{priority} ,
1641
markprio => $markprio ,
1642
flow => $tcref->{flow} ,
1643
pfifo => $tcref->{pfifo},
1645
parent => $parentclass,
1646
limit => $tcref->{limit},
1647
red => $tcref->{red},
1648
redopts => $tcref->{redopts},
1649
fq_codel => $tcref->{fq_codel},
1650
codelopts => $tcref->{codelopts},
1223
1652
push @tcclasses, "$device:$classnumber";
1232
1661
# Process a record from the tcfilters file
1234
sub process_tc_filter() {
1236
my ( $devclass, $source, $dest , $proto, $portlist , $sportlist, $tos, $length ) = split_line 'tcfilters file', { class => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, tos => 6, length => 7 };
1238
fatal_error 'CLASS must be specified' if $devclass eq '-';
1663
sub process_tc_filter1( $$$$$$$$$ ) {
1665
my ( $devclass, $source, $dest , $proto, $portlist , $sportlist, $tos, $length, $priority ) = @_;
1240
1667
my ($device, $class, $rest ) = split /:/, $devclass, 3;
1936
sub process_tc_filter() {
1938
my ( $devclass, $source, $dest , $protos, $portlist , $sportlist, $tos, $length, $priority )
1939
= split_line 'tcfilters file', { class => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, tos => 6, length => 7 , priority => 8 };
1941
fatal_error 'CLASS must be specified' if $devclass eq '-';
1943
for my $proto ( split_list $protos, 'Protocol' ) {
1944
process_tc_filter1( $devclass, $source, $dest , $proto, $portlist , $sportlist, $tos, $length, $priority );
1498
1949
# Process the tcfilter file storing the compiled filters in the %tcdevices table
2037
sub process_tc_priority() {
2038
my ( $band, $protos, $ports , $address, $interface, $helper ) = split_line1 'tcpri', { band => 0, proto => 1, port => 2, address => 3, interface => 4, helper => 5 };
2040
fatal_error 'BAND must be specified' if $band eq '-';
2042
fatal_error "Invalid tcpri entry" if ( $protos eq '-' &&
2045
$interface eq '-' &&
2048
my $val = numeric_value $band;
2050
fatal_error "Invalid PRIORITY ($band)" unless $val && $val <= 3;
2052
for my $proto ( split_list $protos, 'Protocol' ) {
2053
process_tc_priority1( $band, $proto, $ports , $address, $interface, $helper );
1601
2058
# Process tcinterfaces
1708
2164
emit ( "qt \$TC qdisc del dev $device root",
1709
"qt \$TC qdisc del dev $device ingress",
1710
"${dev}_mtu=\$(get_device_mtu $device)",
2165
"qt \$TC qdisc del dev $device ingress" );
2167
emit ( "${dev}_mtu=\$(get_device_mtu $device)",
1711
2168
"${dev}_mtu1=\$(get_device_mtu1 $device)"
1714
if ( $devref->{qdisc} eq 'htb' ) {
1715
emit ( "run_tc qdisc add dev $device root handle $devnum: htb default $defmark r2q $r2q" ,
2169
) if $qdisc eq 'htb';
2173
if ( $devref->{linklayer} ) {
2174
$stab = "stab linklayer $devref->{linklayer} overhead $devref->{overhead} ";
2175
$stab .= "mtu $devref->{mtu} " if $devref->{mtu};
2176
$stab .= "mpu $devref->{mpu} " if $devref->{mpu};
2177
$stab .= "tsize $devref->{tsize} " if $devref->{tsize};
2182
if ( $qdisc eq 'htb' ) {
2183
emit ( "run_tc qdisc add dev $device ${stab}root handle $devnum: htb default $defmark r2q $r2q" ,
1716
2184
"run_tc class add dev $device parent $devnum: classid $devnum:1 htb rate $devref->{out_bandwidth} \$${dev}_mtu1" );
1718
emit ( "run_tc qdisc add dev $device root handle $devnum: hfsc default $defmark" ,
2186
emit ( "run_tc qdisc add dev $device ${stab}root handle $devnum: hfsc default $defmark" ,
1719
2187
"run_tc class add dev $device parent $devnum: classid $devnum:1 hfsc sc rate $devref->{out_bandwidth} ul rate $devref->{out_bandwidth}" );
1762
2230
my $mark = $tcref->{mark};
1763
2231
my $devicenumber = in_hexp $devref->{number};
1764
2232
my $classid = join( ':', $devicenumber, $classnum);
1765
my $rate = "$tcref->{rate}kbit";
1766
my $quantum = calculate_quantum $rate, calculate_r2q( $devref->{out_bandwidth} );
2233
my $rawrate = $tcref->{rate};
2234
my $rate = "${rawrate}kbit";
2235
my $lsceil = $tcref->{lsceil};
1768
2238
$classids{$classid}=$devname;
1770
my $priority = $tcref->{priority} << 8;
1771
2240
my $parent = in_hexp $tcref->{parent};
1773
emit ( "[ \$${dev}_mtu -gt $quantum ] && quantum=\$${dev}_mtu || quantum=$quantum" );
1775
2242
if ( $devref->{qdisc} eq 'htb' ) {
2243
$quantum = calculate_quantum $rate, calculate_r2q( $devref->{out_bandwidth} );
2244
emit ( "[ \$${dev}_mtu -gt $quantum ] && quantum=\$${dev}_mtu || quantum=$quantum" );
1776
2245
emit ( "run_tc class add dev $device parent $devicenumber:$parent classid $classid htb rate $rate ceil $tcref->{ceiling}kbit prio $tcref->{priority} \$${dev}_mtu1 quantum \$quantum" );
1778
2247
my $dmax = $tcref->{dmax};
2248
my $rule = "run_tc class add dev $device parent $devicenumber:$parent classid $classid hfsc";
1781
2251
my $umax = $tcref->{umax} ? "$tcref->{umax}b" : "\${${dev}_mtu}b";
1782
emit ( "run_tc class add dev $device parent $devicenumber:$parent classid $classid hfsc sc umax $umax dmax ${dmax}ms rate $rate ul rate $tcref->{ceiling}kbit" );
2252
$rule .= " sc umax $umax dmax ${dmax}ms";
2253
$rule .= " rate $rate" if $rawrate;
1784
emit ( "run_tc class add dev $device parent $devicenumber:$parent classid $classid hfsc sc rate $rate ul rate $tcref->{ceiling}kbit" );
2255
$rule .= " sc rate $rate" if $rawrate;
2258
$rule .= " ls rate ${lsceil}kbit" if $lsceil;
2259
$rule .= " ul rate $tcref->{ceiling}kbit" if $tcref->{ceiling};
1788
if ( $tcref->{leaf} && ! $tcref->{pfifo} ) {
1789
1 while $devnums[++$sfq];
1791
$sfqinhex = in_hexp( $sfq);
1792
if ( $devref->{qdisc} eq 'htb' ) {
1793
emit( "run_tc qdisc add dev $device parent $classid handle $sfqinhex: sfq quantum \$quantum limit $tcref->{limit} perturb 10" );
1795
emit( "run_tc qdisc add dev $device parent $classid handle $sfqinhex: sfq limit $tcref->{limit} perturb 10" );
2264
if ( $tcref->{leaf} ) {
2265
if ( $tcref->{red} ) {
2266
1 while $devnums[++$sfq];
2267
$sfqinhex = in_hexp( $sfq);
2269
my ( $options, $redopts ) = ( '', $tcref->{redopts} );
2271
while ( my ( $option, $type ) = each %validredoptions ) {
2272
if ( my $value = $redopts->{$option} ) {
2273
if ( $type == RED_NONE ) {
2274
$options = join( ' ', $options, $option ) if $value;
2276
$options = join( ' ', $options, $option, $value );
2281
emit( "run_tc qdisc add dev $device parent $classid handle $sfqinhex: red${options}" );
2282
} elsif ( $tcref->{fq_codel} ) {
2283
1 while $devnums[++$sfq];
2284
$sfqinhex = in_hexp( $sfq);
2286
my ( $options, $codelopts ) = ( '', $tcref->{codelopts} );
2288
while ( my ( $option, $type ) = each %validcodeloptions ) {
2289
if ( my $value = $codelopts->{$option} ) {
2290
if ( $type == CODEL_NONE ) {
2291
$options = join( ' ', $options, $option );
2293
$options = join( ' ', $options, $option, $value );
2298
emit( "run_tc qdisc add dev $device parent $classid handle $sfqinhex: fq_codel${options}" );
2300
} elsif ( ! $tcref->{pfifo} ) {
2301
1 while $devnums[++$sfq];
2303
$sfqinhex = in_hexp( $sfq);
2304
if ( $qdisc eq 'htb' ) {
2305
emit( "run_tc qdisc add dev $device parent $classid handle $sfqinhex: sfq quantum \$quantum limit $tcref->{limit} perturb 10" );
2307
emit( "run_tc qdisc add dev $device parent $classid handle $sfqinhex: sfq limit $tcref->{limit} perturb 10" );
1801
2314
unless ( $mark eq '-' ) {
1802
emit "run_tc filter add dev $device protocol all parent $devicenumber:0 prio " . ( $priority | 20 ) . " handle $mark fw classid $classid" if $tcref->{occurs} == 1;
2315
emit "run_tc filter add dev $device protocol all parent $devicenumber:0 prio $tcref->{markprio} handle $mark fw classid $classid" if $tcref->{occurs} == 1;
1805
2318
emit "run_tc filter add dev $device protocol all prio 1 parent $sfqinhex: handle $classnum flow hash keys $tcref->{flow} divisor 1024" if $tcref->{flow};
1809
emit( "run_tc filter add dev $device parent $devicenumber:0 protocol ip prio " . ( $priority | 10 ) . ' u32' .
2322
emit( "run_tc filter add dev $device parent $devicenumber:0 protocol ip prio $tcref->{tcp_ack} u32" .
1810
2323
"\\\n match ip protocol 6 0xff" .
1811
2324
"\\\n match u8 0x05 0x0f at 0" .
1812
2325
"\\\n match u16 0x0000 0xffc0 at 2" .
1813
2326
"\\\n match u8 0x10 0xff at 33 flowid $classid" ) if $tcref->{tcp_ack};
1815
2328
for my $tospair ( @{$tcref->{tos}} ) {
2329
( $tospair, my $priority ) = split /:/, $tospair;
1816
2330
my ( $tos, $mask ) = split q(/), $tospair;
1817
emit "run_tc filter add dev $device parent $devicenumber:0 protocol ip prio " . ( $priority | 10 ) . " u32 match ip tos $tos $mask flowid $classid";
2331
emit "run_tc filter add dev $device parent $devicenumber:0 protocol ip prio $priority u32 match ip tos $tos $mask flowid $classid";
1820
2334
save_progress_message_short qq(" TC Class $classid defined.");
1899
2413
# Process a record in the secmarks file
1901
sub process_secmark_rule() {
1902
my ( $secmark, $chainin, $source, $dest, $proto, $dport, $sport, $user, $mark ) =
1903
split_line1( 'Secmarks file' , { secmark => 0, chain => 1, source => 2, dest => 3, proto => 4, dport => 5, sport => 6, user => 7, mark => 8 } );
1905
fatal_error 'SECMARK must be specified' if $secmark eq '-';
1907
if ( $secmark eq 'COMMENT' ) {
2415
sub process_secmark_rule1( $$$$$$$$$ ) {
2416
my ( $secmark, $chainin, $source, $dest, $proto, $dport, $sport, $user, $mark ) = @_;
1912
2418
my %chns = ( T => 'tcpost' ,
2478
# Process a record in the secmarks file
2480
sub process_secmark_rule() {
2481
my ( $secmark, $chainin, $source, $dest, $protos, $dport, $sport, $user, $mark ) =
2482
split_line1( 'Secmarks file' , { secmark => 0, chain => 1, source => 2, dest => 3, proto => 4, dport => 5, sport => 6, user => 7, mark => 8 } );
2484
fatal_error 'SECMARK must be specified' if $secmark eq '-';
2486
for my $proto ( split_list( $protos, 'Protocol' ) ) {
2487
process_secmark_rule1( $secmark, $chainin, $source, $dest, $proto, $dport, $sport, $user, $mark );
1967
2492
# Process the tcrules file and setup traffic shaping
1969
2494
sub setup_tc() {
2011
2536
append_file $globals{TC_SCRIPT};
2013
2538
process_tcpri if $config{TC_ENABLED} eq 'Simple';
2014
setup_traffic_shaping unless $config{TC_ENABLED} eq 'Shared';
2539
setup_traffic_shaping if @tcdevices && $config{TC_ENABLED} ne 'Shared';
2017
if ( $config{TC_ENABLED} ) {
2018
our @tccmd = ( { match => sub ( $ ) { $_[0] eq 'SAVE' } ,
2019
target => 'CONNMARK --save-mark --mask' ,
2020
mark => $config{TC_EXPERT} ? HIGHMARK : SMALLMARK,
2021
mask => in_hex( $globals{TC_MASK} ) ,
2024
{ match => sub ( $ ) { $_[0] eq 'RESTORE' },
2025
target => 'CONNMARK --restore-mark --mask' ,
2026
mark => $config{TC_EXPERT} ? HIGHMARK : SMALLMARK ,
2027
mask => in_hex( $globals{TC_MASK} ) ,
2030
{ match => sub ( $ ) { $_[0] eq 'CONTINUE' },
2031
target => 'RETURN' ,
2036
{ match => sub ( $ ) { $_[0] eq 'SAME' },
2037
target => 'sticky' ,
2042
{ match => sub ( $ ) { $_[0] =~ /^IPMARK/ },
2043
target => 'IPMARK' ,
2048
{ match => sub ( $ ) { $_[0] =~ '\|.*'} ,
2049
target => 'MARK --or-mark' ,
2052
{ match => sub ( $ ) { $_[0] =~ '&.*' },
2053
target => 'MARK --and-mark' ,
2058
{ match => sub ( $ ) { $_[0] =~ /^TPROXY/ },
2063
{ match => sub( $ ) { $_[0] =~ /^DIVERT/ },
2068
{ match => sub( $ ) { $_[0] =~ /^TTL/ },
2074
{ match => sub( $ ) { $_[0] =~ /^HL/ },
2080
{ match => sub( $ ) { $_[0] =~ /^IMQ\(\d+\)$/ },
2086
{ match => sub( $ ) { $_[0] =~ /^DSCP\(\w+\)$/ },
2092
{ match => sub( $ ) { $_[0] =~ /^TOS\(.+\)$/ },
2100
if ( my $fn = open_file 'tcrules' ) {
2542
if ( $config{MANGLE_ENABLED} ) {
2544
if ( my $fn = open_file( 'tcrules' , 2, 1 ) ) {
2104
2546
first_entry "$doing $fn...";
2106
2548
process_tc_rule while read_a_line( NORMAL_READ );
2113
if ( $config{MANGLE_ENABLED} ) {
2114
if ( my $fn = open_file 'secmarks' ) {
2552
if ( my $fn = open_file( 'secmarks', 1, 1 ) ) {
2116
2554
first_entry "$doing $fn...";
2118
2556
process_secmark_rule while read_a_line( NORMAL_READ );
2123
2560
handle_stickiness( $sticky );