Implement intermediate rule representation

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2011-07-16 09:41:53 -07:00
parent 15a88f962f
commit 0f742187ae
5 changed files with 407 additions and 216 deletions

View File

@ -38,6 +38,11 @@ our @EXPORT = qw(
add_rule add_rule
add_jump add_jump
insert_rule insert_rule
rule_target
clear_rule_target
set_rule_target
set_rule_option
add_transformed_rule
add_commands add_commands
incr_cmd_level incr_cmd_level
decr_cmd_level decr_cmd_level
@ -515,18 +520,238 @@ sub decr_cmd_level( $ ) {
assert( --$_[0]->{cmdlevel} >= 0); assert( --$_[0]->{cmdlevel} >= 0);
} }
#
# Transform the passed iptables rule into an internal-form hash reference
#
sub set_rule_option( $$$ ) {
my ( $ruleref, $option, $value ) = @_;
assert( defined $value );
if ( exists $ruleref->{$option} ) {
assert( defined $ruleref->{$option} );
$ruleref->{$option} = [ $ruleref->{$option} ] unless reftype $ruleref->{$option};
push @{$ruleref->{$option}}, ( reftype $value ? @$value : $value );
} else {
$ruleref->{$option} = $value;
}
}
sub transform_rule( $ ) {
my $input = shift;
my $ruleref = { mode => CAT_MODE, target => '' };
my $simple = 1;
#
# This is the first step in converting the compiler to use an intermediate form for rules.
# We parse the iptables input and convert it into a hash. Eventually, the upper levels
# will generate the hash from the outset
#
while ( $input ) {
assert( $input =~ s/^(!|-[psdjgiom])\s*//, $input );
my $option = $1;
my $invert = '';
if ( $option eq '!' ) {
$invert = '! ';
assert( $input =~ s/^-([psdjgiom])\s*//, $input );
$option = $1;
} else {
$option =~ s/^-//;
}
if ( $option eq 'j' or $option eq 'g' ) {
$ruleref->{jump} = $option;
assert( $input =~ s/([^\s]+)\s*//, $input );
$ruleref->{target} = $1;
$option = 'targetopts';
} else {
$simple = 0;
if ( $option eq 'm' ) {
assert( $input =~ s/(\w+)\s*//, $input );
$option = $1;
}
}
my $params = $invert;
PARAM:
{
while ( $input ne '' && $input !~ /^(?:!|-[psdjgiom])\s/ ) {
assert( $input =~ s/^([^\s]+)\s*// , $input );
$params = $params eq '' ? $1 : join( ' ' , $params, $1);
}
if ( $input =~ /^(?:!\s+--|!\s+[^-])/ ) {
$params = $params eq '' ? '!' : join( ' ' , $params, '!' );
$input =~ s/^!\s+//;
redo PARAM;
}
}
set_rule_option( $ruleref, $option, $params ) unless $params eq '';
}
$ruleref->{target} ||= '';
$ruleref->{simple} = $simple;
$ruleref;
}
#
# A couple of small functions for other modules to use to manipulate rules
#
sub rule_target( $ ) {
my $ruleref = shift;
assert( reftype $ruleref );
$ruleref->{mode} == CAT_MODE ? $ruleref->{target} || '' : '';
}
sub clear_rule_target( $ ) {
my $ruleref = shift;
assert( reftype $ruleref );
delete $ruleref->{jump};
delete $ruleref->{target};
delete $ruleref->{targetopts};
1;
}
sub set_rule_target( $$$ ) {
my ( $ruleref, $target, $opts) = @_;
assert( reftype $ruleref );
$ruleref->{jump} = 'j';
$ruleref->{target} = $target;
$ruleref->{targetopts} = $opts if defined $opts;
1
}
my %special = ( rule => 1,
mode => 1,
cmdlevel => 1,
simple => 1,
i => 1,
s => 1,
o => 1,
d => 1,
p => 1,
comment => 1,
policy => 1,
state => 1,
ctstate => 1,
jump => 1,
target => 1,
targetopts => 1 );
#
# Convert a transformed rule into iptables input
#
# First, a helper function
#
sub format_option( $$ ) {
my ( $option, $value ) = @_;
my $list = reftype $value ? $value : [ $value ];
my $rule = '';
$rule .= join( ' ' , ' -m', $option, $_ ) for @$list;
$rule;
}
sub format_rule( $$;$ ) {
my ( $chainref, $ruleref, $suppresshdr ) = @_;
my $rule = $suppresshdr ? '' : "-A $chainref->{name}";
for ( qw/ p i s o d / ) {
if ( exists $ruleref->{$_} ) {
my $value = $ruleref->{$_};
$rule .= ' !' if $value =~ s/^! //;
$rule .= join( '' , ' -', $_, ' ', $value );
}
}
$rule .= format_option( 'state', $ruleref->{state} ) if defined $ruleref->{state};
$rule .= format_option( 'ctstate', $ruleref->{ctstate} ) if defined $ruleref->{ctstate};
$rule .= format_option( 'policy', $ruleref->{policy} ) if defined $ruleref->{policy};
$rule .= format_option( $_, $ruleref->{$_} ) for sort ( grep ! $special{$_}, keys %{$ruleref} );
if ( $ruleref->{target} ) {
$rule .= join( ' ', " -$ruleref->{jump}", $ruleref->{target} );
$rule .= join( '', ' ', $ruleref->{targetopts} ) if $ruleref->{targetopts};
}
$rule .= join( '', ' -m comment --comment "', $ruleref->{comment}, '"' ) if $ruleref->{comment};
$rule;
}
#
# Merge two rules.
#
sub merge_rules( $$$ ) {
my ( $tableref, $toref, $fromref ) = @_;
my $target = $fromref->{target};
for my $option ( qw/p i o s d/ ) {
$toref->{$option} = $fromref->{$option} if exists $fromref->{$option};
}
for my $option ( grep ! $special{$_}, keys %$fromref ) {
set_rule_option( $toref, $option, $fromref->{$option} );
}
unless ( $toref->{state} || $toref->{ctstate} ) {
set_rule_option ( $toref, 'state', $fromref->{state} ) if $fromref->{state};
set_rule_option ( $toref, 'ctstate', $fromref->{ctstate} ) if $fromref->{ctstate};
}
set_rule_option( $toref, 'policy', $fromref->{policy} ) if exists $fromref->{policy};
$toref->{target} = $target;
$toref->{targetopts} = $fromref->{targetopts} if $fromref->{targetopts};
$toref->{jump} = 'j' unless $tableref->{$target};
unless ( $toref->{comment} ) {
$toref->{comment} = $fromref->{comment} if exists $fromref->{comment};
}
$toref->{rule} = format_rule( $fromref, $_, 1 ) if $toref->{rule};
}
# #
# Trace a change to the chain table # Trace a change to the chain table
# #
sub trace( $$$$ ) { sub trace( $$$$ ) {
my ($chainref, $action, $rulenum, $message) = @_; my ($chainref, $action, $rulenum, $message) = @_;
my $heading = $rulenum ? sprintf "NF-(%s)-> %s:%s:%d", $action, $chainref->{table}, $chainref->{name}, $rulenum : sprintf "NF-(%s)-> %s:%s", $action, $chainref->{table}, $chainref->{name}; my $heading = $rulenum ?
sprintf " NF-(%s)-> %s:%s:%d", $action, $chainref->{table}, $chainref->{name}, $rulenum :
sprintf " NF-(%s)-> %s:%s", $action, $chainref->{table}, $chainref->{name};
my $length = length $heading; my $length = length $heading;
if ( $length < 32 ) { $message = format_rule( $chainref, $message ) if reftype $message;
print $heading . ' ' x ( 32 - $length) . "$message\n";
if ( $length < 48 ) {
print $heading . ' ' x ( 48 - $length) . "$message\n";
} else { } else {
print $heading . ' ' x 8 * ( ( $length + 8 ) / 8 ) . "$message\n"; print $heading . ' ' x 8 * ( ( $length + 8 ) / 8 ) . "$message\n";
} }
@ -539,32 +764,37 @@ sub trace( $$$$ ) {
# #
sub add_commands ( $$;@ ) { sub add_commands ( $$;@ ) {
my $chainref = shift @_; my $chainref = shift @_;
my $indentation = ' ' x $chainref->{cmdlevel};
if ( $debug ) { if ( $debug ) {
my $rulenum = @{$chainref->{rules}}; my $rulenum = @{$chainref->{rules}};
trace( $chainref, 'T', ++$rulenum, "${indentation}$_\n" ) for @_; trace( $chainref, 'T', ++$rulenum, "$_\n" ) for @_;
} }
push @{$chainref->{rules}}, join ('', $indentation , $_ ) for @_; push @{$chainref->{rules}}, { mode => CMD_MODE, cmd => $_ , cmdlevel => $chainref->{cmdlevel} } for @_;
$chainref->{referenced} = 1; $chainref->{referenced} = 1;
} }
sub push_rule( $$ ) { sub push_rule( $$ ) {
my $chainref = $_[0]; my $chainref = $_[0];
my $rule = join( ' ', '-A' , $_[1]); my $ruleref = transform_rule( $_[1] );
$rule .= qq( -m comment --comment "$comment") if $comment; $ruleref->{comment} = "$comment" if $comment;
$ruleref->{mode} = CMD_MODE if $ruleref->{cmdlevel} = $chainref->{cmdlevel};
if ( $chainref->{cmdlevel} ) { push @{$chainref->{rules}}, $ruleref;
$rule =~ s/"/\\"/g; #Must preserve quotes in the rule $chainref->{referenced} = 1;
add_commands $chainref , qq(echo "$rule" >&3); trace( $chainref, 'A', @{$chainref->{rules}}, "-A $chainref->{name} $_[1]" ) if $debug;
} else { }
push @{$chainref->{rules}}, $rule;
$chainref->{referenced} = 1; sub add_transformed_rule( $$ ) {
trace( $chainref, 'A', @{$chainref->{rules}}, $rule ) if $debug; my ( $chainref, $ruleref ) = @_;
}
assert( reftype $ruleref );
push @{$chainref->{rules}}, $ruleref;
$chainref->{referenced} = 1;
trace( $chainref, 'A', @{$chainref->{rules}}, format_rule( $chainref, $ruleref ) ) if $debug;
} }
# #
@ -727,19 +957,20 @@ sub insert_rule1($$$)
{ {
my ($chainref, $number, $rule) = @_; my ($chainref, $number, $rule) = @_;
assert( ! $chainref->{cmdlevel}); my $ruleref = transform_rule( $rule );
$rule .= "-m comment --comment \"$comment\"" if $comment; $ruleref->{comment} = "$comment" if $comment;
$rule = join( ' ', '-A', $rule ); assert( ! ( $ruleref->{cmdlevel} = $chainref->{cmdlevel}) );
$ruleref->{mode} = CAT_MODE;
if ( $number < 0 ) { if ( $number < 0 ) {
$chainref->{blacklist}++; $chainref->{blacklist}++;
$number = 0; $number = 0;
} }
splice( @{$chainref->{rules}}, $number, 0, $rule ); splice( @{$chainref->{rules}}, $number, 0, $ruleref );
trace( $chainref, 'I', ++$number, $rule ) if $debug; trace( $chainref, 'I', ++$number, $ruleref ) if $debug;
$iprangematch = 0; $iprangematch = 0;
@ -778,7 +1009,7 @@ sub delete_chain_and_references( $ ) {
my $tableref = $chain_table{$chainref->{table}}; my $tableref = $chain_table{$chainref->{table}};
my $name1 = $chainref->{name}; my $name1 = $chainref->{name};
for ( @{$chainref->{rules}} ) { for ( @{$chainref->{rules}} ) {
decrement_reference_count( $tableref->{$1}, $name1 ) if / -[jg] ([^\s]+)/; decrement_reference_count( $tableref->{$_->{target}}, $name1 ) if $_->{target};
} }
delete_chain $chainref; delete_chain $chainref;
@ -854,10 +1085,8 @@ sub move_rules( $$ ) {
# #
# We allow '+' in chain names and '+' is an RE meta-character. Escape it. # We allow '+' in chain names and '+' is an RE meta-character. Escape it.
# #
$name1 =~ s/\+/\\+/;
for ( @{$chain1->{rules}} ) { for ( @{$chain1->{rules}} ) {
adjust_reference_counts( $tableref->{$1}, $name1, $name2 ) if / -[jg] ([^\s]+)/; adjust_reference_counts( $tableref->{$_->{target}}, $name1, $name2 ) if $_->{target};
} }
# #
# We set aside the filtered rules for the time being # We set aside the filtered rules for the time being
@ -884,12 +1113,14 @@ sub move_rules( $$ ) {
# In a firewall->x policy chain, multiple DHCP ACCEPT rules can be moved to the head of the chain. # In a firewall->x policy chain, multiple DHCP ACCEPT rules can be moved to the head of the chain.
# This hack avoids that. # This hack avoids that.
# #
$_->{rule} = format_rule( $chain2, $_ ) for @$rules;
if ( $blacklist ) { if ( $blacklist ) {
my $rule = shift @{$rules}; my $rule = shift @{$rules};
shift @{$rules} while @{$rules} > 1 && $rules->[0] eq $rules->[1]; shift @{$rules} while @{$rules} > 1 && $rules->[0]{rule} eq $rules->[1]{rule};
unshift @{$rules}, $rule; unshift @{$rules}, $rule;
} else { } else {
shift @{$rules} while @{$rules} > 1 && $rules->[0] eq $rules->[1]; shift @{$rules} while @{$rules} > 1 && $rules->[0]{rule} eq $rules->[1]{rule};
} }
# #
@ -947,8 +1178,6 @@ sub copy_rules( $$;$ ) {
# #
# We allow '+' in chain names and '+' is an RE meta-character. Escape it. # We allow '+' in chain names and '+' is an RE meta-character. Escape it.
# #
$name1 =~ s/\+/\\+/;
pop @$rules2 unless $nojump; # Delete the jump to chain1 pop @$rules2 unless $nojump; # Delete the jump to chain1
if ( $blacklist2 && $blacklist1 ) { if ( $blacklist2 && $blacklist1 ) {
@ -957,9 +1186,7 @@ sub copy_rules( $$;$ ) {
# #
my $rule = shift @rules1; my $rule = shift @rules1;
$rule =~ / -j ([^\s])/; my $chainb = $rule->{target};
my $chainb = $1;
assert( $chainb =~ /^black/ ); assert( $chainb =~ /^black/ );
@ -972,7 +1199,7 @@ sub copy_rules( $$;$ ) {
# Chain2 is now a referent of all of Chain1's targets # Chain2 is now a referent of all of Chain1's targets
# #
for ( @rules1 ) { for ( @rules1 ) {
increment_reference_count( $tableref->{$1}, $name2 ) if / -[jg] ([^\s]+)/; increment_reference_count( $tableref->{$_->{target}}, $name2 ) if $_->{target};
} }
if ( $blacklist1 ) { if ( $blacklist1 ) {
@ -1361,7 +1588,7 @@ sub delete_jumps ( $$ ) {
# deleting elements from the array over which we are iterating. # deleting elements from the array over which we are iterating.
# #
for ( my $rule = 0; $rule <= $#{$rules}; $rule++ ) { for ( my $rule = 0; $rule <= $#{$rules}; $rule++ ) {
if ( $rules->[$rule] =~ / -[gj] ${to}(\s+-m comment .*)?\s*$/ ) { if ( $rules->[$rule]->{target} eq $to ) {
trace( $fromref, 'D', $rule + 1, $rules->[$rule] ) if $debug; trace( $fromref, 'D', $rule + 1, $rules->[$rule] ) if $debug;
splice( @$rules, $rule, 1 ); splice( @$rules, $rule, 1 );
last unless --$refs > 0; last unless --$refs > 0;
@ -1760,7 +1987,7 @@ sub optimize_chain( $ ) {
pop @$rules; # Pop the plain -j ACCEPT rule at the end of the chain pop @$rules; # Pop the plain -j ACCEPT rule at the end of the chain
pop @$rules, $count++ while @$rules && $rules->[-1] =~ /-j ACCEPT(?:$|\s)/; pop @$rules, $count++ while @$rules && $rules->[-1]->{target} eq 'ACCEPT';
if ( @${rules} ) { if ( @${rules} ) {
add_rule $chainref, '-j ACCEPT'; add_rule $chainref, '-j ACCEPT';
@ -1781,7 +2008,9 @@ sub optimize_chain( $ ) {
my $rule = 0; my $rule = 0;
for ( @{$fromref->{rules}} ) { for ( @{$fromref->{rules}} ) {
$rule++; $rule++;
if ( s/ -[jg] $chainref->{name}(\s|$)/ -j ACCEPT$1/ ) { if ( $_->{target} eq $chainref->{name} ) {
$_->{target} = 'ACCEPT';
$_->{jump} = 'j';
$count++; $count++;
trace( $chainref, 'R', $rule, $_ ) if $debug; trace( $chainref, 'R', $rule, $_ ) if $debug;
} }
@ -1823,55 +2052,45 @@ sub delete_references( $ ) {
# #
# Replace jumps to the passed chain with jumps to the passed target # Replace jumps to the passed chain with jumps to the passed target
# #
sub replace_references( $$ ) { sub replace_references( $$$ ) {
my ( $chainref, $target ) = @_; my ( $chainref, $target, $targetopts ) = @_;
my $tableref = $chain_table{$chainref->{table}}; my $tableref = $chain_table{$chainref->{table}};
my $count = 0; my $count = 0;
my $name = $chainref->{name}; my $name = $chainref->{name};
assert defined $target;
my $targetref = $tableref->{$target}; my $targetref = $tableref->{$target};
$name =~ s/\+/\\+/; for my $fromref ( map $tableref->{$_} , keys %{$chainref->{references}} ) {
if ( $fromref->{referenced} ) {
my $rule = 0;
for ( @{$fromref->{rules}} ) {
$rule++;
if ( $_->{target} eq $name ) {
$_->{target} = $target;
$_->{targetopts} = $targetopts if $targetopts;
if ( $targetref ) { if ( $targetref ) {
#
# The target is a chain -- use the jump type from each referencing rule
#
for my $fromref ( map $tableref->{$_} , keys %{$chainref->{references}} ) {
if ( $fromref->{referenced} ) {
my $rule = 0;
for ( @{$fromref->{rules}} ) {
$rule++;
if ( s/ -([jg]) $name(\s|$)/ -$1 ${target}$2/ ) {
add_reference ( $fromref, $targetref ); add_reference ( $fromref, $targetref );
$count++; } else {
trace( $fromref, 'R', $rule, $_ ) if $debug; $_->{jump} = 'j';
} }
}
delete $chainref->{references}{$fromref->{name}};
}
}
delete $targetref->{references}{$chainref->{name}}; $count++;
} else { trace( $fromref, 'R', $rule, $_ ) if $debug;
#
# The target is a builtin -- we must use '-j'
#
for my $fromref ( map $tableref->{$_} , keys %{$chainref->{references}} ) {
if ( $fromref->{referenced} ) {
my $rule = 0;
for ( @{$fromref->{rules}} ) {
$rule++;
if ( s/ -[jg] $name(\s|$)/ -j ${target}$1/ ) {
$count++ ;
trace( $fromref, 'R', $rule, $_ ) if $debug;
}
} }
delete $chainref->{references}{$fromref->{name}};
} }
#
# The passed chain is no longer referenced by chain $fromref
#
delete $chainref->{references}{$fromref->{name}};
} }
} }
#
# The passed chain is no longer referenced by the target chain
#
delete $targetref->{references}{$chainref->{name}} if $targetref;
progress_message " $count references to chain $chainref->{name} replaced" if $count; progress_message " $count references to chain $chainref->{name} replaced" if $count;
@ -1879,86 +2098,36 @@ sub replace_references( $$ ) {
} }
# #
# Replace jumps to the passed chain with jumps to the passed target while # Replace jumps to the passed chain with jumps to the target of the passed rule while merging
# adding the passed matches to the rule. # options and matches
# #
sub replace_references1( $$$ ) { sub replace_references1( $$ ) {
my ( $chainref, $target, $matches ) = @_; my ( $chainref, $ruleref ) = @_;
my $tableref = $chain_table{$chainref->{table}}; my $tableref = $chain_table{$chainref->{table}};
my $count = 0; my $count = 0;
my $name = $chainref->{name}; my $name = $chainref->{name};
# my $target = $ruleref->{target};
# The caller has ensured that $matches does not contain /! -[piosd] /
#
my $hasp = $matches =~ / -p /;
my $hasi = $matches =~ / -i /;
my $haso = $matches =~ / -o /;
my $hass = $matches =~ / -s /;
my $hasd = $matches =~ / -d /;
$name =~ s/\+/\\+/; for my $fromref ( map $tableref->{$_} , keys %{$chainref->{references}} ) {
# my $rule = 0;
# Note: If $matches is non-empty, then it begins with white space if ( $fromref->{referenced} ) {
# for ( @{$fromref->{rules}} ) {
if ( defined $tableref->{$target} && ! $tableref->{$target}{builtin} ) { $rule++;
# if ( ( $_->{target} || '' ) eq $name ) {
# The target is a chain -- use the jump type from each referencing rule #
# # The target is the passed chain -- merge the two rules into one
for my $fromref ( map $tableref->{$_} , keys %{$chainref->{references}} ) { #
if ( $fromref->{referenced} ) { merge_rules( $tableref, $_, $ruleref );
my $rule = 0;
for ( @{$fromref->{rules}} ) {
$rule++;
if ( /^-A .*-[jg] $name(?:$|\s)/ ) {
#
# Prevent multiple '-p', '-i', '-o', '-s' and '-d' matches
#
s/( !)? -p [^ ]+ / / if $hasp;
s/( !)? -i [^ ]+ / / if $hasi;
s/( !)? -o [^ ]+ / / if $haso;
s/( !)? -s [^ ]+ / / if $hass;
s/( !)? -d [^ ]+ / / if $hasd;
s/\s+-([jg]) $name($|\s)/$matches -$1 ${target}$2/; $count++;
add_reference ( $fromref, $tableref->{$target} ); trace( $fromref, 'R', $rule, $_ ) if $debug;
$count++;
trace( $fromref, 'R', $rule, $_ ) if $debug;
}
} }
} }
} }
delete $tableref->{target}{references}{$chainref->{name}}; delete $tableref->{$target}{references}{$chainref->{name}} if $tableref->{$target};
} else {
#
# The target is a builtin -- we must use '-j'
#
for my $fromref ( map $tableref->{$_} , keys %{$chainref->{references}} ) {
my $rule = 0;
if ( $fromref->{referenced} ) {
for ( @{$fromref->{rules}} ) {
$rule++;
if ( /^-A .*-[jg] $name(?:$|\s)/ ) {
#
# Prevent multiple '-p', '-i', '-o', '-s' and '-d' matches
#
s/( !)? -p [^ ]+ / / if $hasp;
s/( !)? -i [^ ]+ / / if $hasi;
s/( !)? -o [^ ]+ / / if $haso;
s/( !)? -s [^ ]+ / / if $hass;
s/( !)? -d [^ ]+ / / if $hasd;
s/\s+-[jg] $name($|\s)/$matches -j ${target}$1/;
$count++;
trace( $fromref, 'R', $rule, $_ ) if $debug;
}
}
}
}
} }
progress_message " $count references to chain $chainref->{name} replaced" if $count; progress_message " $count references to chain $chainref->{name} replaced" if $count;
delete_chain $chainref; delete_chain $chainref;
@ -1969,23 +2138,17 @@ sub replace_references1( $$$ ) {
# chain to the builtin and return true; otherwise, do nothing and return false. # chain to the builtin and return true; otherwise, do nothing and return false.
# #
sub conditionally_copy_rules( $$ ) { sub conditionally_copy_rules( $$ ) {
my ( $chainref, $target ) = @_; my ( $chainref, $basictarget ) = @_;
if ( $target =~ /^\s*([^\s]+)/ ) { my $targetref = $chain_table{$chainref->{table}}{$basictarget};
#
# 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} ) { if ( $targetref && ! $targetref->{dont_move} ) {
# #
# Move is safe -- start with an empty rule list # Move is safe -- start with an empty rule list
# #
$chainref->{rules} = []; $chainref->{rules} = [];
copy_rules( $targetref, $chainref ); copy_rules( $targetref, $chainref );
1; 1;
}
} }
} }
@ -1999,7 +2162,7 @@ sub check_optimization( $ ) {
my $chainref = shift; my $chainref = shift;
for ( @{$chainref->{rules}} ) { for ( @{$chainref->{rules}} ) {
dont_optimize $chainref, return 0 if / -s /; dont_optimize $chainref, return 0 if $_->{s};
} }
} }
@ -2016,7 +2179,7 @@ sub optimize_level4( $$ ) {
# #
# Make repeated passes through each table looking for short chains (those with less than 2 entries) # 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 unreferenced chain is found, itis deleted unless its 'dont_delete' flag is set.
# When an empty chain is found, delete the references to it. # 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 # When a chain with a single entry is found, replace it's references by its contents
# #
@ -2065,7 +2228,7 @@ sub optimize_level4( $$ ) {
# #
# Chain has a single rule # Chain has a single rule
# #
if ( $firstrule =~ /^-A -[jg] ([^\s]+)(\s+-m comment .*)?\s*$/ ) { if ( $firstrule ->{simple} ) {
# #
# Easy case -- the rule is a simple jump # Easy case -- the rule is a simple jump
# #
@ -2074,7 +2237,7 @@ sub optimize_level4( $$ ) {
# A built-in chain. If the target is a user chain without 'dont_move', # A built-in chain. If the target is a user chain without 'dont_move',
# we can copy its rules to the built-in # we can copy its rules to the built-in
# #
if ( conditionally_copy_rules $chainref, $1 ) { if ( conditionally_copy_rules $chainref, $firstrule->{target} ) {
# #
# Target was a user chain -- rules moved # Target was a user chain -- rules moved
# #
@ -2089,10 +2252,10 @@ sub optimize_level4( $$ ) {
# #
# Replace all references to this chain with references to the target # Replace all references to this chain with references to the target
# #
replace_references $chainref, $1; replace_references $chainref, $firstrule->{target}, $firstrule->{targetopts};
$progress = 1; $progress = 1;
} }
} elsif ( $firstrule =~ /-A(.+) -[jg] (.*)$/ ) { } elsif ( $firstrule->{target} ) {
# #
# Not so easy -- the rule contains matches # Not so easy -- the rule contains matches
# #
@ -2106,7 +2269,7 @@ sub optimize_level4( $$ ) {
# #
# Replace references to this chain with the target and add the matches # Replace references to this chain with the target and add the matches
# #
replace_references1 $chainref, $2, $1; replace_references1 $chainref, $firstrule;
$progress = 1; $progress = 1;
} }
} }
@ -2133,10 +2296,10 @@ sub optimize_level4( $$ ) {
for my $chainref ( @chains ) { for my $chainref ( @chains ) {
my $lastrule = $chainref->{rules}[-1]; my $lastrule = $chainref->{rules}[-1];
if ( defined $lastrule && $lastrule =~ /^-A -[jg] (.*)$/ ) { if ( defined $lastrule && $lastrule->{simple} ) {
# #
# Last rule is a simple branch # Last rule is a simple branch
my $targetref = $tableref->{$1}; my $targetref = $tableref->{$lastrule->{target}};
if ( $targetref && ! ( $targetref->{builtin} || $targetref->{dont_move} ) ) { if ( $targetref && ! ( $targetref->{builtin} || $targetref->{dont_move} ) ) {
copy_rules( $targetref, $chainref ); copy_rules( $targetref, $chainref );
@ -2162,6 +2325,12 @@ sub optimize_level8( $$$ ) {
$passes++; $passes++;
progress_message "\n Table $table pass $passes, $chains referenced user chains, level 8..."; progress_message "\n Table $table pass $passes, $chains referenced user chains, level 8...";
for my $chainref ( @chains ) {
for ( @{$chainref->{rules}} ) {
$_->{rule} = format_rule( $chainref, $_, 1 );
}
}
for my $chainref ( @chains ) { for my $chainref ( @chains ) {
my $rules = $chainref->{rules}; my $rules = $chainref->{rules};
@ -2181,10 +2350,10 @@ sub optimize_level8( $$$ ) {
next if $chainref1->{dont_delete}; next if $chainref1->{dont_delete};
for ( my $i = 0; $i < $numrules; $i++ ) { for ( my $i = 0; $i < $numrules; $i++ ) {
next CHAIN unless $rules->[$i] eq $rules1->[$i]; next CHAIN unless $rules->[$i]->{rule} eq $rules1->[$i]->{rule};
} }
replace_references1 $chainref1, $chainref->{name}, ''; replace_references $chainref1, $chainref->{name}, undef;
} }
} }
@ -2547,7 +2716,7 @@ sub mac_match( $ ) {
fatal_error "Invalid MAC address ($mac)" unless $mac =~ /^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/; fatal_error "Invalid MAC address ($mac)" unless $mac =~ /^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/;
"--match mac ${invert}--mac-source $mac "; "-m mac ${invert}--mac-source $mac ";
} }
# #
@ -4475,26 +4644,33 @@ sub enter_cmd_mode() {
# Emits the passed rule (input to iptables-restore) or command # Emits the passed rule (input to iptables-restore) or command
# #
sub emitr( $$ ) { sub emitr( $$ ) {
my ( $chain, $rule ) = @_; my ( $chainref, $ruleref ) = @_;
assert( $chain ); assert( $chainref );
if ( $rule ) { if ( $ruleref ) {
my $replaced = ($rule =~ s/((^|[ "])?)-A /$1-A $chain /); if ( $ruleref->{mode} == CAT_MODE ) {
if ( substr( $rule, 0, 2 ) eq '-A' ) {
# #
# A rule # A rule
# #
assert( $replaced);
enter_cat_mode unless $mode == CAT_MODE; enter_cat_mode unless $mode == CAT_MODE;
emit_unindented $rule; emit_unindented format_rule( $chainref, $ruleref );
} else { } else {
# #
# A command # A command
# #
enter_cmd_mode unless $mode == CMD_MODE; enter_cmd_mode unless $mode == CMD_MODE;
emit $rule;
if ( $ruleref->{cmd} ) {
emit join( '', ' ' x $ruleref->{cmdlevel}, $ruleref->{cmd} );
} else {
#
# Must preserve quotes in the rule
#
( my $rule = format_rule( $chainref, $ruleref ) ) =~ s/"/\\"/g;
emit join( '', ' ' x $ruleref->{cmdlevel} , 'echo "' , $rule, '" >&3' );
}
} }
} }
} }
@ -4517,21 +4693,25 @@ sub emitr1( $$ ) {
my ( $chain, $rule ) = @_; my ( $chain, $rule ) = @_;
if ( $rule ) { if ( $rule ) {
$rule =~ s/( ?)-A /$1-A $chain /; if ( $rule->{mode} == CAT_MODE ) {
if ( substr( $rule, 0, 2 ) eq '-A' ) {
# #
# A rule # A rule
# #
enter_cat_mode1 unless $mode == CAT_MODE; enter_cat_mode1 unless $mode == CAT_MODE;
print "$rule\n";
my $formated = format_rule $chain, $rule;
print "$formated\n";
} else { } else {
# #
# A command # A command
# #
enter_cmd_mode1 unless $mode == CMD_MODE; enter_cmd_mode1 unless $mode == CMD_MODE;
$rule =~ s/ >&3//;
emitstd $rule; if ( $rule->{cmd} ) {
emitstd $rule->{cmd};
} else {
emitstd format_rule $chain, $rule;
}
} }
} }
} }
@ -4776,8 +4956,7 @@ sub create_netfilter_load( $ ) {
# Then emit the rules # Then emit the rules
# #
for my $chainref ( @chains ) { for my $chainref ( @chains ) {
my $name = $chainref->{name}; emitr( $chainref, $_ ) for @{$chainref->{rules}};
emitr( $name, $_ ) for @{$chainref->{rules}};
} }
# #
# Commit the changes to the table # Commit the changes to the table
@ -4859,8 +5038,7 @@ sub preview_netfilter_load() {
# Then emit the rules # Then emit the rules
# #
for my $chainref ( @chains ) { for my $chainref ( @chains ) {
my $name = $chainref->{name}; emitr1($chainref, $_ ) for @{$chainref->{rules}};
emitr1($name, $_ ) for @{$chainref->{rules}};
} }
# #
# Commit the changes to the table # Commit the changes to the table
@ -4977,7 +5155,7 @@ sub create_chainlist_reload($) {
# #
# Emit the chain rules # Emit the chain rules
# #
emitr($name, $_) for @rules; emitr($chainref, $_) for @rules;
} }
# #
# Commit the changes to the table # Commit the changes to the table
@ -5084,8 +5262,7 @@ sub create_stop_load( $ ) {
# Then emit the rules # Then emit the rules
# #
for my $chainref ( @chains ) { for my $chainref ( @chains ) {
my $name = $chainref->{name}; emitr( $chainref, $_ ) for @{$chainref->{rules}};
emitr( $name, $_ ) for @{$chainref->{rules}};
} }
# #
# Commit the changes to the table # Commit the changes to the table

View File

@ -242,6 +242,7 @@ my %capdesc = ( NAT_ENABLED => 'NAT',
OWNER_MATCH => 'Owner Match', OWNER_MATCH => 'Owner Match',
IPSET_MATCH => 'Ipset Match', IPSET_MATCH => 'Ipset Match',
OLD_IPSET_MATCH => 'Old Ipset Match', OLD_IPSET_MATCH => 'Old Ipset Match',
IPSET_V5 => 'Version 5 ipsets',
CONNMARK => 'CONNMARK Target', CONNMARK => 'CONNMARK Target',
XCONNMARK => 'Extended CONNMARK Target', XCONNMARK => 'Extended CONNMARK Target',
CONNMARK_MATCH => 'Connmark Match', CONNMARK_MATCH => 'Connmark Match',
@ -635,10 +636,10 @@ sub initialize( $ ) {
CONNLIMIT_MATCH => undef, CONNLIMIT_MATCH => undef,
TIME_MATCH => undef, TIME_MATCH => undef,
GOTO_TARGET => undef, GOTO_TARGET => undef,
LOG_TARGET => 1, # Assume that we have it.
LOGMARK_TARGET => undef, LOGMARK_TARGET => undef,
IPMARK_TARGET => undef, IPMARK_TARGET => undef,
TPROXY_TARGET => undef, TPROXY_TARGET => undef,
LOG_TARGET => 1, # Assume that we have it.
PERSISTENT_SNAT => undef, PERSISTENT_SNAT => undef,
OLD_HL_MATCH => undef, OLD_HL_MATCH => undef,
FLOW_FILTER => undef, FLOW_FILTER => undef,
@ -806,7 +807,7 @@ sub fatal_error1 {
# #
# C/C++-like assertion checker # C/C++-like assertion checker
# #
sub assert( $ ) { sub assert( $;$ ) {
unless ( $_[0] ) { unless ( $_[0] ) {
my @caller0 = caller 0; # Where assert() was called my @caller0 = caller 0; # Where assert() was called
my @caller1 = caller 1; # Who called assert() my @caller1 = caller 1; # Who called assert()

View File

@ -488,8 +488,8 @@ sub add_common_rules() {
my $rejectref = $filter_table->{reject}; my $rejectref = $filter_table->{reject};
if ( $config{DYNAMIC_BLACKLIST} ) { if ( $config{DYNAMIC_BLACKLIST} ) {
add_rule_pair dont_delete( new_standard_chain( 'logdrop' ) ), ' ' , 'DROP' , $level ; add_rule_pair dont_delete( new_standard_chain( 'logdrop' ) ), '' , 'DROP' , $level ;
add_rule_pair dont_delete( new_standard_chain( 'logreject' ) ), ' ' , 'reject' , $level ; add_rule_pair dont_delete( new_standard_chain( 'logreject' ) ), '' , 'reject' , $level ;
$dynamicref = dont_optimize( new_standard_chain( 'dynamic' ) ); $dynamicref = dont_optimize( new_standard_chain( 'dynamic' ) );
add_jump $filter_table->{INPUT}, $dynamicref, 0, $state; add_jump $filter_table->{INPUT}, $dynamicref, 0, $state;
add_commands( $dynamicref, '[ -f ${VARDIR}/.dynamic ] && cat ${VARDIR}/.dynamic >&3' ); add_commands( $dynamicref, '[ -f ${VARDIR}/.dynamic ] && cat ${VARDIR}/.dynamic >&3' );
@ -992,11 +992,12 @@ sub setup_mac_lists( $ ) {
my $variable = get_interface_addresses source_port_to_bridge( $interface ); my $variable = get_interface_addresses source_port_to_bridge( $interface );
if ( have_capability( 'ADDRTYPE' ) ) { if ( have_capability( 'ADDRTYPE' ) ) {
add_commands( $chainref, add_commands( $chainref, "for address in $variable; do" );
"for address in $variable; do", incr_cmd_level( $chainref );
" echo \"-A -s \$address -m addrtype --dst-type BROADCAST -j RETURN\" >&3", add_rule( $chainref, '-s $address -m addrtype --dst-type BROADCAST -j RETURN' );
" echo \"-A -s \$address -d 224.0.0.0/4 -j RETURN\" >&3", add_rule( $chainref, '-s $address -d 224.0.0.0/4 -j RETURN' );
'done' ); decr_cmd_level( $chainref );
add_commands( $chainref, 'done' );
} else { } else {
my $bridge = source_port_to_bridge( $interface ); my $bridge = source_port_to_bridge( $interface );
my $bridgeref = find_interface( $bridge ); my $bridgeref = find_interface( $bridge );

View File

@ -1095,7 +1095,7 @@ sub handle_stickiness( $ ) {
my $base = uc chain_base $interface; my $base = uc chain_base $interface;
my $mark = $providerref->{mark}; my $mark = $providerref->{mark};
for ( grep /-j sticky/, @{$tcpreref->{rules}} ) { for ( grep rule_target($_) eq 'sticky', @{$tcpreref->{rules}} ) {
my $stickyref = ensure_mangle_chain 'sticky'; my $stickyref = ensure_mangle_chain 'sticky';
my ( $rule1, $rule2 ); my ( $rule1, $rule2 );
my $list = sprintf "sticky%03d" , $sticky++; my $list = sprintf "sticky%03d" , $sticky++;
@ -1103,26 +1103,32 @@ sub handle_stickiness( $ ) {
for my $chainref ( $stickyref, $setstickyref ) { for my $chainref ( $stickyref, $setstickyref ) {
if ( $chainref->{name} eq 'sticky' ) { if ( $chainref->{name} eq 'sticky' ) {
$rule1 = $_; $rule1 = $_;
$rule1 =~ s/-j sticky/-m recent --name $list --update --seconds 300 -j MARK --set-mark $mark/;
set_rule_target( $rule1, 'MARK', "--set-mark $mark" );
set_rule_option( $rule1, 'recent', "--name $list --update --seconds 300" );
$rule2 = $_; $rule2 = $_;
$rule2 =~ s/-j sticky/-m mark --mark 0\/$mask -m recent --name $list --remove/;
clear_rule_target( $rule2 );
set_rule_option( $rule2, 'mark', "--mark 0/$mask -m recent --name $list --remove" );
} else { } else {
$rule1 = $_; $rule1 = $_;
$rule1 =~ s/-j sticky/-m mark --mark $mark\/$mask -m recent --name $list --set/;
clear_rule_target( $rule1 );
set_rule_option( $rule1, 'mark', "--mark $mark\/$mask -m recent --name $list --set" );
$rule2 = ''; $rule2 = '';
} }
assert ( $rule1 =~ s/^-A // ); add_transformed_rule $chainref, $rule1;
add_rule $chainref, $rule1;
if ( $rule2 ) { if ( $rule2 ) {
assert ( $rule2 =~ s/^-A // ); add_transformed_rule $chainref, $rule2;
add_rule $chainref, $rule2;
} }
} }
} }
for ( grep /-j sticko/, @{$tcoutref->{rules}} ) { for ( grep rule_target( $_ ) eq 'sticko', , @{$tcoutref->{rules}} ) {
my ( $rule1, $rule2 ); my ( $rule1, $rule2 );
my $list = sprintf "sticky%03d" , $sticky++; my $list = sprintf "sticky%03d" , $sticky++;
my $stickoref = ensure_mangle_chain 'sticko'; my $stickoref = ensure_mangle_chain 'sticko';
@ -1130,21 +1136,27 @@ sub handle_stickiness( $ ) {
for my $chainref ( $stickoref, $setstickoref ) { for my $chainref ( $stickoref, $setstickoref ) {
if ( $chainref->{name} eq 'sticko' ) { if ( $chainref->{name} eq 'sticko' ) {
$rule1 = $_; $rule1 = $_;
$rule1 =~ s/-j sticko/-m recent --name $list --rdest --update --seconds 300 -j MARK --set-mark $mark/;
set_rule_target( $rule1, 'MARK', "--set-mark $mark" );
set_rule_option( $rule1, 'recent', " --name $list --rdest --update --seconds 300 -j MARK --set-mark $mark" );
$rule2 = $_; $rule2 = $_;
$rule2 =~ s/-j sticko/-m mark --mark 0\/$mask -m recent --name $list --rdest --remove/;
clear_rule_target( $rule2 );
set_rule_option ( $rule2, 'mark', "--mark 0\/$mask -m recent --name $list --rdest --remove" );
} else { } else {
$rule1 = $_; $rule1 = $_;
$rule1 =~ s/-j sticko/-m mark --mark $mark -m recent --name $list --rdest --set/;
clear_rule_target( $rule1 );
set_rule_option ( $rule1, 'mark', "--mark $mark -m recent --name $list --rdest --set" );
$rule2 = ''; $rule2 = '';
} }
assert( $rule1 =~ s/-A // ); add_transformed_rule $chainref, $rule1;
add_rule $chainref, $rule1;
if ( $rule2 ) { if ( $rule2 ) {
$rule2 =~ s/-A //; add_transformed_rule $chainref, $rule2;
add_rule $chainref, $rule2;
} }
} }
} }

View File

@ -704,7 +704,7 @@ sub optimize_policy_chains() {
# #
my $outputrules = $filter_table->{OUTPUT}{rules}; my $outputrules = $filter_table->{OUTPUT}{rules};
if ( @{$outputrules} && $outputrules->[-1] =~ /-j ACCEPT/ ) { if ( @{$outputrules} && $outputrules->[-1]->{target} eq 'ACCEPT' ) {
optimize_chain( $filter_table->{OUTPUT} ); optimize_chain( $filter_table->{OUTPUT} );
} }