diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index c69a9bf6c..19b675bd2 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -2437,7 +2437,7 @@ sub do_length( $ ) { # sub match_source_dev( $ ) { my $interface = shift; - my $interfaceref = known_interface( $interface ); + my $interfaceref = known_interface( $interface, 0 ); $interface = $interfaceref->{physical} if $interfaceref; return '' if $interface eq '+'; if ( $interfaceref && $interfaceref->{options}{port} ) { @@ -2452,7 +2452,7 @@ sub match_source_dev( $ ) { # sub match_dest_dev( $ ) { my $interface = shift; - my $interfaceref = known_interface( $interface ); + my $interfaceref = known_interface( $interface, 0 ); $interface = $interfaceref->{physical} if $interfaceref; return '' if $interface eq '+'; if ( $interfaceref && $interfaceref->{options}{port} ) { @@ -3251,7 +3251,7 @@ sub expand_rule( $$$$$$$$$$;$ ) # Verify Interface, if any # if ( $iiface ) { - fatal_error "Unknown Interface ($iiface)" unless known_interface $iiface; + fatal_error "Unknown Interface ($iiface)" unless known_interface( $iiface, 0 ); if ( $restriction & POSTROUTE_RESTRICT ) { # @@ -3341,7 +3341,7 @@ sub expand_rule( $$$$$$$$$$;$ ) # Verify Destination Interface, if any # if ( $diface ) { - fatal_error "Unknown Interface ($diface)" unless known_interface $diface; + fatal_error "Unknown Interface ($diface)" unless known_interface( $diface, 0 ); if ( $restriction & PREROUTE_RESTRICT ) { # diff --git a/Shorewall/Perl/Shorewall/Nat.pm b/Shorewall/Perl/Shorewall/Nat.pm index b73dc3be7..fdfcc51b4 100644 --- a/Shorewall/Perl/Shorewall/Nat.pm +++ b/Shorewall/Perl/Shorewall/Nat.pm @@ -142,7 +142,7 @@ sub process_one_masq( ) $rule .= "-m realm --realm $realm "; } - fatal_error "Unknown interface ($interface)" unless my $interfaceref = known_interface( $interface ); + fatal_error "Unknown interface ($interface)" unless my $interfaceref = known_interface( $interface, 0 ); unless ( $interfaceref->{root} ) { $rule .= match_dest_dev( $interface ); @@ -314,7 +314,7 @@ sub do_one_nat( $$$$$ ) my $rulein = ''; my $ruleout = ''; - fatal_error "Unknown interface ($interface)" unless my $interfaceref = known_interface( $interface ); + fatal_error "Unknown interface ($interface)" unless my $interfaceref = known_interface( $interface, 0 ); unless ( $interfaceref->{root} ) { $rulein = match_source_dev $interface; @@ -408,7 +408,7 @@ sub setup_netmap() { my $ruleout = ''; my $iface = $interface; - fatal_error "Unknown interface ($interface)" unless my $interfaceref = known_interface( $interface ); + fatal_error "Unknown interface ($interface)" unless my $interfaceref = known_interface( $interface, 0 ); unless ( $interfaceref->{root} ) { $rulein = match_source_dev( $interface ); diff --git a/Shorewall/Perl/Shorewall/Providers.pm b/Shorewall/Perl/Shorewall/Providers.pm index ca4df0203..f9e35d5c5 100644 --- a/Shorewall/Perl/Shorewall/Providers.pm +++ b/Shorewall/Perl/Shorewall/Providers.pm @@ -35,7 +35,7 @@ use strict; our @ISA = qw(Exporter); our @EXPORT = qw( setup_providers @routemarked_interfaces handle_stickiness handle_optional_interfaces ); our @EXPORT_OK = qw( initialize lookup_provider ); -our $VERSION = '4.4_11'; +our $VERSION = '4.4_13'; use constant { LOCAL_TABLE => 255, MAIN_TABLE => 254, @@ -275,7 +275,7 @@ sub add_a_provider( ) { require_capability 'REALM_MATCH', "Configuring multiple providers through one interface", "s"; } - fatal_error "Unknown Interface ($interface)" unless known_interface $interface; + fatal_error "Unknown Interface ($interface)" unless known_interface( $interface, 1 ); fatal_error "A bridge port ($interface) may not be configured as a provider interface" if port_to_bridge $interface; my $physical = get_physical $interface; @@ -846,53 +846,100 @@ sub lookup_provider( $ ) { sub handle_optional_interfaces( $ ) { my $returnvalue = verify_required_interfaces( shift ); - # - # find_interfaces_by_option1() does not return wildcard interfaces. If an interface is defined - # as a wildcard in /etc/shorewall/interfaces, then only specific interfaces matching that - # wildcard are returned. - # - my $interfaces = find_interfaces_by_option1 'optional'; + my $require = $config{REQUIRE_INTERFACE}; + my $wildcards = 0; + my $interfaces = find_interfaces_by_option1 'optional', $wildcards; - if ( $config{REQUIRE_INTERFACE} ) { - emit( 'HAVE_INTERFACE=' ); - emit( '' ); - } + emit( 'HAVE_INTERFACE=', '' ) if $require; if ( @$interfaces ) { - for my $interface ( @$interfaces ) { - my $provider = $provider_interfaces{$interface}; - my $physical = get_physical $interface; - my $base = uc chain_base( $physical ); + # + # Clear the '_IS_USABLE' variables + # + emit( join( '_', 'SW', uc chain_base( get_physical( $_ ) ) , 'IS_USABLE=' ) ) for @$interfaces; - emit( '' ); + if ( $wildcards ) { + emit( '', + 'interfaces=$($IP -' . $family . ' addr list | egrep \'^[[:digit:]]+\' | while read number interface rest; do echo ${interface%:}; done)', + '', + 'for interface in $interfaces; do' + ); - if ( $provider ) { - # - # This interface is associated with a non-shared provider -- get the provider table entry - # - my $providerref = $providers{$provider}; + push_indent; - if ( $providerref->{gatewaycase} eq 'detect' ) { - emit qq(if interface_is_usable $physical && [ -n "$providerref->{gateway}" ]; then); - } else { - emit qq(if interface_is_usable $physical; then); - } + emit ( 'case "$interface" in' + ); + + push_indent; + } else { + emit ''; + } + + for my $interface ( grep $provider_interfaces{$_}, @$interfaces ) { + my $provider = $provider_interfaces{$interface}; + my $physical = get_physical $interface; + my $base = uc chain_base( $physical ); + my $providerref = $providers{$provider}; + + emit( "$physical)" ) if $wildcards; + + push_indent; + + if ( $providerref->{gatewaycase} eq 'detect' ) { + emit qq(if interface_is_usable $physical && [ -n "$providerref->{gateway}" ]; then); } else { - # - # Not a provider interface - # emit qq(if interface_is_usable $physical; then); } - emit( ' HAVE_INTERFACE=Yes' ) if $config{REQUIRE_INTERFACE}; + emit( ' HAVE_INTERFACE=Yes' ) if $require; emit( " SW_${base}_IS_USABLE=Yes" , - 'else' , - " SW_${base}_IS_USABLE=" , 'fi' ); + + emit( ';;' ), pop_indent if $wildcards; } - if ( $config{REQUIRE_INTERFACE} ) { + for my $interface ( grep ! $provider_interfaces{$_}, @$interfaces ) { + my $physical = get_physical $interface; + my $base = uc chain_base( $physical ); + my $case = $physical; + my $wild = $case =~ s/\+$/*/; + + if ( $wildcards ) { + emit( "$case)" ); + push_indent; + + + if ( $wild ) { + emit( qq(if [ -z "\$SW_${base}_IS_USABLE" ]; then) ); + push_indent; + emit ( 'if interface_is_usable $interface; then' ); + } else { + emit ( "if interface_is_usable $physical; then" ); + } + } else { + emit ( "if interface_is_usable $physical; then" ); + } + + emit ( ' HAVE_INTERFACE=Yes' ) if $require; + + emit ( " SW_${base}_IS_USABLE=Yes" , + 'fi' ); + + if ( $wildcards ) { + pop_indent, emit( 'fi' ) if $wild; + emit( ';;' ); + pop_indent; + } + } + + if ( $wildcards ) { + emit( 'esac' ); + pop_indent; + emit('done' ); + } + + if ( $require ) { emit( '', 'if [ -z "$HAVE_INTERFACE" ]; then' , ' case "$COMMAND" in', diff --git a/Shorewall/Perl/Shorewall/Rules.pm b/Shorewall/Perl/Shorewall/Rules.pm index 788d84ebf..45d2bf37a 100644 --- a/Shorewall/Perl/Shorewall/Rules.pm +++ b/Shorewall/Perl/Shorewall/Rules.pm @@ -177,7 +177,7 @@ sub setup_ecn() my ($interface, $hosts ) = split_line 1, 2, 'ecn file entry'; - fatal_error "Unknown interface ($interface)" unless known_interface $interface; + fatal_error "Unknown interface ($interface)" unless known_interface( $interface, 0 ); $interfaces{$interface} = 1; @@ -328,7 +328,7 @@ sub process_routestopped() { my $interfaceref; - fatal_error "Unknown interface ($interface)" unless $interfaceref = known_interface $interface; + fatal_error "Unknown interface ($interface)" unless $interfaceref = known_interface( $interface, 0 ); $hosts = ALLIP unless $hosts && $hosts ne '-'; my $routeback = 0; @@ -769,7 +769,7 @@ sub setup_mac_lists( $ ) { my $targetref = $maclist_targets{$disposition}; fatal_error "Invalid DISPOSITION ($original_disposition)" if ! $targetref || ( ( $table eq 'mangle' ) && ! $targetref->{mangle} ); - fatal_error "Unknown Interface ($interface)" unless known_interface( $interface ); + fatal_error "Unknown Interface ($interface)" unless known_interface( $interface, 0 ); fatal_error "No hosts on $interface have the maclist option specified" unless $maclist_interfaces{$interface}; my $chainref = $chain_table{$table}{( $ttl ? macrecent_target $interface : mac_chain $interface )}; diff --git a/Shorewall/Perl/Shorewall/Zones.pm b/Shorewall/Perl/Shorewall/Zones.pm index 83e7e1e12..14e66b85b 100644 --- a/Shorewall/Perl/Shorewall/Zones.pm +++ b/Shorewall/Perl/Shorewall/Zones.pm @@ -83,7 +83,7 @@ our @EXPORT = qw( NOTHING ); our @EXPORT_OK = qw( initialize ); -our $VERSION = '4.4_12'; +our $VERSION = '4.4_13'; # # IPSEC Option types @@ -1152,11 +1152,12 @@ sub map_physical( $$ ) { # # Returns true if passed interface matches an entry in /etc/shorewall/interfaces # -# If the passed name matches a wildcard, an entry for the name is added in %interfaces to speed up validation of other references to that name. +# If the passed name matches a wildcard and 'cache' is true, an entry for the name is added in +# %interfaces. # -sub known_interface($) +sub known_interface($$) { - my $interface = $_[0]; + my ( $interface, $cache ) = @_; my $interfaceref = $interfaces{$interface}; return $interfaceref if $interfaceref; @@ -1165,18 +1166,19 @@ sub known_interface($) $interfaceref = $interfaces{$i}; my $root = $interfaceref->{root}; if ( $i ne $root && substr( $interface, 0, length $root ) eq $root ) { - # - # Cache this result for future reference. We set the 'name' to the name of the entry that appears in /etc/shorewall/interfaces and we do not set the root; - # my $physical = map_physical( $interface, $interfaceref ); + + my $copyref = { options => $interfaceref->{options}, + bridge => $interfaceref->{bridge} , + name => $i , + number => $interfaceref->{number} , + physical => $physical , + base => chain_base( $physical ) , + }; - return $interfaces{$interface} = { options => $interfaceref->{options}, - bridge => $interfaceref->{bridge} , - name => $i , - number => $interfaceref->{number} , - physical => $physical, - base => chain_base( $physical ), - }; + $interfaces{$interface} = $copyref if $cache; + + return $copyref; } } @@ -1228,7 +1230,7 @@ sub get_physical( $ ) { # sub physical_name( $ ) { my $device = shift; - my $devref = known_interface $device; + my $devref = known_interface( $device, 0 ); $devref ? $devref->{physical} : $device; } @@ -1287,24 +1289,31 @@ sub find_interfaces_by_option( $ ) { } # -# Returns reference to array of interfaces with the passed option +# Returns reference to array of interfaces with the passed option. Unlike the preceding function, this one: # -sub find_interfaces_by_option1( $ ) { - my $option = $_[0]; +# - All entries in %interfaces are searched. +# - The second argument is used to return an indication of the presents of wildcard interfaces +# +sub find_interfaces_by_option1( $\$ ) { + my ( $option, $wildref) = @_; my @ints = (); + my $wild = 0; - for my $interface ( keys %interfaces ) { + for my $interface ( sort { $interfaces{$a}->{number} <=> $interfaces{$b}->{number} } + keys %interfaces ) { my $interfaceref = $interfaces{$interface}; next unless defined $interfaceref->{physical}; - next if $interfaceref->{physical} =~ /\+/; my $optionsref = $interfaceref->{options}; + if ( $optionsref && defined $optionsref->{$option} ) { + $wild ||= ( $interfaceref->{physical} =~ /\+$/ ); push @ints , $interface } } + $$wildref = $wild; \@ints; } diff --git a/Shorewall/changelog.txt b/Shorewall/changelog.txt index 0e8c8298e..378c07ebc 100644 --- a/Shorewall/changelog.txt +++ b/Shorewall/changelog.txt @@ -8,6 +8,8 @@ Changes in Shorewall 4.4.13 4) Fix exclusion with CONTINUE/NONAT/ACCEPT+ +5) Re-implement optional interface handling. + Changes in Shorewall 4.4.12 1) Fix IPv6 shorecap program. diff --git a/Shorewall/releasenotes.txt b/Shorewall/releasenotes.txt index 5556502e7..c5e38add6 100644 --- a/Shorewall/releasenotes.txt +++ b/Shorewall/releasenotes.txt @@ -42,6 +42,58 @@ VI. PROBLEMS CORRECTED AND NEW FEATURES IN PRIOR RELEASES who utilize a capabilities file should re-generate the file using this release. +7) Interface handling has been extensively modified in this release + to correct a number of problems with the earlier + implementation. Among those problems: + + - Invalid shell variable names could be generated in the firewall + script. The generated firewall script uses shell variables to + track the availability of optional and required interfaces and + to record detected gateways, detected addresses, etc. + + - The same shell variable name could be generated by two different + interface names. + + - Entries in the interfaces file with a wildcard physical name + (physical name ends with "+") and with the 'optional' option were + handled strangely. + + o If there were references to specific interfaces that matched + the wildcard, those entries were handled as if they had been + defined as optional in the interfaces file. + + o If there were no references matching the wildcard, then the + 'optional' option was effectively ignored. + + The new implementation: + + - Insures valid shell variable names. + + - Insures that shell variable names are unique. + + - Handles interface names appearing in the INTERFACE column of the + providers file as a special case for 'optional'. If the name + matches a wildcard entry in the interfaces file then the + usability of the specific interface is tracked individually. + + - Handles the availabilty of other interfaces matching a wildcard + as a group; if there is one useable interface in the group then + the wildcard itself is considered usable. + + The following example illustrates this use case: + + /etc/shorewall/interfaces + + net ppp+ - optional + + /etc/shorewall/shorewall.conf + + REQUIRE_INTERFACE=Yes + + If there is any usable PPP interface then the firewall will be + allowed to start. Previously, the firewall would never be allowed + to start. + ---------------------------------------------------------------------------- I I. K N O W N P R O B L E M S R E M A I N I N G ----------------------------------------------------------------------------