Force a reload when enabling an interface whose IP address has changed

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2016-10-26 13:39:50 -07:00
parent 0b9387f09c
commit 21877d5fcb
No known key found for this signature in database
GPG Key ID: 96E6B3F2423A4D10
7 changed files with 151 additions and 55 deletions

View File

@ -266,10 +266,13 @@ our %EXPORT_TAGS = (
set_chain_variables set_chain_variables
mark_firewall_not_started mark_firewall_not_started
mark_firewall6_not_started mark_firewall6_not_started
interface_address
get_interface_address get_interface_address
used_address_variable
get_interface_addresses get_interface_addresses
get_interface_bcasts get_interface_bcasts
get_interface_acasts get_interface_acasts
interface_gateway
get_interface_gateway get_interface_gateway
get_interface_mac get_interface_mac
have_global_variables have_global_variables
@ -5777,7 +5780,7 @@ sub have_ipset_rules() {
sub get_interface_address( $ ); sub get_interface_address( $ );
sub get_interface_gateway ( $;$ ); sub get_interface_gateway ( $;$$ );
sub record_runtime_address( $$;$ ) { sub record_runtime_address( $$;$ ) {
my ( $addrtype, $interface, $protect ) = @_; my ( $addrtype, $interface, $protect ) = @_;
@ -5821,12 +5824,18 @@ sub conditional_rule( $$ ) {
if ( $type eq '&' ) { if ( $type eq '&' ) {
$variable = get_interface_address( $interface ); $variable = get_interface_address( $interface );
add_commands( $chainref , "if [ $variable != " . NILIP . ' ]; then' ); add_commands( $chainref , "if [ $variable != " . NILIP . ' ]; then' );
incr_cmd_level $chainref;
} else { } else {
$variable = get_interface_gateway( $interface ); $variable = get_interface_gateway( $interface );
if ( $variable =~ /^\$/ ) {
add_commands( $chainref , qq(if [ -n "$variable" ]; then) ); add_commands( $chainref , qq(if [ -n "$variable" ]; then) );
incr_cmd_level $chainref;
} else {
return 0;
}
} }
incr_cmd_level $chainref;
return 1; return 1;
} }
} elsif ( $type eq '%' && $interface =~ /^{([a-zA-Z_]\w*)}$/ ) { } elsif ( $type eq '%' && $interface =~ /^{([a-zA-Z_]\w*)}$/ ) {
@ -6801,6 +6810,10 @@ sub get_interface_address ( $ ) {
"\$$variable"; "\$$variable";
} }
sub used_address_variable( $ ) {
defined $interfaceaddr{$_[0]}
}
# #
# Returns the name of the shell variable holding the broadcast addresses of the passed interface # Returns the name of the shell variable holding the broadcast addresses of the passed interface
# #
@ -6858,14 +6871,21 @@ sub interface_gateway( $ ) {
# #
# Record that the ruleset requires the gateway address on the passed interface # Record that the ruleset requires the gateway address on the passed interface
# #
sub get_interface_gateway ( $;$ ) { sub get_interface_gateway ( $;$$ ) {
my ( $logical, $protect ) = @_; my ( $logical, $protect, $provider ) = @_;
my $interface = get_physical $logical; my $interface = get_physical $logical;
my $variable = interface_gateway( $interface ); my $variable = interface_gateway( $interface );
my $gateway = get_interface_option( $interface, 'gateway' );
$global_variables |= ALL_COMMANDS; $global_variables |= ALL_COMMANDS;
if ( $gateway ) {
fatal_error q(A gateway variable cannot be used for a provider interface with GATEWAY set to 'none' in the providers file) if $gateway eq 'none';
fatal_error q(A gateway variable cannot be used for a provider interface with an empty GATEWAY column in the providers file) if $gateway eq 'omitted';
return $gateway if $gateway ne 'detect';
}
if ( interface_is_optional $logical ) { if ( interface_is_optional $logical ) {
$interfacegateways{$interface} = qq([ -n "\$$variable" ] || $variable=\$(detect_gateway $interface)); $interfacegateways{$interface} = qq([ -n "\$$variable" ] || $variable=\$(detect_gateway $interface));
} else { } else {
@ -6873,6 +6893,8 @@ sub get_interface_gateway ( $;$ ) {
[ -n "\$$variable" ] || startup_error "Unable to detect the gateway through interface $interface"); [ -n "\$$variable" ] || startup_error "Unable to detect the gateway through interface $interface");
} }
set_interface_option($interface, 'used_gateway_variable', 1) unless $provider;
$protect ? "\${$variable:-" . NILIP . '}' : "\$$variable"; $protect ? "\${$variable:-" . NILIP . '}' : "\$$variable";
} }

View File

@ -282,13 +282,8 @@ sub generate_script_2() {
'detect_configuration()', 'detect_configuration()',
'{' ); '{' );
my $optional_interfaces = find_interfaces_by_option( 'optional' );
#
# Force address detection for all optional interfaces
#
get_interface_address( $_ ) for @$optional_interfaces;
my $global_variables = have_global_variables; my $global_variables = have_global_variables;
my $optional_interfaces = find_interfaces_by_option( 'optional' );
push_indent; push_indent;
@ -809,33 +804,8 @@ sub compiler {
# Validate the TC files so that the providers will know what interfaces have TC # Validate the TC files so that the providers will know what interfaces have TC
# #
my $tcinterfaces = process_tc; my $tcinterfaces = process_tc;
#
# Generate a function to bring up each provider
#
process_providers( $tcinterfaces ); process_providers( $tcinterfaces );
#
# [Re-]establish Routing
#
if ( $scriptfilename || $debug ) {
emit( "\n#",
'# Setup routing and traffic shaping',
'#',
'setup_routing_and_traffic_shaping() {'
);
push_indent;
}
setup_providers;
#
# TCRules and Traffic Shaping
#
setup_tc( $update );
if ( $scriptfilename || $debug ) {
pop_indent;
emit "}\n"; # End of setup_routing_and_traffic_shaping()
}
$have_arptables = process_arprules if $family == F_IPV4; $have_arptables = process_arprules if $family == F_IPV4;
@ -846,11 +816,7 @@ sub compiler {
# #
process_tos; process_tos;
# #
# ECN # Setup Masquerade/SNAT
#
setup_ecn if $family == F_IPV4 && have_capability( 'MANGLE_ENABLED' ) && $config{MANGLE_ENABLED};
#
# Setup Masquerading/SNAT
# #
setup_snat( $update ); setup_snat( $update );
# #
@ -894,6 +860,37 @@ sub compiler {
# #
setup_accounting if $config{ACCOUNTING}; setup_accounting if $config{ACCOUNTING};
enable_script;
#
# Generate a function to bring up each provider
#
if ( $scriptfilename || $debug ) {
emit( "\n#",
'# Setup routing and traffic shaping',
'#',
'setup_routing_and_traffic_shaping() {'
);
push_indent;
}
setup_providers;
#
# TCRules and Traffic Shaping
#
setup_tc( $update );
if ( $scriptfilename || $debug ) {
pop_indent;
emit "}\n"; # End of setup_routing_and_traffic_shaping()
}
#
# ECN
#
setup_ecn if $family == F_IPV4 && have_capability( 'MANGLE_ENABLED' ) && $config{MANGLE_ENABLED};
disable_script;
if ( $scriptfilename ) { if ( $scriptfilename ) {
# #
# Compiling a script - generate the zone by zone matrix # Compiling a script - generate the zone by zone matrix

View File

@ -2740,6 +2740,9 @@ EOF
pop_indent; pop_indent;
emit ' emit '
rm -f ${VARDIR}/*.address
rm -f ${VARDIR}/*.gateway
run_stopped_exit'; run_stopped_exit';
my @ipsets = all_ipsets; my @ipsets = all_ipsets;

View File

@ -472,12 +472,14 @@ sub process_a_provider( $ ) {
if ( ( $gw = lc $gateway ) eq 'detect' ) { if ( ( $gw = lc $gateway ) eq 'detect' ) {
fatal_error "Configuring multiple providers through one interface requires an explicit gateway" if $shared; fatal_error "Configuring multiple providers through one interface requires an explicit gateway" if $shared;
$gateway = get_interface_gateway $interface; $gateway = get_interface_gateway( $interface, undef, 1 );
$gatewaycase = 'detect'; $gatewaycase = 'detect';
set_interface_option( $interface, 'gateway', 'detect' );
} elsif ( $gw eq 'none' ) { } elsif ( $gw eq 'none' ) {
fatal_error "Configuring multiple providers through one interface requires a gateway" if $shared; fatal_error "Configuring multiple providers through one interface requires a gateway" if $shared;
$gatewaycase = 'none'; $gatewaycase = 'none';
$gateway = ''; $gateway = '';
set_interface_option( $interface, 'gateway', 'none' );
} elsif ( $gateway && $gateway ne '-' ) { } elsif ( $gateway && $gateway ne '-' ) {
( $gateway, $mac ) = split_host_list( $gateway, 0 ); ( $gateway, $mac ) = split_host_list( $gateway, 0 );
validate_address $gateway, 0; validate_address $gateway, 0;
@ -491,12 +493,15 @@ sub process_a_provider( $ ) {
} }
$gatewaycase = 'specified'; $gatewaycase = 'specified';
set_interface_option( $interface, 'gateway', $gateway );
} else { } else {
$gatewaycase = 'omitted'; $gatewaycase = 'omitted';
fatal_error "Configuring multiple providers through one interface requires a gateway" if $shared; fatal_error "Configuring multiple providers through one interface requires a gateway" if $shared;
$gateway = ''; $gateway = '';
set_interface_option( $interface, 'gateway', $pseudo ? 'detect' : 'omitted' );
} }
my ( $loose, $track, $balance, $default, $default_balance, $optional, $mtu, $tproxy, $local, $load, $what, $hostroute, $persistent ); my ( $loose, $track, $balance, $default, $default_balance, $optional, $mtu, $tproxy, $local, $load, $what, $hostroute, $persistent );
if ( $pseudo ) { if ( $pseudo ) {
@ -725,9 +730,9 @@ sub emit_started_message( $$$$$ ) {
my ( $spaces, $level, $pseudo, $name, $number ) = @_; my ( $spaces, $level, $pseudo, $name, $number ) = @_;
if ( $pseudo ) { if ( $pseudo ) {
emit qq(${spaces}progress_message${level} " Optional interface $name Started"); emit qq(${spaces}progress_message${level} "Optional interface $name Started");
} else { } else {
emit qq(${spaces}progress_message${level} " Provider $name ($number) Started"); emit qq(${spaces}progress_message${level} "Provider $name ($number) Started");
} }
} }
@ -1033,6 +1038,16 @@ CEOF
emit( qq(rm -f \${VARDIR}/${physical}_disabled) ); emit( qq(rm -f \${VARDIR}/${physical}_disabled) );
emit_started_message( '', 2, $pseudo, $table, $number ); emit_started_message( '', 2, $pseudo, $table, $number );
if ( used_address_variable( $interface ) || get_interface_option( $interface, 'used_gateway_variable' ) ) {
emit( '',
'if [ -n "$g_forcereload" ]; then',
" progress_message2 \"The IP address or gateway of $physical has changed -- forcing reload of the ruleset\"",
' COMMAND=reload',
' detect_configuration',
' define_firewall',
'fi' );
}
pop_indent; pop_indent;
unless ( $pseudo ) { unless ( $pseudo ) {
@ -1043,6 +1058,17 @@ CEOF
} }
emit "fi\n"; emit "fi\n";
if ( used_address_variable( $interface ) ) {
my $variable = interface_address( $interface );
emit( "echo \$$variable > \${VARDIR}/${physical}.address" );
}
if ( get_interface_option( $interface, 'used_gateway_variable' ) ) {
my $variable = interface_gateway( $interface );
emit( qq(echo "\$$variable" > \${VARDIR}/${physical}.gateway\n) );
}
} else { } else {
emit( qq(progress_message "Provider $table ($number) Started") ); emit( qq(progress_message "Provider $table ($number) Started") );
} }
@ -1067,6 +1093,17 @@ CEOF
} else { } else {
emit ( "error_message \"WARNING: Interface $physical is not usable -- Provider $table ($number) not Started\"" ); emit ( "error_message \"WARNING: Interface $physical is not usable -- Provider $table ($number) not Started\"" );
} }
if ( used_address_variable( $interface ) ) {
my $variable = interface_address( $interface );
emit( "\necho \$$variable > \${VARDIR}/${physical}.address" );
}
if ( get_interface_option( $interface, 'used_gateway_variable' ) ) {
my $variable = interface_gateway( $interface );
emit( qq(\necho "\$$variable" > \${VARDIR}/${physical}.gateway) );
}
} else { } else {
if ( $shared ) { if ( $shared ) {
emit( "fatal_error \"Gateway $gateway is not reachable -- Provider $table ($number) Cannot be Started\"" ); emit( "fatal_error \"Gateway $gateway is not reachable -- Provider $table ($number) Cannot be Started\"" );
@ -2139,6 +2176,7 @@ sub handle_optional_interfaces( $ ) {
} }
push_indent; push_indent;
if ( $providerref->{gatewaycase} eq 'detect' ) { if ( $providerref->{gatewaycase} eq 'detect' ) {
emit qq(if interface_is_usable $physical && [ -n "$providerref->{gateway}" ]; then); emit qq(if interface_is_usable $physical && [ -n "$providerref->{gateway}" ]; then);
} else { } else {
@ -2151,6 +2189,28 @@ sub handle_optional_interfaces( $ ) {
emit( " SW_${wildbase}_IS_USABLE=Yes" ) if $interfaceref->{wildcard}; emit( " SW_${wildbase}_IS_USABLE=Yes" ) if $interfaceref->{wildcard};
emit( 'fi' ); emit( 'fi' );
if ( used_address_variable( $interface ) ) {
my $variable = interface_address( $interface );
emit( '',
"if [ -f \${VARDIR}/${physical}.address ]; then",
" if [ \$(cat \${VARDIR}/${physical}.address) != \$$variable ]; then",
' g_forcereload=Yes',
' fi',
'fi' );
}
if ( get_interface_option( $interface, 'used_gateway_variable' ) ) {
my $variable = interface_gateway( $interface );
emit( '',
"if [ -f \${VARDIR}/${physical}.gateway ]; then",
" if [ \$(cat \${VARDIR}/${physical}.gateway) != \"\$$variable\" ]; then",
' g_forcereload=Yes',
' fi',
'fi' );
}
pop_indent; pop_indent;
emit( "fi\n" ); emit( "fi\n" );
@ -2161,6 +2221,7 @@ sub handle_optional_interfaces( $ ) {
my $base = uc var_base( $physical ); my $base = uc var_base( $physical );
my $case = $physical; my $case = $physical;
my $wild = $case =~ s/\+$/*/; my $wild = $case =~ s/\+$/*/;
my $variable = interface_address( $interface );
if ( $wildcards ) { if ( $wildcards ) {
emit( "$case)" ); emit( "$case)" );
@ -2181,6 +2242,15 @@ sub handle_optional_interfaces( $ ) {
emit ( " SW_${base}_IS_USABLE=Yes" , emit ( " SW_${base}_IS_USABLE=Yes" ,
'fi' ); 'fi' );
if ( used_address_variable( $interface ) ) {
emit( '',
"if [ -f \${VARDIR}/${physical}.address ]; then",
" if [ \$(cat \${VARDIR}/${physical}.address) != \$$variable ]; then",
' g_forcereload=Yes',
' fi',
'fi' );
}
if ( $wildcards ) { if ( $wildcards ) {
pop_indent, emit( 'fi' ) if $wild; pop_indent, emit( 'fi' ) if $wild;
emit( ';;' ); emit( ';;' );

View File

@ -95,7 +95,6 @@ our @EXPORT = ( qw( NOTHING
get_interface_origin get_interface_origin
interface_has_option interface_has_option
set_interface_option set_interface_option
set_interface_provider
interface_zone interface_zone
interface_zones interface_zones
verify_required_interfaces verify_required_interfaces
@ -195,7 +194,6 @@ our %reservedName = ( all => 1,
# number => <ordinal position in the interfaces file> # number => <ordinal position in the interfaces file>
# physical => <physical interface name> # physical => <physical interface name>
# base => <shell variable base representing this interface> # base => <shell variable base representing this interface>
# provider => <Provider Name, if interface is associated with a provider>
# wildcard => undef|1 # Wildcard Name # wildcard => undef|1 # Wildcard Name
# zones => { zone1 => 1, ... } # zones => { zone1 => 1, ... }
# origin => <where defined> # origin => <where defined>

View File

@ -128,6 +128,7 @@ g_compiled=
g_file= g_file=
g_docker= g_docker=
g_dockernetwork= g_dockernetwork=
g_forcereload=
initialize initialize

View File

@ -1655,11 +1655,17 @@ SSH(ACCEPT) net:$MYIP $FW
<title>Address Variables</title> <title>Address Variables</title>
<caution> <caution>
<para>If you use address variables that refer to an optional interface, <para>Prior to Shorewall 5.0.14, if you use address variables that refer
the <command>enable</command> command will not change/insert the rules to an optional interface, the <command>enable</command> command will not
that use the variable. Therefore, to be completely safe, if you use such change/insert the rules that use the variable. Therefore, to be
address variables then you must follow an <command>enable</command> completely safe, if you use such address variables then you must follow
command with a <command>reload</command> command.</para> a successful <command>enable</command> command with a
<command>reload</command> command.</para>
<para>Beginning with Shorewall 5.0.14, if a Shorewall-defined address
variable's value has changed since the Netfilter ruleset was
instantiated, then a successful <command>enable</command> command will
automatically reload the ruleset.</para>
</caution> </caution>
<para>Given that shell variables are expanded at compile time, there is no <para>Given that shell variables are expanded at compile time, there is no
@ -1891,9 +1897,8 @@ SSH(ACCEPT) net:$MYIP $FW
</varlistentry> </varlistentry>
</variablelist> </variablelist>
<para>If there is no gateway out of the named interface, the nil IP <para>If there is no gateway out of the named interface, rules containing
address is used (0.0.0.0 in IPv4 and :: in IPv6). That way, the generated the intefaces's run-time gateway variable are omitted.</para>
rule will match no packets (or all packets if used with exclusion).</para>
</section> </section>
<section id="ActionVariables"> <section id="ActionVariables">