From 7555a0953de0573311fcee2fdf7be4427d251d9a Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Sat, 29 Jan 2011 12:15:34 -0800 Subject: [PATCH] Add conditional logic for optional run-time address variables Signed-off-by: Tom Eastep --- Shorewall/Perl/Shorewall/Chains.pm | 97 +++++++++++++++++++++++++++-- Shorewall/Perl/Shorewall/IPAddrs.pm | 34 +++++++++- Shorewall/Perl/Shorewall/Nat.pm | 12 ++-- Shorewall/releasenotes.txt | 5 +- 4 files changed, 133 insertions(+), 15 deletions(-) diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index ffb5a2922..31aa50f36 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -146,6 +146,8 @@ our %EXPORT_TAGS = ( do_headers have_ipset_rules record_runtime_address + conditional_rule + conditional_rule_end match_source_dev match_dest_dev iprange_match @@ -2689,6 +2691,38 @@ sub record_runtime_address( $ ) { get_interface_address( $interface ) . ' '; } +# +# If the passed address is a run-time address variable for an optional interface, then +# begin a conditional rule block that tests the address for nil. +# +sub conditional_rule( $$ ) { + my ( $chainref, $address ) = @_; + + if ( $address =~ /^!?&(.+)$/ ) { + my $interface = $1; + if ( my $ref = known_interface $interface ) { + if ( $ref->{options}{optional} ) { + my $variable = get_interface_address( $interface ); + add_commands( $chainref , "if [ $variable != " . NILIP . ' ]; then' ); + incr_cmd_level $chainref; + return 1; + } + }; + } + + 0; +} + +# +# If end a conditional in a chain +# + +sub conditional_rule_end( $ ) { + my $chainref = shift; + decr_cmd_level $chainref; + add_commands( $chainref , "fi\n" ); +} + sub mysplit( $ ); # @@ -3453,6 +3487,8 @@ sub handle_network_list( $$ ) { } + + ################################################################################################################ # # This function provides a uniform way to generate Netfilter[6] rules (something the original Shorewall @@ -3800,9 +3836,23 @@ sub expand_rule( $$$$$$$$$$;$ ) # my $exclude = '-j MARK --or-mark ' . in_hex( $globals{EXCLUSION_MASK} ); - add_rule $chainref, ( match_source_net $_ , $restriction ) . $exclude for ( mysplit $iexcl ); - add_rule $chainref, ( match_dest_net $_ ) . $exclude for ( mysplit $dexcl ); - add_rule $chainref, ( match_orig_dest $_ ) . $exclude for ( mysplit $oexcl ); + for ( mysplit $iexcl ) { + my $cond = conditional_rule( $chainref, $_ ); + add_rule $chainref, ( match_source_net $_ , $restriction ) . $exclude; + conditional_rule_end( $chainref ) if $cond; + } + + for ( mysplit $dexcl ) { + my $cond = conditional_rule( $chainref, $_ ); + add_rule $chainref, ( match_dest_net $_ ) . $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' # @@ -3818,25 +3868,47 @@ sub expand_rule( $$$$$$$$$$;$ ) # 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 ) if have_capability( 'KLUDGEFREE' ); for my $dnet ( mysplit $dnets ) { $source_match = match_source_net( $inet, $restriction ) unless have_capability( 'KLUDGEFREE' ); add_jump( $chainref, $echainref, 0, join( '', $rule, $source_match, match_dest_net( $dnet ), $onet ), 1 ); } + + conditional_rule_end( $chainref ) if $cond; } + + conditional_rule_end( $chainref ) if $cond; } # # Generate RETURNs for each exclusion # - add_rule $echainref, ( match_source_net $_ , $restriction ) . '-j RETURN' for ( mysplit $iexcl ); - add_rule $echainref, ( match_dest_net $_ ) . '-j RETURN' for ( mysplit $dexcl ); - add_rule $echainref, ( match_orig_dest $_ ) . '-j RETURN' for ( mysplit $oexcl ); + for ( mysplit $iexcl ) { + my $cond = conditional_rule( $echainref, $_ ); + add_rule $echainref, ( match_source_net $_ , $restriction ) . '-j RETURN'; + conditional_rule_end( $echainref ) if $cond; + } + + for ( mysplit $dexcl ) { + my $cond = conditional_rule( $echainref, $_ ); + add_rule $echainref, ( match_dest_net $_ ) . '-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 # @@ -3863,10 +3935,15 @@ 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 ); + $onet = match_orig_dest $onet; + for my $inet ( mysplit $inets ) { my $source_match; + my $cond = conditional_rule( $chainref, $inet ); + $source_match = match_source_net( $inet, $restriction ) if have_capability( 'KLUDGEFREE' ); for my $dnet ( mysplit $dnets ) { @@ -3874,6 +3951,8 @@ sub expand_rule( $$$$$$$$$$;$ ) my $dest_match = match_dest_net( $dnet ); my $matches = join( '', $rule, $source_match, $dest_match, $onet ); + my $cond = conditional_rule( $chainref, $dnet ); + if ( $loglevel eq '' ) { # # No logging -- add the target rule with matches to the rule chain @@ -3916,8 +3995,14 @@ sub expand_rule( $$$$$$$$$$;$ ) $matches, 1 ); } + + conditional_rule_end( $chainref ) if $cond; } + + conditional_rule_end( $chainref ) if $cond; } + + conditional_rule_end( $chainref ) if $cond; } } # diff --git a/Shorewall/Perl/Shorewall/IPAddrs.pm b/Shorewall/Perl/Shorewall/IPAddrs.pm index 68a3e60a2..eef06db45 100644 --- a/Shorewall/Perl/Shorewall/IPAddrs.pm +++ b/Shorewall/Perl/Shorewall/IPAddrs.pm @@ -34,6 +34,8 @@ use strict; our @ISA = qw(Exporter); our @EXPORT = qw( ALLIPv4 ALLIPv6 + NILIPv4 + NILIPv6 IPv4_MULTICAST IPv6_MULTICAST IPv6_LINKLOCAL @@ -44,6 +46,7 @@ our @EXPORT = qw( ALLIPv4 IPv6_SITE_ALLNODES IPv6_SITE_ALLRTRS ALLIP + NILIP ALL TCP UDP @@ -63,6 +66,9 @@ our @EXPORT = qw( ALLIPv4 allipv4 allipv6 allip + nilipv4 + nilipv6 + nilip rfc1918_networks resolve_proto proto_name @@ -73,7 +79,7 @@ our @EXPORT = qw( ALLIPv4 validate_icmp6 ); our @EXPORT_OK = qw( ); -our $VERSION = '4.4_14'; +our $VERSION = '4.4_17'; # # Some IPv4/6 useful stuff @@ -82,6 +88,10 @@ our @allipv4 = ( '0.0.0.0/0' ); our @allipv6 = ( '::/0' ); our $allip; our @allip; +our @nilipv4 = ( '0.0.0.0' ); +our @nilipv6 = ( '::' ); +our $nilip; +our @nilip; our $valid_address; our $validate_address; our $validate_net; @@ -91,6 +101,8 @@ our $family; use constant { ALLIPv4 => '0.0.0.0/0' , ALLIPv6 => '::/0' , + NILIPv4 => '0.0.0.0' , + NILIPv6 => '::' , IPv4_MULTICAST => '224.0.0.0/4' , IPv6_MULTICAST => 'ff00::/8' , IPv6_LINKLOCAL => 'fe80::/10' , @@ -280,6 +292,14 @@ sub allipv6() { @allipv6; } +sub nilipv4() { + @nilipv4; +} + +sub nilipv6() { + @nilipv6; +} + sub rfc1918_networks() { @rfc1918_networks } @@ -674,6 +694,14 @@ sub allip() { @allip; } +sub NILIP() { + $nilip; +} + +sub nilip() { + @nilip; +} + sub valid_address ( $ ) { $valid_address->(@_); } @@ -710,6 +738,8 @@ sub initialize( $ ) { if ( $family == F_IPV4 ) { $allip = ALLIPv4; @allip = @allipv4; + $nilip = NILIPv4; + @nilip = @nilipv4; $valid_address = \&valid_4address; $validate_address = \&validate_4address; $validate_net = \&validate_4net; @@ -718,6 +748,8 @@ sub initialize( $ ) { } else { $allip = ALLIPv6; @allip = @allipv6; + $nilip = NILIPv6; + @nilip = @nilipv6; $valid_address = \&valid_6address; $validate_address = \&validate_6address; $validate_net = \&validate_6net; diff --git a/Shorewall/Perl/Shorewall/Nat.pm b/Shorewall/Perl/Shorewall/Nat.pm index 2bb190be3..de0da9ec8 100644 --- a/Shorewall/Perl/Shorewall/Nat.pm +++ b/Shorewall/Perl/Shorewall/Nat.pm @@ -155,6 +155,7 @@ sub process_one_masq( ) my $exceptionrule = ''; my $randomize = ''; my $persistent = ''; + my $conditional = 0; # # Parse the ADDRESSES column # @@ -188,7 +189,11 @@ sub process_one_masq( ) for my $addr ( split_list $addresses , 'address' ) { if ( $addr =~ /^&(.+)$/ ) { $target = 'SNAT '; - $addrlist .= '--to-source ' . record_runtime_address $1; + if ( $conditional = conditional_rule( $chainref, $addr ) ) { + $addrlist .= '--to-source ' . get_interface_address $1; + } else { + $addrlist .= '--to-source ' . record_runtime_address $1; + } } elsif ( $addr =~ /^.*\..*\..*\./ ) { $target = 'SNAT '; my ($ipaddr, $rest) = split ':', $addr; @@ -232,10 +237,7 @@ sub process_one_masq( ) '' , $exceptionrule ); - if ( $detectaddress ) { - decr_cmd_level( $chainref ); - add_commands( $chainref , 'fi' ); - } + conditional_rule_end( $chainref ) if $detectaddress || $conditional; if ( $add_snat_aliases ) { my ( $interface, $alias , $remainder ) = split( /:/, $fullinterface, 3 ); diff --git a/Shorewall/releasenotes.txt b/Shorewall/releasenotes.txt index 68098c4ec..9e39b7f8b 100644 --- a/Shorewall/releasenotes.txt +++ b/Shorewall/releasenotes.txt @@ -109,9 +109,8 @@ Beta 2 file. For optional interfaces, if the interface is not usable at the time - that the firewall starts the all-zero address (0.0.0.0 in IPv4 and - :: in IPv6) will be substituted, resulting in no packets matching - the rule. + that the firewall starts, the resulting Netfilter rule(s) + containing the interface address are not added. Beta 1