diff --git a/Shorewall/Perl/Shorewall/Accounting.pm b/Shorewall/Perl/Shorewall/Accounting.pm index 956a15878..d2868e3ff 100644 --- a/Shorewall/Perl/Shorewall/Accounting.pm +++ b/Shorewall/Perl/Shorewall/Accounting.pm @@ -3,7 +3,7 @@ # # This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt] # -# (c) 2007,2008 - Tom Eastep (teastep@shorewall.net) +# (c) 2007,2008,2009,2010 - Tom Eastep (teastep@shorewall.net) # # Complete documentation is available at http://shorewall.net # @@ -35,7 +35,7 @@ use strict; our @ISA = qw(Exporter); our @EXPORT = qw( setup_accounting ); our @EXPORT_OK = qw( ); -our $VERSION = '4.4_6'; +our $VERSION = '4.4.7'; # # Called by the compiler to [re-]initialize this module's state @@ -185,17 +185,17 @@ sub setup_accounting() { if ( have_bridges ) { if ( $filter_table->{accounting} ) { for my $chain ( qw/INPUT FORWARD/ ) { - insert_rule1 $filter_table->{$chain}, 0, '-j accounting'; + add_jump( $filter_table->{$chain}, 'accounting', 0, '', 0, 0 ); } } if ( $filter_table->{accountout} ) { - insert_rule1 $filter_table->{OUTPUT}, 0, '-j accountout'; + add_jump( $filter_table->{OUTPUT}, 'accountout', 0, '', 0, 0 ); } } else { if ( $filter_table->{accounting} ) { for my $chain ( qw/INPUT FORWARD OUTPUT/ ) { - insert_rule1 $filter_table->{$chain}, 0, '-j accounting'; + add_jump( $filter_table->{$chain}, 'accounting', 0, '', 0, 0 ); } } } diff --git a/Shorewall/Perl/Shorewall/Actions.pm b/Shorewall/Perl/Shorewall/Actions.pm index b5a0d428e..184988ec4 100644 --- a/Shorewall/Perl/Shorewall/Actions.pm +++ b/Shorewall/Perl/Shorewall/Actions.pm @@ -57,7 +57,7 @@ our @EXPORT = qw( merge_levels $macro_commands ); our @EXPORT_OK = qw( initialize ); -our $VERSION = '4.4_6'; +our $VERSION = '4.4_7'; # # Used Actions. Each action that is actually used has an entry with value 1. @@ -305,10 +305,10 @@ sub map_old_actions( $ ) { # Create and record a log action chain -- Log action chains have names # that are formed from the action name by prepending a "%" and appending # a 1- or 2-digit sequence number. In the functions that follow, -# the CHAIN, LEVEL and TAG variable serves as arguments to the user's +# the $chain, $level and $tag variable serves as arguments to the user's # exit. We call the exit corresponding to the name of the action but we -# set CHAIN to the name of the iptables chain where rules are to be added. -# Similarly, LEVEL and TAG contain the log level and log tag respectively. +# set $chain to the name of the iptables chain where rules are to be added. +# Similarly, $level and $tag contain the log level and log tag respectively. # # The maximum length of a chain name is 30 characters -- since the log # action chain name is 2-3 characters longer than the base chain name, @@ -341,6 +341,8 @@ sub createlogactionchain( $$ ) { unless ( $targets{$action} & BUILTIN ) { + dont_optimize $chainref; + my $file = find_file $chain; if ( -f $file ) { @@ -367,6 +369,8 @@ sub createsimpleactionchain( $ ) { unless ( $targets{$action} & BUILTIN ) { + dont_optimize $chainref; + my $file = find_file $action; if ( -f $file ) { @@ -384,7 +388,7 @@ sub createsimpleactionchain( $ ) { } # -# Create an action chain and run it's associated user exit +# Create an action chain and run its associated user exit # sub createactionchain( $ ) { my ( $action , $level ) = split_action $_[0]; @@ -574,7 +578,7 @@ sub process_actions2 () { for my $target (keys %usedactions) { my ($action, $level) = split_action $target; my $actionref = $actions{$action}; - fatal_error "Null Action Reference in process_actions2" unless $actionref; + assert( $actionref ); for my $action1 ( keys %{$actionref->{requires}} ) { my $action2 = merge_levels $target, $action1; unless ( $usedactions{ $action2 } ) { @@ -834,15 +838,15 @@ sub allowBcast( $$$ ) { sub dropNotSyn ( $$$ ) { my ($chainref, $level, $tag) = @_; - log_rule_limit $level, $chainref, 'dropNotSyn' , 'DROP', '', $tag, 'add', '-p tcp ! --syn ' if $level ne ''; - add_rule $chainref , '-p tcp ! --syn -j DROP'; + log_rule_limit $level, $chainref, 'dropNotSyn' , 'DROP', '', $tag, 'add', '-p 6 ! --syn ' if $level ne ''; + add_rule $chainref , '-p 6 ! --syn -j DROP'; } sub rejNotSyn ( $$$ ) { my ($chainref, $level, $tag) = @_; - log_rule_limit $level, $chainref, 'rejNotSyn' , 'REJECT', '', $tag, 'add', '-p tcp ! --syn ' if $level ne ''; - add_rule $chainref , '-p tcp ! --syn -j REJECT --reject-with tcp-reset'; + log_rule_limit $level, $chainref, 'rejNotSyn' , 'REJECT', '', $tag, 'add', '-p 6 ! --syn ' if $level ne ''; + add_rule $chainref , '-p 6 ! --syn -j REJECT --reject-with tcp-reset'; } sub dropInvalid ( $$$ ) { @@ -860,18 +864,19 @@ sub allowInvalid ( $$$ ) { } sub forwardUPnP ( $$$ ) { + dont_optimize 'forwardUPnP'; } sub allowinUPnP ( $$$ ) { my ($chainref, $level, $tag) = @_; if ( $level ne '' ) { - log_rule_limit $level, $chainref, 'allowinUPnP' , 'ACCEPT', '', $tag, 'add', '-p udp --dport 1900 '; - log_rule_limit $level, $chainref, 'allowinUPnP' , 'ACCEPT', '', $tag, 'add', '-p tcp --dport 49152 '; + log_rule_limit $level, $chainref, 'allowinUPnP' , 'ACCEPT', '', $tag, 'add', '-p 17 --dport 1900 '; + log_rule_limit $level, $chainref, 'allowinUPnP' , 'ACCEPT', '', $tag, 'add', '-p 6 --dport 49152 '; } - add_rule $chainref, '-p udp --dport 1900 -j ACCEPT'; - add_rule $chainref, '-p tcp --dport 49152 -j ACCEPT'; + add_rule $chainref, '-p 17 --dport 1900 -j ACCEPT'; + add_rule $chainref, '-p 6 --dport 49152 -j ACCEPT'; } sub Limit( $$$ ) { @@ -897,7 +902,7 @@ sub Limit( $$$ ) { my $xchainref = new_chain 'filter' , "$chainref->{name}%"; log_rule_limit $level, $xchainref, $tag[0], 'DROP', '', '', 'add', ''; add_rule $xchainref, '-j DROP'; - add_rule $chainref, "-m recent --name $set --update --seconds $tag[2] --hitcount $count -j $xchainref->{name}"; + add_jump $chainref, $xchainref, 0, "-m recent --name $set --update --seconds $tag[2] --hitcount $count "; } else { add_rule $chainref, "-m recent --update --name $set --seconds $tag[2] --hitcount $count -j DROP"; } diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index cb6f24106..36f38f05f 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -3,7 +3,7 @@ # # This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt] # -# (c) 2007,2008,2009 - Tom Eastep (teastep@shorewall.net) +# (c) 2007,2008,2009,2010 - Tom Eastep (teastep@shorewall.net) # # Complete documentation is available at http://shorewall.net # @@ -42,6 +42,9 @@ our @EXPORT = qw( new_manual_chain ensure_manual_chain log_rule_limit + dont_optimize + dont_delete + dont_move %chain_table $raw_table @@ -113,6 +116,8 @@ our %EXPORT_TAGS = ( new_nat_chain ensure_filter_chain finish_section + optimize_chain + optimize_ruleset setup_zone_mss newexclusionchain newnonatchain @@ -168,7 +173,7 @@ our %EXPORT_TAGS = ( Exporter::export_ok_tags('internal'); -our $VERSION = '4.4_6'; +our $VERSION = '4.4_7'; # # Chain Table @@ -181,6 +186,9 @@ our $VERSION = '4.4_6'; # builtin => undef|1 -- If 1, one of Netfilter's built-in chains. # manual => undef|1 -- If 1, a manual chain. # accounting => undef|1 -- If 1, an accounting chain +# dont_optimize=> undef|1 -- Don't optimize away if this chain is 'short' +# dont_delete => undef|1 -- Don't delete if this chain is not referenced +# dont_move => undef|1 -- Don't copy the rules of this chain somewhere else # log => # policy => # policychain => -- self-reference if this is a policy chain @@ -195,6 +203,8 @@ our $VERSION = '4.4_6'; # # ... # ] +# logchains => { = , ... } +# references => { => , => , ... } # } , # => ... # } @@ -362,7 +372,9 @@ sub initialize( $ ) { $idiotcount = 0; $idiotcount1 = 0; $warningcount = 0; - + # + # The chain table is initialized via a call to initialize_chain_table() after the configuration and capabilities have been determined. + # } # @@ -541,40 +553,14 @@ sub add_rule($$;$) } # -# Add a jump from the chain represented by the reference in the first argument to -# the target in the second argument. The third argument determines if a GOTO may be -# used rather than a jump. The optional fourth argument specifies any matches to be -# included in the rule and must end with a space character if it is non-null. The -# optional 5th argument causes long port lists to be split. +# Make the first chain a referent of the second # +sub add_reference ( $$ ) { + my ( $fromref, $to ) = @_; -sub add_jump( $$$;$$ ) { - my ( $fromref, $to, $goto_ok, $predicate, $expandports ) = @_; + my $toref = reftype $to ? $to : $chain_table{$fromref->{table}}{$to}; - $predicate |= ''; - - my $toref; - # - # The second argument may be a scalar (chain name or builtin target) or a chain reference - # - if ( reftype $to ) { - $toref = $to; - $to = $toref->{name}; - } else { - # - # Ensure that we have the chain unless it is a builtin like 'ACCEPT' - # - $toref = ensure_chain( $fromref->{table} , $to ) unless $builtin_target{ $to }; - } - - # - # If the destination is a chain, mark it referenced - # - $toref->{referenced} = 1 if $toref; - - my $param = $goto_ok && $toref && $capabilities{GOTO_TARGET} ? 'g' : 'j'; - - add_rule ($fromref, join( '', $predicate, "-$param $to" ), $expandports || 0 ); + $toref->{references}{$fromref->{name}}++; } # @@ -586,7 +572,7 @@ sub purge_jump ( $$ ) { my $to = $toref->{name}; for ( @{$fromref->{rules}} ) { - $_ = undef if / -[gj] ${to}\b/; + $_ = undef if defined && / -[gj] ${to}\b/; } $toref->{referenced} = 0 unless @{$toref->{rules}}; @@ -621,6 +607,49 @@ sub insert_rule($$$) { insert_rule1( $chainref, $number - 1, $rule ); } +# +# Add a jump from the chain represented by the reference in the first argument to +# the target in the second argument. The third argument determines if a GOTO may be +# used rather than a jump. The optional fourth argument specifies any matches to be +# included in the rule and must end with a space character if it is non-null. The +# optional 5th argument causes long port lists to be split. The optional 6th +# argument, if passed, gives the 0-relative index where the jump is to be inserted. +# + +sub add_jump( $$$;$$$ ) { + my ( $fromref, $to, $goto_ok, $predicate, $expandports, $index ) = @_; + + $predicate |= ''; + + my $toref; + # + # The second argument may be a scalar (chain name or builtin target) or a chain reference + # + if ( reftype $to ) { + $toref = $to; + $to = $toref->{name}; + } else { + # + # Ensure that we have the chain unless it is a builtin like 'ACCEPT' + # + $toref = ensure_chain( $fromref->{table} , $to ) unless $builtin_target{ $to }; + } + + # + # If the destination is a chain, mark it referenced + # + $toref->{referenced} = 1, add_reference $fromref, $toref if $toref; + + my $param = $goto_ok && $toref && $capabilities{GOTO_TARGET} ? 'g' : 'j'; + + if ( defined $index ) { + assert( ! $expandports ); + insert_rule1( $fromref, $index, join( '', $predicate, "-$param $to" )); + } else { + add_rule ($fromref, join( '', $predicate, "-$param $to" ), $expandports || 0 ); + } +} + # # Insert a tunnel rule into the passed chain. Tunnel rules are inserted sequentially # at the beginning of the 'NEW' section. @@ -643,6 +672,8 @@ sub move_rules( $$ ) { if ( $chain1->{referenced} ) { my $name = $chain1->{name}; + my $rules = $chain2->{rules}; + my $count = @{$chain1->{rules}}; # # We allow '+' in chain names and '+' is an RE meta-character. Escape it. # @@ -650,11 +681,52 @@ sub move_rules( $$ ) { ( s/\-([AI]) $name /-$1 $chain2->{name} / ) for @{$chain1->{rules}}; - splice @{$chain2->{rules}}, 0, 0, @{$chain1->{rules}}; + splice @{$rules}, 0, 0, @{$chain1->{rules}}; + # + # In a firewall->x policy chain, multiple DHCP ACCEPT rules can be moved to the head of the chain. + # This hack avoids that. + # + shift @{$rules} if @{$rules} > 1 && $rules->[0] eq $rules->[1]; $chain2->{referenced} = 1; $chain1->{referenced} = 0; $chain1->{rules} = []; + $count; + } +} + +# +# Replace the jump at the end of one chain (chain2) with the rules from another chain (chain1). +# + +sub copy_rules( $$ ) { + my ($chain1, $chain2 ) = @_; + + my $name1 = $chain1->{name}; + my $name2 = $chain2->{name}; + my @rules = @{$chain1->{rules}}; + my $rules = $chain2->{rules}; + my $count = @{$chain1->{rules}}; + # + # We allow '+' in chain names and '+' is an RE meta-character. Escape it. + # + $name1 =~ s/\+/\\+/; + + ( s/\-([AI]) $name1(\b)/-$1 ${name2}$2/ ) for @rules; + + pop @$rules; # Delete the jump to chain1 + + push @$rules, @rules; + # + # Add chain1's references to $chain2 + # + $chain2->{references}{$_} += $chain1->{references}{$_} for keys %{$chain1->{references}}; + + progress_message " $count rules from $chain1->{name} appended to $chain2->{name}"; + + unless ( --$chain1->{references}{$name2} ) { + delete $chain1->{references}{$name2}; + $chain1->{referenced} = 0, progress_message " Unreferenced chain $name1 deleted" unless keys %{$chain1->{references}}; } } @@ -894,12 +966,13 @@ sub new_chain($$) assert( $chain_table{$table} && ! ( $chain_table{$table}{$chain} || $builtin_target{ $chain } ) ); - $chain_table{$table}{$chain} = { name => $chain, - rules => [], - table => $table, - loglevel => '', - log => 1, - cmdlevel => 0 }; + $chain_table{$table}{$chain} = { name => $chain, + rules => [], + table => $table, + loglevel => '', + log => 1, + cmdlevel => 0, + references => {} }; } # @@ -916,6 +989,45 @@ sub ensure_chain($$) $ref ? $ref : new_chain $table, $chain; } +# +# Set the dont_optimize flag for a chain +# +sub dont_optimize( $ ) { + my $chain = shift; + + my $chainref = reftype $chain ? $chain : $filter_table->{$chain}; + + $chainref->{dont_optimize} = 1; + + $chainref; +} + +# +# Set the dont_optimize and dont_delete flags for a chain +# +sub dont_delete( $ ) { + my $chain = shift; + + my $chainref = reftype $chain ? $chain : $filter_table->{$chain}; + + $chainref->{dont_optimize} = $chainref->{dont_delete} = 1; + + $chainref; +} + +# +# Set the dont_move flag for a chain +# +sub dont_move( $ ) { + my $chain = shift; + + my $chainref = reftype $chain ? $chain : $filter_table->{$chain}; + + $chainref->{dont_move} = 1; + + $chainref; +} + sub finish_chain_section( $$ ); # @@ -957,6 +1069,7 @@ sub ensure_accounting_chain( $ ) $chainref = new_chain 'filter' , $chain; $chainref->{accounting} = 1; $chainref->{referenced} = 1; + $chainref->{dont_optimize} = 1 unless $config{OPTIMIZE_ACCOUNTING}; if ( $chain ne 'accounting' ) { my $file = find_file $chain; @@ -1010,9 +1123,11 @@ sub new_builtin_chain($$$) my ( $table, $chain, $policy ) = @_; my $chainref = new_chain $table, $chain; - $chainref->{referenced} = 1; - $chainref->{policy} = $policy; - $chainref->{builtin} = 1; + $chainref->{referenced} = 1; + $chainref->{policy} = $policy; + $chainref->{builtin} = 1; + $chainref->{dont_delete} = 1; + $chainref->{dont_move} = 1; $chainref; } @@ -1188,6 +1303,335 @@ sub finish_section ( $ ) { } } +# +# Delete redundant ACCEPT rules from the end of a policy chain whose policy is ACCEPT +# +sub optimize_chain( $ ) { + my $chainref = shift; + + if ( $chainref->{referenced} ) { + my $rules = $chainref->{rules}; + my $count = 0; + + pop @$rules; + + pop @$rules, $count++ while @$rules && $rules->[-1] =~ /-j ACCEPT/; + + if ( @${rules} ) { + add_rule $chainref, '-j ACCEPT'; + progress_message " $count ACCEPT rules deleted from policy chain $chainref->{name}" if $count; + } else { + # + # The chain is now empty -- change all references to ACCEPT + # + $count = 0; + + for my $fromref ( map $filter_table->{$_} , keys %{$chainref->{references}} ) { + defined && s/ -[jg] $chainref->{name}$/ -j ACCEPT/ && $count++ for @{$fromref->{rules}}; + } + + progress_message " $count references to ACCEPT policy chain $chainref->{name} replaced"; + $chainref->{referenced} = 0; + } + } +} + +# +# Delete the references to the passed chain +# + +sub delete_references( $ ) { + my $chainref = shift; + my $table = $chainref->{table}; + my $count = 0; + + for my $fromref ( map $chain_table{$table}{$_} , keys %{$chainref->{references}} ) { + for ( @{$fromref->{rules}} ) { + $_ = undef, $count++ if defined && / -[jg] $chainref->{name}$/; + } + } + + if ( $count ) { + progress_message " $count references to empty chain $chainref->{name} deleted"; + } else { + progress_message " Empty chain $chainref->{name} deleted"; + } + + $chainref->{referenced} = 0; + + $count; +} + +# +# Replace jumps to the passed chain with jumps to the passed target +# +sub replace_references( $$ ) { + my ( $chainref, $target ) = @_; + my $table = $chainref->{table}; + my $count = 0; + + if ( defined $chain_table{$table}{$target} && ! $chain_table{$table}{$target}{builtin} ) { + # + # The target is a chain -- use the jump type from each referencing rule + # + for my $fromref ( map $chain_table{$table}{$_} , keys %{$chainref->{references}} ) { + if ( $fromref->{referenced} ) { + defined && s/ -([jg]) $chainref->{name}(\b)/ -$1 ${target}$2/ && $count++ for @{$fromref->{rules}}; + } + } + } else { + # + # The target is a builtin -- we must use '-j' + # + for my $fromref ( map $chain_table{$table}{$_} , keys %{$chainref->{references}} ) { + if ( $fromref->{referenced} ) { + defined && s/ -[jg] $chainref->{name}(\b)/ -j ${target}$1/ && $count++ for @{$fromref->{rules}}; + } + } + } + + progress_message " $count references to 1-rule chain $chainref->{name} replaced" if $count; + + $chainref->{referenced} = 0; +} + +# +# Replace jumps to the passed chain with jumps to the passed target while +# adding the passed matches to the rule. +# +sub replace_references1( $$$ ) { + my ( $chainref, $target, $matches ) = @_; + my $table = $chainref->{table}; + my $count = 0; + # + # Note: If $matches is non-empty, then it begins with white space + # + if ( defined $chain_table{$table}{$target} && ! $chain_table{$table}{$target}{builtin} ) { + # + # The target is a chain -- use the jump type from each referencing rule + # + for my $fromref ( map $chain_table{$table}{$_} , keys %{$chainref->{references}} ) { + if ( $fromref->{referenced} ) { + for ( @{$fromref->{rules}} ) { + if ( defined && /^-A $fromref->{name} .*-[jg] $chainref->{name}\b/ ) { + # + # Prevent multiple '-p' matches + # + s/ -p [^ ]+ / / if / -p / && $matches =~ / -p /; + s/\s+-([jg]) $chainref->{name}(\b)/$matches -$1 ${target}$2/; + $count++; + } + } + } + } + } else { + # + # The target is a builtin -- we must use '-j' + # + for my $fromref ( map $chain_table{$table}{$_} , keys %{$chainref->{references}} ) { + if ( $fromref->{referenced} ) { + for ( @{$fromref->{rules}} ) { + if ( defined && /^-A $fromref->{name} .*-[jg] $chainref->{name}\b/ ) { + # + # Prevent multiple '-p' matches + # + s/ -p [^ ]+ / / if / -p / && $matches =~ / -p /; + s/\s+-[jg] $chainref->{name}(\b)/$matches -j ${target}$1/; + $count++; + } + } + } + } + } + + progress_message " $count references to 1-rule chain $chainref->{name} replaced" if $count; + + $chainref->{referenced} = 0; +} + +# +# The passed builtin chain has a single rule. If the target is a user chain without 'dont"move', move the rules from the +# chain to the builtin and return true; otherwise, do nothing and return false. +# +sub conditionally_move_rules( $$ ) { + my ( $chainref, $target ) = @_; + + if ( $target =~ /^\s*([^\s]+)/ ) { + # + # The above test is simply to isolate the basic target in $1 + # + my $basictarget = $1; + my $targetref = $chain_table{$chainref->{table}}{$basictarget}; + + if ( $targetref && ! $targetref->{dont_move} ) { + # + # Move is safe -- start with an empty rule list + # + $chainref->{rules} = []; + my $count = move_rules( $targetref, $chainref ); + progress_message " $count rules moved from chain $targetref->{name} to chain $chainref->{name}" if $count; + 1; + } + } +} + +# +# Perform Optimization +# +sub optimize_ruleset() { + # + # Make repeated passes through each table looking for short chains (those with less than 2 entries) + # + # When an unreferenced chain is found, it is deleted unless its 'dont_delete' flag is set. + # When an empty chain is found, delete the references to it. + # When a chain with a single entry is found, replace it's references by its contents + # + # The search continues until no short chains remain + # Chains with 'dont_optimize = 1' are exempted from optimization + # + for my $table ( qw/ raw mangle nat filter/ ) { + + next if $family == F_IPV6 && $table eq 'nat'; + + my $progress = 1; + my $passes = 0; + + while ( $progress ) { + $progress = 0; + $passes++; + + for my $chainref ( grep $_->{referenced}, values %{$chain_table{$table}} ) { + # + # If the chain isn't branched to, then delete it + # + unless ( $chainref->{dont_delete} || keys %{$chainref->{references}} ) { + $chainref->{referenced} = 0; + progress_message " Unreferenced chain $chainref->{name} deleted"; + next; + } + + unless ( $chainref->{dont_optimize} ) { + # + # Next count the rules -- we must do that because + # we delete rules by setting them + # to nil. + my $numrules = 0; + my $firstrule; + my $lastrule; + + for ( @{$chainref->{rules}} ) { + if ( defined ) { + $numrules++; + $lastrule = $_; + $firstrule = $_ unless defined $firstrule; + } + } + + if ( $numrules == 0 ) { + # + # No rules in this chain + # + if ( $chainref->{builtin} ) { + # + # Built-in -- mark it 'dont_optimize' so we ignore it in follow-on passes + # + $chainref->{dont_optimize} = 1; + } else { + # + # Not a built-in -- we can delete it and it's references + # + delete_references $chainref; + $progress = 1; + } + } elsif ( $numrules == 1 ) { + # + # Chain has a single non-nil rule which is in $firstrule + # + if ( $firstrule =~ /^-A $chainref->{name} -[jg] (.*)$/ ) { + # + # Easy case -- the rule is a simple jump + # + if ( $chainref->{builtin} ) { + # + # A built-in chain. If the target is a user chain without 'dont_move', + # we can move its rules to the built-in + # + if ( conditionally_move_rules $chainref, $1 ) { + # + # Target was a user chain -- rules moved + # + $progress = 1; + } else { + # + # Target was a built-in. Ignore this chain in follow-on passes + # + $chainref->{dont_optimize} = 1; + } + } else { + # + # Replace all references to this chain with references to the target + # + replace_references $chainref, $1; + $progress = 1; + } + } elsif ( $firstrule =~ /-A $chainref->{name}( .*) -[jg] (.*)$/ ) { + # + # Not so easy -- the rule contains matches + # + if ( $chainref->{builtin} ) { + # + # This case requires a new rule merging algorithm. Ignore this chain for + # now. + # + $chainref->{dont_optimize} = 1; + } else { + # + # Replace references to this chain with the target and add the predicates + # + replace_references1 $chainref, $2, $1; + $progress = 1; + } + } + } + } + } + } + + # + # In this loop, we look for chains that end in an unconditional jump. If the target of the jump + # is subject to optimization (dont_optimize = false), the jump is replaced by target's rules. + # + $progress = 1; + + while ( $progress ) { + $progress = 0; + $passes++; + + for my $chainref ( grep $_->{referenced}, values %{$chain_table{$table}} ) { + my $lastrule; + + for ( @{$chainref->{rules}} ) { + $lastrule = $_ if defined; + } + + if ( defined $lastrule && $lastrule =~ /^-A $chainref->{name} -[jg] (.*)$/ ) { + # + # Last rule is a simple branch + my $targetref = $chain_table{$table}{$1}; + + if ( $targetref && ! ( $targetref->{builtin} || $targetref->{dont_move} ) ) { + copy_rules( $targetref, $chainref ); + $progress = 1; + } + } + } + } + + progress_message " Table $table Optimized -- Passes = $passes"; + progress_message ''; + } +} + # # Helper for set_mss # @@ -1242,6 +1686,39 @@ sub newlogchain() { "log${seq}"; } +# +# If there is already a logging chain associated with the passed rules chain that matches these +# parameters, then return a reference to it. +# +# Otherwise, create such a chain and store a reference in chainref's 'logchains' hash. Return the +# reference. +# +sub logchain( $$$$$$ ) { + my ( $chainref, $loglevel, $logtag, $exceptionrule, $disposition, $target ) = @_; + my $key = join( ':', $loglevel, $logtag, $exceptionrule, $disposition, $target ); + my $logchainref = $chainref->{logchains}{$key}; + + unless ( $logchainref ) { + $logchainref = $chainref->{logchains}{$key} = new_chain $chainref->{table}, newlogchain; + # + # Now add the log rule and target rule without matches to the log chain. + # + log_rule_limit( + $loglevel , + $logchainref , + $chainref->{name} , + $disposition , + '', + $logtag, + 'add', + '' ); + + add_rule( $logchainref, $exceptionrule . $target ); + } + + $logchainref; +} + sub newnonatchain() { my $seq = $chainseq++; "nonat${seq}"; @@ -1915,7 +2392,7 @@ sub match_ipsec_out( $$ ) { # Generate a log message # sub log_rule_limit( $$$$$$$$ ) { - my ($level, $chainref, $chain, $disposition, $limit, $tag, $command, $predicates ) = @_; + my ($level, $chainref, $chain, $disposition, $limit, $tag, $command, $matches ) = @_; my $prefix = ''; @@ -1923,11 +2400,11 @@ sub log_rule_limit( $$$$$$$$ ) { return 1 if $level eq ''; - $predicates .= ' ' if $predicates && substr( $predicates, -1, 1 ) ne ' '; + $matches .= ' ' if $matches && substr( $matches, -1, 1 ) ne ' '; - unless ( $predicates =~ /-m limit / ) { + unless ( $matches =~ /-m limit / ) { $limit = $globals{LOGLIMIT} unless $limit && $limit ne '-'; - $predicates .= $limit if $limit; + $matches .= $limit if $limit; } if ( $config{LOGFORMAT} =~ /^\s*$/ ) { @@ -1976,28 +2453,28 @@ sub log_rule_limit( $$$$$$$$ ) { } if ( $command eq 'add' ) { - add_rule ( $chainref, $predicates . $prefix , 1 ); + add_rule ( $chainref, $matches . $prefix , 1 ); } else { - insert_rule1 ( $chainref , 0 , $predicates . $prefix ); + insert_rule1 ( $chainref , 0 , $matches . $prefix ); } } sub log_rule( $$$$ ) { - my ( $level, $chainref, $disposition, $predicates ) = @_; + my ( $level, $chainref, $disposition, $matches ) = @_; - log_rule_limit $level, $chainref, $chainref->{name} , $disposition, $globals{LOGLIMIT}, '', 'add', $predicates; + log_rule_limit $level, $chainref, $chainref->{name} , $disposition, $globals{LOGLIMIT}, '', 'add', $matches; } # # If the destination chain exists, then at the end of the source chain add a jump to the destination. # sub addnatjump( $$$ ) { - my ( $source , $dest, $predicates ) = @_; + my ( $source , $dest, $matches ) = @_; my $destref = $nat_table->{$dest} || {}; if ( $destref->{referenced} ) { - add_rule $nat_table->{$source} , $predicates . "-j $dest"; + add_jump $nat_table->{$source} , $dest , 0, $matches; } else { clearrule; } @@ -2371,7 +2848,7 @@ sub expand_rule( $$$$$$$$$$;$ ) $origdest, # ORIGINAL DEST $target, # Target ('-j' part of the rule) $loglevel , # Log level (and tag) - $disposition, # Primative part of the target (RETURN, ACCEPT, ...) + $disposition, # Primtive part of the target (RETURN, ACCEPT, ...) $exceptionrule,# Caller's matches used in exclusion case $logname, # Name of chain to name in log messages ) = @_; @@ -2424,9 +2901,12 @@ sub expand_rule( $$$$$$$$$$;$ ) # # Mark Target as referenced, if it's a chain # - if ( $disposition ) { - my $targetref = $chain_table{$chainref->{table}}{$disposition}; - $targetref->{referenced} = 1 if $targetref; + if ( $target =~ /-[jg]\s+([^\s]+)\b/ ) { + my $targetref = $chain_table{$chainref->{table}}{$1}; + if ( $targetref ) { + $targetref->{referenced} = 1; + add_reference $chainref, $targetref; + } } # @@ -2704,8 +3184,13 @@ sub expand_rule( $$$$$$$$$$;$ ) # fatal_error "Exclusion is not possible in ACCEPT+/CONTINUE/NONAT rules" if $disposition eq 'RETURN'; + # + # Create the Exclusion Chain + # my $echain = newexclusionchain; + my $echainref = new_chain $chainref->{table}, $echain; + # # Use the current rule and send all possible matches to the exclusion chain # @@ -2716,16 +3201,11 @@ sub expand_rule( $$$$$$$$$$;$ ) # # We evaluate the source net match in the inner loop to accomodate systems without $capabilities{KLUDGEFREE} # - add_rule( $chainref, join( '', $rule, match_source_net( $inet, $restriction ), match_dest_net( $dnet ), $onet, "-j $echain" ), 1 ); + add_jump( $chainref, $echainref, 0, join( '', $rule, match_source_net( $inet, $restriction ), match_dest_net( $dnet ), $onet ), 1 ); } } } - # - # Create the Exclusion Chain - # - my $echainref = new_chain $chainref->{table}, $echain; - # # Generate RETURNs for each exclusion # @@ -2754,33 +3234,20 @@ sub expand_rule( $$$$$$$$$$;$ ) for my $dnet ( mysplit $dnets ) { $source_match = match_source_net( $inet, $restriction ) unless $capabilities{KLUDGEFREE}; my $dest_match = match_dest_net( $dnet ); - my $predicates = join( '', $rule, $source_match, $dest_match, $onet ); + my $matches = join( '', $rule, $source_match, $dest_match, $onet ); if ( $loglevel ne '' ) { if ( $disposition ne 'LOG' ) { unless ( $logname ) { # - # Create a chain that both logs and applies the target action + # Find/Create a chain that both logs and applies the target action + # and jump to the log chain if all of the rule's conditions are met # - my $logchainref = new_chain $chainref->{table}, newlogchain; - # - # Jump to the log chain if all of the rule's conditions are met - # - add_jump( $chainref, $logchainref, $builtin_target{$disposition}, $predicates, 1 ); - # - # Now add the log rule and target rule without predicates to the log chain. - # - log_rule_limit( - $loglevel , - $logchainref , - $chain , - $disposition , - '', - $logtag, - 'add', - '' ); - - add_rule( $logchainref, $exceptionrule . $target ); + add_jump( $chainref, + logchain( $chainref, $loglevel, $logtag, $exceptionrule , $disposition, $target ), + $builtin_target{$disposition}, + $matches, + 1 ); } else { log_rule_limit( $loglevel , @@ -2790,13 +3257,13 @@ sub expand_rule( $$$$$$$$$$;$ ) '', $logtag, 'add', - $predicates ); + $matches ); - add_rule( $chainref, $predicates . $target, 1 ); + add_rule( $chainref, $matches . $target, 1 ); } } else { # - # The log rule must be added with predicates to the rule chain + # The log rule must be added with matches to the rule chain # log_rule_limit( $loglevel , @@ -2806,14 +3273,14 @@ sub expand_rule( $$$$$$$$$$;$ ) '' , $logtag , 'add' , - $predicates + $matches ); } } else { # - # No logging -- add the target rule with predicates to the rule chain + # No logging -- add the target rule with matches to the rule chain # - add_rule( $chainref, $predicates . $target , 1 ); + add_rule( $chainref, $matches . $target , 1 ); } } } diff --git a/Shorewall/Perl/Shorewall/Compiler.pm b/Shorewall/Perl/Shorewall/Compiler.pm index 76ed53c1d..26d8be173 100644 --- a/Shorewall/Perl/Shorewall/Compiler.pm +++ b/Shorewall/Perl/Shorewall/Compiler.pm @@ -4,7 +4,7 @@ # # This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt] # -# (c) 2007,2008,2009 - Tom Eastep (teastep@shorewall.net) +# (c) 2007,2008,2009,2010 - Tom Eastep (teastep@shorewall.net) # # Complete documentation is available at http://shorewall.net # @@ -43,7 +43,7 @@ use Shorewall::Raw; our @ISA = qw(Exporter); our @EXPORT = qw( compiler EXPORT TIMESTAMP DEBUG ); our @EXPORT_OK = qw( $export ); -our $VERSION = '4.4_6'; +our $VERSION = '4.4_7'; our $export; @@ -816,7 +816,7 @@ sub compiler { # # Accounting. # - setup_accounting; + setup_accounting if $config{ACCOUNTING}; if ( $scriptfilename ) { # @@ -824,6 +824,18 @@ sub compiler { # generate_matrix; + if ( $config{OPTIMIZE} & 6 ) { + progress_message2 'Optimizing Ruleset...'; + # + # Optimize Policy Chains + # + optimize_policy_chains if $config{OPTIMIZE} & 2; + # + # More Optimization + # + optimize_ruleset if $config{OPTIMIZE} & 4; + } + enable_script; # # I N I T I A L I Z E @@ -845,7 +857,7 @@ sub compiler { # S T O P _ F I R E W A L L # (Writes the stop_firewall() function to the compiled script) # - compile_stop_firewall( $test ); + compile_stop_firewall( $test, $export ); # # Copy the footer to the script # @@ -868,10 +880,26 @@ sub compiler { enable_script, generate_aux_config if $export; } else { # - # Checking the configuration only + # Just checking the configuration # if ( $preview ) { + # + # User wishes to preview the ruleset -- generate the rule matrix + # generate_matrix; + + if ( $config{OPTIMIZE} & 6 ) { + progress_message2 'Optimizing Ruleset...'; + # + # Optimize Policy Chains + # + optimize_policy_chains if $config{OPTIMIZE} & 2; + # + # Ruleset Optimization + # + optimize_ruleset if $config{OPTIMIZE} & 4; + } + preview_netfilter_load; } # diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm index 896102601..3ffff3091 100644 --- a/Shorewall/Perl/Shorewall/Config.pm +++ b/Shorewall/Perl/Shorewall/Config.pm @@ -128,7 +128,7 @@ our %EXPORT_TAGS = ( internal => [ qw( create_temp_script Exporter::export_ok_tags('internal'); -our $VERSION = '4.4_6'; +our $VERSION = '4.4_7'; # # describe the current command, it's present progressive, and it's completion. @@ -445,6 +445,9 @@ sub initialize( $ ) { WIDE_TC_MARKS => undef, TRACK_PROVIDERS => undef, ZONE2ZONE => undef, + ACCOUNTING => undef, + OPTIMIZE_ACCOUNTING => undef, + DYNAMIC_BLACKLIST => undef, # # Packet Disposition # @@ -561,6 +564,9 @@ sub initialize( $ ) { WIDE_TC_MARKS => undef, TRACK_PROVIDERS => undef, ZONE2ZONE => undef, + ACCOUNTING => undef, + OPTIMIZE_ACCOUNTING => undef, + DYNAMIC_BLACKLIST => undef, # # Packet Disposition # @@ -2517,6 +2523,9 @@ sub get_configuration( $ ) { default_yes_no 'AUTOMAKE' , ''; default_yes_no 'WIDE_TC_MARKS' , ''; default_yes_no 'TRACK_PROVIDERS' , ''; + default_yes_no 'ACCOUNTING' , 'Yes'; + default_yes_no 'OPTIMIZE_ACCOUNTING' , ''; + default_yes_no 'DYNAMIC_BLACKLIST' , 'Yes'; numeric_option 'TC_BITS', $config{WIDE_TC_MARKS} ? 14 : 8 , 0; numeric_option 'MASK_BITS', $config{WIDE_TC_MARKS} ? 16 : 8, $config{TC_BITS}; @@ -2638,7 +2647,7 @@ sub get_configuration( $ ) { $val = numeric_value $config{OPTIMIZE}; - fatal_error "Invalid OPTIMIZE value ($config{OPTIMIZE})" unless defined( $val ) && $val >= 0 && $val <= 1; + fatal_error "Invalid OPTIMIZE value ($config{OPTIMIZE})" unless defined( $val ) && $val >= 0 && $val <= 7; $globals{MARKING_CHAIN} = $config{MARK_IN_FORWARD_CHAIN} ? 'tcfor' : 'tcpre'; diff --git a/Shorewall/Perl/Shorewall/Policy.pm b/Shorewall/Perl/Shorewall/Policy.pm index b0273f215..2d88aea08 100644 --- a/Shorewall/Perl/Shorewall/Policy.pm +++ b/Shorewall/Perl/Shorewall/Policy.pm @@ -3,7 +3,7 @@ # # This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt] # -# (c) 2007,2008,2009 - Tom Eastep (teastep@shorewall.net) +# (c) 2007,2008,2009,2010 - Tom Eastep (teastep@shorewall.net) # # Complete documentation is available at http://shorewall.net # @@ -32,9 +32,9 @@ use Shorewall::Actions; use strict; our @ISA = qw(Exporter); -our @EXPORT = qw( validate_policy apply_policy_rules complete_standard_chain setup_syn_flood_chains save_policies ); +our @EXPORT = qw( validate_policy apply_policy_rules complete_standard_chain setup_syn_flood_chains save_policies optimize_policy_chains); our @EXPORT_OK = qw( ); -our $VERSION = '4.4_4'; +our $VERSION = '4.4_7'; # @policy_chains is a list of references to policy chains in the filter table @@ -362,7 +362,7 @@ sub policy_rules( $$$$$ ) { unless ( $target eq 'NONE' ) { add_rule $chainref, "-d 224.0.0.0/4 -j RETURN" if $dropmulticast && $target ne 'CONTINUE' && $target ne 'ACCEPT'; - add_rule $chainref, "-j $default" if $default && $default ne 'none'; + add_jump $chainref, $default, 0 if $default && $default ne 'none'; log_rule $loglevel , $chainref , $target , '' if $loglevel ne ''; fatal_error "Null target in policy_rules()" unless $target; @@ -418,10 +418,21 @@ sub apply_policy_rules() { my $provisional = $chainref->{provisional}; my $default = $chainref->{default}; my $name = $chainref->{name}; + my $synparms = $chainref->{synparms}; if ( $policy ne 'NONE' ) { - if ( ! $chainref->{referenced} && ( ! $provisional && $policy ne 'CONTINUE' ) ) { - ensure_filter_chain $name, 1; + unless ( $chainref->{referenced} || $provisional || $policy eq 'CONTINUE' ) { + if ( $config{OPTIMIZE} & 2 ) { + # + # This policy chain is empty and the only thing that we would put in it is + # the policy-related stuff. Don't create it if all we are going to put in it + # is a single jump. Generate_matrix() will just use the policy target when + # needed. + # + ensure_filter_chain $name, 1 if $default ne 'none' || $loglevel || $synparms || $config{MULTICAST} || ! ( $policy eq 'ACCEPT' || $config{FASTACCEPT} ); + } else { + ensure_filter_chain $name, 1; + } } if ( $name =~ /^all[-2]|[-2]all$/ ) { @@ -487,4 +498,24 @@ sub setup_syn_flood_chains() { } } +# +# Optimize Policy chains with ACCEPT policy +# +sub optimize_policy_chains() { + for my $chainref ( grep $_->{policy} eq 'ACCEPT', @policy_chains ) { + optimize_chain ( $chainref ); + } + # + # Often, fw->all has an ACCEPT policy. This code allows optimization in that case + # + my $outputrules = $filter_table->{OUTPUT}{rules}; + + if ( @{$outputrules} && $outputrules->[-1] =~ /-j ACCEPT/ ) { + optimize_chain( $filter_table->{OUTPUT} ); + } + + progress_message ' Policy chains optimized'; + progress_message ''; +} + 1; diff --git a/Shorewall/Perl/Shorewall/Rules.pm b/Shorewall/Perl/Shorewall/Rules.pm index 8fe3a7909..26f238b14 100644 --- a/Shorewall/Perl/Shorewall/Rules.pm +++ b/Shorewall/Perl/Shorewall/Rules.pm @@ -46,7 +46,7 @@ our @EXPORT = qw( process_tos compile_stop_firewall ); our @EXPORT_OK = qw( process_rule process_rule1 initialize ); -our $VERSION = '4.4_6'; +our $VERSION = '4.4_7'; # # Set to one if we find a SECTION @@ -157,8 +157,8 @@ sub process_tos() { } unless ( $first_entry ) { - add_rule $mangle_table->{$stdchain}, "-j $chain" if $pretosref->{referenced}; - add_rule $mangle_table->{OUTPUT}, "-j outtos" if $outtosref->{referenced}; + add_jump( $mangle_table->{$stdchain}, $chain, 0 ) if $pretosref->{referenced}; + add_jump( $mangle_table->{OUTPUT}, 'outtos', 0 ) if $outtosref->{referenced}; } } } @@ -214,7 +214,7 @@ sub add_rule_pair( $$$$ ) { my ($chainref , $predicate , $target , $level ) = @_; log_rule( $level, $chainref, "\U$target", $predicate ) if defined $level && $level ne ''; - add_rule $chainref , "${predicate}-j $target"; + add_jump( $chainref , $target, 0, $predicate ); } sub setup_blacklist() { @@ -232,7 +232,7 @@ sub setup_blacklist() { log_rule_limit( $level , $logchainref , 'blacklst' , $disposition , "$globals{LOGLIMIT}" , '', 'add', '' ); - add_rule $logchainref, "-j $target" ; + add_jump $logchainref, $target, 1; $target = 'blacklog'; } @@ -419,17 +419,21 @@ sub setup_mss(); sub add_common_rules() { my $interface; my $chainref; - my $level; my $target; my $rule; my $list; my $chain; - new_standard_chain 'dynamic'; + my $state = $config{BLACKLISTNEWONLY} ? $globals{UNTRACKED} ? '-m state --state NEW,INVALID,UNTRACKED ' : '-m state --state NEW,INVALID ' : ''; + my $level = $config{BLACKLIST_LOGLEVEL}; + my $rejectref = dont_move new_standard_chain 'reject'; - my $state = $config{BLACKLISTNEWONLY} ? $globals{UNTRACKED} ? '-m state --state NEW,INVALID,UNTRACKED ' : '-m state --state NEW,INVALID ' : ''; - - add_rule $filter_table->{$_}, "$state -j dynamic" for qw( INPUT FORWARD ); + if ( $config{DYNAMIC_BLACKLIST} ) { + add_rule_pair dont_delete( new_standard_chain( 'logdrop' ) ), ' ' , 'DROP' , $level ; + add_rule_pair dont_delete( new_standard_chain( 'logreject' ) ), ' ' , 'reject' , $level ; + $chainref = dont_optimize( new_standard_chain( 'dynamic' ) ); + add_jump $filter_table->{$_}, $chainref, 0, $state for qw( INPUT FORWARD ); + } setup_mss; @@ -437,13 +441,6 @@ sub add_common_rules() { add_rule( $filter_table->{$_} , "-m state --state ESTABLISHED,RELATED -j ACCEPT" ) for qw( INPUT FORWARD OUTPUT ); } - my $rejectref = new_standard_chain 'reject'; - - $level = $config{BLACKLIST_LOGLEVEL}; - - add_rule_pair new_standard_chain( 'logdrop' ), ' ' , 'DROP' , $level ; - add_rule_pair new_standard_chain( 'logreject' ), ' ' , 'reject' , $level ; - for $interface ( all_interfaces ) { ensure_chain( 'filter', $_ ) for first_chains( $interface ), output_chain( $interface ); } @@ -591,11 +588,11 @@ sub add_common_rules() { $disposition = $config{TCP_FLAGS_DISPOSITION}; } - add_rule $chainref , "-p tcp --tcp-flags ALL FIN,URG,PSH -j $disposition"; - add_rule $chainref , "-p tcp --tcp-flags ALL NONE -j $disposition"; - add_rule $chainref , "-p tcp --tcp-flags SYN,RST SYN,RST -j $disposition"; - add_rule $chainref , "-p tcp --tcp-flags SYN,FIN SYN,FIN -j $disposition"; - add_rule $chainref , "-p tcp --syn --sport 0 -j $disposition"; + add_jump $chainref , $disposition, 1, '-p tcp --tcp-flags ALL FIN,URG,PSH '; + add_jump $chainref , $disposition, 1, '-p tcp --tcp-flags ALL NONE '; + add_jump $chainref , $disposition, 1, '-p tcp --tcp-flags SYN,RST SYN,RST '; + add_jump $chainref , $disposition, 1, '-p tcp --tcp-flags SYN,FIN SYN,FIN '; + add_jump $chainref , $disposition, 1, '-p tcp --syn --sport 0 '; for my $hostref ( @$list ) { my $interface = $hostref->[0]; @@ -618,12 +615,12 @@ sub add_common_rules() { if ( @$list ) { progress_message2 "$doing UPnP"; - new_nat_chain( 'UPnP' ); + dont_optimize new_nat_chain( 'UPnP' ); $announced = 1; for $interface ( @$list ) { - add_rule $nat_table->{PREROUTING} , match_source_dev ( $interface ) . '-j UPnP'; + add_jump $nat_table->{PREROUTING} , 'UPnP', 0, match_source_dev ( $interface ); } } @@ -706,7 +703,7 @@ sub setup_mac_lists( $ ) { my $chain = $chainref->{name}; add_rule $chainref, "-m recent --rcheck --seconds $ttl --name $chain -j RETURN"; - add_rule $chainref, "-j $chain1ref->{name}"; + add_jump $chainref, $chain1ref, 0; add_rule $chainref, "-m recent --update --name $chain -j RETURN"; add_rule $chainref, "-m recent --set --name $chain"; } @@ -834,7 +831,7 @@ sub setup_mac_lists( $ ) { run_user_exit2( 'maclog', $chainref ); log_rule_limit $level, $chainref , $chain , $disposition, '', '', 'add', '' if $level ne ''; - add_rule $chainref, "-j $target"; + add_jump $chainref, $target, 0; } } } @@ -958,7 +955,7 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) { my ( $basictarget, $param ) = get_target_param $action; my $rule = ''; my $actionchainref; - my $optimize = $wildcard ? ( $basictarget =~ /!$/ ? 0 : $config{OPTIMIZE} ) : 0; + my $optimize = $wildcard ? ( $basictarget =~ /!$/ ? 0 : $config{OPTIMIZE} & 1 ) : 0; $param = '' unless defined $param; @@ -1128,7 +1125,7 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) { } } - $chain = rules_chain( ${sourcezone}, ${destzone} ); + $chain = rules_chain( ${sourcezone}, ${destzone} ); # # Ensure that the chain exists but don't mark it as referenced until after optimization is checked # @@ -1154,12 +1151,22 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) { # Mark the chain as referenced and add appropriate rules from earlier sections. # $chainref = ensure_filter_chain $chain, 1; + # + # Don't let the rules in this chain be moved elsewhere + # + dont_move $chainref; } # # Generate Fixed part of the rule # - $rule = join( '', do_proto($proto, $ports, $sports), do_ratelimit( $ratelimit, $basictarget ) , do_user( $user ) , do_test( $mark , $globals{TC_MASK} ) , do_connlimit( $connlimit ), do_time( $time ) ); + $rule = join( '', + do_proto($proto, $ports, $sports), + do_ratelimit( $ratelimit, $basictarget ) , + do_user( $user ) , + do_test( $mark , $globals{TC_MASK} ) , + do_connlimit( $connlimit ), + do_time( $time ) ); unless ( $section eq 'NEW' ) { fatal_error "Entries in the $section SECTION of the rules file not permitted with FASTACCEPT=Yes" if $config{FASTACCEPT}; @@ -1292,7 +1299,11 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) { # - the target will be ACCEPT. # unless ( $actiontype & NATONLY ) { - $rule = join( '', do_proto( $proto, $ports, $sports ), do_ratelimit( $ratelimit, 'ACCEPT' ), do_user $user , do_test( $mark , $globals{TC_MASK} ) ); + $rule = join( '', + do_proto( $proto, $ports, $sports ), + do_ratelimit( $ratelimit, 'ACCEPT' ), + do_user $user , + do_test( $mark , $globals{TC_MASK} ) ); $loglevel = ''; $dest = $server; $action = 'ACCEPT'; @@ -1370,7 +1381,7 @@ sub process_rule1 ( $$$$$$$$$$$$$ ) { "-j $tgt", $loglevel , $log_action , - '' + '' , ); # # Possible optimization if the rule just generated was a simple jump to the nonat chain @@ -1624,7 +1635,7 @@ sub add_interface_jumps { my $fw = firewall_zone; my $chainref = $filter_table->{rules_chain( ${fw}, ${fw} )}; - add_rule $filter_table->{OUTPUT} , "-o lo -j " . ($chainref->{referenced} ? "$chainref->{name}" : 'ACCEPT' ); + add_jump $filter_table->{OUTPUT} , ($chainref->{referenced} ? $chainref : 'ACCEPT' ), 0, '-o lo '; add_rule $filter_table->{INPUT} , '-i lo -j ACCEPT'; } @@ -1657,7 +1668,8 @@ sub generate_matrix() { if ( $chainref->{policy} ne 'CONTINUE' ) { my $policyref = $filter_table->{$chainref->{policychain}}; assert( $policyref ); - return $policyref->{name}; + return $policyref->{name} if $policyref ne $chainref; + return $chainref->{policy} eq 'REJECT' ? 'reject' : $chainref->{policy}; } ''; # CONTINUE policy @@ -1739,7 +1751,7 @@ sub generate_matrix() { # # NOTRACK from firewall # - add_rule $raw_table->{OUTPUT}, "-j $notrackref->{name}" if $notrackref->{referenced}; + add_jump $raw_table->{OUTPUT}, $notrackref, 0 if $notrackref->{referenced}; # # Main source-zone matrix-generation loop # @@ -1906,7 +1918,7 @@ sub generate_matrix() { my @dest_zones; my $last_chain = ''; - if ( $config{OPTIMIZE} > 0 ) { + if ( $config{OPTIMIZE} & 1 ) { my @temp_zones; for my $zone1 ( @zones ) { @@ -2007,7 +2019,7 @@ sub generate_matrix() { my $match_source_dev = ''; my $forwardchainref = $filter_table->{forward_chain $interface}; - if ( use_forward_chain( $interface ) || ( @{$forwardchainref->{rules} } && ! $chainref ) ) { + if ( use_forward_chain $interface || ( @{$forwardchainref->{rules} } && ! $chainref ) ) { # # Either we must use the interface's forwarding chain or that chain has rules and we have nowhere to move them # @@ -2122,7 +2134,7 @@ sub setup_mss( ) { # # Send all forwarded SYN packets to the 'settcpmss' chain # - add_rule $filter_table->{FORWARD} , "-p tcp --tcp-flags SYN,RST SYN -j settcpmss"; + add_jump $filter_table->{FORWARD} , $chainref, 0, '-p tcp --tcp-flags SYN,RST SYN '; my $in_match = ''; my $out_match = ''; @@ -2150,8 +2162,8 @@ sub setup_mss( ) { # # Compile the stop_firewall() function # -sub compile_stop_firewall( $ ) { - my $test = shift; +sub compile_stop_firewall( $$ ) { + my ( $test, $export ) = @_; my $input = $filter_table->{INPUT}; my $output = $filter_table->{OUTPUT};