File Coverage

IPTables.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # -*- mode: cperl; cperl-indent-level: 4; -*-
2             # vi:ai:sm:et:sw=4:ts=4
3              
4             # $Id: IPTables.pm,v 1.9 2005/11/18 23:40:00 paulv Exp $
5              
6             package POE::Filter::Log::IPTables;
7              
8 2     2   74491 use strict;
  2         5  
  2         88  
9 2     2   11 use warnings;
  2         4  
  2         62  
10 2     2   930 use POE::Filter::Line;
  0            
  0            
11             use Carp qw(carp croak);
12              
13             our $VERSION = '0.02';
14              
15             sub new {
16             my $class = shift;
17              
18             croak "$class requires an even number of parameters" if @_ and @_ & 1;
19              
20             my %params = @_;
21              
22             my $self = {};
23              
24             $self->{line} = POE::Filter::Line->new();
25              
26             # the types of ICMP messages that require us to do further
27             # processing
28             $self->{icmp_types} = ['3', # destination unreachable
29             '4', # source quench
30             '11', # time exceeded
31             ];
32            
33             if (defined $params{Debug} and $params{Debug} > 0) {
34             $self->{debug} = 1;
35             } else {
36             $self->{debug} = 0;
37             }
38              
39             if (defined $params{Syslog} and $params{Syslog} > 0) {
40             $self->{syslog} = 1;
41             } else {
42             $self->{syslog} = 0;
43             }
44            
45             bless ($self, $class);
46             return $self;
47             }
48              
49             sub get {
50             my $self = shift;
51             my $chunk = shift;
52             my @queue;
53              
54             my $lines = $self->{line}->get($chunk);
55             foreach my $line (@$lines) {
56             push(@queue, $self->_parse($line));
57             }
58             return \@queue;
59             }
60              
61             sub _parse {
62             my $self = shift;
63             my $line = shift;
64            
65             my $ds;
66             my $leftover;
67            
68             # save the whole line so the user can twiddle it if she wants.
69             $ds->{line} = $line;
70              
71             # strip off syslog stuff
72             if ($self->{syslog}) {
73             # Jan 11 17:30:35 hostname kernel:
74             $line =~ s/^\w\w\w\s+\d+ \d\d:\d\d:\d\d (?:\w|-|\.|_)+ //;
75             }
76              
77             # remove prefix
78             $line =~ s/kernel: (.{0,29}) ?IN/IN/;
79            
80             # IN, OUT, and MAC
81             print "base: [$line]\n" if $self->{debug};
82            
83             # inbound interface
84             if ($line =~ /^IN=(\w+)? /) {
85             $ds->{in_int} = $1;
86              
87             if ($1) {
88             $line =~ s/IN=$1 //;
89             } else {
90             $line =~ s/IN= //;
91             }
92             }
93              
94             # outbound interface
95             if ($line =~ / ?OUT=([A-Za-z0-9]+)? /) {
96             $ds->{out_int} = $1;
97              
98             if ($1) {
99             $line =~ s/ ?OUT=$1 //;
100             } else {
101             $line =~ s/ ?OUT= //;
102             }
103             }
104              
105             # input MAC address
106             if ($line =~ / ?MAC=([0-9A-F:]+)? /i) {
107             $ds->{mac} = $1;
108              
109             if ($1) {
110             $line =~ s/ ?MAC=$1 //;
111             } else {
112             $line =~ s/ ?MAC= //;
113             }
114             }
115              
116             print "BASE done: [$line]\n" if $self->{debug};
117            
118             # the IP header
119             ($line, $ds->{ip}) = $self->_parseIP($line);
120              
121             # everything else
122              
123             # ICMP has to be done first because ICMP messages sometimes have IP
124             # headers in them.
125             if ($line =~/ ?PROTO=ICMP /) {
126             $line =~ s/^PROTO=ICMP //g;
127             $ds->{ip}->{type} = "icmp";
128             ($ds->{ip}->{icmp}, $leftover) = $self->_parseICMP($line);
129             } elsif ($line =~ / ?PROTO=TCP /) {
130             $line =~ s/^PROTO=TCP //g;
131             $ds->{ip}->{type} = "tcp";
132             ($ds->{ip}->{tcp}, $leftover) = $self->_parseTCP($line);
133             } elsif ($line =~/ ?PROTO=UDP /) {
134             $line =~ s/^PROTO=UDP //g;
135             $ds->{ip}->{type} = "udp";
136             ($ds->{ip}->{udp}, $leftover) = $self->_parseUDP($line);
137             } else {
138             # $line =~ s/^PROTO=//g;
139             ($ds->{ip}->{type}, $leftover) = $self->_parseUnknown($line);
140             }
141              
142             $ds->{leftover} = $leftover;
143            
144             return $ds;
145             }
146              
147             sub _parseIP {
148             my $self = shift;
149             my $line = shift;
150             my $ds;
151              
152             print "IP: [$line]\n" if $self->{debug};
153            
154             # source address
155             if ($line =~ / ?SRC=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) /) {
156             $ds->{src_addr} = $1;
157             $line =~ s/ ?SRC=$1 //;
158             }
159              
160             # destination address
161             if ($line =~ / ?DST=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) /) {
162             $ds->{dst_addr} = $1;
163             $line =~ s/ ?DST=$1 //;
164             }
165              
166             # length
167             if ($line =~ / ?LEN=(\d+) /) {
168             $ds->{len} = $1;
169             $line =~ s/ ?LEN=$1 //;
170             }
171              
172             # type of service
173             if ($line =~ / ?TOS=(0x[0-9A-Z]{2}) /i) {
174             $ds->{tos} = $1;
175             $line =~ s/ ?TOS=$1 //;
176             }
177              
178             # PREC?
179             if ($line =~ / ?PREC=(0x[0-9A-Z]{2}) /i) {
180             $ds->{prec} = $1;
181             $line =~ s/ ?PREC=$1 //;
182             }
183              
184             # time to live
185             if ($line =~ / ?TTL=(\d+) /) {
186             $ds->{ttl} = $1;
187             $line =~ s/ ?TTL=$1 //;
188             }
189              
190             # ip id
191             if ($line =~ / ?ID=(\d+) /) {
192             $ds->{id} = $1;
193             $line =~ s/ ?ID=$1 //;
194             }
195              
196             # congestion
197             if ($line =~ /^CE /) {
198             push(@{$ds->{fragment_flags}}, "CE");
199             $line =~ s/^CE //;
200             }
201              
202             # don't fragment
203             if ($line =~ /^DF /) {
204             push(@{$ds->{fragment_flags}}, "DF");
205             $line =~ s/^DF //;
206             }
207              
208             # more fragments
209             if ($line =~ /^MF /) {
210             push(@{$ds->{fragment_flags}}, "MF");
211             $line =~ s/^MF //;
212             }
213              
214             print "IP done: [$line]\n" if $self->{debug};
215            
216             return ($line, $ds);
217             }
218              
219             sub _parseTCP {
220             my $self = shift;
221             my $line = shift;
222             my $ds;
223              
224             print "TCP: [$line]\n" if $self->{debug};
225            
226             # SPT=36073 DPT=22 WINDOW=5840 RES=0x00 SYN URGP=0
227              
228             # source port
229             if ($line =~ /SPT=(\d+) /) {
230             $ds->{src_port} = $1;
231             $line =~ s/SPT=$1 //;
232             }
233              
234             # destination port
235             if ($line =~ /DPT=(\d+) /) {
236             $ds->{dst_port} = $1;
237             $line =~ s/DPT=$1 //;
238             }
239              
240             # window length
241             if ($line =~ /WINDOW=(\d+) /) {
242             $ds->{window} = $1;
243             $line =~ s/WINDOW=$1 //;
244             }
245              
246             # reserved bits
247             if ($line =~ /RES=(0x[0-9A-Z]{2}) /i) {
248             $ds->{res} = $1;
249             $line =~ s/RES=$1 //;
250             }
251              
252             # flags
253             #
254             # CWR ECE URG ACK PSH RST SYN FIN
255             # CWR - Congestion Window Reduced
256             # ECE - Explicit Congestion Notification echo
257             # URG - Urgent
258             # ACK - Acknowledgement
259             # PSH - Push
260             # RST - Reset
261             # SYN - Synchronize
262             # FIN - Finished
263            
264             if ($line =~ /CWR /) {
265             push(@{$ds->{flags}}, "CWR");
266             $line =~ s/CWR //;
267             }
268              
269             if ($line =~ /ECE /) {
270             push(@{$ds->{flags}}, "ECE");
271             $line =~ s/ECE //;
272             }
273              
274             if ($line =~ /URG /) {
275             push(@{$ds->{flags}}, "URG");
276             $line =~ s/URG //;
277             }
278              
279             if ($line =~ /ACK /) {
280             push(@{$ds->{flags}}, "ACK");
281             $line =~ s/ACK //;
282             }
283              
284             if ($line =~ /PSH /) {
285             push(@{$ds->{flags}}, "PSH");
286             $line =~ s/PSH //;
287             }
288            
289             if ($line =~ /RST /) {
290             push(@{$ds->{flags}}, "RST");
291             $line =~ s/RST //;
292             }
293              
294             if ($line =~ /SYN /) {
295             push(@{$ds->{flags}}, "SYN");
296             $line =~ s/SYN //;
297             }
298              
299             if ($line =~ /FIN /) {
300             push(@{$ds->{flags}}, "FIN");
301             $line =~ s/FIN //;
302             }
303              
304             # urgent pointer
305              
306             if ($line =~ /URGP=(\d+) ?/) {
307             $ds->{urgp} = $1;
308             $line =~ s/URGP=$1 ?//;
309             }
310              
311             print "TCP done: [$line]\n" if $self->{debug};
312              
313             if ($line and $line !~ /^\s+$/) {
314             return ($ds, $line);
315             } else {
316             return ($ds, undef);
317             }
318             }
319              
320             sub _parseUDP {
321             my $self = shift;
322             my $line = shift;
323             my $ds;
324              
325             print "UDP: [$line]\n" if $self->{debug};
326            
327             # SPT=10890 DPT=33440 LEN=12
328              
329             # source port
330             if ($line =~ /SPT=(\d+) /) {
331             $ds->{src_port} = $1;
332             $line =~ s/SPT=$1 //;
333             }
334              
335             # destination port
336             if ($line =~ /DPT=(\d+) /) {
337             $ds->{dst_port} = $1;
338             $line =~ s/DPT=$1 //;
339             }
340              
341             # length
342             if ($line =~ /LEN=(\d+) ?/) {
343             $ds->{len} = $1;
344             $line =~ s/LEN=$1 ?//;
345             }
346              
347             print "UDP done: [$line]\n" if $self->{debug};
348              
349             if ($line and $line !~ /^\s+$/) {
350             return ($ds, $line);
351             } else {
352             return ($ds, undef);
353             }
354              
355             }
356              
357             sub _parseICMP {
358             my $self = shift;
359             my $line = shift;
360             my $ds;
361              
362             print "ICMP: [$line]\n" if $self->{debug};
363              
364             if ($line =~ /TYPE=(\d+) /) {
365             $ds->{type} = $1;
366             $line =~ s/TYPE=$1 //;
367             }
368              
369             if ($line =~ /CODE=(\d+) /) {
370             $ds->{code} = $1;
371             $line =~ s/CODE=$1 //;
372             }
373              
374             my $packet;
375             foreach my $type (@{$self->{icmp_types}}) {
376              
377             if ($type == $ds->{type}) {
378             if ($line =~ /\[(.+)\]/) {
379             $packet = $1;
380             $ds->{error_header} = $self->_parse($packet);
381             } else {
382             carp("got a ICMP packet with type of $type, but couldn't " .
383             "extract the IP header...");
384             }
385             }
386             }
387              
388             # strip out the thing
389             if ($packet) {
390             my $quoted = quotemeta($packet);
391             $line =~ s/\[$quoted\]//;
392             }
393              
394             if ($line =~ /ID=(\d+) /) {
395             $ds->{id} = $1;
396             $line =~ s/ID=$1 //;
397             }
398              
399             if ($line =~ /SEQ=(\d+) ?/) {
400             $ds->{seq} = $1;
401             $line =~ s/SEQ=$1 ?//;
402             }
403            
404             print "ICMP done: ($line)\n" if $self->{debug};
405              
406             if ($line and $line !~ /^\s+$/) {
407             return ($ds, $line);
408             } else {
409             return ($ds, undef);
410             }
411             }
412              
413             sub _parseUnknown {
414             my $self = shift;
415             my $line = shift;
416             my $proto;
417              
418             if ($line =~ /PROTO=(\w+) ?/) {
419             $proto = (getprotobynumber($1))[0];
420              
421             if (not defined($proto)) {
422             $proto = "unknown";
423             }
424              
425             $line =~ s/PROTO=$1 ?//;
426             }
427              
428             if ($line and $line !~ /^\s+$/) {
429             return ($proto, $line);
430             } else {
431             return ($proto, undef);
432             }
433             }
434              
435              
436             1;
437             __END__