line
stmt
bran
cond
sub
pod
time
code
1
#!/usr/bin/perl
2
3
#=======================================================================
4
# Set.pm / IPTables::Log::Set
5
# $Id: Set.pm 21 2010-12-17 21:07:37Z andys $
6
# $HeadURL: https://daedalus.dmz.dn7.org.uk/svn/IPTables-Log/trunk/IPTables-Log/lib/IPTables/Log/Set.pm $
7
# (c)2009 Andy Smith
8
#-----------------------------------------------------------------------
9
#:Description
10
# This class holds a set of IPTables::Log::Set::Record objects
11
#-----------------------------------------------------------------------
12
#:Synopsis
13
# NOTE: This class isn't designed to be created directly.
14
#
15
# use IPTables::Log;
16
# my $l = IPTables::Log->new;
17
# my $s = $l->create_set;
18
# my $r = $s->create_record({text => '...IN=eth0 OUT=eth1 MAC=00:...'});
19
# $r->parse;
20
# $s->add($r);
21
#=======================================================================
22
23
# The pod (Perl Documentation) for this module is provided inline. For a
24
# better-formatted version, please run:-
25
# $ perldoc Set.pm
26
27
=head1 NAME
28
29
IPTables::Log::Set - Holds a set of IPTables::Log::Set::Record objects.
30
31
=head1 SYNOPSIS
32
33
Note that this class isn't designed to be created directly. You can create these objects via a C object.
34
35
use IPTables::Log;
36
my $l = IPTables::Log->new;
37
my $s = $l->create_set;
38
39
=head1 DEPENDENCIES
40
41
=over 4
42
43
=item * Class::Accessor - for accessor methods
44
45
=item * Data::GUID - for GUID generation
46
47
=item * NetAddr::IP - for the C and C methods (required by L)
48
49
=back
50
51
=cut
52
53
# Set our package name
54
package IPTables::Log::Set;
55
56
# Minimum version
57
3
3
53
use 5.010000;
3
12
3
130
58
# Use strict and warnings
59
3
3
17
use strict;
3
7
3
89
60
3
3
13
use warnings;
3
4
3
67
61
62
# Use Carp for erroring
63
3
3
14
use Carp;
3
5
3
166
64
# Use Data::GUID for generating GUIDs
65
3
3
3172
use Data::GUID;
3
95590
3
46
66
# Use IPTables::Log::Set::Record for individual log entries
67
3
3
2933
use IPTables::Log::Set::Record;
3
11
3
36
68
# Use Data::Dumper
69
3
3
224
use Data::Dumper;
3
7
3
315
70
71
# Inherit from Class::Accessor to simplify accessor method generation
72
3
3
19
use base qw(Class::Accessor);
3
15
3
3064
73
# Follow best practice
74
__PACKAGE__->follow_best_practice;
75
# Create log and guid as read-only accessor methods
76
__PACKAGE__->mk_ro_accessors( qw(log guid) );
77
78
# Set version information
79
our $VERSION = '0.0005';
80
81
=head1 CONSTRUCTORS
82
83
=head2 Set->create
84
85
Creates a new C object. This isn't the recommended way to do this, however. The proper way is to create an object via a L object with C.
86
87
=cut
88
89
sub create
90
{
91
2
2
1
4
my ($class, $args) = @_;
92
93
2
19
my $self = __PACKAGE__->new($args);
94
2
30
$self->{records} = {};
95
96
# Generate a GUID for the set
97
2
23
my $g = Data::GUID->new;
98
2
390878
$self->{guid} = $g->as_string;
99
2
163
$self->{no_header} = $args->{'no_header'};
100
101
2
28
return $self;
102
}
103
104
=head1 METHODS
105
106
=head2 $set->create_record(I<{text => '...IN=eth0 OUT=eth1 MAC=00:...'}>))
107
108
Creates a new L object. This is the B way to create C objects, as it ensures various settings are inherited from the C class.
109
110
The text of the log entry can be passed here, or it can be passed with the C accessor method to the C object itself.
111
112
=cut
113
114
sub create_record
115
{
116
9
9
1
845
my ($self, $args) = @_;
117
118
#$args->{log} = $self->get_log;
119
120
9
49
my $record = IPTables::Log::Set::Record->create($args);
121
122
9
19
return $record;
123
}
124
125
=head2 $set->load_file($filename)
126
127
Loads in logs from I<$filename>, discarding any which don't appear to be iptables/netfilter logs. A L object is then created for each entry, and the content is then parsed. Finally, each entry is then added to the set created with C.
128
129
=cut
130
131
sub load_file
132
{
133
1
1
1
375
my ($self, $filename) = @_;
134
135
# Check we've been passed a filename
136
1
50
3
if(!$filename)
137
{
138
0
0
croak "No filename given to load_file().";
139
#$self->get_log->fatal("No filename given!");
140
}
141
142
# Check that the file exists, and barf if not.
143
1
50
22
if(!-f $filename)
144
{
145
0
0
croak $filename." does not exist.";
146
#$self->get_log->fatal("Cannot find ".$self->get_log->fcolour('yellow', $filename));
147
}
148
149
#$self->get_log->debug("Opening ".$self->get_log->fcolour('yellow', $filename)."...");
150
# Open the logfile
151
1
50
40
open(LOGFILE, $filename) || $self->get_log->fatal("Cannot open ".$self->get_log->fcolour('yellow', $filename));
152
1
46
my @logs = ;
153
#$self->get_log->debug("Finished reading in logs.");
154
155
# It's a fair bet that if we don't have an IN= and an OUT= and it doesn't have a source of 'kernel', then it's not an iptables log.
156
# We'll discard those before even attempting to parse it.
157
1
4
foreach my $log (@logs)
158
{
159
8
50
133
if($log =~ /kernel.+IN=.+OUT=/)
160
{
161
8
17
chomp($log);
162
#$self->get_log->debug_nolf("Parsing iptables log entry... ");
163
8
38
my $record = $self->create_record({'text' => $log, 'no_header' => $self->{no_header}});
164
8
29
$record->parse;
165
#$self->get_log->debug("done.");
166
8
19
$self->add($record);
167
8
22
$self->get_log->debug("Added record with GUID ".$self->get_log->fcolour('yellow', $record->get_guid). " to set.");
168
#return 1;
169
}
170
else
171
{
172
#$self->get_log->debug("Log entry is not an iptables log entry, so skipping...");
173
}
174
}
175
1
18
return 1;
176
}
177
178
=head2 $set->add($record)
179
180
Adds a L object to a set created with C.
181
182
=cut
183
184
sub add
185
{
186
8
8
1
13
my ($self, $record) = @_;
187
188
8
50
15
if($record)
189
{
190
8
20
my $guid = $record->get_guid;
191
192
8
38
$self->{records}{$guid} = $record;
193
}
194
}
195
196
=head2 $set->get_by('field')
197
198
Returns a hash of record identifiers, indexed by I. Field can be one of I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I.
199
200
If you attempt to sort on a field that isn't present in all records in the set, get_by will only return records which have that field. For example, if you attempt to get_by('dpt'), any ICMP log messages will be silently excluded from the returned set.
201
202
=cut
203
204
sub get_by
205
{
206
19
19
1
10470
my ($self, $by) = @_;
207
208
# Check that $by is set
209
19
50
63
if($by)
210
{
211
# Create a hash to hold the index values
212
19
88
my %indexes;
213
19
34
$indexes{by} = $by;
214
215
19
23
foreach my $r (keys %{$self->{records}})
19
84
216
{
217
# Step through each record.
218
152
264
my $record = $self->{records}{$r};
219
152
643
my $value = $record->get($by);
220
221
# If $value is blank, it means not all records have this field.
222
# For now, we'll refuse to add these.
223
152
100
313
if($value)
224
{
225
127
100
331
if(!$indexes{$by}{$record->get($by)})
226
{
227
49
174
$indexes{$by}{$record->get($by)} = [];
228
}
229
127
165
push (@{$indexes{$by}{$record->get($by)}}, $record);
127
429
230
}
231
}
232
233
19
200
return %indexes;
234
}
235
}
236
237
=head1 CAVEATS
238
239
None.
240
241
=head1 BUGS
242
243
None that I'm aware of ;-)
244
245
=head1 AUTHOR
246
247
This module was written by B .
248
249
=head1 COPYRIGHT
250
251
$Id: Set.pm 21 2010-12-17 21:07:37Z andys $
252
253
(c)2009 Andy Smith (L )
254
255
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
256
257
=cut
258
259
1