From 21877d5fcb5e56a8f5dc7c7622bbec4e70ada87f Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Wed, 26 Oct 2016 13:39:50 -0700 Subject: [PATCH] Force a reload when enabling an interface whose IP address has changed Signed-off-by: Tom Eastep --- Shorewall/Perl/Shorewall/Chains.pm | 32 +++++++++-- Shorewall/Perl/Shorewall/Compiler.pm | 71 ++++++++++++------------- Shorewall/Perl/Shorewall/Misc.pm | 3 ++ Shorewall/Perl/Shorewall/Providers.pm | 76 +++++++++++++++++++++++++-- Shorewall/Perl/Shorewall/Zones.pm | 2 - Shorewall/Perl/prog.footer | 1 + docs/configuration_file_basics.xml | 21 +++++--- 7 files changed, 151 insertions(+), 55 deletions(-) diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index c83ab7d71..91e373f98 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -266,10 +266,13 @@ our %EXPORT_TAGS = ( set_chain_variables mark_firewall_not_started mark_firewall6_not_started + interface_address get_interface_address + used_address_variable get_interface_addresses get_interface_bcasts get_interface_acasts + interface_gateway get_interface_gateway get_interface_mac have_global_variables @@ -5777,7 +5780,7 @@ sub have_ipset_rules() { sub get_interface_address( $ ); -sub get_interface_gateway ( $;$ ); +sub get_interface_gateway ( $;$$ ); sub record_runtime_address( $$;$ ) { my ( $addrtype, $interface, $protect ) = @_; @@ -5821,12 +5824,18 @@ sub conditional_rule( $$ ) { if ( $type eq '&' ) { $variable = get_interface_address( $interface ); add_commands( $chainref , "if [ $variable != " . NILIP . ' ]; then' ); + incr_cmd_level $chainref; } else { $variable = get_interface_gateway( $interface ); - add_commands( $chainref , qq(if [ -n "$variable" ]; then) ); + + if ( $variable =~ /^\$/ ) { + add_commands( $chainref , qq(if [ -n "$variable" ]; then) ); + incr_cmd_level $chainref; + } else { + return 0; + } } - incr_cmd_level $chainref; return 1; } } elsif ( $type eq '%' && $interface =~ /^{([a-zA-Z_]\w*)}$/ ) { @@ -6801,6 +6810,10 @@ sub get_interface_address ( $ ) { "\$$variable"; } +sub used_address_variable( $ ) { + defined $interfaceaddr{$_[0]} +} + # # 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 # -sub get_interface_gateway ( $;$ ) { - my ( $logical, $protect ) = @_; +sub get_interface_gateway ( $;$$ ) { + my ( $logical, $protect, $provider ) = @_; my $interface = get_physical $logical; my $variable = interface_gateway( $interface ); + my $gateway = get_interface_option( $interface, 'gateway' ); $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 ) { $interfacegateways{$interface} = qq([ -n "\$$variable" ] || $variable=\$(detect_gateway $interface)); } else { @@ -6873,6 +6893,8 @@ sub get_interface_gateway ( $;$ ) { [ -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"; } diff --git a/Shorewall/Perl/Shorewall/Compiler.pm b/Shorewall/Perl/Shorewall/Compiler.pm index e2fbb222f..975a0f0e0 100644 --- a/Shorewall/Perl/Shorewall/Compiler.pm +++ b/Shorewall/Perl/Shorewall/Compiler.pm @@ -282,13 +282,8 @@ sub generate_script_2() { '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 $optional_interfaces = find_interfaces_by_option( 'optional' ); push_indent; @@ -809,33 +804,8 @@ sub compiler { # Validate the TC files so that the providers will know what interfaces have TC # my $tcinterfaces = process_tc; - # - # Generate a function to bring up each provider - # + 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; @@ -846,11 +816,7 @@ sub compiler { # process_tos; # - # ECN - # - setup_ecn if $family == F_IPV4 && have_capability( 'MANGLE_ENABLED' ) && $config{MANGLE_ENABLED}; - # - # Setup Masquerading/SNAT + # Setup Masquerade/SNAT # setup_snat( $update ); # @@ -894,6 +860,37 @@ sub compiler { # 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 ) { # # Compiling a script - generate the zone by zone matrix diff --git a/Shorewall/Perl/Shorewall/Misc.pm b/Shorewall/Perl/Shorewall/Misc.pm index 89779316f..7465809ef 100644 --- a/Shorewall/Perl/Shorewall/Misc.pm +++ b/Shorewall/Perl/Shorewall/Misc.pm @@ -2740,6 +2740,9 @@ EOF pop_indent; emit ' + rm -f ${VARDIR}/*.address + rm -f ${VARDIR}/*.gateway + run_stopped_exit'; my @ipsets = all_ipsets; diff --git a/Shorewall/Perl/Shorewall/Providers.pm b/Shorewall/Perl/Shorewall/Providers.pm index a906c6189..00bbb0966 100644 --- a/Shorewall/Perl/Shorewall/Providers.pm +++ b/Shorewall/Perl/Shorewall/Providers.pm @@ -472,12 +472,14 @@ sub process_a_provider( $ ) { if ( ( $gw = lc $gateway ) eq 'detect' ) { 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'; + set_interface_option( $interface, 'gateway', 'detect' ); } elsif ( $gw eq 'none' ) { fatal_error "Configuring multiple providers through one interface requires a gateway" if $shared; $gatewaycase = 'none'; $gateway = ''; + set_interface_option( $interface, 'gateway', 'none' ); } elsif ( $gateway && $gateway ne '-' ) { ( $gateway, $mac ) = split_host_list( $gateway, 0 ); validate_address $gateway, 0; @@ -491,12 +493,15 @@ sub process_a_provider( $ ) { } $gatewaycase = 'specified'; + set_interface_option( $interface, 'gateway', $gateway ); } else { $gatewaycase = 'omitted'; fatal_error "Configuring multiple providers through one interface requires a gateway" if $shared; $gateway = ''; + set_interface_option( $interface, 'gateway', $pseudo ? 'detect' : 'omitted' ); } + my ( $loose, $track, $balance, $default, $default_balance, $optional, $mtu, $tproxy, $local, $load, $what, $hostroute, $persistent ); if ( $pseudo ) { @@ -725,9 +730,9 @@ sub emit_started_message( $$$$$ ) { my ( $spaces, $level, $pseudo, $name, $number ) = @_; if ( $pseudo ) { - emit qq(${spaces}progress_message${level} " Optional interface $name Started"); + emit qq(${spaces}progress_message${level} "Optional interface $name Started"); } 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_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; unless ( $pseudo ) { @@ -1043,6 +1058,17 @@ CEOF } 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 { emit( qq(progress_message "Provider $table ($number) Started") ); } @@ -1067,6 +1093,17 @@ CEOF } else { 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 { if ( $shared ) { emit( "fatal_error \"Gateway $gateway is not reachable -- Provider $table ($number) Cannot be Started\"" ); @@ -2139,6 +2176,7 @@ sub handle_optional_interfaces( $ ) { } push_indent; + if ( $providerref->{gatewaycase} eq 'detect' ) { emit qq(if interface_is_usable $physical && [ -n "$providerref->{gateway}" ]; then); } else { @@ -2151,6 +2189,28 @@ sub handle_optional_interfaces( $ ) { emit( " SW_${wildbase}_IS_USABLE=Yes" ) if $interfaceref->{wildcard}; 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; emit( "fi\n" ); @@ -2161,6 +2221,7 @@ sub handle_optional_interfaces( $ ) { my $base = uc var_base( $physical ); my $case = $physical; my $wild = $case =~ s/\+$/*/; + my $variable = interface_address( $interface ); if ( $wildcards ) { emit( "$case)" ); @@ -2181,6 +2242,15 @@ sub handle_optional_interfaces( $ ) { emit ( " SW_${base}_IS_USABLE=Yes" , '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 ) { pop_indent, emit( 'fi' ) if $wild; emit( ';;' ); diff --git a/Shorewall/Perl/Shorewall/Zones.pm b/Shorewall/Perl/Shorewall/Zones.pm index f96a451eb..f7247e8ce 100644 --- a/Shorewall/Perl/Shorewall/Zones.pm +++ b/Shorewall/Perl/Shorewall/Zones.pm @@ -95,7 +95,6 @@ our @EXPORT = ( qw( NOTHING get_interface_origin interface_has_option set_interface_option - set_interface_provider interface_zone interface_zones verify_required_interfaces @@ -195,7 +194,6 @@ our %reservedName = ( all => 1, # number => # physical => # base => -# provider => # wildcard => undef|1 # Wildcard Name # zones => { zone1 => 1, ... } # origin => diff --git a/Shorewall/Perl/prog.footer b/Shorewall/Perl/prog.footer index 4105fdfb3..e5c81f5e7 100644 --- a/Shorewall/Perl/prog.footer +++ b/Shorewall/Perl/prog.footer @@ -128,6 +128,7 @@ g_compiled= g_file= g_docker= g_dockernetwork= +g_forcereload= initialize diff --git a/docs/configuration_file_basics.xml b/docs/configuration_file_basics.xml index aa187b1fb..188be8573 100644 --- a/docs/configuration_file_basics.xml +++ b/docs/configuration_file_basics.xml @@ -1655,11 +1655,17 @@ SSH(ACCEPT) net:$MYIP $FW Address Variables - If you use address variables that refer to an optional interface, - the enable command will not change/insert the rules - that use the variable. Therefore, to be completely safe, if you use such - address variables then you must follow an enable - command with a reload command. + Prior to Shorewall 5.0.14, if you use address variables that refer + to an optional interface, the enable command will not + change/insert the rules that use the variable. Therefore, to be + completely safe, if you use such address variables then you must follow + a successful enable command with a + reload command. + + 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 enable command will + automatically reload the ruleset. Given that shell variables are expanded at compile time, there is no @@ -1891,9 +1897,8 @@ SSH(ACCEPT) net:$MYIP $FW - If there is no gateway out of the named interface, the nil IP - address is used (0.0.0.0 in IPv4 and :: in IPv6). That way, the generated - rule will match no packets (or all packets if used with exclusion). + If there is no gateway out of the named interface, rules containing + the intefaces's run-time gateway variable are omitted.