1# Copyright (c) 2004,2005 Damien Miller <djm@mindrot.org>
2#
3# Permission to use, copy, modify, and distribute this software for any
4# purpose with or without fee is hereby granted, provided that the above
5# copyright notice and this permission notice appear in all copies.
6#
7# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15# $Id: Flowd.pm,v 1.7 2008/07/25 00:04:17 djm Exp $
16
17package Flowd;
18
19use 5.006;
20use strict;
21use warnings;
22
23use constant VERSION		=> "0.9.1";
24
25# Flowd log header fields
26use constant TAG		=> 0x00000001;
27use constant RECV_TIME		=> 0x00000002;
28use constant PROTO_FLAGS_TOS	=> 0x00000004;
29use constant AGENT_ADDR4	=> 0x00000008;
30use constant AGENT_ADDR6	=> 0x00000010;
31use constant SRC_ADDR4		=> 0x00000020;
32use constant SRC_ADDR6		=> 0x00000040;
33use constant DST_ADDR4		=> 0x00000080;
34use constant DST_ADDR6		=> 0x00000100;
35use constant GATEWAY_ADDR4	=> 0x00000200;
36use constant GATEWAY_ADDR6	=> 0x00000400;
37use constant SRCDST_PORT	=> 0x00000800;
38use constant PACKETS		=> 0x00001000;
39use constant OCTETS		=> 0x00002000;
40use constant IF_INDICES		=> 0x00004000;
41use constant AGENT_INFO		=> 0x00008000;
42use constant FLOW_TIMES		=> 0x00010000;
43use constant AS_INFO		=> 0x00020000;
44use constant FLOW_ENGINE_INFO	=> 0x00040000;
45use constant CRC32		=> 0x40000000;
46
47# Some useful combinations
48use constant AGENT_ADDR		=> 0x00000018;
49use constant SRC_ADDR		=> 0x00000060;
50use constant DST_ADDR		=> 0x00000180;
51use constant SRCDST_ADDR	=> 0x000001e0;
52use constant GATEWAY_ADDR	=> 0x00000600;
53use constant BRIEF		=> 0x000039ff;
54use constant ALL		=> 0x4007ffff;
55
56require Exporter;
57
58our @ISA = qw(Exporter);
59
60# Items to export into callers namespace by default. Note: do not export
61# names by default without a very good reason. Use EXPORT_OK instead.
62# Do not simply export all your public functions/methods/constants.
63
64# This allows declaration	use Flowd ':all';
65# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
66# will save memory.
67our %EXPORT_TAGS = ( 'all' => [ qw(
68
69) ] );
70
71our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
72
73our @EXPORT = qw(
74
75);
76
77our $VERSION = '0.9.1';
78
79require XSLoader;
80XSLoader::load('Flowd', $VERSION);
81
82# Preloaded methods go here.
83sub iso_time {
84	my $timet = shift;
85	my $utc = 0;
86	my @tm;
87
88	Carp::confess("missing argument") if not defined $timet;
89
90	@tm = localtime($timet) unless $utc;
91	@tm = gmtime($timet) if $utc;
92
93	return sprintf("%04u-%02u-%02uT%02u:%02u:%02u",
94	    1900 + $tm[5], 1 + $tm[4], $tm[3], $tm[2], $tm[1], $tm[0]);
95}
96
97sub interval_time {
98	my $t = shift;
99	my @ivs = (
100		[ "m", 60 ], [ "h", 60 ], [ "d", 24 ],
101		[ "w", 7 ], [ "y", 52 ]
102	);
103	my $ret = "s";
104
105	Carp::confess("missing argument") if not defined $t;
106
107	foreach my $iv (@ivs) {
108		$ret = sprintf "%u%s", $t % @$iv[1], $ret;
109		$t = int($t / @$iv[1]);
110		last if $t <= 0;
111		$ret = @$iv[0] . $ret;
112	}
113	return $ret;
114}
115
116sub interval_time_ms
117{
118	my $tms = shift;
119
120	return sprintf "%s.%03u", interval_time($tms / 1000), $tms % 1000,
121}
122
123sub new {
124	my $class = shift;
125	my @args = (@_);
126
127	my $self = {};
128	bless($self, $class);
129
130	$self->init(@args);
131
132	return $self;
133}
134
135sub init {
136	my $self = shift;
137	my $filename = shift;
138	my $fhandle;
139	my $hdr;
140	my $r;
141
142	$self->{filename} = $filename;
143	open($fhandle, "<$filename") or die "open($filename): $!";
144	$self->{handle} = $fhandle;
145}
146
147sub finish {
148	my $self = shift;
149
150	close($self->{handle});
151	$self->{handle} = undef;
152}
153
154sub read_flow {
155	my $self = shift;
156	my $hdr;
157	my $fdata;
158	my $r;
159	my $need;
160
161	# Read initial flow header
162	$need = Flowd::header_length();
163	$r = read($self->{handle}, $hdr, $need);
164	die "read($self->{filename}): $!" if not defined $r;
165	return 0 if $r == 0;
166	die "early EOF reading header on $self->{filename}" if $r < $need;
167
168	# Calculate length of flow and read it in
169	$need = Flowd::flow_length($hdr);
170	$r = read($self->{handle}, $fdata, $need);
171	die "read($self->{filename}): $!" if not defined $r;
172	die "early EOF reading flow on $self->{filename}" if $r < $need;
173
174	return Flowd::deserialise($hdr . $fdata);
175}
176
177sub format
178{
179	my $self = shift;
180	my $field_mask = shift;
181	my $utc_flag = shift;
182	my $flowfields = shift;
183	my $fields = $flowfields->{fields} & $field_mask;
184
185	my $ret = "";
186
187	$ret .= "FLOW ";
188
189	if ($fields & TAG) {
190		$ret .= sprintf "tag %u ", $flowfields->{tag};
191	}
192	if ($fields & RECV_TIME) {
193		$ret .= sprintf "recv_time %s.%05d ",
194		    Flowd::iso_time($flowfields->{recv_sec}, $utc_flag),
195		    $flowfields->{recv_usec};
196	}
197	if ($fields & PROTO_FLAGS_TOS) {
198		$ret .= sprintf "proto %u ", $flowfields->{protocol};
199		$ret .= sprintf "tcpflags %02x ", $flowfields->{tcp_flags};
200		$ret .= sprintf "tos %02x ", $flowfields->{tos};
201	}
202	if ($fields & AGENT_ADDR) {
203		$ret .= sprintf "agent [%s] ", $flowfields->{agent_addr};
204	}
205	if ($fields & SRC_ADDR) {
206		$ret .= sprintf "src [%s]", $flowfields->{src_addr};
207		if ($fields & SRCDST_PORT) {
208			$ret .= sprintf ":%u", $flowfields->{src_port};
209		}
210		$ret .= " ";
211	}
212	if ($fields & DST_ADDR) {
213		$ret .= sprintf "dst [%s]", $flowfields->{dst_addr};
214		if ($fields & SRCDST_PORT) {
215			$ret .= sprintf ":%u", $flowfields->{dst_port};
216		}
217		$ret .= " ";
218	}
219	if ($fields & GATEWAY_ADDR) {
220		$ret .= sprintf "gateway [%s] ",
221		    $flowfields->{gateway_addr};
222	}
223	if ($fields & PACKETS) {
224		my $p = $flowfields->{flow_packets};
225		$p =~ s/^\+//;
226		$ret .= sprintf "packets %s ", $p;
227	}
228	if ($fields & OCTETS) {
229		my $o = $flowfields->{flow_octets};
230		$o =~ s/^\+//;
231		$ret .= sprintf "octets %s ", $o;
232	}
233	if ($fields & IF_INDICES) {
234		$ret .= sprintf "in_if %u ", $flowfields->{if_index_in};
235		$ret .= sprintf "out_if %u ", $flowfields->{if_index_out};
236	}
237	if ($fields & AGENT_INFO) {
238		$ret .= sprintf "sys_uptime_ms %s ",
239		    Flowd::interval_time_ms($flowfields->{sys_uptime_ms});
240		$ret .= sprintf "time_sec %s ",
241		    Flowd::iso_time($flowfields->{time_sec}, $utc_flag);
242		$ret .= sprintf "time_nanosec %u ",
243		    $flowfields->{time_nanosec};
244		$ret .= sprintf "netflow ver %u ",
245			$flowfields->{netflow_version};
246	}
247	if ($fields & FLOW_TIMES) {
248		$ret .= sprintf "flow_start %s ",
249		    Flowd::interval_time_ms($flowfields->{flow_start});
250		$ret .= sprintf "flow_finish %s ",
251		    Flowd::interval_time_ms($flowfields->{flow_finish});
252	}
253	if ($fields & AS_INFO) {
254		$ret .= sprintf "src_AS %u ", $flowfields->{src_as};
255		$ret .= sprintf "src_masklen %u ",
256		    $flowfields->{src_masklen};
257		$ret .= sprintf "dst_AS %u ", $flowfields->{dst_as};
258		$ret .= sprintf "dst_masklen %u ",
259		    $flowfields->{dst_masklen};
260	}
261	if ($fields & FLOW_ENGINE_INFO) {
262		$ret .= sprintf "engine_type %u ",
263		    $flowfields->{engine_type};
264		$ret .= sprintf "engine_id %u ", $flowfields->{engine_id};
265		$ret .= sprintf "seq %u ", $flowfields->{flow_sequence};
266	}
267	if ($fields & CRC32) {
268		$ret .= sprintf "crc32 %08x ", $flowfields->{crc};
269	}
270
271	return $ret;
272}
273
2741;
275__END__
276=head1 NAME
277
278Flowd::Serialiser - Perl extension for blah blah blah
279
280=head1 SYNOPSIS
281
282  use Flowd::Serialiser;
283
284=head1 DESCRIPTION
285
286This module isn't really intended for public consumption. It is just a thin
287wrapper over the flowd C library. If you are really curious, have a look at
288Flowd.pm to see how it uses it (it is very simple).
289
290=head2 EXPORT
291
292None by default.
293
294=head1 SEE ALSO
295
296Refer to Flowd.pm for usage information.
297
298=head1 AUTHOR
299
300Damien Miller, E<lt>djm@mindrot.orgE<gt>
301
302=head1 COPYRIGHT AND LICENSE
303
304Copyright (c) 2004 Damien Miller <djm@mindrot.org>
305
306Permission to use, copy, modify, and distribute this software for any
307purpose with or without fee is hereby granted, provided that the above
308copyright notice and this permission notice appear in all copies.
309
310THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
311WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
312MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
313ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
314WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
315ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
316OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
317
318=cut
319