File Coverage

blib/lib/Net/TcpDumpLog.pm
Criterion Covered Total %
statement 82 129 63.5
branch 16 42 38.1
condition 5 21 23.8
subroutine 6 14 42.8
pod 11 12 91.6
total 120 218 55.0


line stmt bran cond sub pod time code
1             #!/bin/perl -w
2             #
3             # TcpDumpLog.pm - Net::TcpDumpLog library to read tcpdump/libpcap files.
4             #
5             # 17-Oct-2003 Brendan Gregg
6             # 19-Oct-2003 Brendan Gregg Added code to check endian of files.
7              
8             package Net::TcpDumpLog;
9              
10 1     1   8189 use strict;
  1         3  
  1         53  
11 1     1   7 use vars qw($VERSION);
  1         4  
  1         2155  
12              
13             $VERSION = '0.11';
14              
15             # new - create the tcpdump object.
16             # An optional argument is the number of bits this OS uses to store
17             # times. Without this argument, this will use whatever the OS thinks
18             # it should use. By using this argument (32/64) you can force
19             # behaviour, which may be useful when transferring logs from one
20             # OS to another. Why this is so important is that the actual
21             # tcpdump/libpcap file format changes depending on the bits.
22             #
23             sub new {
24 1     1 1 160 my $proto = shift;
25 1   33     10 my $class = ref($proto) || $proto;
26 1         3 my $self = {};
27              
28 1         2 my $bits = shift;
29 1         2 my $skip = shift;
30              
31 1         3 $self->{major} = undef;
32 1         4 $self->{minor} = undef;
33 1         3 $self->{zoneoffset} = undef;
34 1         2 $self->{accuracy} = undef;
35 1         4 $self->{dumplength} = undef;
36 1         2 $self->{linktype} = undef;
37 1         2 $self->{bigendian} = undef;
38 1         5 $self->{data} = [];
39 1         2 $self->{length_orig} = [];
40 1         3 $self->{length_inc} = [];
41 1         3 $self->{drops} = [];
42 1         4 $self->{seconds} = [];
43 1         2 $self->{msecs} = [];
44 1         3 $self->{count} = 0;
45 1         3 $self->{sizeint} = length(pack("I",0));
46              
47 1 50 33     15 if (defined $bits && $bits == 64) {
    50 33        
48 0         0 $self->{bits} = 64;
49             } elsif (defined $bits && $bits == 32) {
50 1         4 $self->{bits} = 32;
51             } else {
52 0         0 $self->{bits} = 0; # Use native OS bits
53             }
54              
55 1 50 33     7 if (defined $skip && $skip > 0) {
56 0         0 $self->{skip} = $skip;
57             }
58              
59 1         3 bless($self,$class);
60 1         4 return $self;
61             }
62              
63             # read - read the tcpdump file into memory
64             #
65             sub read {
66 1     1 1 7 my $self = shift;
67 1         3 my $file = shift;
68 1         3 my ($header,$length,$ident,$version,$linktype,$header_rec,
69             $zoneoffset,$accuracy,$frame_length_inc,$frame_length_orig,
70             $frame_drops,$frame_seconds,$frame_msecs,$frame_data,
71             $pad,$major,$minor,$dumplength,$rest,$more);
72 1         6 $self->{count} = 0;
73 1         3 my $num = 0;
74              
75             ### Open tcpdump file
76 1 50       43 open(TCPDUMPFILE,"$file") ||
77             die "ERROR: Can't read log $file: $!\n";
78 1         4 binmode(TCPDUMPFILE); # backward OSs
79              
80             ### Fetch tcpdump header
81 1         38 $length = read(TCPDUMPFILE,$header,24);
82 1 50       6 die "ERROR: Can't read from log $file\n" if $length < 24;
83              
84             ### Check file really is a tcpdump file
85 1         10 ($ident,$rest) = unpack('a4a20',$header);
86              
87 1 0 33     10 if ($ident !~ /^\241\262\303\324/ &&
      0        
      0        
88             $ident !~ /^\324\303\262\241/ &&
89             $ident !~ /^\241\262\315\064/ &&
90             $ident !~ /^\064\315\262\241/){
91 0         0 die "ERROR: Not a tcpdump file (or unknown version) $file\n";
92             }
93              
94             ### Find out what type of tcpdump file it is
95 1 50       7 if ($ident =~ /^\241\262\303\324/) {
96             #
97             # Standard format big endian, header "a1b2c3d4"
98             # Seen from:
99             # Solaris tcpdump
100             # Solaris Ethereal "libpcap" format
101             #
102 1         32 $self->{style} = "standard1";
103 1         3 $self->{bigendian} = 1;
104 1         15 ($ident,$major,$minor,$zoneoffset,$accuracy,$dumplength,
105             $linktype) = unpack('a4nnNNNN',$header);
106             }
107 1 50       6 if ($ident =~ /^\324\303\262\241/) {
108             #
109             # Standard format little endian, header "d4c3b2a1"
110             # Seen from:
111             # Windows Ethereal "libpcap" format
112             #
113 0         0 $self->{style} = "standard2";
114 0         0 $self->{bigendian} = 0;
115 0         0 ($ident,$major,$minor,$zoneoffset,$accuracy,$dumplength,
116             $linktype) = unpack('a4vvVVVV',$header);
117             }
118 1 50       5 if ($ident =~ /^\241\262\315\064/) {
119             #
120             # Modified format big endian, header "a1b2cd34"
121             # Seen from:
122             # Solaris Ethereal "modified" format
123             #
124 0         0 $self->{style} = "modified1";
125 0         0 $self->{bigendian} = 1;
126 0         0 ($ident,$major,$minor,$zoneoffset,$accuracy,$dumplength,
127             $linktype) = unpack('a4nnNNNN',$header);
128             }
129 1 50       6 if ($ident =~ /^\064\315\262\241/) {
130             #
131             # Modified format little endian, header "cd34a1b2"
132             # Seen from:
133             # Red Hat tcpdump
134             # Windows Ethereal "modified" format
135             #
136 0         0 $self->{style} = "modified2";
137 0         0 $self->{bigendian} = 0;
138 0         0 ($ident,$major,$minor,$zoneoffset,$accuracy,$dumplength,
139             $linktype) = unpack('a4vvVVVV',$header);
140             }
141              
142             ### Store values
143 1         2 $self->{version} = $version;
144 1         3 $self->{major} = $major;
145 1         4 $self->{minor} = $minor;
146 1         2 $self->{zoneoffset} = $zoneoffset;
147 1         2 $self->{accuracy} = $accuracy;
148 1         1 $self->{dumplength} = $dumplength;
149 1         3 $self->{linktype} = $linktype;
150              
151             #
152             # Read all packets into memory
153             #
154 1         2 $num = 0;
155 1         2 while (1) {
156            
157 186 50       475 if ($self->{bits} == 64) {
    50          
158             #
159             # 64-bit timestamps, quads
160             #
161              
162             ### Fetch record header
163 0         0 $length = read(TCPDUMPFILE,$header_rec,24);
164              
165             ### Quit loop if at end of file
166 0 0       0 last if $length < 24;
167              
168             ### Unpack header
169 0         0 ($frame_seconds,$frame_msecs,$frame_length_inc,
170             $frame_length_orig) = unpack('QQLL',$header_rec);
171              
172             } elsif ($self->{bits} == 32) {
173             #
174             # 32-bit timestamps, big-endian
175             #
176              
177             ### Fetch record header
178 186         284 $length = read(TCPDUMPFILE,$header_rec,16);
179              
180             ### Quit loop if at end of file
181 186 100       317 last if $length < 16;
182              
183             ### Unpack header
184 185 50       333 if ($self->{bigendian}) {
185 185         448 ($frame_seconds,$frame_msecs,
186             $frame_length_inc,$frame_length_orig)
187             = unpack('NNNN',$header_rec);
188             } else {
189 0         0 ($frame_seconds,$frame_msecs,
190             $frame_length_inc,$frame_length_orig)
191             = unpack('VVVV',$header_rec);
192             }
193              
194             } else {
195             #
196             # Default to OS native timestamps
197             #
198              
199             ### Fetch record header
200 0         0 $length = read(TCPDUMPFILE,$header_rec,
201             ($self->{sizeint} * 2 + 8) );
202              
203             ### Quit loop if at end of file
204 0 0       0 last if $length < ($self->{sizeint} * 2 + 8);
205              
206             ### Unpack header
207 0 0       0 if ($self->{sizeint} == 4) { # 32-bit
208 0 0       0 if ($self->{bigendian}) {
209 0         0 ($frame_seconds,$frame_msecs,
210             $frame_length_inc,$frame_length_orig)
211             = unpack('NNNN',$header_rec);
212             } else {
213 0         0 ($frame_seconds,$frame_msecs,
214             $frame_length_inc,$frame_length_orig)
215             = unpack('VVVV',$header_rec);
216             }
217             } else { # 64-bit?
218 0 0       0 if ($self->{bigendian}) {
219 0         0 ($frame_seconds,$frame_msecs,
220             $frame_length_inc,$frame_length_orig)
221             = unpack('IINN',$header_rec);
222             } else {
223 0         0 ($frame_seconds,$frame_msecs,
224             $frame_length_inc,$frame_length_orig)
225             = unpack('IIVV',$header_rec);
226             }
227             }
228              
229             }
230              
231             ### Fetch extra info if in modified format
232 185 50       454 if ($self->{style} =~ /^modified/) {
233 0         0 $length = read(TCPDUMPFILE,$more,8);
234             }
235            
236             ### Check for skip bytes
237 185 50       334 if (defined $self->{skip}) {
238 0         0 $length = read(TCPDUMPFILE,$more,$self->{skip});
239             }
240              
241             ### Fetch the data
242 185         335 $length = read(TCPDUMPFILE,$frame_data,$frame_length_inc);
243              
244 185         205 $frame_drops = $frame_length_orig - $frame_length_inc;
245              
246             ### Store values in memory
247 185         403 $self->{data}[$num] = $frame_data;
248 185         273 $self->{length_orig}[$num] = $frame_length_orig;
249 185         253 $self->{length_inc}[$num] = $frame_length_inc;
250 185         258 $self->{drops}[$num] = $frame_drops;
251 185         271 $self->{seconds}[$num] = $frame_seconds;
252 185         251 $self->{msecs}[$num] = $frame_msecs;
253 185         207 $self->{count}++;
254 185         182 $num++;
255             }
256              
257 1         22 close TCPDUMPFILE;
258             }
259              
260             # indexes - return a list of index numbers for the packets.
261             # indexes start at "0"
262             #
263             sub indexes {
264 1     1 1 108 my $self = shift;
265 1         4 my $max = $self->{count} - 1;
266 1         36 return (0..$max);
267             }
268              
269             # maxindex - return the index number for the last packet.
270             # indexes start at "0"
271             #
272             sub maxindex {
273 0     0 1 0 my $self = shift;
274 0         0 my $max = $self->{count} - 1;
275 0         0 return $max;
276             }
277              
278             # header - return header data for a given index
279             #
280             sub header {
281 0     0 1 0 my $self = shift;
282 0         0 my $num = shift;
283 0         0 return ($self->{length_orig}[$num],
284             $self->{length_inc}[$num],
285             $self->{drops}[$num],
286             $self->{seconds}[$num],
287             $self->{msecs}[$num]);
288             }
289              
290             # data - return packet data for a given index
291             #
292             sub data {
293 1     1 1 84 my $self = shift;
294 1         3 my $num = shift;
295 1         5 return $self->{data}[$num];
296             }
297              
298             # version - return log file version
299             #
300             sub version {
301 0     0 1   my $self = shift;
302 0           return sprintf("%u.%u",$self->{major},$self->{minor});
303             }
304              
305             # linktype - return linktype
306             #
307             sub linktype {
308 0     0 1   my $self = shift;
309 0           return sprintf("%u",$self->{linktype});
310             }
311              
312             # zoneoffset - return zoneoffset
313             #
314             sub zoneoffset {
315 0     0 1   my $self = shift;
316 0           return sprintf("%u",$self->{zoneoffset});
317             }
318              
319             # accuracy - return accuracy
320             #
321             sub accuracy {
322 0     0 1   my $self = shift;
323 0           return sprintf("%u",$self->{accuracy});
324             }
325              
326             # dumplength - return dumplength
327             #
328             sub dumplength {
329 0     0 1   my $self = shift;
330 0           return sprintf("%u",$self->{dumplength});
331             }
332              
333             # clear - clear tcpdump file from memory
334             #
335             sub clear {
336 0     0 0   my $self = shift;
337 0           delete $self->{data};
338 0           $self
339             }
340              
341              
342             1;
343             __END__