Don't combine incompatible chains

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2011-10-21 11:55:30 -07:00
parent f31f3dc92a
commit d3d9380df5

View File

@ -809,21 +809,40 @@ sub format_rule( $$;$ ) {
$rule;
}
#
# Check two rules to determine if the second rule can be merged into the first.
#
sub compatible( $$ ) {
my ( $ref1, $ref2 ) = @_;
my ( $val1, $val2 );
for ( @unique_options ) {
if ( defined( $val1 = $ref1->{$_} ) && defined( $val2 = $ref2->{$_} ) ) {
unless ( $val1 eq $val2 ) {
my @val1 = split ' ', $val1;
my @val2 = split ' ', $val2;
return 0 if @val1 > @val2 || $val1[0] ne $val2[0];
}
}
}
return 1;
}
#
# Merge two rules - If the target of the merged rule is a chain, a reference to its
# chain table entry is returned. It is the caller's responsibility
# to handle reference counting. If the target is a builtin, '' is
# returned.
# chain table entry is returned. It is the caller's responsibility to
# ensure that the rules being merged are compatible.
#
# It is also the caller's responsibility to handle reference counting.
# If the target is a builtin, '' is returned.
#
sub merge_rules( $$$ ) {
my ( $tableref, $toref, $fromref ) = @_;
my $target = $fromref->{target};
#
# Since the 'to' rule is a jump to a chain containing the 'from' rule, we
# assume that common unique option values are compatible (such as 'tcp' and
# 'tcp ! syn').
#
for my $option ( @unique_options ) {
$toref->{$option} = $fromref->{$option} if exists $fromref->{$option};
}
@ -2452,33 +2471,45 @@ sub replace_references1( $$ ) {
my $count = 0;
my $name = $chainref->{name};
my $target = $ruleref->{target};
my $delete = 1;
for my $fromref ( map $tableref->{$_} , keys %{$chainref->{references}} ) {
my $rule = 0;
my $rule = 0;
my $skipped = 0;
if ( $fromref->{referenced} ) {
for ( @{$fromref->{rules}} ) {
$rule++;
if ( $_->{target} eq $name ) {
#
# The target is the passed chain -- merge the two rules into one
#
if ( my $targetref = merge_rules( $tableref, $_, $ruleref ) ) {
add_reference( $fromref, $targetref );
delete_reference( $fromref, $chainref );
}
if ( $_->{target} eq $name ) {
if ( compatible( $_ , $ruleref ) ) {
#
# The target is the passed chain -- merge the two rules into one
#
if ( my $targetref = merge_rules( $tableref, $_, $ruleref ) ) {
add_reference( $fromref, $targetref );
delete_reference( $fromref, $chainref );
}
$count++;
trace( $fromref, 'R', $rule, $_ ) if $debug;
$count++;
trace( $fromref, 'R', $rule, $_ ) if $debug;
} else {
$skipped++;
}
}
}
}
delete $tableref->{$target}{references}{$chainref->{name}} if $tableref->{$target};
if ( $skipped ) {
$delete = 0;
} else {
delete $tableref->{$target}{references}{$chainref->{name}} if $tableref->{$target};
}
}
progress_message " $count references to chain $chainref->{name} replaced" if $count;
delete_chain $chainref;
delete_chain $chainref if $delete;
$count;
}
#
@ -2617,8 +2648,8 @@ sub optimize_level4( $$ ) {
#
# Replace references to this chain with the target and add the matches
#
replace_references1 $chainref, $firstrule;
$progress = 1;
$progress = 1 if replace_references1 $chainref, $firstrule;
}
}
}