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