line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package ThreatNet::Filter::ThreatCache; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=pod |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
ThreatNet::Filter::ThreatCache - A Threat Cache implementated as a filter |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 DESCRIPTION |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
A C is a basic filter-based implementation |
12
|
|
|
|
|
|
|
of a I, as defined in the ThreatNet concept paper. |
13
|
|
|
|
|
|
|
(L) |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
The consistent use of Threat Caches by all nodes is the key to keeping |
16
|
|
|
|
|
|
|
message quantities to a minimum, and allows the entire network to safely |
17
|
|
|
|
|
|
|
run without any canonical state. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
As each message is provided to the filter, it stores an IPs that it sees, |
20
|
|
|
|
|
|
|
filtering out any ips that have already been seen in the last hour (or |
21
|
|
|
|
|
|
|
custom period if provided). |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 METHODS |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=cut |
26
|
|
|
|
|
|
|
|
27
|
3
|
|
|
3
|
|
12247
|
use strict; |
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
143
|
|
28
|
3
|
|
|
3
|
|
17
|
use Params::Util '_INSTANCE'; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
492
|
|
29
|
3
|
|
|
3
|
|
20
|
use base 'ThreatNet::Filter'; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
652
|
|
30
|
|
|
|
|
|
|
|
31
|
3
|
|
|
3
|
|
22
|
use vars qw{$VERSION}; |
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
421
|
|
32
|
|
|
|
|
|
|
BEGIN { |
33
|
3
|
|
|
3
|
|
3154
|
$VERSION = '0.20'; |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
##################################################################### |
41
|
|
|
|
|
|
|
# ThreatNet::Filter Interface |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=pod |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head2 new [ $param => $value, ... ] |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
The C method creates a new Threat Cache object. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
It takes a single optional parameter C which should the |
50
|
|
|
|
|
|
|
positive integer number of seconds the channel dictates as the mimumum |
51
|
|
|
|
|
|
|
time before an event can be rementioned. The default value is 3600 |
52
|
|
|
|
|
|
|
(1 hour). |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
Returns a new C object. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=cut |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub new { |
59
|
2
|
50
|
|
2
|
1
|
642
|
my $class = ref $_[0] ? ref shift : shift; |
60
|
2
|
|
|
|
|
10
|
my %args = @_; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# Add the cache attributes to the base filter object |
63
|
2
|
|
|
|
|
16
|
my $self = $class->SUPER::new; |
64
|
2
|
|
|
|
|
13
|
$self->{cache_ip} = {}; |
65
|
2
|
|
|
|
|
5
|
$self->{cache_time} = []; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# When we will be syncronised? |
68
|
|
|
|
|
|
|
# Add a couple of seconds to allow for small initialization delays. |
69
|
2
|
|
|
|
|
6
|
my $t = time(); |
70
|
2
|
50
|
|
|
|
9
|
$self->{timeout} = defined $args{timeout} ? $args{timeout} : 3600; |
71
|
2
|
|
|
|
|
7
|
$self->{sync_at} = $self->{timeout} + $t + 2; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Collect some very basic statistic |
74
|
2
|
|
|
|
|
8
|
$self->{stats} = { time_start => $t, => seen => 0, kept => 0 }; |
75
|
|
|
|
|
|
|
|
76
|
2
|
|
|
|
|
15
|
$self; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=pod |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=head2 keep $Message |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
As in the parent L class, the C method takes as |
84
|
|
|
|
|
|
|
argument a single C object. |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
It returns true if the message can be kept, or false if the message should |
87
|
|
|
|
|
|
|
be dropped. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=cut |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
sub keep { |
92
|
8
|
|
|
8
|
1
|
1428
|
my $self = shift; |
93
|
8
|
100
|
|
|
|
65
|
my $Message = _INSTANCE(shift, 'ThreatNet::Message') or return undef; |
94
|
6
|
50
|
|
|
|
44
|
$Message->can('ip') or return undef; # WTF is it if it can't? |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# Flush old events out of the cache |
97
|
6
|
|
|
|
|
26
|
while ( my $event = $self->{cache_time}->[0] ) { |
98
|
4
|
50
|
|
|
|
14
|
if ( time() > $event->{time} + $self->{timeout} ) { |
99
|
|
|
|
|
|
|
# Block has expired |
100
|
0
|
|
|
|
|
0
|
shift @{$self->{cache_time}}; |
|
0
|
|
|
|
|
0
|
|
101
|
0
|
|
|
|
|
0
|
delete $self->{cache_ip}->{$event->{ip}}; |
102
|
|
|
|
|
|
|
} else { |
103
|
|
|
|
|
|
|
# Because the events are added (we assume) in creation |
104
|
|
|
|
|
|
|
# order, as soon as we encounter one that has not expired, |
105
|
|
|
|
|
|
|
# we can assume the rest have not expired. |
106
|
4
|
|
|
|
|
7
|
last; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# We only want the time and IP |
111
|
|
|
|
|
|
|
### Convert this to ->event_time later? |
112
|
6
|
50
|
|
|
|
28
|
my $created = $Message->created or return undef; |
113
|
6
|
50
|
|
|
|
20
|
my $ip = $Message->ip or return undef; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# At this point, we've officially "seen" the message |
116
|
6
|
|
|
|
|
41
|
$self->{stats}->{seen}++; |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# If the ip is in the cache, don't keep it |
119
|
6
|
100
|
|
|
|
25
|
return '' if $self->{cache_ip}->{$ip}; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# Add to the cache and signal to keep. |
122
|
|
|
|
|
|
|
### Stricly speaking, just dropping it onto the end of the |
123
|
|
|
|
|
|
|
### queue is not good enough. Convert this to something that |
124
|
|
|
|
|
|
|
### ensures correct order once there is a chance that the events |
125
|
|
|
|
|
|
|
### may come in out of order, such as if we change from object |
126
|
|
|
|
|
|
|
### creation time to event time. |
127
|
4
|
|
|
|
|
12
|
$self->{cache_ip}->{$ip} = $created; |
128
|
4
|
|
|
|
|
5
|
push @{$self->{cache_time}}, { time => $created, ip => $ip }; |
|
4
|
|
|
|
|
26
|
|
129
|
4
|
|
|
|
|
9
|
$self->{stats}->{kept}++; |
130
|
|
|
|
|
|
|
|
131
|
4
|
|
|
|
|
15
|
1; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=pod |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=head2 synced |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
The C method checks to see if the Threat Cache has synchronised |
139
|
|
|
|
|
|
|
with the channel. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
Returns true if the current time is past the sync time, or false if not. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=cut |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub synced { |
146
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
147
|
0
|
|
|
|
|
0
|
!! (time() > $self->{sync_at}); |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=pod |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=head2 stats |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
The C method returns a hash with a variety of statistics from |
155
|
|
|
|
|
|
|
the Threat Cache. |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Returns the stats as a C reference. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=cut |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub stats { |
162
|
2
|
|
|
2
|
1
|
4
|
my $self = shift; |
163
|
2
|
|
|
|
|
6
|
my %stats = (); |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# Generate the general statistics |
166
|
2
|
|
|
|
|
2
|
$stats{size} = scalar @{$self->{cache_time}}; |
|
2
|
|
|
|
|
8
|
|
167
|
2
|
|
|
|
|
8
|
$stats{seen} = $self->{stats}->{seen}; |
168
|
2
|
|
|
|
|
5
|
$stats{kept} = $self->{stats}->{kept}; |
169
|
2
|
|
|
|
|
6
|
$stats{discard} = $stats{seen} - $stats{kept}; |
170
|
2
|
|
|
|
|
5
|
$stats{expired} = $stats{kept} - $stats{size}; |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# Add the time statistics |
173
|
2
|
|
|
|
|
5
|
$stats{time_start} = $self->{stats}->{time_start}; |
174
|
2
|
|
|
|
|
6
|
$stats{time_current} = time(); |
175
|
2
|
|
|
|
|
7
|
$stats{time_running} = $stats{time_current} - $stats{time_start}; |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# Percentages |
178
|
2
|
|
|
|
|
8
|
$stats{percent_kept} = $self->_perc($stats{kept}, $stats{seen}); |
179
|
2
|
|
|
|
|
6
|
$stats{percent_discard} = $self->_perc($stats{discard}, $stats{seen}); |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# Rates |
182
|
2
|
|
|
|
|
7
|
$stats{rate_seen} = $self->_rate($stats{seen}, $stats{time_running}); |
183
|
2
|
|
|
|
|
5
|
$stats{rate_kept} = $self->_rate($stats{kept}, $stats{time_running}); |
184
|
|
|
|
|
|
|
|
185
|
2
|
|
|
|
|
9
|
\%stats; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub _perc { |
189
|
4
|
|
|
4
|
|
9
|
my (undef, $items, $total) = @_; |
190
|
4
|
50
|
|
|
|
13
|
my $perc = $total ? ($items / $total) : 0; |
191
|
4
|
|
|
|
|
7
|
$perc = $perc * 100; |
192
|
4
|
|
|
|
|
37
|
sprintf("%0.1f", $perc) . '%'; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub _rate { |
196
|
4
|
|
|
4
|
|
8
|
my (undef, $items, $interval) = @_; |
197
|
4
|
50
|
|
|
|
10
|
my $rate = $interval ? ($items / $interval) : 0; |
198
|
4
|
|
|
|
|
21
|
sprintf("%0.1f", $rate); |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
1; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=pod |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=head1 SUPPORT |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
All bugs should be filed via the bug tracker at |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
L |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
For other issues, or commercial enhancement and support, contact the author |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=head1 AUTHORS |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Adam Kennedy Eadamk@cpan.orgE |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=head1 SEE ALSO |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
L, L |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=head1 COPYRIGHT |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
Copyright (c) 2005 Adam Kennedy. All rights reserved. |
224
|
|
|
|
|
|
|
This program is free software; you can redistribute |
225
|
|
|
|
|
|
|
it and/or modify it under the same terms as Perl itself. |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
The full text of the license can be found in the |
228
|
|
|
|
|
|
|
LICENSE file included with this module. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=cut |