From e177916c12e0fba295dc564f32294b45f5027231 Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Fri, 26 Oct 2012 07:10:26 -0700 Subject: [PATCH] Implement statistical marking in the tcrules file. Signed-off-by: Tom Eastep --- Shorewall/Perl/Shorewall/Chains.pm | 11 ++ Shorewall/Perl/Shorewall/Tc.pm | 145 +++++++++++++----- Shorewall/manpages/shorewall-masq.xml | 23 +++ Shorewall/manpages/shorewall-tcrules.xml | 166 ++++++++++++++++++--- Shorewall6/manpages/shorewall6-tcrules.xml | 143 +++++++++++++++--- docs/PacketMarking.xml | 9 +- 6 files changed, 419 insertions(+), 78 deletions(-) diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index 2c66c22b6..0d2c7c921 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -117,6 +117,7 @@ our %EXPORT_TAGS = ( OPTIMIZE_RULESET_MASK OPTIMIZE_MASK + state_match state_imatch initialize_chain_table copy_rules @@ -3715,6 +3716,16 @@ sub port_count( $ ) { # # Generate a state match # +sub state_match( $ ) { + my $state = shift; + + if ( $state eq 'ALL' ) { + '' + } else { + have_capability 'CONNTRACK_MATCH' ? ( "-m conntrack --ctstate $state " ) : ( "-m state --state $state " ); + } +} + sub state_imatch( $ ) { my $state = shift; diff --git a/Shorewall/Perl/Shorewall/Tc.pm b/Shorewall/Perl/Shorewall/Tc.pm index c85226fd0..897a6c865 100644 --- a/Shorewall/Perl/Shorewall/Tc.pm +++ b/Shorewall/Perl/Shorewall/Tc.pm @@ -174,6 +174,12 @@ my $family; my $divertref; # DIVERT chain +my %validstates = ( NEW => 0, + RELATED => 0, + ESTABLISHED => 0, + UNTRACKED => 0, + INVALID => 0, + ); # # Rather than initializing globals in an INIT block or during declaration, # we initialize them in a function. This is done for two reasons: @@ -199,14 +205,14 @@ sub initialize( $ ) { } sub process_tc_rule( ) { - my ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability , $dscp ); + my ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability , $dscp , $state ); if ( $family == F_IPV4 ) { - ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $probability, $dscp ) = - 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; + ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $probability, $dscp, $state ) = + 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 }, { COMMENT => 0, FORMAT => 2 } , 15; $headers = '-'; } else { - ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability, $dscp ) = - 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; + ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers, $probability, $dscp, $state ) = + 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 }, { COMMENT => 0, FORMAT => 2 }, 16; } our @tccmd; @@ -259,6 +265,7 @@ sub process_tc_rule( ) { my $cmd; my $rest; my $matches = ''; + my $mark1; my %processtcc = ( sticky => sub() { if ( $chain eq 'tcout' ) { @@ -501,7 +508,7 @@ sub process_tc_rule( ) { $chain = $tcsref->{chain} if $tcsref->{chain}; $target = $tcsref->{target} if $tcsref->{target}; - $mark = "$mark/" . in_hex( $globals{TC_MASK} ) if $connmark = $tcsref->{connmark}; + $mark = "$mark/" . in_hex( $globals{TC_MASK} ) if $connmark = $tcsref->{connmark} && $mark !~ m'/'; require_capability ('CONNMARK' , "CONNMARK Rules", '' ) if $connmark; @@ -584,15 +591,23 @@ sub process_tc_rule( ) { } } - validate_mark $mark; + if ( $mark =~ /-/ ) { + ( $mark, $mark1 ) = split /-/, $mark, 2; + validate_mark $mark; + fatal_error "Invalid mark range ($mark-$mark1)" if $mark =~ m'/'; + validate_mark $mark1; + require_capability 'STATISTIC_MATCH', 'A mark range', 's'; + } else { + validate_mark $mark; - if ( $config{PROVIDER_OFFSET} ) { - my $val = numeric_value( $cmd ); - fatal_error "Invalid MARK/CLASSIFY ($cmd)" unless defined $val; - my $limit = $globals{TC_MASK}; - unless ( have_capability 'FWMARK_RT_MASK' ) { - fatal_error "Marks <= $limit may not be set in the PREROUTING or OUTPUT chains when HIGH_ROUTE_MARKS=Yes" - if $cmd && ( $chain eq 'tcpre' || $chain eq 'tcout' ) && $val <= $limit; + if ( $config{PROVIDER_OFFSET} ) { + my $val = numeric_value( $cmd ); + fatal_error "Invalid MARK/CLASSIFY ($cmd)" unless defined $val; + my $limit = $globals{TC_MASK}; + unless ( have_capability 'FWMARK_RT_MASK' ) { + fatal_error "Marks <= $limit may not be set in the PREROUTING or OUTPUT chains when HIGH_ROUTE_MARKS=Yes" + if $cmd && ( $chain eq 'tcpre' || $chain eq 'tcout' ) && $val <= $limit; + } } } } @@ -600,26 +615,88 @@ sub process_tc_rule( ) { fatal_error "USER/GROUP only allowed in the OUTPUT chain" unless ( $user eq '-' || ( $chain eq 'tcout' || $chain eq 'tcpost' ) ); - if ( ( my $result = expand_rule( ensure_chain( 'mangle' , $chain ) , - $restrictions{$chain} | $restriction, - do_proto( $proto, $ports, $sports) . $matches . - do_user( $user ) . - do_test( $testval, $globals{TC_MASK} ) . - do_length( $length ) . - do_tos( $tos ) . - do_connbytes( $connbytes ) . - do_helper( $helper ) . - do_headers( $headers ) . - do_probability( $probability ) . - do_dscp( $dscp ) , - $source , - $dest , - '' , - $mark ? "$target $mark" : $target, - '' , - $target , - '' ) ) - && $device ) { + if ( $state ne '-' ) { + my @state = split_list( $state, 'state' ); + my %state = %validstates; + + for ( @state ) { + fatal_error "Invalid STATE ($_)" unless exists $state{$_}; + fatal_error "Duplicate STATE ($_)" if $state{$_}; + } + } else { + $state = 'ALL'; + } + + if ( $mark1 ) { + # + # A Mark Range + # + my $chainref = ensure_chain( 'mangle', $chain ); + + ( $mark1, my $mask ) = split( '/', $mark1 ); + + my ( $markval, $mark1val ) = ( numeric_value $mark, numeric_value $mark1 ); + + fatal_error "Invalid mark range ($mark-$mark1)" unless $markval < $mark1val; + + $mask = $globals{TC_MASK} unless supplied $mask; + + $mask = numeric_value $mask; + + my $increment = 1; + + $increment <<= 1 until $increment & $mask; + + $mask = in_hex $mask; + + my $marks = $mark1val - $markval + 1; + + for ( my $packet = 0; $packet < $marks; $packet++, $markval += $increment ) { + my $match = "-m statistic --mode nth --every $marks --packet $packet "; + + expand_rule( $chainref, + $restrictions{$chain} | $restriction, + $match . + do_user( $user ) . + do_test( $testval, $globals{TC_MASK} ) . + do_test( $testval, $globals{TC_MASK} ) . + do_length( $length ) . + do_tos( $tos ) . + do_connbytes( $connbytes ) . + do_helper( $helper ) . + do_headers( $headers ) . + do_probability( $probability ) . + do_dscp( $dscp ) . + state_match( $state ) , + $source , + $dest , + '' , + "$target " . join( '/', in_hex( $markval ) , $mask ) , + '', + $target , + '' ); + } + } elsif ( ( my $result = expand_rule( ensure_chain( 'mangle' , $chain ) , + $restrictions{$chain} | $restriction, + do_proto( $proto, $ports, $sports) . $matches . + do_user( $user ) . + do_test( $testval, $globals{TC_MASK} ) . + do_length( $length ) . + do_tos( $tos ) . + do_connbytes( $connbytes ) . + do_helper( $helper ) . + do_headers( $headers ) . + do_probability( $probability ) . + do_dscp( $dscp ) . + state_match( $state ) , + $source , + $dest , + '' , + $mark ? "$target $mark" : $target, + '' , + $target , + '' ) ) + && $device ) { # # expand_rule() returns destination device if any # diff --git a/Shorewall/manpages/shorewall-masq.xml b/Shorewall/manpages/shorewall-masq.xml index 92b731c9a..be2dc7a25 100644 --- a/Shorewall/manpages/shorewall-masq.xml +++ b/Shorewall/manpages/shorewall-masq.xml @@ -619,6 +619,29 @@ eth0:+myset[dst] - 206.124.146.177 + + + Example 7: + + + SNAT outgoing connections on eth0 from 192.168.1.0/24 in + round-robin fashion between addresses 1.1.1.1, 1.1.1.3, and 1.1.1.9 + (Shorewall 4.5.9 and later). + + /etc/shorewall/tcrules: + + #ACTION SOURCE DEST PROTO PORT(S) SOURCE USER TEST + # PORT(S) + 1-3:CF 192.168.1.0/24 eth0 ; state=NEW + +/etc/shorewall/masq: + + #INTERFACE SOURCE ADDRESS ... + eth0 192.168.1.0/24 1.1.1.1 ; mark=1:C + eth0 192.168.1.0/24 1.1.1.3 ; mark=2:C + eth0 192.168.1.0/24 1.1.1.4 ; mark=3:C + + diff --git a/Shorewall/manpages/shorewall-tcrules.xml b/Shorewall/manpages/shorewall-tcrules.xml index d23953d94..60875b96c 100644 --- a/Shorewall/manpages/shorewall-tcrules.xml +++ b/Shorewall/manpages/shorewall-tcrules.xml @@ -131,8 +131,12 @@ The mark value may be optionally followed by "/" and a mask value (used to determine those bits of the connection mark - to actually be set). The mark and optional mask are then - followed by one of: + to actually be set). When a mask is specified, the result of + logically ANDing the mark value with the mask must be the same + as the mark value. + + The mark and optional mask are then followed by one + of: @@ -178,26 +182,114 @@ + - Special considerations for If - HIGH_ROUTE_MARKS=Yes in shorewall.conf(5). + + A mark range which is a pair of integers separated by a + dash ("-"). Added in Shorewall 4.5.9. - If HIGH_ROUTE_MARKS=Yes, then you may also specify a value - in the range 0x0100-0xFF00 with the low-order byte being zero. - Such values may only be used in the PREROUTING chain (value - followed by :P or you have set - MARK_IN_FORWARD_CHAIN=No in shorewall.conf(5) and have not - followed the value with ) or the OUTPUT chain - (SOURCE is $FW). With - HIGH_ROUTE_MARKS=Yes, non-zero mark values less that 256 are not - permitted. Shorewall prohibits non-zero mark values less that - 256 in the OUTPUT chain when HIGH_ROUTE_MARKS=Yes. While earlier - versions allow such values in the OUTPUT chain, it is strongly - recommended that with HIGH_ROUTE_MARKS=Yes, you use the - POSTROUTING chain to apply traffic shaping - marks/classification. + May be optionally followed by a slash ("/") and a mask and + requires the Statistics Match capability + in iptables and kernel. Marks in the specified range are + assigned to packets on a round-robin fashion. + + When a mask is specified, the result of logically ANDing + the mark value with the mask must be the same as the mark value. + The least significant bit in the mask is used as an increment. + For example, if '0x200-0x400/0xff00' is specified, then the + assigned mark values are 0x200, 0x300 and 0x400 in equal + proportions. If no mask is specified, then ( 2 ** MASK_BITS ) - + 1 is assumed (MASK_BITS is set in shorewall.conf(5)). + + May optionally be followed by :P, :F,:T or + :I where + :P indicates that marking should occur in the + PREROUTING chain, :F indicates + that marking should occur in the FORWARD chain, :I indicates that marking should occur in + the INPUT chain (added in Shorewall 4.4.13), and :T indicates that marking should occur in + the POSTROUTING chain. If neither :P, :F + nor :T follow the mark value + then the chain is determined as follows: + + - If the SOURCE is $FW[:address-or-range[,address-or-range]...], + then the rule is inserted into the OUTPUT chain. When + HIGH_ROUTE_MARKS=Yes, only high mark values may be assigned + there. Packet marking rules for traffic shaping of packets + originating on the firewall must be coded in the POSTROUTING + chain (see below). + + - Otherwise, the chain is determined by the setting of + MARK_IN_FORWARD_CHAIN in shorewall.conf(5). + + Please note that :I is + included for completeness and affects neither traffic shaping + nor policy routing. + + If your kernel and iptables include CONNMARK support then + you can also mark the connection rather than the packet. + + The mark range may be optionally followed by "/" and a + mask value (used to determine those bits of the connection mark + to actually be set). When a mask is specified, the result of + logically ANDing the mark value with each of the masks must be + the same as the mark value. + + The mark and optional mask are then followed by one + of: + + + + C + + + Mark the connection in the chain determined by the + setting of MARK_IN_FORWARD_CHAIN + + + + + CF + + + Mark the connection in the FORWARD chain + + + + + CP + + + Mark the connection in the PREROUTING chain. + + + + + CT + + + Mark the connecdtion in the POSTROUTING chain + + + + + CI + + + Mark the connection in the INPUT chain. This option + is included for completeness and has no applicability to + traffic shaping or policy routing. + + + @@ -530,6 +622,17 @@ SAME $FW 0.0.0.0/0 tcp 80,443 role="bold">:F + + STATE {NEW|RELATED|ESTABLISHED|INVALID} [,...] + + Added in Shorewall 4.5.9. The rule will only match if the + packet's connection is in one of the listed states. + + TOS(tos[/mask]) @@ -1124,6 +1227,29 @@ Normal-Service => 0x00 mark has been set, save it to the connection mark. + + + Example 2: + + + SNAT outgoing connections on eth0 from 192.168.1.0/24 in + round-robin fashion between addresses 1.1.1.1, 1.1.1.3, and 1.1.1.9 + (Shorewall 4.5.9 and later). + + /etc/shorewall/tcrules: + + #ACTION SOURCE DEST PROTO PORT(S) SOURCE USER TEST + # PORT(S) + 1-3:CF 192.168.1.0/24 eth0 ; state=NEW + +/etc/shorewall/masq: + + #INTERFACE SOURCE ADDRESS ... + eth0 192.168.1.0/24 1.1.1.1 ; mark=1:C + eth0 192.168.1.0/24 1.1.1.3 ; mark=2:C + eth0 192.168.1.0/24 1.1.1.4 ; mark=3:C + + diff --git a/Shorewall6/manpages/shorewall6-tcrules.xml b/Shorewall6/manpages/shorewall6-tcrules.xml index b999b82b2..6ef430563 100644 --- a/Shorewall6/manpages/shorewall6-tcrules.xml +++ b/Shorewall6/manpages/shorewall6-tcrules.xml @@ -131,8 +131,12 @@ The mark value may be optionally followed by "/" and a mask value (used to determine those bits of the connection mark - to actually be set). The mark and optional mask are then - followed by one of:+ + to actually be set). When a mask is specified, the result of + logically ANDing the mark value with the mask must be the same + as the mark value. + + The mark and optional mask are then followed by one + of:+ @@ -178,26 +182,114 @@ + - Special considerations for If - HIGH_ROUTE_MARKS=Yes in shorewall6.conf(5). + + A mark range which is a pair of integers separated by a + dash ("-"). Added in Shorewall 4.5.9. - If HIGH_ROUTE_MARKS=Yes, then you may also specify a value - in the range 0x0100-0xFF00 with the low-order byte being zero. - Such values may only be used in the PREROUTING chain (value - followed by :P or you have set - MARK_IN_FORWARD_CHAIN=No in shorewall6.conf(5) and have - not followed the value with ) or the OUTPUT - chain (SOURCE is $FW). With - HIGH_ROUTE_MARKS=Yes, non-zero mark values less that 256 are not - permitted. Shorewall6 prohibits non-zero mark values less that - 256 in the OUTPUT chain when HIGH_ROUTE_MARKS=Yes. While earlier - versions allow such values in the OUTPUT chain, it is strongly - recommended that with HIGH_ROUTE_MARKS=Yes, you use the - POSTROUTING chain to apply traffic shaping - marks/classification. + May be optionally followed by a slash ("/") and a mask and + requires the Statistics Match capability + in iptables and kernel. Marks in the specified range are + assigned to packets on a round-robin fashion. + + When a mask is specified, the result of logically ANDing + the mark value with the mask must be the same as the mark value. + The least significant bit in the mask is used as an increment. + For example, if '0x200-0x400/0xff00' is specified, then the + assigned mark values are 0x200, 0x300 and 0x400 in equal + proportions. If no mask is specified, then ( 2 ** MASK_BITS ) - + 1 is assumed (MASK_BITS is set in shorewall6.conf(5)). + + May optionally be followed by :P, :F,:T or + :I where + :P indicates that marking should occur in the + PREROUTING chain, :F indicates + that marking should occur in the FORWARD chain, :I indicates that marking should occur in + the INPUT chain (added in Shorewall 4.4.13), and :T indicates that marking should occur in + the POSTROUTING chain. If neither :P, :F + nor :T follow the mark value + then the chain is determined as follows: + + - If the SOURCE is $FW[:address-or-range[,address-or-range]...], + then the rule is inserted into the OUTPUT chain. When + HIGH_ROUTE_MARKS=Yes, only high mark values may be assigned + there. Packet marking rules for traffic shaping of packets + originating on the firewall must be coded in the POSTROUTING + chain (see below). + + - Otherwise, the chain is determined by the setting of + MARK_IN_FORWARD_CHAIN in shorewall.conf(5). + + Please note that :I is + included for completeness and affects neither traffic shaping + nor policy routing. + + If your kernel and iptables include CONNMARK support then + you can also mark the connection rather than the packet. + + The mark range may be optionally followed by "/" and a + mask value (used to determine those bits of the connection mark + to actually be set). When a mask is specified, the result of + logically ANDing the mark value with each of the masks must be + the same as the mark value. + + The mark and optional mask are then followed by one + of: + + + + C + + + Mark the connection in the chain determined by the + setting of MARK_IN_FORWARD_CHAIN + + + + + CF + + + Mark the connection in the FORWARD chain + + + + + CP + + + Mark the connection in the PREROUTING chain. + + + + + CT + + + Mark the connecdtion in the POSTROUTING chain + + + + + CI + + + Mark the connection in the INPUT chain. This option + is included for completeness and has no applicability to + traffic shaping or policy routing. + + + @@ -451,6 +543,17 @@ SAME $FW 0.0.0.0/0 tcp 80,443 role="bold">:F + + STATE {NEW|RELATED|ESTABLISHED|INVALID} [,...] + + Added in Shorewall 4.5.9. The rule will only match if the + packet's connection is in one of the listed states. + + TOS(tos[/mask]) diff --git a/docs/PacketMarking.xml b/docs/PacketMarking.xml index d05f85d5d..1defd85e1 100644 --- a/docs/PacketMarking.xml +++ b/docs/PacketMarking.xml @@ -278,8 +278,9 @@ tcp 6 19 TIME_WAIT src=206.124.146.176 dst=192.136.34.98 sport=58597 dport= Shorewall actually allows you to have complete control over the layout of the 32-bit mark using the following options in shorewall.conf (5) (these - options were documents in the shorewall.conf manpage in Shorewall - 4.4.26): + options were documentrf in the shorewall.conf(5) manpage in + Shorewall 4.4.26): @@ -339,9 +340,9 @@ tcp 6 19 TIME_WAIT src=206.124.146.176 dst=192.136.34.98 sport=58597 dport= The relationship between these options is shown in this diagram. - + - + The default values of these options are determined by the settings of other options as follows: