File Coverage

blib/lib/ThreatNet/Filter/ThreatCache.pm
Criterion Covered Total %
statement 61 66 92.4
branch 12 20 60.0
condition n/a
subroutine 10 11 90.9
pod 4 4 100.0
total 87 101 86.1


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