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 # Issue an invalid list error message
# #
@ -5701,93 +5728,15 @@ sub handle_network_list( $$ ) {
} }
################################################################################################################
# #
# This function provides a uniform way to generate Netfilter[6] rules (something the original Shorewall # Split an interface:address pair and returns its components
# sorely needed).
# #
# Returns the destination interface specified in the rule, if any. sub isolate_source_interface( $ ) {
# my ( $source ) = @_;
sub expand_rule( $$$$$$$$$$;$ )
{
my ($chainref , # Chain
$restriction, # Determines what to do with interface names in the SOURCE or DEST
$rule, # Caller's matches that don't depend on the SOURCE, DEST and ORIGINAL DEST
$source, # SOURCE
$dest, # DEST
$origdest, # ORIGINAL DEST
$target, # Target ('-j' part of the rule - may be empty)
$loglevel , # Log level (and tag)
$disposition, # Primtive part of the target (RETURN, ACCEPT, ...)
$exceptionrule,# Caller's matches used in exclusion case
$logname, # Name of chain to name in log messages
) = @_;
my ($iiface, $diface, $inets, $dnets, $iexcl, $dexcl, $onets , $oexcl, $trivialiexcl, $trivialdexcl ); my ( $iiface, $inets );
my $chain = $chainref->{name};
my $table = $chainref->{table};
my $jump;
my $mac;
my $targetref;
my $basictarget;
if ( $target ) { if ( $family == F_IPV4 ) {
( $basictarget, my $rest ) = split ' ', $target, 2;
$jump = '-j ' . $target unless $targetref = $chain_table{$table}{$basictarget};
} else {
$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.
#
# $chainref = Reference to the chain
# $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;
if ( $loglevel ne '' ) {
( $loglevel, $logtag, my $remainder ) = split( /:/, $loglevel, 3 );
fatal_error "Invalid log tag" if defined $remainder;
if ( $loglevel =~ /^none!?$/i ) {
return if $disposition eq 'LOG';
$loglevel = $logtag = '';
} else {
$loglevel = validate_level( $loglevel );
$logtag = '' unless defined $logtag;
}
} elsif ( $disposition eq 'LOG' ) {
fatal_error "LOG requires a level";
}
#
# Isolate Source Interface, if any
#
if ( supplied $source ) {
if ( $source eq '-' ) {
$source = '';
} elsif ( $family == F_IPV4 ) {
if ( $source =~ /^~/ ) { if ( $source =~ /^~/ ) {
$inets = $source; $inets = $source;
} elsif ( $source =~ /^(.+?):(.+)$/ ) { } elsif ( $source =~ /^(.+?):(.+)$/ ) {
@ -5812,14 +5761,19 @@ sub expand_rule( $$$$$$$$$$;$ )
} else { } else {
$iiface = $source; $iiface = $source;
} }
} else {
$source = ''; ( $iiface, $inets );
} }
# #
# Verify Interface, if any # Verify the source interface -- returns a rule fragment to be added to the rule being created
# #
if ( supplied $iiface ) { sub verify_source_interface( $$$$ ) {
my ( $iiface, $restriction, $table, $chainref ) = @_;
my $rule = '';
fatal_error "Unknown Interface ($iiface)" unless known_interface $iiface; fatal_error "Unknown Interface ($iiface)" unless known_interface $iiface;
if ( $restriction & POSTROUTE_RESTRICT ) { if ( $restriction & POSTROUTE_RESTRICT ) {
@ -5850,15 +5804,19 @@ sub expand_rule( $$$$$$$$$$;$ )
$chainref->{restricted} |= $restriction; $chainref->{restricted} |= $restriction;
$rule .= match_source_dev( $iiface ); $rule .= match_source_dev( $iiface );
} }
$rule;
} }
# #
# Isolate Destination Interface, if any # Splits an interface:address pair. Updates that passed rule and returns ($rule, $interface, $address )
# #
if ( supplied $dest ) { sub isolate_dest_interface( $$$$ ) {
if ( $dest eq '-' ) { my ( $restriction, $dest, $chainref, $rule ) = @_;
$dest = '';
} elsif ( ( $restriction & PREROUTE_RESTRICT ) && $dest =~ /^detect:(.*)$/ ) { my ( $diface, $dnets );
if ( ( $restriction & PREROUTE_RESTRICT ) && $dest =~ /^detect:(.*)$/ ) {
# #
# DETECT_DNAT_IPADDRS=Yes and we're generating the nat rule # DETECT_DNAT_IPADDRS=Yes and we're generating the nat rule
# #
@ -5886,8 +5844,6 @@ sub expand_rule( $$$$$$$$$$;$ )
$rule .= "-d $variable "; $rule .= "-d $variable ";
} }
$dest = '';
} elsif ( $family == F_IPV4 ) { } elsif ( $family == F_IPV4 ) {
if ( $dest =~ /^(.+?):(.+)$/ ) { if ( $dest =~ /^(.+?):(.+)$/ ) {
$diface = $1; $diface = $1;
@ -5911,14 +5867,18 @@ sub expand_rule( $$$$$$$$$$;$ )
} else { } else {
$diface = $dest; $diface = $dest;
} }
} else {
$dest = ''; ( $diface, $dnets, $rule );
} }
# #
# Verify Destination Interface, if any # Verify the destination interface. Returns a rule fragment to be added to the rule being created
# #
if ( supplied $diface ) { sub verify_dest_interface( $$$$ ) {
my ( $diface, $restriction, $chainref, $iiface ) = @_;
my $rule = '';
fatal_error "Unknown Interface ($diface)" unless known_interface $diface; fatal_error "Unknown Interface ($diface)" unless known_interface $diface;
if ( $restriction & PREROUTE_RESTRICT ) { if ( $restriction & PREROUTE_RESTRICT ) {
@ -5949,11 +5909,17 @@ sub expand_rule( $$$$$$$$$$;$ )
$chainref->{restricted} |= $restriction; $chainref->{restricted} |= $restriction;
$rule .= match_dest_dev( $diface ); $rule .= match_dest_dev( $diface );
} }
} else {
$diface = ''; $rule;
} }
if ( $origdest ) { #
# 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' ) ) { if ( $origdest eq '-' || ! have_capability( 'CONNTRACK_MATCH' ) ) {
$onets = $oexcl = ''; $onets = $oexcl = '';
} elsif ( $origdest =~ /^detect:(.*)$/ ) { } elsif ( $origdest =~ /^detect:(.*)$/ ) {
@ -6008,59 +5974,33 @@ sub expand_rule( $$$$$$$$$$;$ )
} }
} }
} }
} else {
$oexcl = ''; ( $onets, $oexcl, $rule );
} }
# #
# Determine if there is Source Exclusion # Handles non-trivial exclusion. Updates the passed rule and returns ( $rule, $done )
# #
if ( $inets ) { sub handle_exclusion( $$$$$$$$$$$$$$$$$$ ) {
( $inets, $iexcl ) = handle_network_list( $inets, 'SOURCE' ); my ( $disposition,
$table,
$rule,
$restriction,
$inets,
$iexcl,
$onets,
$oexcl,
$dnets,
$dexcl,
$chainref,
$chain,
$mac,
$loglevel,
$logtag,
$targetref,
$exceptionrule,
$jump ) = @_;
unless ( $inets || $iexcl =~ /^\+\[/ || ( $iiface && $restriction & POSTROUTE_RESTRICT ) ) {
my @iexcl = mysplit $iexcl, 1;
if ( @iexcl == 1 ) {
$rule .= match_source_net "!$iexcl" , $restriction;
$iexcl = '';
$trivialiexcl = 1;
}
}
} else {
$iexcl = '';
}
#
# Determine if there is Destination Exclusion
#
if ( $dnets ) {
( $dnets, $dexcl ) = handle_network_list( $dnets, 'DEST' );
unless ( $dnets || $dexcl =~ /^\+\[/ ) {
my @dexcl = mysplit $dexcl, 1;
if ( @dexcl == 1 ) {
$rule .= match_dest_net "!$dexcl", $restriction;
$dexcl = '';
$trivialdexcl = 1;
}
}
} else {
$dexcl = '';
}
$inets = ALLIP unless $inets;
$dnets = ALLIP unless $dnets;
$onets = ALLIP unless $onets;
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 );
if ( $iexcl || $dexcl || $oexcl ) {
#
# We have non-trivial exclusion
#
if ( $disposition eq 'RETURN' || $disposition eq 'CONTINUE' ) { 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 # 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
@ -6099,6 +6039,8 @@ sub expand_rule( $$$$$$$$$$;$ )
# Augment the rule to include 'not excluded' # Augment the rule to include 'not excluded'
# #
$rule .= '-m mark --mark 0/' . in_hex( $globals{EXCLUSION_MASK} ) . ' '; $rule .= '-m mark --mark 0/' . in_hex( $globals{EXCLUSION_MASK} ) . ' ';
( $rule, 0 );
} else { } else {
# #
# Create the Exclusion Chain # Create the Exclusion Chain
@ -6131,7 +6073,6 @@ sub expand_rule( $$$$$$$$$$;$ )
conditional_rule_end( $chainref ) if $cond; conditional_rule_end( $chainref ) if $cond;
} }
# #
# Generate RETURNs for each exclusion # Generate RETURNs for each exclusion
# #
@ -6168,28 +6109,180 @@ sub expand_rule( $$$$$$$$$$;$ )
# Generate Final Rule # Generate Final Rule
# #
if ( $targetref ) { if ( $targetref ) {
add_expanded_jump( $fromref = $echainref, $targetref, 0, $exceptionrule ); add_expanded_jump( $echainref, $targetref, 0, $exceptionrule );
} else { } else {
add_rule( $fromref = $echainref, $exceptionrule . $jump , 1 ) unless $disposition eq 'LOG'; add_rule( $echainref, $exceptionrule . $jump , 1 ) unless $disposition eq 'LOG';
} }
$done = 1; ( $rule, 1 );
} }
} }
################################################################################################################
#
# 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.
#
sub expand_rule( $$$$$$$$$$;$ )
{
my ($chainref , # Chain
$restriction, # Determines what to do with interface names in the SOURCE or DEST
$rule, # Caller's matches that don't depend on the SOURCE, DEST and ORIGINAL DEST
$source, # SOURCE
$dest, # DEST
$origdest, # ORIGINAL DEST
$target, # Target ('-j' part of the rule - may be empty)
$loglevel , # Log level (and tag)
$disposition, # Primtive part of the target (RETURN, ACCEPT, ...)
$exceptionrule,# Caller's matches used in exclusion case
$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 $table = $chainref->{table};
my ( $jump, $mac, $targetref, $basictarget );
our @ends = ();
if ( $target ) {
( $basictarget, my $rest ) = split ' ', $target, 2;
$jump = '-j ' . $target unless $targetref = $chain_table{$table}{$basictarget};
} else {
$jump = $basictarget = '';
}
#
# In the generated rules, we sometimes need run-time loops or conditional blocks. This function is used
# to define such a loop or block.
#
# $chainref = Reference to the chain
# $command = The shell command that begins the loop or conditional
# $end = The shell keyword ('done' or 'fi') that ends the loop or conditional
#
# Trim disposition
#
$disposition =~ s/\s.*//;
#
# Handle Log Level
#
our $logtag = undef;
if ( $loglevel ne '' ) {
( $loglevel, $logtag, my $remainder ) = split( /:/, $loglevel, 3 );
fatal_error "Invalid log tag" if defined $remainder;
if ( $loglevel =~ /^none!?$/i ) {
return if $disposition eq 'LOG';
$loglevel = $logtag = '';
} else {
$loglevel = validate_level( $loglevel );
$logtag = '' unless defined $logtag;
}
} elsif ( $disposition eq 'LOG' ) {
fatal_error "LOG requires a level";
}
#
# Isolate Source Interface, if any
#
( $iiface, $inets ) = isolate_source_interface( $source ) if supplied $source && $source ne '-';
#
# Verify Source Interface, if any
#
$rule .= verify_source_interface( $iiface, $restriction, $table, $chainref ) if supplied $iiface;
#
# Isolate Destination Interface, if any
#
( $diface, $dnets, $rule ) = isolate_dest_interface( $restriction, $dest, $chainref, $rule ) if supplied $dest && $dest ne '-';
#
# Verify Destination Interface, if any
#
$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
#
if ( $inets ) {
( $inets, $iexcl ) = handle_network_list( $inets, 'SOURCE' );
unless ( $inets || $iexcl =~ /^\+\[/ || ( $iiface && $restriction & POSTROUTE_RESTRICT ) ) {
my @iexcl = mysplit $iexcl, 1;
if ( @iexcl == 1 ) {
$rule .= match_source_net "!$iexcl" , $restriction;
$iexcl = '';
$trivialiexcl = 1;
}
}
}
#
# Determine if there is Destination Exclusion
#
if ( $dnets ) {
( $dnets, $dexcl ) = handle_network_list( $dnets, 'DEST' );
unless ( $dnets || $dexcl =~ /^\+\[/ ) {
my @dexcl = mysplit $dexcl, 1;
if ( @dexcl == 1 ) {
$rule .= match_dest_net "!$dexcl", $restriction;
$dexcl = '';
$trivialdexcl = 1;
}
}
}
$inets = ALLIP unless $inets;
$dnets = ALLIP unless $dnets;
$onets = ALLIP unless $onets;
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 $done;
if ( $iexcl || $dexcl || $oexcl ) {
#
# We have non-trivial exclusion
#
( $rule, $done ) = handle_exclusion( $disposition,
$table,
$rule,
$restriction,
$inets,
$iexcl,
$onets,
$oexcl,
$dnets,
$dexcl,
$chainref,
$chain,
$mac,
$loglevel,
$logtag,
$targetref,
$exceptionrule,
$jump );
}
unless ( $done ) { unless ( $done ) {
# #
# No non-trivial exclusions or we're using marks to handle them # No non-trivial exclusions or we're using marks to handle them
# #
for my $onet ( mysplit $onets ) { for my $onet ( mysplit $onets ) {
my $cond = conditional_rule( $chainref, $onet ); my $cond1 = conditional_rule( $chainref, $onet );
$onet = match_orig_dest $onet; $onet = match_orig_dest $onet;
for my $inet ( mysplit $inets ) { for my $inet ( mysplit $inets ) {
my $source_match; 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}; $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 $dest_match = match_dest_net( $dnet, $restriction );
my $matches = join( '', $rule, $source_match, $dest_match, $onet ); my $matches = join( '', $rule, $source_match, $dest_match, $onet );
my $cond = conditional_rule( $chainref, $dnet ); my $cond3 = conditional_rule( $chainref, $dnet );
if ( $loglevel eq '' ) { if ( $loglevel eq '' ) {
# #
# No logging -- add the target rule with matches to the rule chain # No logging -- add the target rule with matches to the rule chain
# #
if ( $targetref ) { if ( $targetref ) {
add_expanded_jump( $fromref = $chainref, $targetref , 0, $matches ); add_expanded_jump( $chainref, $targetref , 0, $matches );
} else { } else {
add_rule( $fromref = $chainref, $matches . $jump , 1 ); add_rule( $chainref, $matches . $jump , 1 );
} }
} elsif ( $disposition eq 'LOG' || $disposition eq 'COUNT' ) { } elsif ( $disposition eq 'LOG' || $disposition eq 'COUNT' ) {
# #
@ -6235,9 +6328,9 @@ sub expand_rule( $$$$$$$$$$;$ )
$matches ); $matches );
if ( $targetref ) { if ( $targetref ) {
add_expanded_jump( $fromref = $chainref, $targetref, 0, $matches ); add_expanded_jump( $chainref, $targetref, 0, $matches );
} else { } else {
add_rule( $fromref = $chainref, $matches . $jump, 1 ); add_rule( $chainref, $matches . $jump, 1 );
} }
} else { } else {
# #
@ -6250,22 +6343,19 @@ sub expand_rule( $$$$$$$$$$;$ )
$matches ); $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; $chainref->{restricted} |= INPUT_RESTRICT if $mac;
while ( @ends ) { pop_commands( $chainref ) if @ends;
decr_cmd_level $chainref;
add_commands $chainref, pop @ends;
}
$diface; $diface;
} }
@ -6288,6 +6378,7 @@ sub add_interface_options( $ ) {
if ( $_[0] ) { if ( $_[0] ) {
# #
# We have blacklist rules. # We have blacklist rules.
#
my %input_chains; my %input_chains;
my %forward_chains; my %forward_chains;