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