From f5bdc9e7f44af949456cfbe6659b80a2b2edc68d Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Wed, 22 Oct 2014 11:21:43 -0700 Subject: [PATCH] Allow two limits in the RATE LIMIT columns Signed-off-by: Tom Eastep --- Shorewall/Perl/Shorewall/Chains.pm | 105 +++++++++++++---------- Shorewall/manpages/shorewall-rules.xml | 64 ++++++++++---- Shorewall6/manpages/shorewall6-rules.xml | 64 ++++++++++---- 3 files changed, 158 insertions(+), 75 deletions(-) diff --git a/Shorewall/Perl/Shorewall/Chains.pm b/Shorewall/Perl/Shorewall/Chains.pm index a46af4ab6..d3ad1747e 100644 --- a/Shorewall/Perl/Shorewall/Chains.pm +++ b/Shorewall/Perl/Shorewall/Chains.pm @@ -4876,62 +4876,79 @@ my %norate = ( DROP => 1, REJECT => 1 ); # Create a "-m limit" match for the passed LIMIT/BURST # sub do_ratelimit( $$ ) { - my ( $rate, $action ) = @_; + my ( $rates, $action ) = @_; - return '' unless $rate and $rate ne '-'; + return '' unless $rates and $rates ne '-'; fatal_error "Rate Limiting not available with $action" if $norate{$action}; - # - # "-m hashlimit" match for the passed LIMIT/BURST - # - if ( $rate =~ /^[sd]:{1,2}/ ) { - require_capability 'HASHLIMIT_MATCH', 'Per-ip rate limiting' , 's'; - my $limit = "-m hashlimit "; - my $match = have_capability( 'OLD_HL_MATCH' ) ? 'hashlimit' : 'hashlimit-upto'; - my $units; + my @rates = split_list $rates, 'rate'; - if ( $rate =~ /^[sd]:((\w*):)?((\d+)(\/(sec|min|hour|day))?):(\d+)$/ ) { - fatal_error "Invalid Rate ($3)" unless $4; - fatal_error "Invalid Burst ($7)" unless $7; - $limit .= "--$match $3 --hashlimit-burst $7 --hashlimit-name "; - $limit .= $2 ? $2 : 'shorewall' . $hashlimitset++; - $limit .= ' --hashlimit-mode '; - $units = $6; - } elsif ( $rate =~ /^[sd]:((\w*):)?((\d+)(\/(sec|min|hour|day))?)$/ ) { - fatal_error "Invalid Rate ($3)" unless $4; - $limit .= "--$match $3 --hashlimit-name "; - $limit .= $2 ? $2 : 'shorewall' . $hashlimitset++; - $limit .= ' --hashlimit-mode '; - $units = $6; - } else { - fatal_error "Invalid rate ($rate)"; - } + if ( @rates == 2 ) { + $rates[0] = 's:' . $rates[0]; + $rates[1] = 'd:' . $rates[1]; + } elsif ( @rates > 2 ) { + fatal error "Only two rates may be specified"; + } - $limit .= $rate =~ /^s:/ ? 'srcip ' : 'dstip '; + my $limit = ''; - if ( $units && $units ne 'sec' ) { - my $expire = 60000; # 1 minute in milliseconds + for my $rate ( @rates ) { + # + # "-m hashlimit" match for the passed LIMIT/BURST + # + if ( $rate =~ /^([sd]):{1,2}/ ) { + require_capability 'HASHLIMIT_MATCH', 'Per-ip rate limiting' , 's'; - if ( $units ne 'min' ) { - $expire *= 60; #At least an hour - $expire *= 24 if $units eq 'day'; + my $match = have_capability( 'OLD_HL_MATCH' ) ? 'hashlimit' : 'hashlimit-upto'; + my $units; + + $limit .= "-m hashlimit "; + + if ( $rate =~ /^[sd]:((\w*):)?((\d+)(\/(sec|min|hour|day))?):(\d+)$/ ) { + fatal_error "Invalid Rate ($3)" unless $4; + fatal_error "Invalid Burst ($7)" unless $7; + $limit .= "--$match $3 --hashlimit-burst $7 --hashlimit-name "; + $limit .= $2 ? $2 : 'shorewall' . $hashlimitset++; + $limit .= ' --hashlimit-mode '; + $units = $6; + } elsif ( $rate =~ /^[sd]:((\w*):)?((\d+)(\/(sec|min|hour|day))?)$/ ) { + fatal_error "Invalid Rate ($3)" unless $4; + $limit .= "--$match $3 --hashlimit-name "; + $limit .= $2 ? $2 : 'shorewall' . $hashlimitset++; + $limit .= ' --hashlimit-mode '; + $units = $6; + } else { + fatal_error "Invalid rate ($rate)"; } - $limit .= "--hashlimit-htable-expire $expire "; - } + $limit .= $rate =~ /^s:/ ? 'srcip ' : 'dstip '; - $limit; - } elsif ( $rate =~ /^((\d+)(\/(sec|min|hour|day))?):(\d+)$/ ) { - fatal_error "Invalid Rate ($1)" unless $2; - fatal_error "Invalid Burst ($5)" unless $5; - "-m limit --limit $1 --limit-burst $5 "; - } elsif ( $rate =~ /^(\d+)(\/(sec|min|hour|day))?$/ ) { - fatal_error "Invalid Rate (${1}${2})" unless $1; - "-m limit --limit $rate "; - } else { - fatal_error "Invalid rate ($rate)"; + if ( $units && $units ne 'sec' ) { + my $expire = 60000; # 1 minute in milliseconds + + if ( $units ne 'min' ) { + $expire *= 60; #At least an hour + $expire *= 24 if $units eq 'day'; + } + + $limit .= "--hashlimit-htable-expire $expire "; + } + } else { + if ( $rate =~ /^((\d+)(\/(sec|min|hour|day))?):(\d+)$/ ) { + fatal_error "Invalid Rate ($1)" unless $2; + fatal_error "Invalid Burst ($5)" unless $5; + $limit = "-m limit --limit $1 --limit-burst $5 "; + } elsif ( $rate =~ /^(\d+)(\/(sec|min|hour|day))?$/ ) { + fatal_error "Invalid Rate (${1}${2})" unless $1; + $limit = "-m limit --limit $rate "; + } else { + fatal_error "Invalid rate ($rate)"; + } + } } + + $limit; } # diff --git a/Shorewall/manpages/shorewall-rules.xml b/Shorewall/manpages/shorewall-rules.xml index af89f1376..0f86e8ebd 100644 --- a/Shorewall/manpages/shorewall-rules.xml +++ b/Shorewall/manpages/shorewall-rules.xml @@ -1226,22 +1226,41 @@ - RATE LIMIT (rate) - [-|[{s|d}:[[name]:]]]rate/{sec|min|hour|day}[:burst] + RATE LIMIT (rate) - + limit + where limit is one of: + + + [-|[{s|d}:[[name]:]]]rate/{sec|min|hour|day}[:burst] + + [name1]:rate1/{sec|min|hour|day}[:burst1],[name2]:rate2/{sec|min|hour|day}[:burst2] + + You may optionally rate-limit the rule by placing a value in this column: - rate is the number of connections per + rate* is the number of connections per interval (sec or min) and burst is the + role="bold">min) and burst* is the largest burst permitted. If no burst is given, a value of 5 is assumed. There may be no no white-space embedded in the specification. @@ -1250,15 +1269,28 @@ When or is specified, the rate applies per source IP address or per destination IP address - respectively. The name may be chosen by - the user and specifies a hash table to be used to count matching + respectively. The names may be chosen by + the user and specifiy a hash table to be used to count matching connections. If not given, the name shorewallN (where N is a unique integer) is - assumed. Where more than one rule specifies the same name, the - connections counts for the rules are aggregated and the individual - rates apply to the aggregated count. + assumed. Where more than one rule or POLICY specifies the same name, + the connections counts for the rules are aggregated and the + individual rates apply to the aggregated count. - Example: s:ssh:3/min:5 + Beginning with Shorewall 4.6.5, two + limits may be specified, separated by a comma. In this + case, the first limit (name1, + rate1, burst1) specifies the per-source + IP limit and the second limit specifies the per-destination IP + limit. + + Example: client:10/sec:20,:60/sec:100 + + In this example, the 'client' hash table will be used to + enforce the per-source limit and the compiler will pick a unique + name for the hash table that tracks the per-destination + limit. diff --git a/Shorewall6/manpages/shorewall6-rules.xml b/Shorewall6/manpages/shorewall6-rules.xml index 26a6a44a9..ca002070c 100644 --- a/Shorewall6/manpages/shorewall6-rules.xml +++ b/Shorewall6/manpages/shorewall6-rules.xml @@ -1127,22 +1127,41 @@ - RATE LIMIT (rate) - [-|[{s|d}:[[name]:]]]rate/{sec|min|hour|day}[:burst] + RATE LIMIT (rate) - + limit + where limit is one of: + + + [-|[{s|d}:[[name]:]]]rate/{sec|min|hour|day}[:burst] + + [name1]:rate1/{sec|min|hour|day}[:burst1],[name2]:rate2/{sec|min|hour|day}[:burst2] + + You may optionally rate-limit the rule by placing a value in this column: - rate is the number of connections per + rate* is the number of connections per interval (sec or min) and burst is the + role="bold">min) and burst* is the largest burst permitted. If no burst is given, a value of 5 is assumed. There may be no no white-space embedded in the specification. @@ -1151,13 +1170,28 @@ When or is specified, the rate applies per source IP address or per destination IP address - respectively. The name may be chosen by - the user and specifies a hash table to be used to count matching + respectively. The names may be chosen by + the user and specifiy a hash table to be used to count matching connections. If not given, the name shorewallN (where N is a unique integer) is - assumed. Where more than one POLICY specifies the same name, the - connections counts for the rules are aggregated and the individual - rates apply to the aggregated count. + assumed. Where more than one rule or POLICY specifies the same name, + the connections counts for the rules are aggregated and the + individual rates apply to the aggregated count. + + Beginning with Shorewall 4.6.5, two + limits may be specified, separated by a comma. In this + case, the first limit (name1, + rate1, burst1) specifies the per-source + IP limit and the second limit specifies the per-destination IP + limit. + + Example: client:10/sec:20,:60/sec:100 + + In this example, the 'client' hash table will be used to + enforce the per-source limit and the compiler will pick a unique + name for the hash table that tracks the per-destination + limit.