2 # SPDX-License-Identifier: GPL-2.0-only
6 BEGIN { pop @INC if $INC[-1] eq '.' };
10 usage: ratelimit.pl [options] <period> <regex> <logfile>
12 The aim of this script is to compute clients' peak sending rates
13 from an Exim log file, using the same formula as Exim's ratelimit
14 ACL condition. This is so that you can get an idea of a reasonable
15 limit setting before you deploy the restrictions.
19 -d Show debugging information to stderr
20 -p Show progress of parse the log to stderr
22 <period> The smoothing period in seconds, as defined by the
23 documentation for the ratelimit ACL condition.
25 This script isn't perfectly accurate, because the time
26 stamps in Exim's log files are only accurate to a second
27 whereas internally Exim computes sender rates to the
28 accuracy of your computer's clock (typically 10ms).
30 <regex> The second argument is a regular expression.
32 Each line is matched against the regular expression.
33 Lines that do not match are ignored. The regex may
34 contain 0, 1, or 2 () capturing sub-expressions.
36 If there are no () sub-expressions, then every line that
37 matches is used to compute a single rate. Its maximum
38 value is reported when the script finishes.
40 If there is one () sub-expression, then the text matched
41 by the sub-expression is used to identify a rate lookup
42 key, similar to the lookup key used by the ratelimit
43 ACL condition. For example, you might write a regex
44 to match the client IP address, or the authenticated
45 username. Separate rates are computed for each different
46 client and the maximum rate for each client is reported
47 when the script finishes.
49 If there are two () sub-expressions, then the text matched
50 by the first sub-expression is used to identify a rate
51 lookup key as above, and the second is used to match the
52 message size recorded in the log line, e.g. "S=(\\d+)".
53 In this case the byte rate is computed instead of the
54 message rate, similar to the per_byte option of the
55 ratelimit ACL condition.
57 <logfile> The log files to be processed can be specified on the
58 command line after the other arguments; if no filenames
59 are specified the script will read from stdin.
63 ./ratelimit.pl 1 ' <= .*? \[(.*?)\]' <logfile>
65 Compute burst sending rate like ACL condition
66 ratelimit = 0 / 1s / strict / \$sender_host_address
68 ./ratelimit.pl 3600 '<= (.*?) ' <logfile>
70 Compute sending rate like ACL condition
71 ratelimit = 0 / 1h / strict / \$sender_address
78 my ($y,$m,$d,$H,$M,$S,$zs,$zh,$zm) = @_;
81 $m += $m < 3 ? 10 : -2;
82 my $z = defined $zs ? "${zs}1" * ($zh * 60 + $zm) : 0;
83 my $t = $y/400 - $y/100 + $y/4 + $y*365
84 + $m*367/12 + $d - 719499;
94 while (@ARGV && $ARGV[0] =~ /^-\w+$/) {
95 $debug = 1 if $ARGV[0] =~ s/(-\w*)d(\w*)/$1$2/;
96 $progress = 1 if $ARGV[0] =~ s/(-\w*)p(\w*)/$1$2/;
97 shift if $ARGV[0] eq "-";
107 my $re = qr{$re_txt}o;
115 printf STDERR "%s\t%12d %8s %5.2f %5.2f\n",
116 $_, $time{$key}, $key, $max{$key}, $rate{$key};
120 next unless $_ =~ $re;
122 my $size = $2 || 1.0;
124 ($_ =~ m{^(\d{4})-(\d\d)-(\d\d)[ ]
125 (\d\d):(\d\d):(\d\d)[ ]
126 (?:([+-])(\d\d)(\d\d)[ ])?
129 my $prog_now = substr $_, 0, 14;
130 if ($progtime ne $prog_now) {
131 $progtime = $prog_now;
132 print STDERR "$progtime\n";
135 if (not defined $time{$key}) {
139 debug $key if $debug;
142 # see acl_ratelimit() for details of the following
143 my $interval = $time - $time{$key};
144 $interval = 1e-9 if $interval <= 0.0;
145 my $i_over_p = $interval / $period;
146 my $a = exp(-$i_over_p);
148 $rate{$key} = $size * (1.0 - $a) / $i_over_p + $a * $rate{$key};
149 $max{$key} = $rate{$key} if $rate{$key} > $max{$key};
150 debug $key if $debug;
154 " " x (20 - length) .
157 $max{$a} <=> $max{$b}