diff --git a/Shorewall-init/shorewall-init.spec b/Shorewall-init/shorewall-init.spec index 4e6a192ec..335fb1687 100644 --- a/Shorewall-init/shorewall-init.spec +++ b/Shorewall-init/shorewall-init.spec @@ -119,8 +119,10 @@ fi %doc COPYING changelog.txt releasenotes.txt %changelog -* Wed Dec 22 2010 Tom Eastep tom@shorewall.net +* Sun Dec 26 2010 Tom Eastep tom@shorewall.net - Updated to 4.4.16-0RC1 +* Sun Dec 26 2010 Tom Eastep tom@shorewall.net +- Updated to 4.4.16-0Beta7 * Mon Dec 20 2010 Tom Eastep tom@shorewall.net - Updated to 4.4.16-0Beta6 * Fri Dec 10 2010 Tom Eastep tom@shorewall.net diff --git a/Shorewall-lite/shorewall-lite.spec b/Shorewall-lite/shorewall-lite.spec index 009df0a85..28b113679 100644 --- a/Shorewall-lite/shorewall-lite.spec +++ b/Shorewall-lite/shorewall-lite.spec @@ -102,8 +102,10 @@ fi %doc COPYING changelog.txt releasenotes.txt %changelog -* Wed Dec 22 2010 Tom Eastep tom@shorewall.net +* Sun Dec 26 2010 Tom Eastep tom@shorewall.net - Updated to 4.4.16-0RC1 +* Sun Dec 26 2010 Tom Eastep tom@shorewall.net +- Updated to 4.4.16-0Beta7 * Mon Dec 20 2010 Tom Eastep tom@shorewall.net - Updated to 4.4.16-0Beta6 * Fri Dec 10 2010 Tom Eastep tom@shorewall.net diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index 28d2ece30..978a3d591 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -2040,8 +2040,7 @@ sub logchain( $$$$$$ ) { $logtag, 'add', '' ); - - add_rule( $logchainref, $exceptionrule . $target ); + add_jump( $logchainref, $target, 0, $exceptionrule ); } $logchainref; @@ -3849,9 +3848,11 @@ sub expand_rule( $$$$$$$$$$;$ ) # # 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 - # + # + assert( $target ); + add_jump( $chainref, - logchain( $chainref, $loglevel, $logtag, $exceptionrule , $disposition, $jump ), + logchain( $chainref, $loglevel, $logtag, $exceptionrule , $disposition, $target ), $builtin_target{$disposition}, $matches, 1 ); diff --git a/Shorewall/Perl/Shorewall/Compiler.pm b/Shorewall/Perl/Shorewall/Compiler.pm index 5a846153d..062a5546c 100644 --- a/Shorewall/Perl/Shorewall/Compiler.pm +++ b/Shorewall/Perl/Shorewall/Compiler.pm @@ -637,6 +637,10 @@ sub compiler { # validate_policy; # + # Process default actions + # + process_actions2; + # # N O T R A C K # (Produces no output to the compiled script) # @@ -761,7 +765,6 @@ sub compiler { # # Post-rules action processing. # - process_actions2; process_actions3; # # MACLIST Filtration again diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm index fb91c2908..19be2d1f2 100644 --- a/Shorewall/Perl/Shorewall/Config.pm +++ b/Shorewall/Perl/Shorewall/Config.pm @@ -96,6 +96,8 @@ our %EXPORT_TAGS = ( internal => [ qw( create_temp_script close_file push_open pop_open + push_params + pop_params read_a_line validate_level which @@ -274,6 +276,10 @@ our @openstack; # From the params file # our %params; +# +# Action parameters +# +our %actparms; our $currentline; # Current config file line image our $currentfile; # File handle reference @@ -717,6 +723,8 @@ sub initialize( $ ) { command => '', files => '', destination => '' ); + + %actparms = (); } my @abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); @@ -1781,6 +1789,29 @@ sub embedded_perl( $ ) { } } +# +# Push/pop action params +# +sub push_params( $ ) { + my @params = split /,/, $_[0]; + my $oldparams = \%actparms; + + %actparms = (); + + for ( my $i = 1; $i <= @params; $i++ ) { + my $val = $params[$i - 1]; + + $actparms{$i} = $val eq '-' ? '' : $val eq '--' ? '-' : $val; + } + + $oldparams; +} + +sub pop_params( $ ) { + my $oldparms = shift; + %actparms = %$oldparms; +} + # # Read a line from the current include stack. # @@ -1856,24 +1887,23 @@ sub read_a_line(;$) { # # Expand Shell Variables using %params and %ENV # - # $1 $2 $3 - $4 + # $1 $2 $3 - $4 while ( $currentline =~ m( ^(.*?) \$({)? (\w+) (?(2)}) (.*)$ )x ) { - unless ( exists $params{$3} ) { - # - # Given the way that getparams works, this should never help but better safe than sorry - # - $params{$3} = $ENV{$3} if exists $ENV{$3}; + my ( $first, $var, $rest ) = ( $1, $3, $4); + + my $val; + + if ( $var =~ /^\d+$/ ) { + fatal_error "Undefined parameter (\$$var)" unless exists $actparms{$var}; + $val = $actparms{$var}; + } else { + fatal_error "Undefined shell variable (\$$var)" unless exists $params{$var}; + $val = $params{$var}; } - my $val = $params{$3}; - - unless ( defined $val ) { - fatal_error "Undefined shell variable (\$$3)" unless exists $params{$3} || exists $ENV{$3}; - $val = ''; - } - - $currentline = join( '', $1 , $val , $4 ); + $val = '' unless defined $val; + $currentline = join( '', $first , $val , $rest ); fatal_error "Variable Expansion Loop" if ++$count > 100; } @@ -2711,7 +2741,7 @@ sub ensure_config_path() { open_file $f; - $ENV{CONFDIR} = $globals{CONFDIR}; + $params{CONFDIR} = $globals{CONFDIR}; while ( read_a_line ) { if ( $currentline =~ /^\s*([a-zA-Z]\w*)=(.*?)\s*$/ ) { diff --git a/Shorewall/Perl/Shorewall/Rules.pm b/Shorewall/Perl/Shorewall/Rules.pm index 8639f8644..bef5995a5 100644 --- a/Shorewall/Perl/Shorewall/Rules.pm +++ b/Shorewall/Perl/Shorewall/Rules.pm @@ -42,7 +42,7 @@ our @EXPORT = qw( process_actions3 process_rules - ); + ); our @EXPORT_OK = qw( initialize ); our $VERSION = '4.4_16'; @@ -58,9 +58,10 @@ our @builtins; # our $rule_commands = { COMMENT => 0, FORMAT => 2 }; -use constant { MAX_MACRO_NEST_LEVEL => 5 }; +use constant { MAX_MACRO_NEST_LEVEL => 5 , MAX_ACTION_NEST_LEVEL => 5 }; our $macro_nest_level; +our $action_nest_level; # # Rather than initializing globals in an INIT block or during declaration, @@ -73,10 +74,10 @@ our $macro_nest_level; # able to re-initialize its dependent modules' state. # sub initialize( $ ) { - - $family = shift; - %macros = (); - $macro_nest_level = 0; + $family = shift; + %macros = (); + $macro_nest_level = 0; + $action_nest_level = 0; if ( $family == F_IPV4 ) { @builtins = qw/dropBcast allowBcast dropNotSyn rejNotSyn dropInvalid allowInvalid allowinUPnP forwardUPnP Limit/; @@ -202,15 +203,7 @@ sub new_action( $ ) { my $action = $_[0]; - $actions{$action} = { actchain => '', requires => {} }; -} - -# -# Record a 'requires' relationship between a pair of actions. -# -sub add_requiredby ( $$ ) { - my ($requiredby , $requires ) = @_; - $actions{$requires}{requires}{$requiredby} = 1; + $actions{$action} = { actchain => '' }; } # @@ -248,17 +241,15 @@ sub map_old_actions( $ ) { # to the target table (%Shorewall::Chains::targets) and actions table, then ${SHAREDIR}/actions.std and # ${CONFDIR}/actions are scanned (in that order). For each action: # -# a) The related action definition file is located and scanned. -# b) Forward and unresolved action references are trapped as errors. -# c) A dependency graph is created using the 'requires' field in the 'actions' table. +# a) The related action definition file is located. +# a) The action is added to the target table # -# As the rules file is scanned, each action[:level[:tag]] is merged onto the 'usedactions' hash. When an -# is merged into the hash, its action chain is created. Where logging is specified, a chain with the name -# %n is used where the name is truncated on the right where necessary to ensure that the total -# length of the chain name does not exceed 30 characters. +# The second phase (process_actions2) occurs after the policy file is scanned. Each default action's file +# is processed by process_action2(). That function recursively processes action files up the action +# invocation tree, adding to the %usedactions hash as each new action is discovered. # -# The second phase (process_actions2) occurs after the rules file is scanned. The transitive closure of -# %usedactions is generated; again, as new actions are merged into the hash, their action chains are created. +# During rules file processing, process_action2() is called when a new action:level:tag:params is encountered. +# Again, each new such tupple is entered into the %usedactions hash. # # The final phase (process_actions3) traverses the keys of %usedactions populating each chain appropriately # by reading the related action definition file and creating rules. Note that a given action definition file is @@ -269,7 +260,7 @@ sub process_rule_common ( $$$$$$$$$$$$$$$$ ); sub process_actions1() { - progress_message2 "Preprocessing Action Files..."; + progress_message2 "Locating Action Files..."; # # Add built-in actions to the target table and create those actions # @@ -297,53 +288,11 @@ sub process_actions1() { new_action $action; + $targets{$action} = ACTION; + my $actionfile = find_file "action.$action"; fatal_error "Missing Action File ($actionfile)" unless -f $actionfile; - - progress_message2 " Pre-processing $actionfile..."; - - push_open( $actionfile ); - # - # We defer assigning a type to the action until we've processed it's action file. - # This allows us to easily catch the case where an action invokes itself. - # - my $actiontype = 0; - - while ( read_a_line ) { - - my ($wholetarget, @rest ) = split_line1 1, 13, 'action file' , $rule_commands; - # - # When passed an action name in the first argument, process_rule_common() only - # deals with the target and the parameter. We pass undef for the rest so we'll - # know if we try to use one of them. - # - # process_rule_common() returns the NATONLY actiontype flag if the target - # of the rule includes NATRULE, NATONLY or NONAT. The flag is LORed into the - # action's type below. - # - $actiontype |= process_rule_common( $action , - $wholetarget , - '' , # Current Param - undef, # source - undef, # dest - undef, # proto - undef, # ports - undef, # sports - undef, # origdest - undef, # ratelimit - undef, # user - undef, # mark - undef, # connlimit - undef, # time - undef, # headers - undef # wildcard - ) unless $wholetarget eq 'FORMAT' || $wholetarget eq 'COMMENT'; - } - - pop_open; - - $targets{$action} = ACTION | $actiontype; } } } @@ -373,22 +322,61 @@ sub merge_action_levels( $$ ) { join ':', $action, $sublevel, $subtag, $subparam; } +sub process_action2( $ ) { + my $wholeaction = shift; + my ( $action , $level, $tag, $param ) = split /:/, $wholeaction; + my $actionfile = find_file "action.$action"; + + fatal_error "Missing Action File ($actionfile)" unless -f $actionfile; + + progress_message2 " Pre-processing $actionfile..."; + + fatal_error "Actions nested too deeply" if ++$action_nest_level > MAX_ACTION_NEST_LEVEL; + + push_open( $actionfile ); + + my $oldparms = push_params( $param ); + + while ( read_a_line ) { + + my ($wholetarget, @rest ) = split_line1 1, 13, 'action file' , $rule_commands; + # + # When passed an action name in the first argument, process_rule_common() only + # deals with the target and the parameter. We pass undef for the rest so we'll + # know if we try to use one of them. + # + process_rule_common( $wholeaction , + $wholetarget , + '' , # Current Param + undef, # source + undef, # dest + undef, # proto + undef, # ports + undef, # sports + undef, # origdest + undef, # ratelimit + undef, # user + undef, # mark + undef, # connlimit + undef, # time + undef, # headers + undef # wildcard + ) unless $wholetarget eq 'FORMAT' || $wholetarget eq 'COMMENT'; + } + + pop_open; + + --$action_nest_level; + + pop_params( $oldparms ); +} + sub process_actions2 () { - progress_message2 'Generating Transitive Closure of Used-action List...'; + progress_message2 "Pre-processing default actions..."; - my $changed = 1; - - while ( $changed ) { - $changed = 0; - for my $target (keys %usedactions) { - my ( $action, $level, $tag, $param ) = split ':', $target; - my $actionref = $actions{$action}; - assert( $actionref ); - for my $action1 ( keys %{$actionref->{requires}} ) { - my $action2 = merge_action_levels( $target, $action1 ); - $changed = 1 if use_action( $action2 ); - } - } + for my $action ( keys %usedactions ) { + my ( $basic_action, undef, undef, undef ) = split /:/, $action; + process_action2( $action ) unless $targets{$basic_action} & BUILTIN; } } @@ -406,6 +394,8 @@ sub process_action3( $$$$$$ ) { open_file $actionfile; + my $oldparms = push_params( $param ); + while ( read_a_line ) { my ($target, $source, $dest, $proto, $ports, $sports, $origdest, $rate, $user, $mark, $connlimit, $time, $headers ); @@ -432,6 +422,8 @@ sub process_action3( $$$$$$ ) { } clear_comment; + + pop_params( $oldparms ); } # @@ -725,10 +717,18 @@ sub process_macro ( $$$$$$$$$$$$$$$$$ ) { # # Once a rule has been expanded via wildcards (source and/or dest zone eq 'all'), it is processed by this function. If # the target is a macro, the macro is expanded and this function is called recursively for each rule in the expansion. +# Rules in both the rules file and in action bodies are processed here. +# +# This function may be called in three different ways: +# +# 1) $chainref undefined -- Being called to process a record in the rules file. All arguments are passed. +# 2) $chainref is a chain name -- Pre-proessing the records in an action file. Only $target is passed. +# 3) $chainref is a chain reference -- Processing the records in an action file. The chain is where the generated +# rules are added. # sub process_rule_common ( $$$$$$$$$$$$$$$$ ) { my ( $chainref, #reference to Action Chain if we are being called from process_action3() - # if defined, we are being called from process_action1() and this is the name of the action + # if defined, we are being called from process_action2() and this is the name of the action $target, $current_param, $source, @@ -749,15 +749,16 @@ sub process_rule_common ( $$$$$$$$$$$$$$$$ ) { my ( $basictarget, $param ) = get_target_param $action; my $rule = ''; my $optimize = $wildcard ? ( $basictarget =~ /!$/ ? 0 : $config{OPTIMIZE} & 1 ) : 0; - my $inaction1; + my $inaction1 = ''; my $inaction3; my $normalized_target; + my $normalized_action; if ( defined $chainref ) { if ( reftype $chainref ) { $inaction3 = 1; } else { - $inaction1 = $chainref; + ( $inaction1, undef, undef, undef ) = split /:/, $normalized_action = $chainref; } } @@ -772,7 +773,7 @@ sub process_rule_common ( $$$$$$$$$$$$$$$$ ) { ( $basictarget, $actiontype , $param ) = map_old_actions( $basictarget ) unless $actiontype || $param; } - fatal_error "Unknown action ($action)" unless $actiontype; + fatal_error "Unknown ACTION ($action)" unless $actiontype; if ( $actiontype == MACRO ) { # @@ -832,21 +833,36 @@ sub process_rule_common ( $$$$$$$$$$$$$$$$ ) { # Create the action:level:tag:param tupple. # $normalized_target = normalize_action( $basictarget, $loglevel, $param ); - - if ( $inaction1 ) { - add_requiredby( $normalized_target , $inaction1 ); - } else { + + unless ( $inaction3 ) { + fatal_error "An action may not invoke itself" if $basictarget eq $inaction1; if ( my $ref = use_action( $normalized_target ) ) { - new_nat_chain $ref->{name} if $actiontype & ( NATRULE | NONAT | NATONLY ); + # + # First reference to this tupple + # + unless ( $actiontype & BUILTIN ) { + # + # Not a built-in - do preprocessing + # + process_action2( $normalized_target ); + # + # Preprocessing may determine that the chain or one of it's dependents does NAT. If so: + # + # - Refresh $actiontype + # - Create the associate nat table chain if appropriate. + # + ensure_chain( 'nat', $ref->{name} ) if ( $actiontype = $targets{$basictarget} ) & NATRULE; + } } } + + $action = $basictarget; # Remove params, if any, from $action. } - # - # Return the NATRULE flag to the caller who will eventually add it - # to $targets{$inaction1} - # - return ( $actiontype & ( NATRULE | NONAT | NATONLY ) ) ? NATRULE : 0 if $inaction1; + if ( $inaction1 ) { + $targets{$inaction1} |= NATRULE if $actiontype & (NATRULE | NONAT | NATONLY ); + return 1; + } # # Take care of irregular syntax and targets # @@ -1103,7 +1119,7 @@ sub process_rule_common ( $$$$$$$$$$$$$$$$ ) { } } } elsif ( $actiontype & ACTION ) { - $target = $action; + $target = $usedactions{$normalized_target}->{name}; } else { if ( $server eq '' ) { fatal_error "A server and/or port must be specified in the DEST column in $action rules" unless $serverport; diff --git a/Shorewall/changelog.txt b/Shorewall/changelog.txt index da22e33e4..3ba24d497 100644 --- a/Shorewall/changelog.txt +++ b/Shorewall/changelog.txt @@ -1,3 +1,7 @@ +Changes in Shorewall 4.4.16 RC 1 + +1) Complete parameterized actions. + Changes in Shorewall 4.4.16 Beta 6 1) Don't let root match wildcard. diff --git a/Shorewall/releasenotes.txt b/Shorewall/releasenotes.txt index 3ecedac53..d1c260755 100644 --- a/Shorewall/releasenotes.txt +++ b/Shorewall/releasenotes.txt @@ -1,6 +1,6 @@ ---------------------------------------------------------------------------- S H O R E W A L L 4 . 4 . 1 6 - R C 1 + R C I ---------------------------------------------------------------------------- I. PROBLEMS CORRECTED IN THIS RELEASE @@ -14,6 +14,10 @@ VI. PROBLEMS CORRECTED AND NEW FEATURES IN PRIOR RELEASES I. P R O B L E M S C O R R E C T E D I N T H I S R E L E A S E ---------------------------------------------------------------------------- +Beta 7 + +None. + Beta 6 1) Previously, the root of a wildcard name erroneously matched that @@ -120,8 +124,17 @@ Beta 1 and in macros invoked from Actions. Additionally, Macros used in Actions are now free to invoke other actions. -4) There is now limited support for parameterized actions. Currently, - the parameters are only available to extensions scripts. See +4) There is now support for parameterized actions. The parameters are + a comma-separated list enclosed in parentheses following the + action name (e.g., ACT(REDIRECT,192.168.1.4)). Within the action + body, the parameter values are available in $1, $2, etc. + + You can 'omit' a parameter in the list by using '-' (e,g, + REDIRECT,-.info) would omit the second parameter (within the action + body, $2 would expand to nothing). If you want to specify '-' as an + parameter value, use '--'. + + Parameter values are also available to extensions scripts. See http://www.shorewall.net/Actions.html#Extension for more information. diff --git a/Shorewall/shorewall.spec b/Shorewall/shorewall.spec index 7a9f968f1..63438da0b 100644 --- a/Shorewall/shorewall.spec +++ b/Shorewall/shorewall.spec @@ -109,8 +109,10 @@ fi %doc COPYING INSTALL changelog.txt releasenotes.txt Contrib/* Samples %changelog -* Wed Dec 22 2010 Tom Eastep tom@shorewall.net +* Sun Dec 26 2010 Tom Eastep tom@shorewall.net - Updated to 4.4.16-0RC1 +* Sun Dec 26 2010 Tom Eastep tom@shorewall.net +- Updated to 4.4.16-0Beta7 * Mon Dec 20 2010 Tom Eastep tom@shorewall.net - Updated to 4.4.16-0Beta6 * Fri Dec 10 2010 Tom Eastep tom@shorewall.net diff --git a/Shorewall6-lite/shorewall6-lite.spec b/Shorewall6-lite/shorewall6-lite.spec index f43991f38..8422d5dd3 100644 --- a/Shorewall6-lite/shorewall6-lite.spec +++ b/Shorewall6-lite/shorewall6-lite.spec @@ -93,8 +93,10 @@ fi %doc COPYING changelog.txt releasenotes.txt %changelog -* Wed Dec 22 2010 Tom Eastep tom@shorewall.net +* Sun Dec 26 2010 Tom Eastep tom@shorewall.net - Updated to 4.4.16-0RC1 +* Sun Dec 26 2010 Tom Eastep tom@shorewall.net +- Updated to 4.4.16-0Beta7 * Mon Dec 20 2010 Tom Eastep tom@shorewall.net - Updated to 4.4.16-0Beta6 * Fri Dec 10 2010 Tom Eastep tom@shorewall.net diff --git a/Shorewall6/shorewall6.spec b/Shorewall6/shorewall6.spec index 1c9a101b9..f95d967ca 100644 --- a/Shorewall6/shorewall6.spec +++ b/Shorewall6/shorewall6.spec @@ -98,8 +98,10 @@ fi %doc COPYING INSTALL changelog.txt releasenotes.txt tunnel ipsecvpn ipv6 Samples6 %changelog -* Wed Dec 22 2010 Tom Eastep tom@shorewall.net +* Sun Dec 26 2010 Tom Eastep tom@shorewall.net - Updated to 4.4.16-0RC1 +* Sun Dec 26 2010 Tom Eastep tom@shorewall.net +- Updated to 4.4.16-0Beta7 * Mon Dec 20 2010 Tom Eastep tom@shorewall.net - Updated to 4.4.16-0Beta6 * Fri Dec 10 2010 Tom Eastep tom@shorewall.net diff --git a/docs/Actions.xml b/docs/Actions.xml index bdd10a822..1404a36b8 100644 --- a/docs/Actions.xml +++ b/docs/Actions.xml @@ -227,6 +227,40 @@ ACCEPT - - tcp 135,139,445 The SOURCE and DEST columns in the action file may not include zone names; those are given when the action is invoked. + + Additionally, it is possible to pass parameters to an action, when + it is invoked in the rules file or in another action. + + Here's a trivial example: + + /etc/shorewall/action.A: + + #TARGET SOURCE DEST PROTO DEST SOURCE ORIGINAL +# PORT(S) PORT(S) DEST +FORMAT 2 +$1 - - tcp 80 - 1.2.3.4 + + /etc/shorewall/rules: + + #TARGET SOURCE DEST PROTO DEST SOURCE ORIGINAL +# PORT(S) PORT(S) DEST + +A(REDIRECT) net fw + + The above is equivalent to this rule: + + #TARGET SOURCE DEST PROTO DEST SOURCE ORIGINAL +# PORT(S) PORT(S) DEST +REDIRECT net - tcp 80 - 1.2.3.4 + + You can 'omit' parameters by using '-'. + + Example: ACTION(REDIRECT,-,info) + + In the above example, $2 would expand to nothing. + + If you want to make '-' a parameter value, use '--' (e.g., + ACTION(REDIRECT,--.info)).
@@ -520,7 +554,8 @@ bar:debug @params is the list of - parameter values (Shorewall 4.4.16 and later). + parameter values (Shorewall 4.4.16 and later). 'Omitted' parameters + contain '-'. @@ -628,13 +663,13 @@ Limit(SSH,3,60):info net $FW tcp 22use Shorewall::Chains; -@params = split /,/, $tag unless @params; +@params = split( /,/, $tag ), $tag='' unless @params; fatal_error 'Limit rules must include <list name>,<max connections>,<interval> as the log tag or params' unless @params == 3; -my $list = $tag[0]; +my $list = $params[0]; -for ( @tag[1,2] ) { +for ( @params[1,2] ) { fatal_error 'Max connections and interval in Limit rules must be numeric (' . $_ . ')' unless /^\d+$/ } @@ -644,7 +679,7 @@ add_rule $chainref, "-m recent --name $list --set"; if ( $level ) { my $xchainref = new_chain 'filter' , "$chainref->{name}%"; - log_rule_limit $level, $xchainref, $params[0], 'DROP', '', '', 'add', ''; + log_rule_limit $level, $xchainref, $params[0], 'DROP', $tag, '', 'add', ''; add_rule $xchainref, '-j DROP'; add_rule $chainref, "-m recent --name $list --update --seconds $params[2] --hitcount $count -j $xchainref->{name}"; } else {