Implement Triggers

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2013-07-10 13:27:58 -07:00
parent 411ca87ec3
commit 3c6df56b57
7 changed files with 227 additions and 12 deletions

View File

@ -619,6 +619,7 @@ use constant { UNIQUE => 1,
COMPLEX => 32, COMPLEX => 32,
NFACCT => 64, NFACCT => 64,
EXPENSIVE => 128, EXPENSIVE => 128,
RECENT => 256,
}; };
our %opttype = ( rule => CONTROL, our %opttype = ( rule => CONTROL,
@ -650,6 +651,7 @@ our %opttype = ( rule => CONTROL,
EXCLUSIVE, EXCLUSIVE,
nfacct => NFACCT, nfacct => NFACCT,
recent => RECENT,
set => EXPENSIVE, set => EXPENSIVE,
geoip => EXPENSIVE, geoip => EXPENSIVE,
@ -843,7 +845,7 @@ sub set_rule_option( $$$ ) {
if ( exists $ruleref->{$option} ) { if ( exists $ruleref->{$option} ) {
assert( defined( my $value1 = $ruleref->{$option} ) , $ruleref ); assert( defined( my $value1 = $ruleref->{$option} ) , $ruleref );
if ( $opttype & ( MATCH | NFACCT | EXPENSIVE ) ) { if ( $opttype & ( MATCH | NFACCT | RECENT | EXPENSIVE ) ) {
if ( $globals{KLUDGEFREE} ) { if ( $globals{KLUDGEFREE} ) {
unless ( reftype $value1 ) { unless ( reftype $value1 ) {
unless ( reftype $value ) { unless ( reftype $value ) {
@ -876,7 +878,7 @@ sub set_rule_option( $$$ ) {
fatal_error "Multiple $option settings in one rule is prohibited"; fatal_error "Multiple $option settings in one rule is prohibited";
} else { } else {
assert(0, $opttype ); assert($opttype == TARGET, $opttype );
} }
} else { } else {
$ruleref->{$option} = $value; $ruleref->{$option} = $value;
@ -1030,6 +1032,7 @@ sub format_rule( $$;$ ) {
# #
my $ruleref = $rulerefp->{complex} ? clone_irule( $rulerefp ) : $rulerefp; my $ruleref = $rulerefp->{complex} ? clone_irule( $rulerefp ) : $rulerefp;
my $nfacct = $rulerefp->{nfacct}; my $nfacct = $rulerefp->{nfacct};
my $recent = $rulerefp->{recent};
my $expensive; my $expensive;
for ( @{$ruleref->{matches}} ) { for ( @{$ruleref->{matches}} ) {
@ -1051,9 +1054,9 @@ sub format_rule( $$;$ ) {
next; next;
} elsif ( $type == EXPENSIVE ) { } elsif ( $type == EXPENSIVE ) {
# #
# Only emit expensive matches now if there are '-m nfacct' matches in the rule # Only emit expensive matches now if there are '-m nfacct' or '-m recent' matches in the rule
# #
if ( $nfacct ) { if ( $nfacct || $recent ) {
$rule .= format_option( $_, pop_match( $ruleref, $_ ) ); $rule .= format_option( $_, pop_match( $ruleref, $_ ) );
} else { } else {
$expensive = 1; $expensive = 1;
@ -1063,7 +1066,7 @@ sub format_rule( $$;$ ) {
} }
} }
# #
# Emit expensive matches last unless we had '-m nfacct' matches in the rule. # Emit expensive matches last unless we had '-m nfacct' pr '-m recent' matches in the rule.
# #
if ( $expensive ) { if ( $expensive ) {
for ( grep( get_opttype( $_, 0 ) == EXPENSIVE, @{$ruleref->{matches}} ) ) { for ( grep( get_opttype( $_, 0 ) == EXPENSIVE, @{$ruleref->{matches}} ) ) {
@ -1131,7 +1134,7 @@ sub merge_rules( $$$ ) {
} }
} }
for my $option ( grep ! $opttype{$_} || $_ eq 'nfacct', keys %$fromref ) { for my $option ( grep ! $opttype{$_} || $_ eq 'nfacct' || $_ eq 'recent', keys %$fromref ) {
set_rule_option( $toref, $option, $fromref->{$option} ); set_rule_option( $toref, $option, $fromref->{$option} );
} }
@ -3337,7 +3340,7 @@ sub optimize_level4( $$ ) {
while ( @$rulesref ) { while ( @$rulesref ) {
my $rule1ref = $rulesref->[-1]; my $rule1ref = $rulesref->[-1];
last unless ( $rule1ref->{target} || '' ) eq $target && ! ( $rule1ref->{targetopts} || $rule1ref->{nfacct} ); last unless ( $rule1ref->{target} || '' ) eq $target && ! ( $rule1ref->{targetopts} || $rule1ref->{nfacct} || $rule1ref->{recent} );
trace ( $chainref, 'D', $rule, $rule1ref ) if $debug; trace ( $chainref, 'D', $rule, $rule1ref ) if $debug;
@ -6052,9 +6055,11 @@ sub do_ipsec($$) {
# Generate a log message # Generate a log message
# #
sub log_rule_limit( $$$$$$$$ ) { sub log_rule_limit( $$$$$$$$ ) {
my ($level, $chainref, $chain, $disposition, $limit, $tag, $command, $matches ) = @_; my ($level, $chainref, $chn, $dispo, $limit, $tag, $command, $matches ) = @_;
my $prefix = ''; my $prefix = '';
my $chain = get_action_chain_name || $chn;
my $disposition = get_action_disposition || $dispo;
$level = validate_level $level; # Do this here again because this function can be called directly from user exits. $level = validate_level $level; # Do this here again because this function can be called directly from user exits.
@ -6143,10 +6148,12 @@ sub log_rule_limit( $$$$$$$$ ) {
} }
sub log_irule_limit( $$$$$$$@ ) { sub log_irule_limit( $$$$$$$@ ) {
my ($level, $chainref, $chain, $disposition, $limit, $tag, $command, @matches ) = @_; my ($level, $chainref, $chn, $dispo, $limit, $tag, $command, @matches ) = @_;
my $prefix = ''; my $prefix = '';
my %matches; my %matches;
my $chain = get_action_chain_name || $chn;
my $disposition = get_action_disposition || $dispo;
$level = validate_level $level; # Do this here again because this function can be called directly from user exits. $level = validate_level $level; # Do this here again because this function can be called directly from user exits.

View File

@ -63,10 +63,13 @@ our @EXPORT = qw(
get_action_params get_action_params
get_action_chain get_action_chain
get_action_chain_name get_action_chain_name
set_action_name_to_caller
get_action_logging get_action_logging
get_action_disposition get_action_disposition
set_action_disposition
set_action_param set_action_param
get_inline_matches get_inline_matches
set_inline_matches
have_capability have_capability
require_capability require_capability
@ -2910,6 +2913,10 @@ sub get_inline_matches() {
"$inline_matches "; "$inline_matches ";
} }
sub set_inline_matches( $ ) {
$inline_matches = $_[0];
}
# #
# Push/pop acton params # Push/pop acton params
# #
@ -3000,10 +3007,18 @@ sub get_action_chain_name() {
$actparms{chain}; $actparms{chain};
} }
sub set_action_name_to_caller() {
$actparms{chain} = $actparms{caller};
}
sub get_action_disposition() { sub get_action_disposition() {
$actparms{disposition}; $actparms{disposition};
} }
sub set_action_disposition($) {
$actparms{disposition} = $_[0];
}
sub set_action_param( $$ ) { sub set_action_param( $$ ) {
my $i = shift; my $i = shift;
@ -5397,10 +5412,14 @@ sub get_configuration( $$$$ ) {
$globals{ZONE_OFFSET} = $config{PROVIDER_BITS}; $globals{ZONE_OFFSET} = $config{PROVIDER_BITS};
} }
#
# It is okay if the trigger mark is outsize of the a 32-bit integer. We check that in IfTrigger"
#
fatal_error 'Invalid Packet Mark layout' if $config{ZONE_BITS} + $globals{ZONE_OFFSET} > 30; fatal_error 'Invalid Packet Mark layout' if $config{ZONE_BITS} + $globals{ZONE_OFFSET} > 30;
$globals{EXCLUSION_MASK} = 1 << ( $globals{ZONE_OFFSET} + $config{ZONE_BITS} ); $globals{EXCLUSION_MASK} = 1 << ( $globals{ZONE_OFFSET} + $config{ZONE_BITS} );
$globals{TPROXY_MARK} = $globals{EXCLUSION_MASK} << 1; $globals{TPROXY_MARK} = $globals{EXCLUSION_MASK} << 1;
$globals{TRIGGER_MARK} = $globals{TPROXY_MARK} << 1;
$globals{PROVIDER_MIN} = 1 << $config{PROVIDER_OFFSET}; $globals{PROVIDER_MIN} = 1 << $config{PROVIDER_OFFSET};
$globals{TC_MAX} = make_mask( $config{TC_BITS} ); $globals{TC_MAX} = make_mask( $config{TC_BITS} );

View File

@ -2128,11 +2128,11 @@ sub process_rule ( $$$$$$$$$$$$$$$$$$$ ) {
if ( $basictarget eq 'INLINE' ) { if ( $basictarget eq 'INLINE' ) {
my $inline_matches = get_inline_matches; my $inline_matches = get_inline_matches;
if ( $inline_matches =~ /^(.*\s+)-j\s+(.+) $/ ) { if ( $inline_matches =~ /^(.*\s+)?-j\s+(.+) $/ ) {
$raw_matches .= $1; $raw_matches .= $1 if supplied $1;
$action = $2; $action = $2;
my ( $target ) = split ' ', $action; my ( $target ) = split ' ', $action;
fatal_error "Unknown jump target ($action)" unless $targets{$target}; fatal_error "Unknown jump target ($action)" unless $targets{$target} || $target eq 'MARK';
fatal_error "INLINE may not have a parameter when '-j' is specified in the free-form area" if $param ne ''; fatal_error "INLINE may not have a parameter when '-j' is specified in the free-form area" if $param ne '';
} else { } else {
$raw_matches .= $inline_matches; $raw_matches .= $inline_matches;
@ -2851,6 +2851,8 @@ sub perl_action_helper($$;$) {
$matches .= ' ' unless $matches =~ /^(?:.+\s)?$/; $matches .= ' ' unless $matches =~ /^(?:.+\s)?$/;
set_inline_matches $matches if $target =~ /^INLINE(?::.*)?$/;
if ( $isstatematch ) { if ( $isstatematch ) {
if ( $statematch ) { if ( $statematch ) {
if ( $statematch eq $isstatematch ) { if ( $statematch eq $isstatematch ) {

View File

@ -0,0 +1,86 @@
#
# Shorewall version 4 - Perform an Action based on a Trigger
#
# /etc/shorewall/action.IfTrigger
#
# Parameters:
# Trigger: Must start with a letter and be composed of letters, digits, '-', and '_'.
# Action: Anything that can appear in the ACTION column of a rule.
# Time Limit: Amount of time the trigger is to remain armed in seconds"
# Hit Count: Number of packets seen within the Timelimit -- default is 1
# Src or Dest: 'src' (default) or 'dst'. Determines if the trigger is associated with the source
# address (src) or destination address (dst)
# Reset/update: '-' (default) 'reset', or 'update'. If 'reset', the trigger will be reset before
# the Action is taken. If 'update', the timestamp associated with the trigger will
# be updated and the action taken if the time limit/hitcount are matched.
# If '-', the action will be taken if the limit/hitcount are matched but the
# trigger's timestamp will not be updated.
# Disposition: Disposition for any event generated.
#
#######################################################################################################
# DO NOT REMOVE THE FOLLOWING LINE
?format 2
#################################################################################################################################################################################################
#ACTION SOURCE DEST PROTO DEST SOURCE ORIGINAL RATE USER/ MARK CONNLIMIT TIME HEADERS SWITCH HELPER
# PORT PORT(S) DEST LIMIT GROUP
DEFAULTS -,ACCEPT,60,1,src,check,-
?begin perl
use Shorewall::Config qw(:DEFAULT :internal);
use Shorewall::Chains;
use Shorewall::Rules;
use strict;
my ( $trigger, $action, $timeout, $hitcount, $destination, $reset, $disposition ) = get_action_params( 7 );
fatal_error "A trigger name is required" unless supplied $trigger;
fatal_error "Invalid trigger name ($trigger)" unless $trigger =~ /^[a-zA-z][-\w]*$/;
fatal_error "Invalid time limit ($timeout)" unless $timeout =~ /^\d+$/;
fatal_error "Invalid hit count ($hitcount)" unless $hitcount =~ /^\d+$/;
fatal_error "Invalid Src or Dest ($destination)" unless $destination =~ /^(?:src|dst)$/;
fatal_error "Invalid reset flag ($reset)" unless $reset =~ /^(?:reset|update|check)$/;
set_action_disposition( $disposition) if supplied $disposition;
set_action_name_to_caller;
require_capability 'RECENT_MATCH', 'Use of triggers', 's';
if ( $reset eq 'reset' ) {
require_capability 'MARK_ANYWHERE', 'Resetting a trigger', 's';
print "Resetting....\n";
my $mark = $globals{TRIGGER_MARK};
#
# The trigger mark bit must be within 32 bits
#
fatal_error "The mark layout does not permit resetting of triggers" unless $mark & 0xffffffff;
#
# Reset the trigger mark bit
#
perl_action_helper( 'INLINE', '-j MARK --and-mark '. in_hex( (~ $mark ) & 0xffffffff ) );
$mark = in_hex $mark;
#
# Mark the packet if trigger is armed
#
if ( $destination eq 'dst' ) {
perl_action_helper( 'INLINE', "-m recent --rcheck --seconds $timeout --hitcount $hitcount --name $trigger --rdest -j MARK --or-mark $mark" );
} else {
perl_action_helper( 'INLINE', "-m recent --rcheck --seconds $timeout --hitcount $hitcount --name $trigger --rsource -j MARK --or-mark $mark" );
}
#
# if the trigger is armed, remove it and perform the action
#
perl_action_helper( $action , "-m mark --mark $mark/$mark -m recent --remove --name $trigger" );
} elsif ( $reset eq 'update' ) {
perl_action_helper( $action, "-m recent --update --seconds $timeout --hitcount $hitcount --name $trigger" );
} else {
perl_action_helper( $action, "-m recent --rcheck --seconds $timeout --hitcount $hitcount --name $trigger" );
}
1;
?end perl

View File

@ -0,0 +1,49 @@
#
# Shorewall version 4 - Reset a Trigger
#
# /etc/shorewall/action.ResetTrigger
#
# Parameters:
# Trigger: Must start with a letter and be composed of letters, digits, '-', and '_'.
# Action: Action to perform after setting the trigger. Default is ACCEPT
# Src or Dest: 'src' (default) or 'dst'. Determines if the trigger is associated with the source
# address (src) or destination address (dst)
# Disposition: Disposition for any event generated.
#
#######################################################################################################
# DO NOT REMOVE THE FOLLOWING LINE
?format 2
#################################################################################################################################################################################################
#ACTION SOURCE DEST PROTO DEST SOURCE ORIGINAL RATE USER/ MARK CONNLIMIT TIME HEADERS SWITCH HELPER
# PORT PORT(S) DEST LIMIT GROUP
DEFAULTS -,ACCEPT,src,-
?begin perl
use Shorewall::Config;
use Shorewall::Chains;
use Shorewall::Rules;
use strict;
my ( $trigger, $action, $destination, $disposition ) = get_action_params( 4 );
require_capability 'RECENT_MATCH', 'Use of triggers', 's';
require_capability 'MARK_ANYWHERE', 'Use of triggers', 's';
fatal_error "A trigger name is required" unless supplied $trigger;
fatal_error "Invalid trigger name ($trigger)" unless $trigger =~ /^[a-zA-z][-\w]*$/;
fatal_error "Invalid Src or Dest ($destination)" unless $destination =~ /^(?:src|dst)$/;
set_action_disposition( $disposition) if supplied $disposition;
set_action_name_to_caller;
if ( $destination eq 'dst' ) {
perl_action_helper( $action, "-m recent --name $trigger --remove --rdest" );
} else {
perl_action_helper( $action, "-m recent --name $trigger --remove --rsource" );
}
1;
?end perl

View File

@ -0,0 +1,49 @@
#
# Shorewall version 4 - Set a Trigger
#
# /etc/shorewall/action.SetTrigger
#
# Parameters:
# Trigger: Must start with a letter and be composed of letters, digits, '-', and '_'.
# Action: Action to perform after setting the trigger. Default is ACCEPT
# Src or Dest: 'src' (default) or 'dst'. Determines if the trigger is associated with the source
# address (src) or destination address (dst)
# Disposition: Disposition for any event generated.
#
#######################################################################################################
# DO NOT REMOVE THE FOLLOWING LINE
?format 2
#################################################################################################################################################################################################
#ACTION SOURCE DEST PROTO DEST SOURCE ORIGINAL RATE USER/ MARK CONNLIMIT TIME HEADERS SWITCH HELPER
# PORT PORT(S) DEST LIMIT GROUP
DEFAULTS -,ACCEPT,src
?begin perl
use Shorewall::Config;
use Shorewall::Chains;
use Shorewall::Rules;
use strict;
my ( $trigger, $action, $destination, $disposition ) = get_action_params( 4 );
require_capability 'RECENT_MATCH', 'Use of triggers', 's';
require_capability 'MARK_ANYWHERE', 'Use of triggers', 's';
fatal_error "A trigger name is required" unless supplied $trigger;
fatal_error "Invalid trigger name ($trigger)" unless $trigger =~ /^[a-zA-z][-\w]*$/;
fatal_error "Invalid Src or Dest ($destination)" unless $destination =~ /^(?:src|dst)$/;
set_action_disposition( $disposition) if supplied $disposition;
set_action_name_to_caller;
if ( $destination eq 'dst' ) {
perl_action_helper( $action, "-m recent --name $trigger --set --rdest" );
} else {
perl_action_helper( $action, "-m recent --name $trigger --set --rsource" );
}
1;
?end perl

View File

@ -33,11 +33,14 @@ Drop # Default Action for DROP policy
dropInvalid inline # Drops packets in the INVALID conntrack state dropInvalid inline # Drops packets in the INVALID conntrack state
DropSmurfs noinline # Drop smurf packets DropSmurfs noinline # Drop smurf packets
Established inline # Handles packets in the ESTABLISHED state Established inline # Handles packets in the ESTABLISHED state
IfTrigger noinline # Perform an action if a trigger is set
Invalid inline # Handles packets in the INVALID conntrack state Invalid inline # Handles packets in the INVALID conntrack state
New inline # Handles packets in the NEW conntrack state New inline # Handles packets in the NEW conntrack state
NotSyn inline # Handles TCP packets which do not have SYN=1 and ACK=0 NotSyn inline # Handles TCP packets which do not have SYN=1 and ACK=0
Reject # Default Action for REJECT policy Reject # Default Action for REJECT policy
Related inline # Handles packets in the RELATED conntrack state Related inline # Handles packets in the RELATED conntrack state
ResetTrigger inline # Reset a Trigger
RST inline # Handle packets with RST set RST inline # Handle packets with RST set
SetTrigger inline # Set a trigger for the packet's source IP
TCPFlags # Handle bad flag combinations. TCPFlags # Handle bad flag combinations.
Untracked inline # Handles packets in the UNTRACKED conntrack state Untracked inline # Handles packets in the UNTRACKED conntrack state