From 2a2a7530c238019b35aa4de418cfe19579449805 Mon Sep 17 00:00:00 2001
From: teastep <teastep@fbd18981-670d-0410-9b5c-8dc0c1a9a2bb>
Date: Wed, 30 Jan 2008 00:03:25 +0000
Subject: [PATCH] Add optimizations in basic chain handling

git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@8122 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb
---
 Shorewall-perl/Shorewall/Chains.pm | 116 +++++++++++++++++++++++++++++
 Shorewall-perl/Shorewall/Rules.pm  |  76 ++++++++++---------
 Shorewall-perl/Shorewall/Zones.pm  |   4 +
 3 files changed, 162 insertions(+), 34 deletions(-)

diff --git a/Shorewall-perl/Shorewall/Chains.pm b/Shorewall-perl/Shorewall/Chains.pm
index 6f56f418f..bec02d3f4 100644
--- a/Shorewall-perl/Shorewall/Chains.pm
+++ b/Shorewall-perl/Shorewall/Chains.pm
@@ -76,10 +76,16 @@ our %EXPORT_TAGS = (
 				       chain_base 
 				       forward_chain
 				       zone_forward_chain
+				       use_interface_forward_chain
+				       interface_forward_chain
 				       input_chain
 				       zone_input_chain
+				       use_interface_input_chain
+				       interface_input_chain
 				       output_chain
 				       zone_output_chain
+				       use_output_chain
+				       interface_output_chain
 				       masq_chain
 				       syn_flood_chain
 				       mac_chain
@@ -520,6 +526,32 @@ sub zone_forward_chain($) {
     chain_base($_[0]) . '_frwd';
 }
 
+#
+# Returns true if we're to use the interface's forward chain
+#
+sub use_interface_forward_chain($) {
+    my $interface = $_[0];
+    my $chainref = $filter_table->{forward_chain($interface)};
+    my $interfaceref = find_interface($interface);
+    #
+    # We must use the interfaces's chain if it is referenced (has rules in it) or if the interface is associated with multiple zone nets
+    #
+    $interfaceref->{nets} != 1 || $chainref->{referenced};
+}
+
+#
+# Returns a reference to the forward chain for an interface
+#
+sub interface_forward_chain($) {
+    my $interface = $_[0];
+    my $chainref = $filter_table->{forward_chain($interface)};
+    my $interfaceref = find_interface($interface);
+    #
+    # We must use the interfaces's chain if it is referenced (has rules in it) or if the interface is associated with multiple zone nets
+    #
+    $interfaceref->{nets} != 1 || $chainref->{referenced} ? $chainref : $filter_table->{zone_forward_chain $interfaceref->{zone}};
+}   
+
 #
 # Input Chain for an interface
 #
@@ -535,6 +567,48 @@ sub zone_input_chain($) {
     chain_base($_[0]) . '_input';
 }
 
+#
+# Returns true if we're to use the interface's input chain
+#
+sub use_interface_input_chain($) {
+    my $interface = $_[0];
+    my $chainref = $filter_table->{input_chain($interface)};
+    my $interfaceref = find_interface($interface);
+    #
+    # We must use the interfaces's chain if it is referenced (has rules in it) or if the interface is associated with multiple zone nets
+    #    
+    return 1 if $interfaceref->{nets} != 1 || $chainref->{referenced};
+    
+    my $chainref1 = $filter_table->{zone_input_chain $interfaceref->{zone}};
+
+    return 1 if $chainref1;
+
+    $chainref1 = $filter_table->{join( '' , $interfaceref->{zone} , '2' , firewall_zone )};
+
+    ! $chainref1->{referenced};
+}   
+
+#
+# Returns a reference to the input chain for the passed interface
+#
+sub interface_input_chain($) {
+    my $interface = $_[0];
+    my $chainref = $filter_table->{input_chain($interface)};
+    my $interfaceref = find_interface($interface);
+    #
+    # We must use the interfaces's chain if it is referenced (has rules in it) or if the interface is associated with multiple zone nets
+    #    
+    return $chainref if $interfaceref->{nets} != 1 || $chainref->{referenced};
+
+    my $chainref1 = $filter_table->{zone_input_chain $interfaceref->{zone}};
+
+    return $chainref1 if $chainref1;
+
+    $chainref1 = $filter_table->{join( '', $interfaceref->{zone} , '2', firewall_zone )};
+
+    $chainref1->{referenced} ? $chainref1 : $chainref;
+}   
+
 #
 # Output Chain for an interface
 #
@@ -551,6 +625,48 @@ sub zone_output_chain($) {
 }
 
 #
+# Returns true if we're to use the interface's output chain
+#
+sub use_output_chain($) {
+    my $interface = $_[0];
+    my $chainref = $filter_table->{output_chain($interface)};
+    my $interfaceref = find_interface($interface);
+    #
+    # We must use the interfaces's chain if it is referenced (has rules in it) or if the interface is associated with multiple zone nets
+    #    
+    return 1 if $interfaceref->{nets} != 1 || $chainref->{referenced};
+    
+    my $chainref1 = $filter_table->{zone_output_chain $interfaceref->{zone}};
+
+    return 1 if $chainref1;
+
+    $chainref1 = $filter_table->{join( '', firewall_zone , '2', $interfaceref->{zone} )};
+
+    ! $chainref1->{referenced};
+}
+
+#
+# Returns a reference to the output chain for a zone
+#
+sub interface_output_chain($) {
+    my $interface = $_[0];
+    my $chainref = $filter_table->{output_chain($interface)};
+    my $interfaceref = find_interface($interface);
+    #
+    # We must use the interfaces's chain if it is referenced (has rules in it) or if the interface is associated with multiple zone nets
+    #    
+    return $chainref if $interfaceref->{nets} != 1 || $chainref->{referenced};
+
+    my $chainref1 = $filter_table->{zone_output_chain $interfaceref->{zone}};
+
+    return $chainref1 if $chainref1;
+
+    $chainref1 = $filter_table->{join( '', firewall_zone , '2', $interfaceref->{zone} )};
+
+    $chainref1->{referenced} ? $chainref1 : $chainref;
+}   
+
+##
 # Masquerade Chain for an interface
 #
 sub masq_chain($)
diff --git a/Shorewall-perl/Shorewall/Rules.pm b/Shorewall-perl/Shorewall/Rules.pm
index 2269ab361..e2938efa8 100644
--- a/Shorewall-perl/Shorewall/Rules.pm
+++ b/Shorewall-perl/Shorewall/Rules.pm
@@ -503,8 +503,8 @@ sub add_common_rules() {
     add_rule_pair new_standard_chain( 'logreject' ), ' ' , 'reject' , $level ;
 
     for $interface ( all_interfaces ) {
-	new_standard_chain( $_ ) for first_chains( $interface );
-	new_standard_chain output_chain( $interface );
+	ensure_chain( 'filter', $_ ) for first_chains( $interface );
+	ensure_chain( 'filter',  output_chain( $interface ) );
     }
 
     run_user_exit1 'initdone';
@@ -1534,14 +1534,16 @@ sub generate_matrix() {
 	    }
 
 	    for my $interface ( keys %$source_ref ) {
-		my $arrayref = $source_ref->{$interface};
-		for my $hostref ( @{$arrayref} ) {
-		    my $ipsec_match = match_ipsec_in $zone , $hostref;
-		    for my $net ( @{$hostref->{hosts}} ) {
-			add_rule(
-				 $filter_table->{forward_chain $interface} ,
-				 join( '', match_source_net( $net ), $ipsec_match, "-j $frwd_ref->{name}" )
-				);
+		if ( use_interface_forward_chain( $interface ) ) {
+		    my $arrayref = $source_ref->{$interface};
+		    for my $hostref ( @{$arrayref} ) {
+			my $ipsec_match = match_ipsec_in $zone , $hostref;
+			for my $net ( @{$hostref->{hosts}} ) {
+			    add_rule(
+				     $filter_table->{forward_chain $interface} ,
+				     join( '', match_source_net( $net ), $ipsec_match, "-j $frwd_ref->{name}" )
+				    );
+			}
 		    }
 		}
 	    }
@@ -1621,20 +1623,22 @@ sub generate_matrix() {
 
 			if ( $chain1 ) {
 			    my $nextchain;
-			    my $outputref = $filter_table->{output_chain $interface};
+			    my $outputref = interface_output_chain $interface;
 
-			    if ( @$exclusions ) {
-				my $output = zone_output_chain $zone;
-				add_rule $outputref , join( '', $dest, $ipsec_out_match, "-j $output" );
-				add_rule $filter_table->{$output} , "-j $chain1";
-				$nextchain = $output;
-			    } else {
-				add_rule $outputref , join( '', $dest, $ipsec_out_match, "-j $chain1" );
-				$nextchain = $chain1;
+			    if ( use_output_chain $interface ) {
+				if ( @$exclusions ) {
+				    my $output = zone_output_chain $zone;
+				    add_rule $outputref , join( '', $dest, $ipsec_out_match, "-j $output" );
+				    add_rule $filter_table->{$output} , "-j $chain1";
+				    $nextchain = $output;
+				} else {
+				    add_rule $outputref , join( '', $dest, $ipsec_out_match, "-j $chain1" );
+				    $nextchain = $chain1;
+				}
+
+				add_rule( $outputref , join('', '-d 255.255.255.255 ' , $ipsec_out_match, "-j $nextchain" ) )
+				    if $hostref->{options}{broadcast};
 			    }
-
-			    add_rule( $outputref , join('', match_source_net $net, '-d 255.255.255.255 ' . $ipsec_out_match, "-j $nextchain" ) )
-				if $hostref->{options}{broadcast};
 			}
 
 			next if $hostref->{options}{destonly}; 
@@ -1654,18 +1658,22 @@ sub generate_matrix() {
 			#
 			add_rule $preroutingref, join( '', match_source_dev( $interface), $source, $ipsec_in_match, '-j RETURN' ) if $nested;
 
-			if ( $chain2 ) {
-			    if ( @$exclusions ) {
-				my $input = zone_input_chain $zone;
-				add_rule $filter_table->{input_chain $interface}, join( '', $source, $ipsec_in_match, "-j $input" );
-				add_rule $filter_table->{ $input } , "-j $chain2";
-			    } else {
-				add_rule $filter_table->{input_chain $interface}, join( '', $source, $ipsec_in_match, "-j $chain2" );
+			if ( use_interface_input_chain $interface ) {
+			    if ( $chain2 ) {
+				if ( @$exclusions ) {
+				    my $input = zone_input_chain $zone;
+				    add_rule $filter_table->{input_chain $interface}, join( '', $source, $ipsec_in_match, "-j $input" );
+				    add_rule $filter_table->{ $input } , "-j $chain2";
+				} else {
+				    add_rule $filter_table->{input_chain $interface}, join( '', $source, $ipsec_in_match, "-j $chain2" );
+				}
 			    }
 			}
 
-			add_rule $filter_table->{forward_chain $interface} , join( '', $source, $ipsec_in_match. "-j $frwd_ref->{name}" )
-			    if $hostref->{ipsec} ne 'ipsec';
+			if ( use_interface_forward_chain $interface ) {
+			    add_rule $filter_table->{forward_chain $interface} , join( '', $source, $ipsec_in_match. "-j $frwd_ref->{name}" )
+				if $hostref->{ipsec} ne 'ipsec';
+			}
 		    }
 		}
 	    }
@@ -1831,9 +1839,9 @@ 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} , 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 get_interface_option( $interface, 'port' );
+	add_rule $filter_table->{FORWARD} , match_source_dev( $interface ) . "-j " . interface_forward_chain($interface)->{name};
+	add_rule $filter_table->{INPUT}   , match_source_dev( $interface ) . "-j " . interface_input_chain($interface)->{name};
+	add_rule $filter_table->{OUTPUT}  , "-o $interface -j " . interface_output_chain($interface)->{name} unless get_interface_option( $interface, 'port' );
 	addnatjump 'POSTROUTING' , masq_chain( $interface ) , match_dest_dev( $interface );
     }
 
diff --git a/Shorewall-perl/Shorewall/Zones.pm b/Shorewall-perl/Shorewall/Zones.pm
index b7f178ae3..27daa8f0a 100644
--- a/Shorewall-perl/Shorewall/Zones.pm
+++ b/Shorewall-perl/Shorewall/Zones.pm
@@ -125,6 +125,7 @@ our %reservedName = ( all => 1,
 #                                                      ...
 #                                                    }
 #                                     zone        => <zone name>
+#                                     nets        => <number of nets in interface/hosts records referring to this interface>
 #                                     bridge      => <bridge>
 #                                     broadcasts  => 'none', 'detect' or [ <addr1>, <addr2>, ... ]
 #                                   }
@@ -461,6 +462,8 @@ sub add_group_to_zone($$$$$)
     $ifacezone = '' unless defined $ifacezone;
 
     for my $host ( @$networks ) {
+	$interfaces{$interface}{nets}++;
+
 	fatal_error "Invalid Host List" unless defined $host and $host ne '';
 
 	if ( substr( $host, 0, 1 ) eq '!' ) {
@@ -636,6 +639,7 @@ sub validate_interfaces_file( $ )
 	}
 
 	$interfaces{$interface}{name} = $interface;
+	$interfaces{$interface}{nets} = 0;
 	
 	my $wildcard = 0;