From a8cb589333ed186bf98b47ae9a6ada2b4e81c78d Mon Sep 17 00:00:00 2001 From: teastep Date: Wed, 6 Jun 2007 00:47:27 +0000 Subject: [PATCH] First installment of New Bridge Code git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@6468 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb --- Shorewall-perl/Shorewall/Hosts.pm | 9 ++ Shorewall-perl/Shorewall/Interfaces.pm | 33 +++++- Shorewall-perl/Shorewall/Rules.pm | 140 ++++++++++++++++--------- Shorewall-perl/Shorewall/Zones.pm | 12 +++ 4 files changed, 140 insertions(+), 54 deletions(-) diff --git a/Shorewall-perl/Shorewall/Hosts.pm b/Shorewall-perl/Shorewall/Hosts.pm index c8ea5e2c3..d3a06b35e 100644 --- a/Shorewall-perl/Shorewall/Hosts.pm +++ b/Shorewall-perl/Shorewall/Hosts.pm @@ -84,6 +84,15 @@ sub validate_hosts_file() fatal_error "Invalid HOST(S) column contents: $hosts"; } + if ( $type eq 'bport4' ) { + if ( $zoneref->{bridge} eq '' ) { + fatal_error 'Bridge Port Zones may only be associated with bridge ports' unless $interfaces{$interface}{options}{port}; + $zoneref->{bridge} = $interfaces{$interface}{bridge}; + } elsif ( $zoneref->{bridge} ne $interfaces{$interface}{bridge} ) { + fatal_error "Interface $interface is not a port on bridge $zoneref->{bridge}"; + } + } + my $optionsref = {}; if ( $options ne '-' ) { diff --git a/Shorewall-perl/Shorewall/Interfaces.pm b/Shorewall-perl/Shorewall/Interfaces.pm index ea3b753f6..55618d56e 100644 --- a/Shorewall-perl/Shorewall/Interfaces.pm +++ b/Shorewall-perl/Shorewall/Interfaces.pm @@ -56,6 +56,7 @@ our @VERSION = 1.00; # ... # } # zone => +# bridge => # } # } # @@ -161,6 +162,7 @@ sub validate_interfaces_file() my %validoptions = (arp_filter => BINARY_IF_OPTION, arp_ignore => ENUM_IF_OPTION, blacklist => SIMPLE_IF_OPTION, + bridge => SIMPLE_IF_OPTION, detectnets => SIMPLE_IF_OPTION, dhcp => SIMPLE_IF_OPTION, maclist => SIMPLE_IF_OPTION, @@ -189,6 +191,7 @@ sub validate_interfaces_file() my ($zone, $interface, $networks, $options ) = split_line 2, 4, 'interfaces file'; my $zoneref; + my $bridge = ''; if ( $zone eq '-' ) { $zone = ''; @@ -202,9 +205,26 @@ sub validate_interfaces_file() $networks = '' if $networks eq '-'; $options = '' if $options eq '-'; + ( $interface, my ($port, $extra) ) = split /:/ , $interface, 3; + + fatal_error "Invalid INTERFACE" if defined $extra || ! $interface; + fatal_error "Duplicate Interface ($interface)" if $interfaces{$interface}; - fatal_error "Invalid Interface Name: $interface" if $interface =~ /:|^\+$/; + 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 "$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 "Bridge Ports may only be associated with 'bport' zones" if $zone && $zoneref->{type} ne 'bport4'; + $interfaces{$port}{bridge} = $bridge = $interface; + $interface = $port; + } else { + 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; @@ -229,6 +249,8 @@ sub validate_interfaces_file() my %options; if ( $options ) { + fatal_error "Bridge Ports may not have options" if defined $port; + for my $option (split ',', $options ) { next if $option eq '-'; @@ -266,7 +288,14 @@ sub validate_interfaces_file() } } - $zoneref->{options}{in_out}{routeback} = 1 if $options{routeback}; + $zoneref->{options}{in_out}{routeback} = 1 if $zoneref && $options{routeback}; + + if ( $options{bridge} ) { + require_capability( 'PHYSDEV_MATCH', 'The "bridge" option', 's'); + fatal_error "Bridges may not have wildcard names" if $wildcard; + } + } elsif ( defined $port ) { + $options{port} = 1; } $interfaces{$interface}{options} = $optionsref = \%options; diff --git a/Shorewall-perl/Shorewall/Rules.pm b/Shorewall-perl/Shorewall/Rules.pm index 5ba1cac5d..d72b1f13a 100644 --- a/Shorewall-perl/Shorewall/Rules.pm +++ b/Shorewall-perl/Shorewall/Rules.pm @@ -782,13 +782,13 @@ sub setup_mac_lists( $ ) { } } -sub process_rule1 ( $$$$$$$$$$ ); +sub process_rule1 ( $$$$$$$$$$$ ); # # Expand a macro rule from the rules file # -sub process_macro ( $$$$$$$$$$$$ ) { - my ($macrofile, $target, $param, $source, $dest, $proto, $ports, $sports, $origdest, $rate, $user, $mark ) = @_; +sub process_macro ( $$$$$$$$$$$$$ ) { + my ($macrofile, $target, $param, $source, $dest, $proto, $ports, $sports, $origdest, $rate, $user, $mark, $wildcard ) = @_; progress_message "..Expanding Macro $macrofile..."; @@ -847,7 +847,7 @@ sub process_macro ( $$$$$$$$$$$$ ) { $mrate = merge_macro_column $mrate, $rate; $muser = merge_macro_column $muser, $user; - process_rule1 $mtarget, $msource, $mdest, $mproto, $mports, $msports, $origdest, $mrate, $muser, $mark; + process_rule1 $mtarget, $msource, $mdest, $mproto, $mports, $msports, $origdest, $mrate, $muser, $mark, $wildcard; progress_message " Rule \"$line\" $done"; } @@ -864,8 +864,8 @@ my @param_stack; # # Once a rule has been completely resolved by macro expansion and wildcard (source and/or dest zone == 'all'), it is processed by this function. # -sub process_rule1 ( $$$$$$$$$$ ) { - my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark ) = @_; +sub process_rule1 ( $$$$$$$$$$$ ) { + my ( $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, $wildcard ) = @_; my ( $action, $loglevel) = split_action $target; my ( $basictarget, $param ) = split '/', $action; my $rule = ''; @@ -902,7 +902,8 @@ sub process_rule1 ( $$$$$$$$$$ ) { $origdest, $ratelimit, $user, - $mark ); + $mark, + $wildcard ); $macro_nest_level--; @@ -974,6 +975,15 @@ sub process_rule1 ( $$$$$$$$$$ ) { $restriction = INPUT_RESTRICT if $destzone eq $firewall_zone; } # + # Check for illegal bridge port rule + # + if ( $zones{$sourcezone}->{type} eq 'bport4' ) { + unless ( $zones{$sourcezone}{bridge} eq $zones{$destzone}{bridge} ) { + return 1 if $wildcard; + fatal_error "Rules with a DESTINATION Bridge Port zone must have a SOURCE zone on the same bridge"; + } + } + # # Take care of chain # my $chain = "${sourcezone}2${destzone}"; @@ -982,8 +992,13 @@ sub process_rule1 ( $$$$$$$$$$ ) { # Validate Policy # my $policy = $chainref->{policy}; + fatal_error "No policy defined from zone $sourcezone to zone $destzone" unless $policy; - fatal_error "Rules may not override a NONE policy" if $policy eq 'NONE'; + + if ( $policy eq 'NONE' ) { + return 1 if $wildcard; + fatal_error "Rules may not override a NONE policy"; + } # # For compatibility with older Shorewall versions # @@ -1211,17 +1226,15 @@ sub process_rule ( $$$$$$$$$$ ) { my $policychainref = $filter_table->{"${zone}2${zone1}"}{policychain}; fatal_error "No policy from zone $zone to zone $zone1" unless $policychainref; my $policy = $policychainref->{policy}; - unless ( $policy eq 'NONE' ) { - if ( $optimize > 0 ) { - my $loglevel = $policychainref->{loglevel}; - if ( $loglevel ne '' ) { - next if $target eq "${policy}:$loglevel}"; - } else { - next if $action eq $policy; - } + if ( $optimize > 0 ) { + my $loglevel = $policychainref->{loglevel}; + if ( $loglevel ne '' ) { + next if $target eq "${policy}:$loglevel}"; + } else { + next if $action eq $policy; } - process_rule1 $target, $zone, $zone1 , $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark; } + process_rule1 $target, $zone, $zone1 , $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, 1; } } } @@ -1232,17 +1245,15 @@ sub process_rule ( $$$$$$$$$$ ) { if ( $intrazone || ( $zone ne $destzone ) ) { fatal_error "No policy from zone $zone to zone $destzone" unless $policychainref; my $policy = $policychainref->{policy}; - unless ( $policy eq 'NONE' ) { - if ( $optimize > 0 ) { - my $loglevel = $policychainref->{loglevel}; - if ( $loglevel ne '') { - next if $target eq "${policy}:$loglevel}"; - } else { - next if $action eq $policy; - } + if ( $optimize > 0 ) { + my $loglevel = $policychainref->{loglevel}; + if ( $loglevel ne '') { + next if $target eq "${policy}:$loglevel}"; + } else { + next if $action eq $policy; } - process_rule1 $target, $zone, $dest , $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark; } + process_rule1 $target, $zone, $dest , $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, 1; } } } @@ -1254,21 +1265,19 @@ sub process_rule ( $$$$$$$$$$ ) { fatal_error "Unknown source zone ($sourcezone)" unless $zones{$sourcezone}; my $policychainref = $filter_table->{"${sourcezone}2${zone}"}{policychain}; my $policy = $policychainref->{policy}; - unless ( $policy eq 'NONE' ) { - if ( $optimize > 0 ) { - my $loglevel = $policychainref->{loglevel}; - if ( $loglevel ne '' ) { - next if $target eq "${policy}:$loglevel}"; - } else { - next if $action eq $policy; - } + if ( $optimize > 0 ) { + my $loglevel = $policychainref->{loglevel}; + if ( $loglevel ne '' ) { + next if $target eq "${policy}:$loglevel}"; + } else { + next if $action eq $policy; } } - process_rule1 $target, $source, $zone , $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark; + process_rule1 $target, $source, $zone , $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, 1; } } } else { - process_rule1 $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark; + process_rule1 $target, $source, $dest, $proto, $ports, $sports, $origdest, $ratelimit, $user, $mark, 0; } progress_message " Rule \"$thisline\" $done"; @@ -1365,6 +1374,32 @@ 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. # @@ -1375,7 +1410,7 @@ sub generate_matrix() { for my $host ( @{$exclusionsref} ) { my ( $interface, $net ) = split /:/, $host; - insert_rule $chainref , $num++, join( '', "-i $interface ", match_source_net( $net ), '-j RETURN' ); + insert_rule $chainref , $num++, join( '', match_source_dev $interface , match_source_net( $net ), '-j RETURN' ); } } @@ -1387,7 +1422,7 @@ sub generate_matrix() { for my $host ( @{$exclusionsref} ) { my ( $interface, $net ) = split /:/, $host; - add_rule $chainref , join( '', "-i $interface ", match_source_net( $net ), '-j RETURN' ); + add_rule $chainref , join( '', match_source_dev $interface, match_source_net( $net ), '-j RETURN' ); } } @@ -1409,12 +1444,12 @@ sub generate_matrix() { my %policy_exclusions; for my $interface ( @interfaces ) { - addnatjump 'POSTROUTING' , snat_chain( $interface ), "-o $interface "; + addnatjump 'POSTROUTING' , snat_chain( $interface ), match_dest_dev( $interface ); } if ( $config{DYNAMIC_ZONES} ) { for my $interface ( @interfaces ) { - addnatjump 'PREROUTING' , dynamic_in( $interface ), "-i $interface "; + addnatjump 'PREROUTING' , dynamic_in( $interface ), match_source_dev( $interface ); } } @@ -1422,8 +1457,8 @@ sub generate_matrix() { addnatjump 'POSTROUTING' , 'nat_out' , ''; for my $interface ( @interfaces ) { - addnatjump 'PREROUTING' , input_chain( $interface ) , "-i $interface "; - addnatjump 'POSTROUTING' , output_chain( $interface ) , "-o $interface "; + addnatjump 'PREROUTING' , input_chain( $interface ) , match_source_dev( $interface ); + addnatjump 'POSTROUTING' , output_chain( $interface ) , match_dest_dev( $interface ); } for my $zone ( grep $zones{$_}{options}{complex} , @zones ) { @@ -1440,9 +1475,10 @@ sub generate_matrix() { for my $host ( @$exclusions ) { my ( $interface, $net ) = split /:/, $host; - add_rule $frwd_ref , "-i $interface -s $net -j RETURN"; - add_rule $in_ref , "-i $interface -s $net -j RETURN"; - add_rule $out_ref , "-i $interface -s $net -j RETURN"; + my $rule = match_source_interface( $interface ) . "-s $net -j RETURN"; + add_rule $frwd_ref , $rule; + add_rule $in_ref , $rule; + add_rule $out_ref , $rule; } } @@ -1520,7 +1556,7 @@ sub generate_matrix() { my $source = match_source_net $net; - insertnatjump 'PREROUTING' , dnat_chain $zone, \$prerouting_rule, join( '', "-i $interface ", $source, $ipsec_in_match ); + insertnatjump 'PREROUTING' , dnat_chain $zone, \$prerouting_rule, join( '', match_source_dev( $interface), $source, $ipsec_in_match ); if ( $chain2 ) { if ( @$exclusions ) { @@ -1665,7 +1701,7 @@ sub generate_matrix() { if ( $zone ne $zone1 || $num_ifaces > 1 || $hostref->{options}{routeback} ) { my $ipsec_out_match = match_ipsec_out $zone1 , $hostref; for my $net ( @{$hostref->{hosts}} ) { - add_rule $frwd_ref, join( '', "-o $interface ", match_dest_net($net), $ipsec_out_match, "-j $chain" ); + add_rule $frwd_ref, join( '', match_dest_dev( $interface) , match_dest_net($net), $ipsec_out_match, "-j $chain" ); } } } @@ -1690,7 +1726,7 @@ sub generate_matrix() { # add_rule( $chain3ref , - join( '', "-o $interface1 ", match_source_net($net), match_dest_net($net1), $ipsec_out_match, "-j $chain" ) + join( '', match_dest_dev($interface), match_source_net($net), match_dest_net($net1), $ipsec_out_match, "-j $chain" ) ); } } @@ -1730,10 +1766,10 @@ sub generate_matrix() { # Now add the jumps to the interface chains from FORWARD, INPUT, OUTPUT and POSTROUTING # for my $interface ( @interfaces ) { - add_rule $filter_table->{FORWARD} , "-i $interface -j " . forward_chain $interface; - add_rule $filter_table->{INPUT} , "-i $interface -j " . input_chain $interface; - add_rule $filter_table->{OUTPUT} , "-o $interface -j " . output_chain $interface; - addnatjump 'POSTROUTING' , masq_chain( $interface ) , "-o $interface "; + add_rule $filter_table->{FORWARD} , match_source_dev( $interface ) . "-j " . forward_chain $interface; + add_rule $filter_table->{INPUT} , match_source_dev( $interface ) . "-j " . input_chain $interface; + add_rule $filter_table->{OUTPUT} , "-o $interface -j " . output_chain $interface unless $interfaces{$interface}{options}{port}; + addnatjump 'POSTROUTING' , masq_chain( $interface ) , match_dest_dev( $interface ); } my $chainref = $filter_table->{"${firewall_zone}2${firewall_zone}"}; diff --git a/Shorewall-perl/Shorewall/Zones.pm b/Shorewall-perl/Shorewall/Zones.pm index 7cd901727..64f06a101 100644 --- a/Shorewall-perl/Shorewall/Zones.pm +++ b/Shorewall-perl/Shorewall/Zones.pm @@ -73,6 +73,7 @@ use constant { NOTHING => 'NOTHING', # parents => [ ] Parents, Children and interfaces are listed by name # children => [ ] # interfaces => [ ] +# bridge => # hosts { } => [ { => { ipsec => 'ipsec'|'none' # options => { => # ... @@ -223,6 +224,7 @@ sub determine_zones() my $zoneref = $zones{$zone} = {}; $zoneref->{parents} = \@parents; $zoneref->{exclusions} = []; + $zoneref->{bridge} = ''; $type = "ipv4" unless $type; @@ -230,6 +232,16 @@ sub determine_zones() $zoneref->{type} = 'ipv4'; } elsif ( $type =~ /^ipsec4?$/i ) { $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' ) { fatal_error 'Firewall zone may not be nested' if @parents; fatal_error "Only one firewall zone may be defined: $zone" if $firewall_zone;