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.