Break up expand_rule()

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2012-05-30 06:49:00 -07:00
parent eb63745352
commit 67932f2d42

View File

@ -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;