File Coverage

blib/lib/IPTables/Log/Set/Record.pm
Criterion Covered Total %
statement 83 88 94.3
branch 15 24 62.5
condition 5 6 83.3
subroutine 13 13 100.0
pod 3 3 100.0
total 119 134 88.8


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             #=======================================================================
4             # Record.pm / IPTables::Log::Set::Record
5             # $Id: Record.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/Record.pm $
7             # (c)2009 Andy Smith
8             #-----------------------------------------------------------------------
9             #:Description
10             # This class holds a single IPTables/Netfilter record.
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             #=======================================================================
21              
22             # The pod (Perl Documentation) for this module is provided inline. For a
23             # better-formatted version, please run:-
24             # $ perldoc Record.pm
25              
26             =head1 NAME
27              
28             IPTables::Log::Set::Record - Holds a single IPTables/Netfilter log entry.
29              
30             =head1 SYNOPSIS
31              
32             Note that this class isn't designed to be created directly. You can create these objects via a C object.
33              
34             use IPTables::Log;
35             my $l = IPTables::Log->new;
36             my $s = $l->create_set;
37             my $r = $s->create_record({text => '...IN=eth0 OUT=eth1 MAC=00:...'});
38             $r->parse;
39              
40             =head1 DEPENDENCIES
41              
42             =over 4
43              
44             =item * Class::Accessor - for accessor methods
45              
46             =item * Data::GUID - for GUID generation
47              
48             =item * NetAddr::IP - for the C and C methods
49              
50             =back
51              
52             =cut
53              
54             # Set the package name
55             package IPTables::Log::Set::Record;
56              
57 3     3   57 use 5.010000;
  3         11  
  3         142  
58 3     3   19 use strict;
  3         6  
  3         104  
59 3     3   16 use warnings;
  3         6  
  3         91  
60              
61             # Use Data::GUID for generating GUIDs
62 3     3   15 use Data::GUID;
  3         15  
  3         19  
63             # Use Data::Dumper for debugging. Can be removed for releases.
64 3     3   16956 use Data::Dumper;
  3         30125  
  3         564  
65             # Use NetAddr::IP for IP addresses
66 3     3   3201 use NetAddr::IP;
  3         187413  
  3         15  
67 3     3   731 use NetAddr::IP::Util qw(inet_aton);
  3         7  
  3         22  
68              
69             # Inherit from Class::Accessor, which saves us quite a bit of time.
70 3     3   293 use base qw(Class::Accessor);
  3         5  
  3         13459  
71             # Follow best practice
72             __PACKAGE__->follow_best_practice;
73             # Make 'text' a read/write accessor method
74             __PACKAGE__->mk_accessors( qw(text parsed) );
75             # Make the rest read-only
76             __PACKAGE__->mk_ro_accessors( qw(log guid date time hostname prefix in out mac src dst proto _spt _dpt spt dpt id len ttl df window syn type code) );
77              
78             # Set version information
79             our $VERSION = '0.0005';
80              
81             =head1 CONSTRUCTORS
82              
83             =head2 Record->create(I<{text => '...IN=eth0 OUT=eth1 MAC=00:...'}>)
84              
85             Creates a new C object. You shouldn't call this directly - see the synopsis for an example.
86              
87             =cut
88              
89             # Call create instead of new, and the GUID will be generated automatically
90             sub create
91             {
92 9     9 1 17 my ($class, $args) = @_;
93              
94 9         53 my $self = __PACKAGE__->new($args);
95             # Generate a GUID for the ID
96 9         116 my $g = Data::GUID->new;
97 9         146 $self->{guid} = $g->as_string;
98 9 50       154 $self->{no_header} = $args->{'no_header'} ? $args->{'no_header'} : 0;
99 9         15 $self->{parsed} = 0;
100              
101 9         29 return $self;
102             }
103              
104             # Private function for checking the content of fields
105             # Not documented in pod format because this is a private function.
106             sub _process_value
107             {
108 139     139   275 my ($self, $value, $name) = @_;
109              
110             # If $value isn't set, set it to "NONE". A blank string will break IPTables::Log::Set->get_by().
111 139 100 66     528 if((!$value) || ($value eq ""))
112             {
113 23         27 $value = "NONE";
114             }
115              
116 139         250 $self->{$name} = $value;
117 139         184 return 1;
118             }
119              
120             # As for _process_value, but if true replaces the value with a 1, otherwise replaces it with a 0
121             # Not documented in pod format because this is a private function.
122             sub _process_present
123             {
124 13     13   19 my ($self, $value, $name) = @_;
125              
126 13 100       25 if($value)
127             {
128 2         5 $self->{$name} = 1;
129             }
130             else
131             {
132 11         21 $self->{$name} = 0;
133             }
134 13         20 return 1;
135             }
136              
137             =head1 METHODS
138              
139             =head2 $record->parse
140              
141             Parses the log message text passed either to the constructor, or via C.
142              
143             =cut
144              
145             # Parses the log text
146             sub parse
147             {
148 9     9 1 18 my ($self, $text) = @_;
149              
150 9 50       28 if(!$self->get_text)
151             {
152 0 0       0 if($text)
153             {
154             # Set the text attribute to the original log message
155 0         0 $self->set_text($text);
156             }
157             else
158             {
159             #$self->get_log->error("No log text found?");
160 0         0 return;
161             }
162             }
163             else
164             {
165 9         24 $text = $self->get_text;
166             }
167              
168             #$self->get_log->debug_value("Original log message is", 'yellow', $text);
169              
170             # First, we pull parts out common to all protocols
171 9         16 my ($date, $time, $hostname, $prefix, $in, $out, $mac, $src, $dst, $len, $ttl, $id, $df, $proto);
172 9 50       29 if($self->{'no_header'} eq 1)
173             {
174 0         0 (undef, $prefix, $in, $out, undef, $mac, $src, $dst, $len, $ttl, $id, $df, $proto)
175             = $text =~ /kernel:(\s\[\d+\.\d+\])?\s(\S*)\sIN=(\S*)\sOUT=(\S*)\s(MAC=)?(\S+)?\s*SRC=(\d+\.\d+\.\d+\.\d+|\S+)\sDST=(\d+\.\d+\.\d+\.\d+|\S+)\sLEN=(\d+).+TTL=(\d+).+ID=(\d+)\s(DF)*\s*PROTO=(\S+)/;
176             }
177             else
178             {
179 9         299 ($date, $time, $hostname, undef, $prefix, $in, $out, undef, $mac, $src, $dst, $len, $ttl, $id, $df, $proto)
180             = $text =~ /(\w{3}\s\d{1,2})\s{1,2}(\d{2}:\d{2}:\d{2})\s(.+)\skernel:(\s\[\d+\.\d+\])?\s(\S*)\sIN=(\S*)\sOUT=(\S*)\s(MAC=)?(\S+)?\s*SRC=(\d+\.\d+\.\d+\.\d+|\S+)\sDST=(\d+\.\d+\.\d+\.\d+|\S+)\sLEN=(\d+).+TTL=(\d+).+ID=(\d+)\s(DF)*\s*PROTO=(\S+)/;
181             }
182              
183             # Get the protocol first. Based on this, we know what regex we need next.
184 9         42 $self->_process_value($proto, 'proto');
185 9 50       25 if(!$proto)
186             {
187             #$self->get_log->error("Cannot determine the protocol for this log message!");
188             #$self->get_log->error("The log text is ".$self->get_log->fcolour('yellow', $text));
189 0         0 return;
190             }
191              
192             # Process values
193             # Date
194 9         19 $self->_process_value($date, 'date');
195             # Time
196 9         20 $self->_process_value($time, 'time');
197             # Hostname
198 9         18 $self->_process_value($hostname, 'hostname');
199             # IPTable logging prefix (as specified by '-j LOG --log-prefix=""'
200 9         20 $self->_process_value($prefix, 'prefix');
201             # Ingress interface
202 9         21 $self->_process_value($in, 'in');
203             # Egress interface
204 9         21 $self->_process_value($out, 'out');
205             # MAC address, if applicable
206 9         18 $self->_process_value($mac, 'mac');
207             # Source IP
208 9         21 $self->_process_value($src, '_src');
209 9 50       24 if($self->{_src})
210             {
211 9         40 $self->{_src} = new_from_aton NetAddr::IP::Lite (inet_aton($self->{_src}));
212 9         633 $self->{src} = $self->{_src}->addr();
213             }
214             # Destination IP
215 9         2248 $self->_process_value($dst, '_dst');
216 9 50       26 if($self->{_dst})
217             {
218 9         36 $self->{_dst} = new_from_aton NetAddr::IP::Lite (inet_aton($self->{_dst}));
219 9         457 $self->{dst} = $self->{_dst}->addr();
220             }
221             # Packet length
222 9         1294 $self->_process_value($len, 'len');
223             # TTL
224 9         22 $self->_process_value($ttl, 'ttl');
225             # Packet ID
226 9         17 $self->_process_value($id, 'id');
227             # Don't fragment
228 9         23 $self->_process_present($df, 'df');
229              
230 9 100 100     4544 if(($proto eq "TCP") || ($proto eq "UDP"))
    50          
231             {
232             # TCP or UDP packet
233 7         188 my ($spt, $dpt) = $text =~ /PROTO=$proto\sSPT=(\d+)\sDPT=(\d+)/;
234              
235             # Source port
236 7         25 $self->_process_value($spt, 'spt');
237             # Destination port
238 7         18 $self->_process_value($dpt, 'dpt');
239              
240 7 100       19 if($proto eq "TCP")
241             {
242             # TCP specifics
243 4         15 my ($window, $syn) = $text =~ /WINDOW=(\d+).*(SYN)/;
244              
245             # TCP window size
246 4         12 $self->_process_value($window, 'window');
247             # SYN present?
248 4         11 $self->_process_present($syn, 'syn');
249             }
250             }
251             elsif($proto eq "ICMP")
252             {
253 2         11 my ($type) = $text =~ /ICMP TYPE=(\d+)\sCODE=(\d+)/;
254              
255             # ICMP Type
256 2         6 $self->_process_value($type, 'type');
257 2         5 $self->_process_value($type, 'code');
258             }
259              
260             # Return true if we've gotten this far.
261 9         32 $self->set_parsed(1);
262 9         114 return 1;
263             }
264              
265             =head2 $record->set_text("...IN=eth0 OUT=eth1 MAC=00:...")
266              
267             Sets the log message text. Either this must be set, or the text must have been passed to C, otherwise C will error.
268              
269             =head1 ACCESSOR METHODS
270              
271             =head2 get(I)
272              
273             Returns the value of I. Field can be one of I, I, I
274              
275             =cut
276              
277             # Get accessor that takes the variable to return as an argument
278             sub get
279             {
280 507     507 1 1469 my ($self, $value) = @_;
281              
282 507         1894 return $self->{$value};
283             }
284              
285             =head2 get_guid
286              
287             Returns the GUID for the packet.
288              
289             =head2 get_date
290              
291             Returns the date portion of the log message.
292              
293             =head2 get_time
294              
295             Returns the time portion of the log message.
296              
297             =head2 get_hostname
298              
299             rETURns the hostname portion of the log message.
300              
301             =head2 get_prefix
302              
303             Returns the iptables/netfilter log prefix for the log message, i.e. the part specified by C<-j LOG --log-prefix='I '>.
304              
305             =head2 get_in
306              
307             Returns the ingress interface, if specified.
308              
309             =head2 get_out
310              
311             Returns the egress interface, if specified.
312              
313             =head2 get_mac
314              
315             Returns the MAC address, if specified.
316              
317             =head2 get_src
318              
319             Returns the source IP address.
320              
321             =head2 get_dst
322              
323             Returns the destination IP address.
324              
325             =head2 get_proto
326              
327             Returns the protocol.
328              
329             =head2 get_spt - TCP and UDP packets only.
330              
331             Returns the source port, if applicable.
332              
333             =head2 get_dpt - TCP and UDP packets only.
334              
335             Returns the destination port, if applicable
336              
337             =head2 get_id
338              
339             Returns the packet ID.
340              
341             =head2 get_len
342              
343             Returns the packet length.
344              
345             =head2 get_ttl
346              
347             Returns the packet's TTL (Time To Live).
348              
349             =head2 get_df
350              
351             Returns the packet's DF (Don't Fragment) value.
352              
353             =head2 get_window - TCP and UDP packets only.
354              
355             Returns the packet's window size.
356              
357             =head2 get_sync
358              
359             Returns 1 if the packet is a SYN, otherwise returns 0.
360              
361             =head2 get_parsed
362              
363             Returns 1 if the packet has been successfully parsed, otherwise returns 0.
364              
365             =head1 CAVEATS
366              
367             It parses log entries. It doesn't do much else, yet.
368              
369             =head1 BUGS
370              
371             None that I'm aware of ;-)
372              
373             =head1 AUTHOR
374              
375             This module was written by B .
376              
377             =head1 COPYRIGHT
378              
379             $Id: Record.pm 21 2010-12-17 21:07:37Z andys $
380              
381             (c)2009 Andy Smith (L)
382              
383             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
384              
385             =cut
386              
387             1