mirror of
https://gitlab.com/shorewall/code.git
synced 2024-12-18 04:11:22 +01:00
Break up expand_rule()
Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
parent
eb63745352
commit
67932f2d42
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user