From 67932f2d42cd6b35a2d194bde71cdcc92fd11a9c Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Wed, 30 May 2012 06:49:00 -0700 Subject: [PATCH] Break up expand_rule() Signed-off-by: Tom Eastep --- Shorewall/Perl/Shorewall/Chains.pm | 837 ++++++++++++++++------------- 1 file changed, 464 insertions(+), 373 deletions(-) diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index 4b3a68d03..2ac1a77f7 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -5623,6 +5623,33 @@ sub set_global_variables( $ ) { } } +############################################################################################ +# Helpers for expand_rule() +############################################################################################ +# +# Loops and conditionals can be opened by calling push_command(). +# All loops/conditionals are terminated by calling pop_commands(). +# +sub push_command( $$$ ) { + my ( $chainref, $command, $end ) = @_; + our @ends; + + add_commands $chainref, $command; + incr_cmd_level $chainref; + push @ends, $end; +} + +sub pop_commands( $ ) { + my $chainref = $_[0]; + + our @ends; + + while ( @ends ) { + decr_cmd_level $chainref; + add_commands $chainref, pop @ends; + } +} + # # Issue an invalid list error message # @@ -5701,9 +5728,399 @@ sub handle_network_list( $$ ) { } +# +# Split an interface:address pair and returns its components +# +sub isolate_source_interface( $ ) { + my ( $source ) = @_; + + my ( $iiface, $inets ); + + if ( $family == F_IPV4 ) { + if ( $source =~ /^~/ ) { + $inets = $source; + } elsif ( $source =~ /^(.+?):(.+)$/ ) { + $iiface = $1; + $inets = $2; + } elsif ( $source =~ /\+|&|~|\..*\./ || $source =~ /^!?\^/ ) { + $inets = $source; + } else { + $iiface = $source; + } + } elsif ( $source =~ /^(.+?):<(.+)>\s*$/ || $source =~ /^(.+?):\[(.+)\]\s*$/ || $source =~ /^(.+?):(!?\+.+)$/ ) { + $iiface = $1; + $inets = $2; + } elsif ( $source =~ /:/ ) { + if ( $source =~ /^<(.+)>$/ || $source =~ /^\[(.+)\]$/ ) { + $inets = $1; + } else { + $inets = $source; + } + } elsif ( $source =~ /(?:\+|&|%|~|\..*\.)/ || $source =~ /^!?\^/ ) { + $inets = $source; + } else { + $iiface = $source; + } + + ( $iiface, $inets ); + +} + +# +# Verify the source interface -- returns a rule fragment to be added to the rule being created +# +sub verify_source_interface( $$$$ ) { + my ( $iiface, $restriction, $table, $chainref ) = @_; + + my $rule = ''; + + fatal_error "Unknown Interface ($iiface)" unless known_interface $iiface; + + if ( $restriction & POSTROUTE_RESTRICT ) { + # + # An interface in the SOURCE column of a masq file + # + fatal_error "Bridge ports may not appear in the SOURCE column of this file" if port_to_bridge( $iiface ); + fatal_error "A wildcard interface ( $iiface) is not allowed in this context" if $iiface =~ /\+$/; + + if ( $table eq 'nat' ) { + warning_message qq(Using an interface as the masq SOURCE requires the interface to be up and configured when $Product starts/restarts) unless $idiotcount++; + } else { + warning_message qq(Using an interface as the SOURCE in a T: rule requires the interface to be up and configured when $Product starts/restarts) unless $idiotcount1++; + } + + push_command $chainref, join( '', 'for source in ', get_interface_nets( $iiface) , '; do' ), 'done'; + + $rule .= '-s $source '; + } else { + if ( $restriction & OUTPUT_RESTRICT ) { + if ( $chainref->{accounting} ) { + fatal_error "Source Interface ($iiface) not allowed in the $chainref->{name} chain"; + } else { + fatal_error "Source Interface ($iiface) not allowed when the SOURCE is the firewall"; + } + } + + $chainref->{restricted} |= $restriction; + $rule .= match_source_dev( $iiface ); + } + + $rule; +} + +# +# Splits an interface:address pair. Updates that passed rule and returns ($rule, $interface, $address ) +# +sub isolate_dest_interface( $$$$ ) { + my ( $restriction, $dest, $chainref, $rule ) = @_; + + my ( $diface, $dnets ); + + if ( ( $restriction & PREROUTE_RESTRICT ) && $dest =~ /^detect:(.*)$/ ) { + # + # DETECT_DNAT_IPADDRS=Yes and we're generating the nat rule + # + my @interfaces = split /\s+/, $1; + + if ( @interfaces > 1 ) { + my $list = ""; + my $optional; + + for my $interface ( @interfaces ) { + $optional++ if interface_is_optional $interface; + $list = join( ' ', $list , get_interface_address( $interface ) ); + } + + push_command( $chainref , "for address in $list; do" , 'done' ); + + push_command( $chainref , 'if [ $address != 0.0.0.0 ]; then' , 'fi' ) if $optional; + + $rule .= '-d $address '; + } else { + my $interface = $interfaces[0]; + my $variable = get_interface_address( $interface ); + + push_command( $chainref , "if [ $variable != 0.0.0.0 ]; then" , 'fi') if interface_is_optional( $interface ); + + $rule .= "-d $variable "; + } + } elsif ( $family == F_IPV4 ) { + if ( $dest =~ /^(.+?):(.+)$/ ) { + $diface = $1; + $dnets = $2; + } elsif ( $dest =~ /\+|&|%|~|\..*\./ || $dest =~ /^!?\^/ ) { + $dnets = $dest; + } else { + $diface = $dest; + } + } elsif ( $dest =~ /^(.+?):<(.+)>\s*$/ || $dest =~ /^(.+?):\[(.+)\]\s*$/ || $dest =~ /^(.+?):(!?\+.+)$/ ) { + $diface = $1; + $dnets = $2; + } elsif ( $dest =~ /:/ ) { + if ( $dest =~ /^<(.+)>$/ || $dest =~ /^\[(.+)\]$/ ) { + $dnets = $1; + } else { + $dnets = $dest; + } + } elsif ( $dest =~ /(?:\+|&|\..*\.)/ || $dest =~ /^!?\^/ ) { + $dnets = $dest; + } else { + $diface = $dest; + } + + ( $diface, $dnets, $rule ); +} + +# +# Verify the destination interface. Returns a rule fragment to be added to the rule being created +# +sub verify_dest_interface( $$$$ ) { + my ( $diface, $restriction, $chainref, $iiface ) = @_; + + my $rule = ''; + + fatal_error "Unknown Interface ($diface)" unless known_interface $diface; + + if ( $restriction & PREROUTE_RESTRICT ) { + # + # Dest interface -- must use routing table + # + fatal_error "A DEST interface is not permitted in the PREROUTING chain" if $restriction & DESTIFACE_DISALLOW; + fatal_error "Bridge port ($diface) not allowed" if port_to_bridge( $diface ); + fatal_error "A wildcard interface ($diface) is not allowed in this context" if $diface =~ /\+$/; + push_command( $chainref , 'for dest in ' . get_interface_nets( $diface) . '; do', 'done' ); + $rule .= '-d $dest '; + } else { + fatal_error "Bridge Port ($diface) not allowed in OUTPUT or POSTROUTING rules" if ( $restriction & ( POSTROUTE_RESTRICT + OUTPUT_RESTRICT ) ) && port_to_bridge( $diface ); + fatal_error "Destination Interface ($diface) not allowed when the destination zone is the firewall" if $restriction & INPUT_RESTRICT; + if ( $restriction & DESTIFACE_DISALLOW ) { + if ( $chainref->{accounting} ) { + fatal_error "Destination Interface ($diface) not allowed in the $chainref->{name} chain"; + } else { + fatal_error "Destination Interface ($diface) not allowed in the mangle OUTPUT chain"; + } + } + + if ( $iiface ) { + my $bridge = port_to_bridge( $diface ); + fatal_error "Source interface ($iiface) is not a port on the same bridge as the destination interface ( $diface )" if $bridge && $bridge ne source_port_to_bridge( $iiface ); + } + + $chainref->{restricted} |= $restriction; + $rule .= match_dest_dev( $diface ); + } + + $rule; +} + +# +# Handles the original destination. Updates the passed rule and returns ( $networks, $exclusion, $rule ) +# +sub handle_original_dest( $$$ ) { + my ( $origdest, $chainref, $rule ) = @_; + my ( $onets, $oexcl ); + + if ( $origdest eq '-' || ! have_capability( 'CONNTRACK_MATCH' ) ) { + $onets = $oexcl = ''; + } elsif ( $origdest =~ /^detect:(.*)$/ ) { + # + # Either the filter part of a DNAT rule or 'detect' was given in the ORIG DEST column + # + my @interfaces = split /\s+/, $1; + + if ( @interfaces > 1 ) { + my $list = ""; + my $optional; + + for my $interface ( @interfaces ) { + $optional++ if interface_is_optional $interface; + $list = join( ' ', $list , get_interface_address( $interface ) ); + } + + push_command( $chainref , "for address in $list; do" , 'done' ); + + push_command( $chainref , 'if [ $address != 0.0.0.0 ]; then' , 'fi' ) if $optional; + + $rule .= '-m conntrack --ctorigdst $address '; + } else { + my $interface = $interfaces[0]; + my $variable = get_interface_address( $interface ); + + push_command( $chainref , "if [ $variable != 0.0.0.0 ]; then" , 'fi' ) if interface_is_optional( $interface ); + + $rule .= "-m conntrack --ctorigdst $variable "; + } + + $onets = $oexcl = ''; + } else { + fatal_error "Invalid ORIGINAL DEST" if $origdest =~ /^([^!]+)?,!([^!]+)$/ || $origdest =~ /.*!.*!/; + + if ( $origdest =~ /^([^!]+)?!([^!]+)$/ ) { + # + # Exclusion + # + $onets = $1; + $oexcl = $2; + } else { + $oexcl = ''; + $onets = $origdest; + } + + unless ( $onets ) { + my @oexcl = mysplit $oexcl; + if ( @oexcl == 1 ) { + $rule .= match_orig_dest( "!$oexcl" ); + $oexcl = ''; + } + } + } + + ( $onets, $oexcl, $rule ); +} + +# +# Handles non-trivial exclusion. Updates the passed rule and returns ( $rule, $done ) +# +sub handle_exclusion( $$$$$$$$$$$$$$$$$$ ) { + my ( $disposition, + $table, + $rule, + $restriction, + $inets, + $iexcl, + $onets, + $oexcl, + $dnets, + $dexcl, + $chainref, + $chain, + $mac, + $loglevel, + $logtag, + $targetref, + $exceptionrule, + $jump ) = @_; + + if ( $disposition eq 'RETURN' || $disposition eq 'CONTINUE' ) { + # + # We can't use an exclusion chain -- we mark those packets to be excluded and then condition the rules generated in the block below on the mark value + # + require_capability 'MARK_ANYWHERE' , 'Exclusion in ACCEPT+/CONTINUE/NONAT rules', 's' unless $table eq 'mangle'; + + fatal_error "Exclusion in ACCEPT+/CONTINUE/NONAT rules requires the Multiple Match capability in your kernel and iptables" + if $rule =~ / -m mark / && ! $globals{KLUDGEFREE}; + # + # Clear the exclusion bit + # + add_ijump $chainref , j => 'MARK', targetopts => '--and-mark ' . in_hex( $globals{EXCLUSION_MASK} ^ 0xffffffff ); + # + # Mark packet if it matches any of the exclusions + # + my $exclude = '-j MARK --or-mark ' . in_hex( $globals{EXCLUSION_MASK} ); + + for ( mysplit $iexcl ) { + my $cond = conditional_rule( $chainref, $_ ); + add_rule $chainref, ( match_source_net $_ , $restriction, $mac ) . $exclude; + conditional_rule_end( $chainref ) if $cond; + } + + for ( mysplit $dexcl ) { + my $cond = conditional_rule( $chainref, $_ ); + add_rule $chainref, ( match_dest_net $_, $restriction ) . $exclude; + conditional_rule_end( $chainref ) if $cond; + } + + for ( mysplit $oexcl ) { + my $cond = conditional_rule( $chainref, $_ ); + add_rule $chainref, ( match_orig_dest $_ ) . $exclude; + conditional_rule_end( $chainref ) if $cond; + } + # + # Augment the rule to include 'not excluded' + # + $rule .= '-m mark --mark 0/' . in_hex( $globals{EXCLUSION_MASK} ) . ' '; + + ( $rule, 0 ); + } else { + # + # Create the Exclusion Chain + # + my $echain = newexclusionchain( $table ); + + my $echainref = new_chain $table, $echain; + # + # Use the current rule and send all possible matches to the exclusion chain + # + for my $onet ( mysplit $onets ) { + + my $cond = conditional_rule( $chainref, $onet ); + + $onet = match_orig_dest $onet; + + for my $inet ( mysplit $inets ) { + + my $cond = conditional_rule( $chainref, $inet ); + + my $source_match = match_source_net( $inet, $restriction, $mac ) if $globals{KLUDGEFREE}; + + for my $dnet ( mysplit $dnets ) { + $source_match = match_source_net( $inet, $restriction, $mac ) unless $globals{KLUDGEFREE}; + add_expanded_jump( $chainref, $echainref, 0, join( '', $rule, $source_match, match_dest_net( $dnet, $restriction ), $onet ) ); + } + + conditional_rule_end( $chainref ) if $cond; + } + + conditional_rule_end( $chainref ) if $cond; + } + # + # Generate RETURNs for each exclusion + # + for ( mysplit $iexcl ) { + my $cond = conditional_rule( $echainref, $_ ); + add_rule $echainref, ( match_source_net $_ , $restriction, $mac ) . '-j RETURN'; + conditional_rule_end( $echainref ) if $cond; + } + + for ( mysplit $dexcl ) { + my $cond = conditional_rule( $echainref, $_ ); + add_rule $echainref, ( match_dest_net $_, $restriction ) . '-j RETURN'; + conditional_rule_end( $echainref ) if $cond; + } + + for ( mysplit $oexcl ) { + my $cond = conditional_rule( $echainref, $_ ); + add_rule $echainref, ( match_orig_dest $_ ) . '-j RETURN'; + conditional_rule_end( $echainref ) if $cond; + } + # + # Log rule + # + log_rule_limit( $loglevel , + $echainref , + $chain, + $disposition eq 'reject' ? 'REJECT' : $disposition , + '' , + $logtag , + 'add' , + '' ) + if $loglevel; + # + # Generate Final Rule + # + if ( $targetref ) { + add_expanded_jump( $echainref, $targetref, 0, $exceptionrule ); + } else { + add_rule( $echainref, $exceptionrule . $jump , 1 ) unless $disposition eq 'LOG'; + } + + ( $rule, 1 ); + } +} + ################################################################################################################ # -# This function provides a uniform way to generate Netfilter[6] rules (something the original Shorewall +# This function provides a uniform way to generate ip[6]tables rules (something the original Shorewall # sorely needed). # # Returns the destination interface specified in the rule, if any. @@ -5723,13 +6140,12 @@ sub expand_rule( $$$$$$$$$$;$ ) $logname, # Name of chain to name in log messages ) = @_; - my ($iiface, $diface, $inets, $dnets, $iexcl, $dexcl, $onets , $oexcl, $trivialiexcl, $trivialdexcl ); - my $chain = $chainref->{name}; + my ( $iiface, $diface, $inets, $dnets, $iexcl, $dexcl, $onets , $oexcl, $trivialiexcl, $trivialdexcl ) = + ( '', '', '', '', '', '', '', '', '', '' ); + my $chain = $chainref->{name}; my $table = $chainref->{table}; - my $jump; - my $mac; - my $targetref; - my $basictarget; + my ( $jump, $mac, $targetref, $basictarget ); + our @ends = (); if ( $target ) { ( $basictarget, my $rest ) = split ' ', $target, 2; @@ -5739,7 +6155,6 @@ sub expand_rule( $$$$$$$$$$;$ ) $jump = $basictarget = ''; } - our @ends = (); # # In the generated rules, we sometimes need run-time loops or conditional blocks. This function is used # to define such a loop or block. @@ -5748,23 +6163,13 @@ sub expand_rule( $$$$$$$$$$;$ ) # $command = The shell command that begins the loop or conditional # $end = The shell keyword ('done' or 'fi') that ends the loop or conditional # - # All open loops and conditionals are closed just before expand_rule() exits - # - sub push_command( $$$ ) { - my ( $chainref, $command, $end ) = @_; - - add_commands $chainref, $command; - incr_cmd_level $chainref; - push @ends, $end; - } - # # Trim disposition # $disposition =~ s/\s.*//; # # Handle Log Level # - my $logtag; + our $logtag = undef; if ( $loglevel ne '' ) { ( $loglevel, $logtag, my $remainder ) = split( /:/, $loglevel, 3 ); @@ -5784,234 +6189,23 @@ sub expand_rule( $$$$$$$$$$;$ ) # # Isolate Source Interface, if any # - if ( supplied $source ) { - if ( $source eq '-' ) { - $source = ''; - } elsif ( $family == F_IPV4 ) { - if ( $source =~ /^~/ ) { - $inets = $source; - } elsif ( $source =~ /^(.+?):(.+)$/ ) { - $iiface = $1; - $inets = $2; - } elsif ( $source =~ /\+|&|~|\..*\./ || $source =~ /^!?\^/ ) { - $inets = $source; - } else { - $iiface = $source; - } - } elsif ( $source =~ /^(.+?):<(.+)>\s*$/ || $source =~ /^(.+?):\[(.+)\]\s*$/ || $source =~ /^(.+?):(!?\+.+)$/ ) { - $iiface = $1; - $inets = $2; - } elsif ( $source =~ /:/ ) { - if ( $source =~ /^<(.+)>$/ || $source =~ /^\[(.+)\]$/ ) { - $inets = $1; - } else { - $inets = $source; - } - } elsif ( $source =~ /(?:\+|&|%|~|\..*\.)/ || $source =~ /^!?\^/ ) { - $inets = $source; - } else { - $iiface = $source; - } - } else { - $source = ''; - } - + ( $iiface, $inets ) = isolate_source_interface( $source ) if supplied $source && $source ne '-'; # - # Verify Interface, if any + # Verify Source Interface, if any # - if ( supplied $iiface ) { - fatal_error "Unknown Interface ($iiface)" unless known_interface $iiface; - - if ( $restriction & POSTROUTE_RESTRICT ) { - # - # An interface in the SOURCE column of a masq file - # - fatal_error "Bridge ports may not appear in the SOURCE column of this file" if port_to_bridge( $iiface ); - fatal_error "A wildcard interface ( $iiface) is not allowed in this context" if $iiface =~ /\+$/; - - if ( $table eq 'nat' ) { - warning_message qq(Using an interface as the masq SOURCE requires the interface to be up and configured when $Product starts/restarts) unless $idiotcount++; - } else { - warning_message qq(Using an interface as the SOURCE in a T: rule requires the interface to be up and configured when $Product starts/restarts) unless $idiotcount1++; - } - - push_command $chainref, join( '', 'for source in ', get_interface_nets( $iiface) , '; do' ), 'done'; - - $rule .= '-s $source '; - } else { - if ( $restriction & OUTPUT_RESTRICT ) { - if ( $chainref->{accounting} ) { - fatal_error "Source Interface ($iiface) not allowed in the $chainref->{name} chain"; - } else { - fatal_error "Source Interface ($iiface) not allowed when the SOURCE is the firewall"; - } - } - - $chainref->{restricted} |= $restriction; - $rule .= match_source_dev( $iiface ); - } - } - + $rule .= verify_source_interface( $iiface, $restriction, $table, $chainref ) if supplied $iiface; # # Isolate Destination Interface, if any # - if ( supplied $dest ) { - if ( $dest eq '-' ) { - $dest = ''; - } elsif ( ( $restriction & PREROUTE_RESTRICT ) && $dest =~ /^detect:(.*)$/ ) { - # - # DETECT_DNAT_IPADDRS=Yes and we're generating the nat rule - # - my @interfaces = split /\s+/, $1; - - if ( @interfaces > 1 ) { - my $list = ""; - my $optional; - - for my $interface ( @interfaces ) { - $optional++ if interface_is_optional $interface; - $list = join( ' ', $list , get_interface_address( $interface ) ); - } - - push_command( $chainref , "for address in $list; do" , 'done' ); - - push_command( $chainref , 'if [ $address != 0.0.0.0 ]; then' , 'fi' ) if $optional; - - $rule .= '-d $address '; - } else { - my $interface = $interfaces[0]; - my $variable = get_interface_address( $interface ); - - push_command( $chainref , "if [ $variable != 0.0.0.0 ]; then" , 'fi') if interface_is_optional( $interface ); - - $rule .= "-d $variable "; - } - - $dest = ''; - } elsif ( $family == F_IPV4 ) { - if ( $dest =~ /^(.+?):(.+)$/ ) { - $diface = $1; - $dnets = $2; - } elsif ( $dest =~ /\+|&|%|~|\..*\./ || $dest =~ /^!?\^/ ) { - $dnets = $dest; - } else { - $diface = $dest; - } - } elsif ( $dest =~ /^(.+?):<(.+)>\s*$/ || $dest =~ /^(.+?):\[(.+)\]\s*$/ || $dest =~ /^(.+?):(!?\+.+)$/ ) { - $diface = $1; - $dnets = $2; - } elsif ( $dest =~ /:/ ) { - if ( $dest =~ /^<(.+)>$/ || $dest =~ /^\[(.+)\]$/ ) { - $dnets = $1; - } else { - $dnets = $dest; - } - } elsif ( $dest =~ /(?:\+|&|\..*\.)/ || $dest =~ /^!?\^/ ) { - $dnets = $dest; - } else { - $diface = $dest; - } - } else { - $dest = ''; - } - + ( $diface, $dnets, $rule ) = isolate_dest_interface( $restriction, $dest, $chainref, $rule ) if supplied $dest && $dest ne '-'; # # Verify Destination Interface, if any # - if ( supplied $diface ) { - fatal_error "Unknown Interface ($diface)" unless known_interface $diface; - - if ( $restriction & PREROUTE_RESTRICT ) { - # - # Dest interface -- must use routing table - # - fatal_error "A DEST interface is not permitted in the PREROUTING chain" if $restriction & DESTIFACE_DISALLOW; - fatal_error "Bridge port ($diface) not allowed" if port_to_bridge( $diface ); - fatal_error "A wildcard interface ($diface) is not allowed in this context" if $diface =~ /\+$/; - push_command( $chainref , 'for dest in ' . get_interface_nets( $diface) . '; do', 'done' ); - $rule .= '-d $dest '; - } else { - fatal_error "Bridge Port ($diface) not allowed in OUTPUT or POSTROUTING rules" if ( $restriction & ( POSTROUTE_RESTRICT + OUTPUT_RESTRICT ) ) && port_to_bridge( $diface ); - fatal_error "Destination Interface ($diface) not allowed when the destination zone is the firewall" if $restriction & INPUT_RESTRICT; - if ( $restriction & DESTIFACE_DISALLOW ) { - if ( $chainref->{accounting} ) { - fatal_error "Destination Interface ($diface) not allowed in the $chainref->{name} chain"; - } else { - fatal_error "Destination Interface ($diface) not allowed in the mangle OUTPUT chain"; - } - } - - if ( $iiface ) { - my $bridge = port_to_bridge( $diface ); - fatal_error "Source interface ($iiface) is not a port on the same bridge as the destination interface ( $diface )" if $bridge && $bridge ne source_port_to_bridge( $iiface ); - } - - $chainref->{restricted} |= $restriction; - $rule .= match_dest_dev( $diface ); - } - } else { - $diface = ''; - } - - if ( $origdest ) { - if ( $origdest eq '-' || ! have_capability( 'CONNTRACK_MATCH' ) ) { - $onets = $oexcl = ''; - } elsif ( $origdest =~ /^detect:(.*)$/ ) { - # - # Either the filter part of a DNAT rule or 'detect' was given in the ORIG DEST column - # - my @interfaces = split /\s+/, $1; - - if ( @interfaces > 1 ) { - my $list = ""; - my $optional; - - for my $interface ( @interfaces ) { - $optional++ if interface_is_optional $interface; - $list = join( ' ', $list , get_interface_address( $interface ) ); - } - - push_command( $chainref , "for address in $list; do" , 'done' ); - - push_command( $chainref , 'if [ $address != 0.0.0.0 ]; then' , 'fi' ) if $optional; - - $rule .= '-m conntrack --ctorigdst $address '; - } else { - my $interface = $interfaces[0]; - my $variable = get_interface_address( $interface ); - - push_command( $chainref , "if [ $variable != 0.0.0.0 ]; then" , 'fi' ) if interface_is_optional( $interface ); - - $rule .= "-m conntrack --ctorigdst $variable "; - } - - $onets = $oexcl = ''; - } else { - fatal_error "Invalid ORIGINAL DEST" if $origdest =~ /^([^!]+)?,!([^!]+)$/ || $origdest =~ /.*!.*!/; - - if ( $origdest =~ /^([^!]+)?!([^!]+)$/ ) { - # - # Exclusion - # - $onets = $1; - $oexcl = $2; - } else { - $oexcl = ''; - $onets = $origdest; - } - - unless ( $onets ) { - my @oexcl = mysplit $oexcl; - if ( @oexcl == 1 ) { - $rule .= match_orig_dest( "!$oexcl" ); - $oexcl = ''; - } - } - } - } else { - $oexcl = ''; - } - + $rule .= verify_dest_interface( $diface, $restriction, $chainref, $iiface ) if supplied $diface; + # + # Handle Original Destination + # + ( $onets, $oexcl, $rule ) = handle_original_dest( $origdest, $chainref, $rule ) if $origdest; # # Determine if there is Source Exclusion # @@ -6026,10 +6220,7 @@ sub expand_rule( $$$$$$$$$$;$ ) $trivialiexcl = 1; } } - } else { - $iexcl = ''; } - # # Determine if there is Destination Exclusion # @@ -6044,8 +6235,6 @@ sub expand_rule( $$$$$$$$$$;$ ) $trivialdexcl = 1; } } - } else { - $dexcl = ''; } $inets = ALLIP unless $inets; @@ -6055,126 +6244,30 @@ sub expand_rule( $$$$$$$$$$;$ ) fatal_error "SOURCE interface may not be specified with a source IP address in the POSTROUTING chain" if $restriction == POSTROUTE_RESTRICT && $iiface && ( $inets ne ALLIP || $iexcl || $trivialiexcl); fatal_error "DEST interface may not be specified with a destination IP address in the PREROUTING chain" if $restriction == PREROUTE_RESTRICT && $diface && ( $dnets ne ALLIP || $dexcl || $trivialdexcl); - my ( $fromref, $done ); + my $done; if ( $iexcl || $dexcl || $oexcl ) { # # We have non-trivial exclusion # - if ( $disposition eq 'RETURN' || $disposition eq 'CONTINUE' ) { - # - # We can't use an exclusion chain -- we mark those packets to be excluded and then condition the rules generated in the block below on the mark value - # - require_capability 'MARK_ANYWHERE' , 'Exclusion in ACCEPT+/CONTINUE/NONAT rules', 's' unless $table eq 'mangle'; - - fatal_error "Exclusion in ACCEPT+/CONTINUE/NONAT rules requires the Multiple Match capability in your kernel and iptables" - if $rule =~ / -m mark / && ! $globals{KLUDGEFREE}; - # - # Clear the exclusion bit - # - add_ijump $chainref , j => 'MARK', targetopts => '--and-mark ' . in_hex( $globals{EXCLUSION_MASK} ^ 0xffffffff ); - # - # Mark packet if it matches any of the exclusions - # - my $exclude = '-j MARK --or-mark ' . in_hex( $globals{EXCLUSION_MASK} ); - - for ( mysplit $iexcl ) { - my $cond = conditional_rule( $chainref, $_ ); - add_rule $chainref, ( match_source_net $_ , $restriction, $mac ) . $exclude; - conditional_rule_end( $chainref ) if $cond; - } - - for ( mysplit $dexcl ) { - my $cond = conditional_rule( $chainref, $_ ); - add_rule $chainref, ( match_dest_net $_, $restriction ) . $exclude; - conditional_rule_end( $chainref ) if $cond; - } - - for ( mysplit $oexcl ) { - my $cond = conditional_rule( $chainref, $_ ); - add_rule $chainref, ( match_orig_dest $_ ) . $exclude; - conditional_rule_end( $chainref ) if $cond; - } - # - # Augment the rule to include 'not excluded' - # - $rule .= '-m mark --mark 0/' . in_hex( $globals{EXCLUSION_MASK} ) . ' '; - } else { - # - # Create the Exclusion Chain - # - my $echain = newexclusionchain( $table ); - - my $echainref = new_chain $table, $echain; - # - # Use the current rule and send all possible matches to the exclusion chain - # - for my $onet ( mysplit $onets ) { - - my $cond = conditional_rule( $chainref, $onet ); - - $onet = match_orig_dest $onet; - - for my $inet ( mysplit $inets ) { - - my $cond = conditional_rule( $chainref, $inet ); - - my $source_match = match_source_net( $inet, $restriction, $mac ) if $globals{KLUDGEFREE}; - - for my $dnet ( mysplit $dnets ) { - $source_match = match_source_net( $inet, $restriction, $mac ) unless $globals{KLUDGEFREE}; - add_expanded_jump( $chainref, $echainref, 0, join( '', $rule, $source_match, match_dest_net( $dnet, $restriction ), $onet ) ); - } - - conditional_rule_end( $chainref ) if $cond; - } - - conditional_rule_end( $chainref ) if $cond; - } - - # - # Generate RETURNs for each exclusion - # - for ( mysplit $iexcl ) { - my $cond = conditional_rule( $echainref, $_ ); - add_rule $echainref, ( match_source_net $_ , $restriction, $mac ) . '-j RETURN'; - conditional_rule_end( $echainref ) if $cond; - } - - for ( mysplit $dexcl ) { - my $cond = conditional_rule( $echainref, $_ ); - add_rule $echainref, ( match_dest_net $_, $restriction ) . '-j RETURN'; - conditional_rule_end( $echainref ) if $cond; - } - - for ( mysplit $oexcl ) { - my $cond = conditional_rule( $echainref, $_ ); - add_rule $echainref, ( match_orig_dest $_ ) . '-j RETURN'; - conditional_rule_end( $echainref ) if $cond; - } - # - # Log rule - # - log_rule_limit( $loglevel , - $echainref , - $chain, - $disposition eq 'reject' ? 'REJECT' : $disposition , - '' , - $logtag , - 'add' , - '' ) - if $loglevel; - # - # Generate Final Rule - # - if ( $targetref ) { - add_expanded_jump( $fromref = $echainref, $targetref, 0, $exceptionrule ); - } else { - add_rule( $fromref = $echainref, $exceptionrule . $jump , 1 ) unless $disposition eq 'LOG'; - } - - $done = 1; - } + ( $rule, $done ) = handle_exclusion( $disposition, + $table, + $rule, + $restriction, + $inets, + $iexcl, + $onets, + $oexcl, + $dnets, + $dexcl, + $chainref, + $chain, + $mac, + $loglevel, + $logtag, + $targetref, + $exceptionrule, + $jump ); } unless ( $done ) { @@ -6182,14 +6275,14 @@ sub expand_rule( $$$$$$$$$$;$ ) # No non-trivial exclusions or we're using marks to handle them # for my $onet ( mysplit $onets ) { - my $cond = conditional_rule( $chainref, $onet ); + my $cond1 = conditional_rule( $chainref, $onet ); $onet = match_orig_dest $onet; for my $inet ( mysplit $inets ) { my $source_match; - my $cond = conditional_rule( $chainref, $inet ); + my $cond2 = conditional_rule( $chainref, $inet ); $source_match = match_source_net( $inet, $restriction, $mac ) if $globals{KLUDGEFREE}; @@ -6198,16 +6291,16 @@ sub expand_rule( $$$$$$$$$$;$ ) my $dest_match = match_dest_net( $dnet, $restriction ); my $matches = join( '', $rule, $source_match, $dest_match, $onet ); - my $cond = conditional_rule( $chainref, $dnet ); + my $cond3 = conditional_rule( $chainref, $dnet ); if ( $loglevel eq '' ) { # # No logging -- add the target rule with matches to the rule chain # if ( $targetref ) { - add_expanded_jump( $fromref = $chainref, $targetref , 0, $matches ); + add_expanded_jump( $chainref, $targetref , 0, $matches ); } else { - add_rule( $fromref = $chainref, $matches . $jump , 1 ); + add_rule( $chainref, $matches . $jump , 1 ); } } elsif ( $disposition eq 'LOG' || $disposition eq 'COUNT' ) { # @@ -6235,9 +6328,9 @@ sub expand_rule( $$$$$$$$$$;$ ) $matches ); if ( $targetref ) { - add_expanded_jump( $fromref = $chainref, $targetref, 0, $matches ); + add_expanded_jump( $chainref, $targetref, 0, $matches ); } else { - add_rule( $fromref = $chainref, $matches . $jump, 1 ); + add_rule( $chainref, $matches . $jump, 1 ); } } else { # @@ -6250,22 +6343,19 @@ sub expand_rule( $$$$$$$$$$;$ ) $matches ); } - conditional_rule_end( $chainref ) if $cond; + conditional_rule_end( $chainref ) if $cond3; } - conditional_rule_end( $chainref ) if $cond; + conditional_rule_end( $chainref ) if $cond2; } - conditional_rule_end( $chainref ) if $cond; + conditional_rule_end( $chainref ) if $cond1; } } $chainref->{restricted} |= INPUT_RESTRICT if $mac; - while ( @ends ) { - decr_cmd_level $chainref; - add_commands $chainref, pop @ends; - } + pop_commands( $chainref ) if @ends; $diface; } @@ -6288,6 +6378,7 @@ sub add_interface_options( $ ) { if ( $_[0] ) { # # We have blacklist rules. + # my %input_chains; my %forward_chains;