diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index 24a470437..5b7527e47 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -209,6 +209,7 @@ our %EXPORT_TAGS = ( do_dscp have_ipset_rules record_runtime_address + verify_address_variables conditional_rule conditional_rule_end match_source_dev @@ -389,6 +390,7 @@ my $idiotcount1; my $warningcount; my $hashlimitset; my $global_variables; +my %address_variables; my $ipset_rules; # @@ -654,6 +656,7 @@ sub initialize( $$$ ) { %interfacebcasts = (); %interfaceacasts = (); %interfacegateways = (); + %address_variables = (); $global_variables = 0; $idiotcount = 0; @@ -4887,6 +4890,13 @@ sub get_interface_address( $ ); sub record_runtime_address( $$;$ ) { my ( $addrtype, $interface, $protect ) = @_; + + if ( $interface =~ /^{([a-zA-Z_]\w*)}$/ ) { + fatal_error "Mixed required/optional usage of address variable $1" if ( $address_variables{$1} || $addrtype ) ne $addrtype; + $address_variables{$1} = $addrtype; + return '$' . "$1 "; + } + fatal_error "Unknown interface address variable (&$interface)" unless known_interface( $interface ); fatal_error "Invalid interface address variable (&$interface)" if $interface =~ /\+$/; @@ -4913,6 +4923,7 @@ sub conditional_rule( $$ ) { if ( $address =~ /^!?([&%])(.+)$/ ) { my ($type, $interface) = ($1, $2); + if ( my $ref = known_interface $interface ) { if ( $ref->{options}{optional} ) { my $variable; @@ -4927,7 +4938,13 @@ sub conditional_rule( $$ ) { incr_cmd_level $chainref; return 1; } - }; + } elsif ( $type eq '%' && $interface =~ /^{([a-zA-Z_]\w*)}$/ ) { + fatal_error "Mixed required/optional usage of address variable $1" if ( $address_variables{$1} || $type ) ne $type; + $address_variables{$1} = $type; + add_commands( $chainref , "if [ \$$1 != " . NILIP . ' ]; then' ); + incr_cmd_level $chainref; + return 1; + } } 0; @@ -5873,6 +5890,27 @@ sub set_global_variables( $ ) { } } +sub verify_address_variables() { + while ( my ( $variable, $type ) = ( each %address_variables ) ) { + my $address = "\$$variable"; + + if ( $type eq '&' ) { + emit( qq([ -n "$address" ] || startup_error "Address variable $variable had not been assigned an address") , + q() , + qq(if qt \$g_tool -A INPUT -s $address; then) ); + } else { + emit( qq(if [ -z "$address" ]; then) , + qq( $variable=) . NILIP , + qq(elif qt \$g_tool -A INPUT -s $address; then) ); + } + + emit( qq( qt \$g_tool -D INPUT -s $address), + q(else), + qq( startup_error "Invalid value ($address) for address variable $variable"), + qq(fi\n) ); + } +} + ############################################################################################ # Helpers for expand_rule() ############################################################################################ @@ -5990,7 +6028,7 @@ sub isolate_source_interface( $ ) { if ( $source =~ /^(.+?):(.+)$/ ) { $iiface = $1; $inets = $2; - } elsif ( $source =~ /^!?(?:\+|&|~|\^|\d+\.)/ ) { + } elsif ( $source =~ /^!?(?:\+|&|~|%|\^|\d+\.)/ ) { $inets = $source; } else { $iiface = $source; @@ -5998,6 +6036,7 @@ sub isolate_source_interface( $ ) { } elsif ( $source =~ /^(.+?):<(.+)>\s*$/ || $source =~ /^(.+?):\[(.+)\]\s*$/ || $source =~ /^(.+?):(!?\+.+)$/ || + $source =~ /^(.+?):(!?[&%].+)$/ || $source =~ /^(.+?):(\[.+\]\/(?:\d+))\s*$/ ) { $iiface = $1; @@ -6107,6 +6146,7 @@ sub isolate_dest_interface( $$$$ ) { } elsif ( $dest =~ /^(.+?):<(.+)>\s*$/ || $dest =~ /^(.+?):\[(.+)\]\s*$/ || $dest =~ /^(.+?):(!?\+.+)$/ || + $dest =~ /^(.+?):(!?[&%].+)$/ || $dest =~ /^(.+?):(\[.+\]\/(?:\d+))\s*$/ ) { $diface = $1; diff --git a/Shorewall/Perl/Shorewall/Compiler.pm b/Shorewall/Perl/Shorewall/Compiler.pm index a27b59af9..feb3bd03d 100644 --- a/Shorewall/Perl/Shorewall/Compiler.pm +++ b/Shorewall/Perl/Shorewall/Compiler.pm @@ -365,8 +365,8 @@ sub generate_script_3($) { 'fi', '' ); + verify_address_variables; save_dynamic_chains; - mark_firewall_not_started; emit ( '', @@ -394,6 +394,7 @@ sub generate_script_3($) { 'fi', '' ); + verify_address_variables; save_dynamic_chains; mark_firewall_not_started; diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm index 7de149542..f6aad8a3c 100644 --- a/Shorewall/Perl/Shorewall/Config.pm +++ b/Shorewall/Perl/Shorewall/Config.pm @@ -1840,7 +1840,7 @@ sub split_line1( $$;$$ ) { # Found it -- be sure there wasn't more than one. # fatal_error "Only one semicolon (';') allowed on a line" if defined $rest; - } elsif ( $currentline =~ /(.*){(.*)}$/ ) { + } elsif ( $currentline =~ /^(\s*|.*[^&@%]){(.*)}$/ ) { # # Pairs are enclosed in curly brackets. # diff --git a/Shorewall/Perl/Shorewall/Nat.pm b/Shorewall/Perl/Shorewall/Nat.pm index edb557cd9..318043529 100644 --- a/Shorewall/Perl/Shorewall/Nat.pm +++ b/Shorewall/Perl/Shorewall/Nat.pm @@ -196,12 +196,16 @@ sub process_one_masq( ) } else { my $addrlist = ''; for my $addr ( split_list $addresses , 'address' ) { - if ( $addr =~ /^&(.+)$/ ) { + if ( $addr =~ /^([&%])(.+)$/ ) { + my ( $type, $interface ) = ( $1, $2 ); $target = 'SNAT '; - if ( $conditional = conditional_rule( $chainref, $addr ) ) { - $addrlist .= '--to-source ' . get_interface_address $1; + if ( $interface =~ /^{([a-zA-Z_]\w*)}$/ ) { + $conditional = conditional_rule( $chainref, $addr ); + $addrlist .= '--to-source ' . "\$$1 "; + } elsif ( $conditional = conditional_rule( $chainref, $addr ) ) { + $addrlist .= '--to-source ' . get_interface_address $interface; } else { - $addrlist .= '--to-source ' . record_runtime_address( '&', $1 ); + $addrlist .= '--to-source ' . record_runtime_address( $type, $interface ); } } elsif ( $addr =~ /^.*\..*\..*\./ ) { $target = 'SNAT '; diff --git a/docs/configuration_file_basics.xml b/docs/configuration_file_basics.xml index e9f7de54e..1aa7f5033 100644 --- a/docs/configuration_file_basics.xml +++ b/docs/configuration_file_basics.xml @@ -1366,6 +1366,66 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2> /dev/null || true + Beginning with Shorewall 4.5.11, you can define your own address + variables by using this syntax: + + + &{variable} + + + where variable is a valid shell variable + name. The generated script will verify that the + variable contains a valid host or network + address, either from the environment or from it being assigned in your + init extension script, and will + raise an error if it does not. In the error case, the state of the + firewall will remain unchanged. + + Example: + + /etc/shorewall/init: + + SMC_ADDR=10.1.10.11 + + /etc/shorewall/rules: + + test:debug net:&{SMC_ADDR} fw + + A second form is also available beginning with Shorewall + 4.5.11 + + + %{variable} + + + Unlike with the first form, this form does not require the variable + to be set. If the variable is empty, the generated script will supply the + all-zeros address (0.0.0.0 in IPv4 and :: in IPv6). In most cases, the + compiler simply omits rules containing matches on the all-zeros + address. + + Example: + + /etc/shorewall/init: + + SMC_ADDR=10.1.10.11 + + /etc/shorewall/rules: + + test:debug net:%{SMC_ADDR} fw + + + For a particular address variable, all references must use the + same prefix character ('&' or '%'). Otherwise, the following error + message is raised: + + + ERROR: Mixed required/optional usage of address variable + variable + + + Run-time address variables may be used in the SOURCE and DEST column of the following configuration files: @@ -1566,7 +1626,7 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2> /dev/null || true - @0 and @chain + @0 and @chain (@{0} and @{chain}) Expands to the name of the current chain. Unlike $0, @0 has @@ -1577,7 +1637,7 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2> /dev/null || true - @loglevel + @loglevel (@{loglevel}) Expands to the log level specified when the action was @@ -1586,7 +1646,7 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2> /dev/null || true - @logtag + @logtag (@{logtag}) Expands to the log tag specified when the action was