diff --git a/Shorewall/Perl/Shorewall/Accounting.pm b/Shorewall/Perl/Shorewall/Accounting.pm index c7908b92e..fe9203eed 100644 --- a/Shorewall/Perl/Shorewall/Accounting.pm +++ b/Shorewall/Perl/Shorewall/Accounting.pm @@ -190,6 +190,7 @@ sub process_accounting_rule1( $$$$$$$$$$$ ) { fatal_error "USER/GROUP may only be specified in the OUTPUT section" unless $user eq '-' || $asection == OUTPUT; my $rule = do_proto( $proto, $ports, $sports ) . do_user ( $user ) . do_test ( $mark, $globals{TC_MASK} ) . do_headers( $headers ); + my $prerule = ''; my $rule2 = 0; my $jump = 0; @@ -222,11 +223,17 @@ sub process_accounting_rule1( $$$$$$$$$$$ ) { } } elsif ( $action =~ /^NFLOG/ ) { $target = validate_level $action; - } elsif ( $action =~ /^NFACCT\(([\w,]+)\)$/ ) { + } elsif ( $action =~ /^NFACCT\(([\w,]+)\)(!)?$/ ) { require_capability 'NFACCT_MATCH', 'The NFACCT action', 's'; $target = ''; my @objects = split_list $1, 'nfacct'; - $rule .= "-m nfacct --nfacct-name $_ ", $nfobjects{$_} = 1 for @objects; + if ( $2 ) { + $prerule .= "-m nfacct --nfacct-name $_ " for @objects; + } else { + $rule .= "-m nfacct --nfacct-name $_ " for @objects; + } + } elsif ( $action eq 'INLINE' ) { + $rule .= get_inline_matches; } else { ( $action, my $cmd ) = split /:/, $action; @@ -267,6 +274,7 @@ sub process_accounting_rule1( $$$$$$$$$$$ ) { expand_rule( ensure_rules_chain ( 'accountout' ) , OUTPUT_RESTRICT , + $prerule , $rule , $source , $dest = ALLIP , @@ -360,6 +368,7 @@ sub process_accounting_rule1( $$$$$$$$$$$ ) { expand_rule $chainref , $restriction , + $prerule , $rule , $source , $dest , @@ -385,17 +394,18 @@ sub process_accounting_rule1( $$$$$$$$$$$ ) { } if ( $rule2 ) { - expand_rule - $jumpchainref , - $restriction , - $rule , - $source , - $dest , - '' , - '' , - '' , - '' , - '' ; + expand_rule( + $jumpchainref , + $restriction , + $prerule , + $rule , + $source , + $dest , + '' , + '' , + '' , + '' , + '' ); } return 1; diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index 4ec5df0e2..03f01c264 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -257,7 +257,6 @@ our %EXPORT_TAGS = ( %targets %builtin_target %dscpmap - %nfobjects ) ], ); @@ -611,13 +610,13 @@ use constant { UNIQUE => 1, MATCH => 8, CONTROL => 16, COMPLEX => 32, - LAST => 64, + NFACCT => 64, }; our %opttype = ( rule => CONTROL, cmd => CONTROL, - dhcp => UNIQUE, + dhcp => CONTROL, mode => CONTROL, cmdlevel => CONTROL, @@ -642,14 +641,13 @@ our %opttype = ( rule => CONTROL, 'conntrack --ctstate' => EXCLUSIVE, + nfacct => NFACCT, + conntrack => COMPLEX, jump => TARGET, target => TARGET, targetopts => TARGET, - - nfacct => LAST, - set => LAST, ); our %aliases = ( protocol => 'p', @@ -775,7 +773,14 @@ sub decr_cmd_level( $ ) { # iptables command strings which are converted into the new form by # transform_rule() # -# First a helper for setting an individual option +# First a helper for recording an nfacct object name +# +sub record_nfobject( $ ) { + my @value = split ' ', $_[0]; + $nfobjects{$value[-1]} = 1; +} + +# # Next a helper for setting an individual option # sub set_rule_option( $$$ ) { my ( $ruleref, $option, $value ) = @_; @@ -808,7 +813,7 @@ sub set_rule_option( $$$ ) { if ( exists $ruleref->{$option} ) { assert( defined( my $value1 = $ruleref->{$option} ) , $ruleref ); - if ( $opttype == MATCH || $opttype == LAST ) { + if ( $opttype & ( MATCH | NFACCT ) ) { if ( $globals{KLUDGEFREE} ) { unless ( reftype $value1 ) { unless ( reftype $value ) { @@ -821,6 +826,8 @@ sub set_rule_option( $$$ ) { push @{$ruleref->{$option}}, ( reftype $value ? @$value : $value ); push @{$ruleref->{matches}}, $option; $ruleref->{complex} = 1; + + record_nfobject( $value ) if $opttype == NFACCT; } else { assert( ! reftype $value ); $ruleref->{$option} = join(' ', $value1, $value ) unless $value1 eq $value; @@ -844,6 +851,7 @@ sub set_rule_option( $$$ ) { } else { $ruleref->{$option} = $value; push @{$ruleref->{matches}}, $option; + record_nfobject( $value ) if $opttype == NFACCT; } } @@ -992,8 +1000,12 @@ sub format_rule( $$;$ ) { # my $ruleref = $rulerefp->{complex} ? clone_rule( $rulerefp ) : $rulerefp; - for ( @unique_options ) { - if ( exists $ruleref->{$_} ) { + for ( @{$ruleref->{matches}} ) { + my $type = $opttype{$_} || 0; + + next if $type & ( CONTROL | TARGET ); + + if ( $type == UNIQUE ) { my $value = $ruleref->{$_}; $rule .= ' !' if $value =~ s/^! //; @@ -1003,25 +1015,13 @@ sub format_rule( $$;$ ) { } else { $rule .= join( '' , ' --', $_, ' ', $value ); } + + next; + } else { + $rule .= format_option( $_, pop_match( $ruleref, $_ ) ); } } - $rule .= format_option( 'policy', $ruleref->{policy} ) if defined $ruleref->{policy}; - - if ( defined ( my $state = $ruleref->{'conntrack --ctstate'} ) ) { - $rule .= format_option( 'conntrack --ctstate' , $state ); - } elsif ( defined ( $state = $ruleref->{state} ) ) { - $rule .= format_option( 'state', $state ); - } - - for ( grep ! $opttype{$_}, @{$ruleref->{matches}} ) { - $rule .= format_option( $_, pop_match( $ruleref, $_ ) ); - } - - for ( grep( ( $opttype{$_} || 0 ) == LAST , @{$ruleref->{matches}} ) ) { - $rule .= format_option( $_, pop_match( $ruleref, $_ ) ); - } - if ( $ruleref->{target} ) { $rule .= join( ' ', " -$ruleref->{jump}", $ruleref->{target} ); $rule .= join( '', ' ', $ruleref->{targetopts} ) if $ruleref->{targetopts}; @@ -1075,8 +1075,13 @@ sub merge_rules( $$$ ) { my $target = $fromref->{target}; + my %added; + for my $option ( @unique_options ) { - $toref->{$option} = $fromref->{$option} if exists $fromref->{$option}; + if ( exists $fromref->{$option} ) { + push( @{$toref->{matches}}, $option ) unless exists $toref->{$option}; + $toref->{$option} = $fromref->{$option}; + } } for my $option ( grep ! $opttype{$_}, keys %$fromref ) { @@ -1095,10 +1100,6 @@ sub merge_rules( $$$ ) { set_rule_option( $toref, 'policy', $fromref->{policy} ) if exists $fromref->{policy}; - for my $option ( grep( ( $opttype{$_} || 0 ) == LAST, keys %$fromref ) ) { - set_rule_option( $toref, $option, $fromref->{$option} ); - } - unless ( $toref->{comment} ) { $toref->{comment} = $fromref->{comment} if exists $fromref->{comment}; } @@ -5436,7 +5437,7 @@ sub match_source_net( $;$\$ ) { if ( $3 ) { require_capability 'NFACCT_MATCH', "An nfacct object list ($3)", 's'; my @objects = split_list $3, 'nfacct'; - $result .= "-m nfacct --nfacct-name $_ ", $nfobjects{$_} = 1 for @objects; + $result .= "-m nfacct --nfacct-name $_ " for @objects; } return $result; @@ -5454,7 +5455,7 @@ sub match_source_net( $;$\$ ) { if ( $3 ) { require_capability 'NFACCT_MATCH', "An nfacct object list ($3)", 's'; my @objects = split_list $3, 'nfacct'; - $result .= "-m nfacct --nfacct-name $_ ", $nfobjects{$_} = 1 for @objects; + $result .= "-m nfacct --nfacct-name $_ " for @objects; } } @@ -5602,7 +5603,7 @@ sub match_dest_net( $;$ ) { if ( $3 ) { require_capability 'NFACCT_MATCH', "An nfacct object list ($3)", 's'; my @objects = split_list $3, 'nfacct'; - $result .= "-m nfacct --nfacct-name $_ ", $nfobjects{$_} = 1 for @objects; + $result .= "-m nfacct --nfacct-name $_ " for @objects; } return $result; @@ -5622,7 +5623,7 @@ sub match_dest_net( $;$ ) { if ( $3 ) { require_capability 'NFACCT_MATCH', "An nfacct object list ($3)", 's'; my @objects = split_list $3, 'nfacct'; - $result .= "-m nfacct --nfacct-name $_ ", $nfobjects{$_} = 1 for @objects; + $result .= "-m nfacct --nfacct-name $_ " for @objects; } return $result; @@ -6803,9 +6804,10 @@ sub handle_original_dest( $$$ ) { # # Handles non-trivial exclusion. Updates the passed rule and returns ( $rule, $done ) # -sub handle_exclusion( $$$$$$$$$$$$$$$$$$ ) { +sub handle_exclusion( $$$$$$$$$$$$$$$$$$$ ) { my ( $disposition, $table, + $prerule, $rule, $restriction, $inets, @@ -6887,7 +6889,7 @@ sub handle_exclusion( $$$$$$$$$$$$$$$$$$ ) { for my $dnet ( split_host_list( $dnets, $config{DEFER_DNS_RESOLUTION} ) ) { $source_match = match_source_net( $inet, $restriction, $mac ) unless $globals{KLUDGEFREE}; - add_expanded_jump( $chainref, $echainref, 0, join( '', $rule, $source_match, match_dest_net( $dnet, $restriction ), $onet ) ); + add_expanded_jump( $chainref, $echainref, 0, join( '', $prerule, $source_match, match_dest_net( $dnet, $restriction ), $onet, $rule ) ); } conditional_rule_end( $chainref ) if $cond; @@ -6947,11 +6949,12 @@ sub handle_exclusion( $$$$$$$$$$$$$$$$$$ ) { # # Returns the destination interface specified in the rule, if any. # -sub expand_rule( $$$$$$$$$$;$ ) +sub expand_rule( $$$$$$$$$$$;$ ) { my ($chainref , # Chain $restriction, # Determines what to do with interface names in the SOURCE or DEST - $callersrule, # Caller's matches that don't depend on the SOURCE, DEST and ORIGINAL DEST + $prerule, # Matches that go at the front of the rule + $rule, # Caller's matches that don't depend on the SOURCE, DEST and ORIGINAL DEST $source, # SOURCE $dest, # DEST $origdest, # ORIGINAL DEST @@ -6971,7 +6974,6 @@ sub expand_rule( $$$$$$$$$$;$ ) my ( $jump, $mac, $targetref, $basictarget ); our @ends = (); my $deferdns = $config{DEFER_DNS_RESOLUTION}; - my $rule = ''; if ( $target ) { ( $basictarget, my $rest ) = split ' ', $target, 2; @@ -7078,7 +7080,8 @@ sub expand_rule( $$$$$$$$$$;$ ) # ( $rule, $done ) = handle_exclusion( $disposition, $table, - $rule . $callersrule, + $prerule, + $rule, $restriction, $inets, $iexcl, @@ -7115,7 +7118,7 @@ sub expand_rule( $$$$$$$$$$;$ ) for my $dnet ( split_host_list( $dnets, $deferdns ) ) { $source_match = match_source_net( $inet, $restriction, $mac ) unless $globals{KLUDGEFREE}; my $dest_match = match_dest_net( $dnet, $restriction ); - my $matches = join( '', $source_match, $dest_match, $onet, $rule, $callersrule ); + my $matches = join( '', $source_match, $dest_match, $onet, $rule ); my $cond3 = conditional_rule( $chainref, $dnet ); @@ -7126,7 +7129,7 @@ sub expand_rule( $$$$$$$$$$;$ ) if ( $targetref ) { add_expanded_jump( $chainref, $targetref , 0, $matches ); } else { - add_rule( $chainref, $matches . $jump , 1 ); + add_rule( $chainref, $prerule . $matches . $jump , 1 ); } } elsif ( $disposition eq 'LOG' || $disposition eq 'COUNT' ) { # diff --git a/Shorewall/Perl/Shorewall/Misc.pm b/Shorewall/Perl/Shorewall/Misc.pm index 976009fb6..241a35016 100644 --- a/Shorewall/Perl/Shorewall/Misc.pm +++ b/Shorewall/Perl/Shorewall/Misc.pm @@ -118,6 +118,7 @@ sub process_tos() { expand_rule $chainref , $restriction , + '', do_proto( $proto, $ports, $sports ) . do_test( $mark , $globals{TC_MASK} ) , $src , $dst , @@ -283,6 +284,7 @@ sub setup_blacklist() { expand_rule( $chainref , NO_RESTRICT , + '' , do_proto( $protocol , $ports, '' ) , $networks, '', @@ -303,6 +305,7 @@ sub setup_blacklist() { expand_rule( $chainref1 , NO_RESTRICT , + '' , do_proto( $protocol , $ports, '' ) , '', $networks, @@ -733,6 +736,7 @@ sub process_stoppedrules() { for my $proto ( split_list $protos, 'Protocol' ) { expand_rule( $chainref , $restriction , + '' , do_proto( $proto, $ports, $sports ) , $source , $dest , diff --git a/Shorewall/Perl/Shorewall/Nat.pm b/Shorewall/Perl/Shorewall/Nat.pm index 46dbf9f7f..4a5d5f4ce 100644 --- a/Shorewall/Perl/Shorewall/Nat.pm +++ b/Shorewall/Perl/Shorewall/Nat.pm @@ -324,6 +324,7 @@ sub process_one_masq1( $$$$$$$$$$ ) # expand_rule( $chainref , POSTROUTE_RESTRICT , + '' , $baserule . $rule , $networks , $destnets , @@ -757,6 +758,7 @@ sub handle_nat_rule( $$$$$$$$$$$$ ) { $firewallsource ? 'OUTPUT' : dnat_chain $sourceref->{name} ) ) , $firewallsource ? OUTPUT_RESTRICT : PREROUTE_RESTRICT , + '' , $rule , $source , $origdest , @@ -826,6 +828,7 @@ sub handle_nonat_rule( $$$$$$$$$$ ) { # expand_rule( $chn, PREROUTE_RESTRICT, + '', # Prerule '', # Rule '', # Source '', # Dest @@ -844,6 +847,7 @@ sub handle_nonat_rule( $$$$$$$$$$ ) { expand_rule( $nonat_chain , PREROUTE_RESTRICT , + '' , $rule , $source , $dest , diff --git a/Shorewall/Perl/Shorewall/Raw.pm b/Shorewall/Perl/Shorewall/Raw.pm index 073e36676..20de9dec2 100644 --- a/Shorewall/Perl/Shorewall/Raw.pm +++ b/Shorewall/Perl/Shorewall/Raw.pm @@ -143,6 +143,7 @@ sub process_conntrack_rule( $$$$$$$$$$ ) { expand_rule( $chainref , $restriction , + '', $rule, $source , $dest , @@ -185,6 +186,7 @@ sub handle_helper_rule( $$$$$$$$$$$ ) { # expand_rule( ensure_raw_chain( $actionchain ) , PREROUTE_RESTRICT , + '', $rule , $source , $dest , @@ -198,6 +200,7 @@ sub handle_helper_rule( $$$$$$$$$$$ ) { ( $sourceref->{type} == FIREWALL || $sourceref->{type} == VSERVER ? OUTPUT_RESTRICT : PREROUTE_RESTRICT ) , + '' , $rule , $source , $dest , diff --git a/Shorewall/Perl/Shorewall/Rules.pm b/Shorewall/Perl/Shorewall/Rules.pm index 3b85a73b7..a349cf828 100644 --- a/Shorewall/Perl/Shorewall/Rules.pm +++ b/Shorewall/Perl/Shorewall/Rules.pm @@ -2666,6 +2666,7 @@ sub process_rule ( $$$$$$$$$$$$$$$$$$$ ) { expand_rule( $chainref , $restriction , + '' , $rule , $source , $dest , diff --git a/Shorewall/Perl/Shorewall/Tc.pm b/Shorewall/Perl/Shorewall/Tc.pm index f337dc981..c79e3a023 100644 --- a/Shorewall/Perl/Shorewall/Tc.pm +++ b/Shorewall/Perl/Shorewall/Tc.pm @@ -634,6 +634,7 @@ sub process_tc_rule1( $$$$$$$$$$$$$$$$ ) { expand_rule( $chainref, $restrictions{$chain} | $restriction, + '' , $match . do_user( $user ) . do_test( $testval, $globals{TC_MASK} ) . @@ -656,6 +657,7 @@ sub process_tc_rule1( $$$$$$$$$$$$$$$$ ) { } } elsif ( ( my $result = expand_rule( ensure_chain( 'mangle' , $chain ) , $restrictions{$chain} | $restriction, + '', do_proto( $proto, $ports, $sports) . $matches . do_user( $user ) . do_test( $testval, $globals{TC_MASK} ) . @@ -2344,6 +2346,7 @@ sub process_secmark_rule1( $$$$$$$$$ ) { expand_rule( ensure_mangle_chain( $chain1 ) , $restrictions{$chain1} , + '' , $state . do_proto( $proto, $dport, $sport ) . do_user( $user ) . diff --git a/Shorewall/manpages/shorewall-accounting.xml b/Shorewall/manpages/shorewall-accounting.xml index 0de8b7a3a..57b2ba2ec 100644 --- a/Shorewall/manpages/shorewall-accounting.xml +++ b/Shorewall/manpages/shorewall-accounting.xml @@ -293,9 +293,20 @@ + + INLINE + + + Added in Shorewall 4.5.16. Allows freeform iptables + matches to be specified following a ';'. In the generated + iptables rule(s), the freeform matches will follow any matches + that are generated by the column contents. + + + NFACCT(object[,...]) + role="bold">NFACCT(object[,...])[!] Added in Shorewall 4.5.7. Provides a form of accounting @@ -312,6 +323,13 @@ object could be specified. Beginning with Shorewall 4.5.16, an arbitrary number of objects may be given. + + With Shorewall 4.5.16 or later, ! may be specified to indicate that the + nfacct object(s) will be + incremented unconditionally. When omitted, the + object(s) will be incremented only + if all of the matches in the rule succeed. diff --git a/Shorewall6/manpages/shorewall6-accounting.xml b/Shorewall6/manpages/shorewall6-accounting.xml index f2ba1ca37..f85f16c92 100644 --- a/Shorewall6/manpages/shorewall6-accounting.xml +++ b/Shorewall6/manpages/shorewall6-accounting.xml @@ -235,9 +235,20 @@ + + INLINE + + + Added in Shorewall 4.5.16. Allows freeform ip6tables + matches to be specified following a ';'. In the generated + ip6tables rule(s), the freeform matches will follow any + matches that are generated by the column contents. + + + NFACCT(object[,...]) + role="bold">NFACCT(object[,...])[!] Added in Shorewall 4.5.7. Provides a form of accounting @@ -254,6 +265,13 @@ object could be specified. Beginning with Shorewall 4.5.16, an arbitrary number of objects may be given. + + With Shorewall 4.5.16 or later, ! may be specified to indicate that the + nfacct object(s) will be + incremented unconditionally. When omitted, the + object(s) will be incremented only + if all of the matches in the rule succeed.