Implement user-defined address variables.

- Also correct handling of ${0} & ${chain} in a SWITCH column.

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2012-12-18 17:58:20 -08:00
parent 2b9af94c59
commit df7785f2e9
5 changed files with 116 additions and 11 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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.
#

View File

@ -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 ';

View File

@ -1366,6 +1366,66 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2&gt; /dev/null || true</programlisting
</varlistentry>
</variablelist>
<para>Beginning with Shorewall 4.5.11, you can define your own address
variables by using this syntax:</para>
<simplelist>
<member>&amp;{<replaceable>variable</replaceable>}</member>
</simplelist>
<para>where <replaceable>variable</replaceable> is a valid shell variable
name. The generated script will verify that the
<replaceable>variable</replaceable> contains a valid host or network
address, either from the environment or from it being assigned in your
<emphasis>init</emphasis> <ulink
url="shorewall_extension_scripts.htm">extension script</ulink>, and will
raise an error if it does not. In the error case, the state of the
firewall will remain unchanged.</para>
<para>Example:</para>
<para>/etc/shorewall/init:</para>
<programlisting><emphasis role="bold">SMC_ADDR</emphasis>=10.1.10.11</programlisting>
<para>/etc/shorewall/rules:</para>
<programlisting>test:debug net:<emphasis role="bold">&amp;{SMC_ADDR}</emphasis> fw</programlisting>
<para>A second form is also available beginning with Shorewall
4.5.11</para>
<simplelist>
<member>%{<replaceable>variable</replaceable>}</member>
</simplelist>
<para>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.</para>
<para>Example:</para>
<para>/etc/shorewall/init:</para>
<programlisting><emphasis role="bold">SMC_ADDR</emphasis>=10.1.10.11</programlisting>
<para>/etc/shorewall/rules:</para>
<programlisting>test:debug net:<emphasis role="bold">%{SMC_ADDR}</emphasis> fw</programlisting>
<important>
<para>For a particular address variable, all references must use the
same prefix character ('&amp;' or '%'). Otherwise, the following error
message is raised:</para>
<simplelist>
<member>ERROR: Mixed required/optional usage of address variable
<replaceable>variable</replaceable></member>
</simplelist>
</important>
<para>Run-time address variables may be used in the SOURCE and DEST column
of the following configuration files:</para>
@ -1566,7 +1626,7 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2&gt; /dev/null || true</programlisting
<variablelist>
<varlistentry>
<term>@0 and @chain</term>
<term>@0 and @chain (@{0} and @{chain})</term>
<listitem>
<para>Expands to the name of the current chain. Unlike $0, @0 has
@ -1577,7 +1637,7 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2&gt; /dev/null || true</programlisting
</varlistentry>
<varlistentry>
<term>@loglevel</term>
<term>@loglevel (@{loglevel})</term>
<listitem>
<para>Expands to the log level specified when the action was
@ -1586,7 +1646,7 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2&gt; /dev/null || true</programlisting
</varlistentry>
<varlistentry>
<term>@logtag</term>
<term>@logtag (@{logtag})</term>
<listitem>
<para>Expands to the log tag specified when the action was