Re-implement optional interface handling

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2010-08-29 12:32:44 -07:00
parent d94f2cc86d
commit 57c54af6ed
7 changed files with 174 additions and 64 deletions

View File

@ -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 ) {
#

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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