diff --git a/Shorewall-perl/Shorewall/Chains.pm b/Shorewall-perl/Shorewall/Chains.pm index d48486ff1..575bd8e4c 100644 --- a/Shorewall-perl/Shorewall/Chains.pm +++ b/Shorewall-perl/Shorewall/Chains.pm @@ -98,6 +98,8 @@ our @EXPORT = qw( STANDARD do_ratelimit do_user do_tos + match_source_dev + match_dest_dev iprange_match match_source_net match_dest_net @@ -1029,6 +1031,32 @@ sub do_tos( $ ) { $tos ne '-' ? "-m tos --tos $tos " : ''; } +# +# Match Source Interface +# +sub match_source_dev( $ ) { + my $interface = shift; + my $interfaceref = $interfaces{$interface}; + if ( $interfaceref->{options}{port} ) { + "-i $interfaceref->{bridge} -m physdev --physdev-in $interface "; + } else { + "-i $interface "; + } +} + +# +# Match Dest device +# +sub match_dest_dev( $ ) { + my $interface = shift; + my $interfaceref = $interfaces{$interface}; + if ( $interfaceref->{options}{port} ) { + "-o $interfaceref->{bridge} -m physdev --physdev-out $interface "; + } else { + "-o $interface "; + } +} + # # Avoid generating a second '-m iprange' in a single rule. # @@ -1414,6 +1442,8 @@ sub expand_rule( $$$$$$$$$$ ) # # An interface in the SOURCE column of a masq file # + fatal_error "Bridge ports may not appear in the SOURCE column of this file" if port_to_bridge( $iiface ); + my $networks = get_interface_nets ( $iiface ); add_command( $chainref , join( '', 'for source in ', $networks, '; do' ) ); @@ -1424,9 +1454,8 @@ sub expand_rule( $$$$$$$$$$ ) # $chainref->{loopcount}++; } else { - fatal_error "Source Interface ($iiface) not allowed when the source zone is $firewall_zone" - if $restriction & OUTPUT_RESTRICT; - $rule .= "-i $iiface "; + fatal_error "Source Interface ($iiface) not allowed when the source zone is $firewall_zone" if $restriction & OUTPUT_RESTRICT; + $rule .= match_source_dev( $iiface ); } } @@ -1480,13 +1509,19 @@ sub expand_rule( $$$$$$$$$$ ) # # ADDRESS 'detect' in the masq file. # + fatal_error "Bridge port ( $diface) not allowed" if port_to_bridge( $diface ); add_command( $chainref , 'for dest in ' . get_interface_addresses( $diface) . '; do' ); $rule .= '-d $dest'; $chainref->{loopcount}++; } else { - fatal_error "Destination Interface ($diface) not allowed when the destination zone is $firewall_zone" - if $restriction & INPUT_RESTRICT; - $rule .= "-o $diface "; + fatal_error "Destination Interface ($diface) not allowed when the destination zone is $firewall_zone" if $restriction & INPUT_RESTRICT; + + if ( $iiface ) { + my $bridge = port_to_bridge( $diface ); + fatal_error "Source interface ( $iiface) is not a port on the same bridge as the destination interface ( $diface )" if $bridge && $bridge ne source_port_to_bridge( $iiface ); + } + + $rule .= match_dest_dev( $diface ); } } diff --git a/Shorewall-perl/Shorewall/Interfaces.pm b/Shorewall-perl/Shorewall/Interfaces.pm index 55618d56e..8fb576897 100644 --- a/Shorewall-perl/Shorewall/Interfaces.pm +++ b/Shorewall-perl/Shorewall/Interfaces.pm @@ -38,6 +38,7 @@ our @ISA = qw(Exporter); our @EXPORT = qw( add_group_to_zone validate_interfaces_file known_interface + port_to_bridge interface_is_optional find_interfaces_by_option get_interface_option @@ -182,13 +183,15 @@ sub validate_interfaces_file() my $first_entry = 1; + my @ifaces; + while ( read_a_line ) { if ( $first_entry ) { progress_message2 "$doing $fn..."; $first_entry = 0; } - + my ($zone, $interface, $networks, $options ) = split_line 2, 4, 'interfaces file'; my $zoneref; my $bridge = ''; @@ -209,23 +212,32 @@ sub validate_interfaces_file() fatal_error "Invalid INTERFACE" if defined $extra || ! $interface; - fatal_error "Duplicate Interface ($interface)" if $interfaces{$interface}; - - fatal_error "Invalid Interface Name: $interface" if $interface eq '+'; + fatal_error "Invalid Interface Name ( $interface )" if $interface eq '+'; if ( defined $port ) { require_capability( 'PHYSDEV_MATCH', 'Bridge Ports', ''); require_capability( 'KLUDGEFREE', 'Bridge Ports', ''); + fatal_error "Duplicate Interface ( $port )" if $interfaces{$port}; fatal_error "$interface is not a defined bridge" unless $interfaces{$interface} && $interfaces{$interface}{options}{bridge}; - fatal_error "Invalid Bridge Port Name ($port)" unless $port =~ /^([\w.@%-]+\+?)$/; + fatal_error "Invalid Interface Name ( $interface:$port )" unless $port =~ /^[\w.@%-]+\+?$/; fatal_error "Bridge Ports may only be associated with 'bport' zones" if $zone && $zoneref->{type} ne 'bport4'; + + if ( $zone ) { + if ( $zoneref->{bridge} ) { + fatal_error "Bridge Port zones may only be associated with a single bridge" if $zoneref->{bridge} ne $interface; + } else { + $zoneref->{bridge} = $interface; + } + } + $interfaces{$port}{bridge} = $bridge = $interface; $interface = $port; } else { + fatal_error "Duplicate Interface ( $interface )" if $interfaces{$interface}; fatal_error "Zones of type 'bport' may only be associated with bridge ports" if $zone && $zoneref->{type} eq 'bport4'; $interfaces{$interface}{bridge} = $interface; } - + my $wildcard = 0; if ( $interface =~ /\+$/ ) { @@ -234,7 +246,7 @@ sub validate_interfaces_file() } else { $interfaces{$interface}{root} = $interface; } - + unless ( $networks eq '' || $networks eq 'detect' ) { for my $address ( split /,/, $networks ) { @@ -300,7 +312,7 @@ sub validate_interfaces_file() $interfaces{$interface}{options} = $optionsref = \%options; - push @interfaces, $interface; + push @ifaces, $interface; my @networks; @@ -316,10 +328,27 @@ sub validate_interfaces_file() add_group_to_zone( $zone, $zoneref->{type}, $interface, \@networks, $optionsref ) if $zone && @networks; $interfaces{$interface}{zone} = $zone; #Must follow the call to add_group_to_zone() - + progress_message " Interface \"$line\" Validated"; } + + # + # We now assemble the @interfaces array such that bridge ports immediately precede their associated bridge + # + for my $interface ( @ifaces ) { + my $interfaceref = $interfaces{$interface}; + + next if $interfaceref->{options}{port}; + + if ( $interfaceref->{options}{bridge} ) { + for my $port ( grep $interfaces{$_}{options}{port} && $interfaces{$_}{bridge} eq $interface, @ifaces ) { + push @interfaces, $port; + } + } + + push @interfaces, $interface; + } } # @@ -349,6 +378,24 @@ sub known_interface($) 0; } +# +# Return the bridge associated with the passed interface. If the interface is not a bridge port, +# return '' +# +sub port_to_bridge( $ ) { + my $portref = $interfaces{$_[0]}; + return $portref && $portref->{options}{port} ? $portref->{bridge} : ''; +} + +# +# Return the bridge associated with the passed interface. If the interface is not a bridge port, return +# the name of the interface itself. +# +sub source_port_to_bridge( $ ) { + my $portref = $interfaces{$_[0]}; + return $portref ? $portref->{bridge} : ''; +} + # # Return the 'optional' setting of the passed interface # diff --git a/Shorewall-perl/Shorewall/Rules.pm b/Shorewall-perl/Shorewall/Rules.pm index 87c42b8ba..adf786f45 100644 --- a/Shorewall-perl/Shorewall/Rules.pm +++ b/Shorewall-perl/Shorewall/Rules.pm @@ -1374,32 +1374,6 @@ sub generate_matrix() { add_rule $chainref, "-j $name"; } - # - # Match Source Interface - # - sub match_source_dev( $ ) { - my $interface = shift; - my $interfaceref = $interfaces{$interface}; - if ( $interfaceref->{options}{port} ) { - "-i $interfaceref->{bridge} -m physdev --physdev-in $interface "; - } else { - "-i $interface "; - } - } - - # - # Match Dest device - # - sub match_dest_dev( $ ) { - my $interface = shift; - my $interfaceref = $interfaces{$interface}; - if ( $interfaceref->{options}{port} ) { - "-o $interfaceref->{bridge} -m physdev --physdev-out $interface "; - } else { - "-o $interface "; - } - } - # # Insert the passed exclusions at the front of the passed chain. # @@ -1611,6 +1585,10 @@ sub generate_matrix() { next if ( %{ $zoneref->{interfaces} } < 2 ) && ! ( $zoneref->{options}{in_out}{routeback} || @$exclusions ); } + if ( $zone1ref->{type} eq 'bport4' ) { + next unless $zoneref->{bridge} eq $zone1ref->{bridge}; + } + if ( $chain =~ /2all$/ ) { if ( $chain ne $last_chain ) { $last_chain = $chain; @@ -1665,6 +1643,10 @@ sub generate_matrix() { next ZONE1 if ( $num_ifaces = %{$zoneref->{interfaces}} ) < 2 && ! ( $zoneref->{options}{in_out}{routeback} || @$exclusions ); } + if ( $zone1ref->{type} eq 'bport4' ) { + next ZONE1 unless $zoneref->{bridge} eq $zone1ref->{bridge}; + } + my $chainref = $filter_table->{$chain}; my $exclusions1 = $zone1ref->{exclusions}; diff --git a/Shorewall-perl/Shorewall/Zones.pm b/Shorewall-perl/Shorewall/Zones.pm index 64f06a101..3257cc1e6 100644 --- a/Shorewall-perl/Shorewall/Zones.pm +++ b/Shorewall-perl/Shorewall/Zones.pm @@ -234,12 +234,6 @@ sub determine_zones() $zoneref->{type} = 'ipsec4'; } elsif ( $type =~ /^bport4?$/i ) { fatal_error "Bridge Port zones must have a single parent zone" unless @parents == 1; - - for my $p ( @parents ) { - my $interfaceref = $interfaces{$1}; - fatal_error "Parent Zone $p is not associated with device $1" unless $interfaceref && $interfaceref->{zone} eq $zone; - } - $zoneref->{type} = 'bport4'; } elsif ( $type eq 'firewall' ) {