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,93 +5728,15 @@ sub handle_network_list( $$ ) {
}
################################################################################################################
#
# This function provides a uniform way to generate Netfilter[6] rules (something the original Shorewall
# sorely needed).
# Split an interface:address pair and returns its components
#
# 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
) = @_;
sub isolate_source_interface( $ ) {
my ( $source ) = @_;
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 ( $iiface, $inets );
if ( $target ) {
( $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 ( $family == F_IPV4 ) {
if ( $source =~ /^~/ ) {
$inets = $source;
} elsif ( $source =~ /^(.+?):(.+)$/ ) {
@ -5812,14 +5761,19 @@ sub expand_rule( $$$$$$$$$$;$ )
} else {
$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;
if ( $restriction & POSTROUTE_RESTRICT ) {
@ -5850,15 +5804,19 @@ sub expand_rule( $$$$$$$$$$;$ )
$chainref->{restricted} |= $restriction;
$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 ) {
if ( $dest eq '-' ) {
$dest = '';
} elsif ( ( $restriction & PREROUTE_RESTRICT ) && $dest =~ /^detect:(.*)$/ ) {
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
#
@ -5886,8 +5844,6 @@ sub expand_rule( $$$$$$$$$$;$ )
$rule .= "-d $variable ";
}
$dest = '';
} elsif ( $family == F_IPV4 ) {
if ( $dest =~ /^(.+?):(.+)$/ ) {
$diface = $1;
@ -5911,14 +5867,18 @@ sub expand_rule( $$$$$$$$$$;$ )
} else {
$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;
if ( $restriction & PREROUTE_RESTRICT ) {
@ -5949,11 +5909,17 @@ sub expand_rule( $$$$$$$$$$;$ )
$chainref->{restricted} |= $restriction;
$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' ) ) {
$onets = $oexcl = '';
} 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 ) {
( $inets, $iexcl ) = handle_network_list( $inets, 'SOURCE' );
sub handle_exclusion( $$$$$$$$$$$$$$$$$$ ) {
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' ) {
#
# 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'
#
$rule .= '-m mark --mark 0/' . in_hex( $globals{EXCLUSION_MASK} ) . ' ';
( $rule, 0 );
} else {
#
# Create the Exclusion Chain
@ -6131,7 +6073,6 @@ sub expand_rule( $$$$$$$$$$;$ )
conditional_rule_end( $chainref ) if $cond;
}
#
# Generate RETURNs for each exclusion
#
@ -6168,28 +6109,180 @@ sub expand_rule( $$$$$$$$$$;$ )
# Generate Final Rule
#
if ( $targetref ) {
add_expanded_jump( $fromref = $echainref, $targetref, 0, $exceptionrule );
add_expanded_jump( $echainref, $targetref, 0, $exceptionrule );
} 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 ) {
#
# 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;