1#  Cflow.pm - perl module for processing raw flow files
2#  Copyright (C) 1998-2005  Dave Plonka
3#
4#  This program is free software; you can redistribute it and/or modify
5#  it under the terms of the GNU General Public License as published by
6#  the Free Software Foundation; either version 2 of the License, or
7#  (at your option) any later version.
8#
9#  This program is distributed in the hope that it will be useful,
10#  but WITHOUT ANY WARRANTY; without even the implied warranty of
11#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12#  GNU General Public License for more details.
13#
14#  You should have received a copy of the GNU General Public License
15#  along with this program; if not, write to the Free Software
16#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18# $Id: Cflow.pm,v 1.53 2005/09/28 15:58:01 dplonka Exp $
19# Dave Plonka <plonka@doit.wisc.edu>
20
21use strict;
22
23# got these from "netinet/tcp.ph" (from "h2ph netinet/tcp.h")
24$Cflow::TH_FIN  = 0x01;
25$Cflow::TH_SYN  = 0x02;
26$Cflow::TH_RST  = 0x04;
27$Cflow::TH_PUSH = 0x08;
28$Cflow::TH_ACK  = 0x10;
29$Cflow::TH_URG  = 0x20;
30
31my %bits = (
32   FIN  => $Cflow::TH_FIN,
33   SYN  => $Cflow::TH_SYN,
34   RST  => $Cflow::TH_RST,
35   PUSH => $Cflow::TH_PUSH,
36   ACK  => $Cflow::TH_ACK,
37   URG  => $Cflow::TH_URG
38);
39
40# got these from "netinet/ip_icmp.ph" (from "h2ph netinet/ip_icmp.h")
41
42# { ICMP Types:
43$Cflow::ICMP_ECHOREPLY = 0;
44$Cflow::ICMP_DEST_UNREACH = 3;
45$Cflow::ICMP_SOURCE_QUENCH = 4;
46$Cflow::ICMP_REDIRECT = 5;
47$Cflow::ICMP_ECHO = 8;
48$Cflow::ICMP_TIME_EXCEEDED = 11;
49$Cflow::ICMP_PARAMETERPROB = 12;
50$Cflow::ICMP_TIMESTAMP = 13;
51$Cflow::ICMP_TIMESTAMPREPLY = 14;
52$Cflow::ICMP_INFO_REQUEST = 15;
53$Cflow::ICMP_INFO_REPLY = 16;
54$Cflow::ICMP_ADDRESS = 17;
55$Cflow::ICMP_ADDRESSREPLY = 18;
56# }{ Codes for UNREACH:
57$Cflow::ICMP_NET_UNREACH = 0;
58$Cflow::ICMP_HOST_UNREACH = 1;
59$Cflow::ICMP_PROT_UNREACH = 2;
60$Cflow::ICMP_PORT_UNREACH = 3;
61$Cflow::ICMP_FRAG_NEEDED = 4;
62$Cflow::ICMP_SR_FAILED = 5;
63$Cflow::ICMP_NET_UNKNOWN = 6;
64$Cflow::ICMP_HOST_UNKNOWN = 7;
65$Cflow::ICMP_HOST_ISOLATED = 8;
66$Cflow::ICMP_NET_ANO = 9;
67$Cflow::ICMP_HOST_ANO = 10;
68$Cflow::ICMP_NET_UNR_TOS = 11;
69$Cflow::ICMP_HOST_UNR_TOS = 12;
70$Cflow::ICMP_PKT_FILTERED = 13;
71$Cflow::ICMP_PREC_VIOLATION = 14;
72$Cflow::ICMP_PREC_CUTOFF = 15;
73$Cflow::ICMP_UNREACH = 15;
74# }{ Codes for REDIRECT:
75$Cflow::ICMP_REDIR_NET = 0;
76$Cflow::ICMP_REDIR_HOST = 1;
77$Cflow::ICMP_REDIR_NETTOS = 2;
78$Cflow::ICMP_REDIR_HOSTTOS = 3;
79# }{ Codes for TIME_EXCEEDED:
80$Cflow::ICMP_EXC_TTL = 0;
81$Cflow::ICMP_EXC_FRAGTIME = 1;
82# }
83
84package Cflow::SymbolicTCPFlags; ###############################################
85use Carp;
86
87sub TIESCALAR {
88   my $class = shift;
89   die unless $class;
90   my $this = shift;
91   die unless ref($this);
92   bless $this, $class
93}
94
95sub FETCH {
96   my $this = shift;
97   my($symbolic_tcp_flags) = '';
98   my(@names);
99
100   if (6 == $Cflow::protocol && 0x0 != $$this) {
101      while (my($name, $value) = each(%bits)) {
102	 push(@names, $name) if ($$this & $value)
103      }
104      $symbolic_tcp_flags = '(' . join('|', @names) . ')'
105   }
106
107   $symbolic_tcp_flags
108}
109
110sub STORE {
111   croak "Can't modify read-only value in scalar assignment"
112}
113
114package Cflow::SymbolicICMPTypeCode; ###########################################
115use Carp;
116
117my @typecode = (
118[ 'ECHOREPLY' ],    # 0 /* echo reply */
119undef,              # 1
120undef,              # 2
121[ 'UNREACH',        # 3 /* dest unreachable, codes: */
122   ['NET_',           # 0 /* bad net */
123    'HOST_',          # 1 /* bad host */
124    'PROTOCOL_',      # 2 /* bad protocol */
125    'PORT_',          # 3 /* bad port */
126    'NEEDFRAG_',      # 4 /* IP_DF caused drop */
127    'SRCFAIL_',       # 5 /* src route failed */
128    'NET_UNKNOWN_',   # 6
129    'HOST_UNKNOWN_',  # 7
130    'HOST_ISOLATED_', # 8
131    'NET_ANO_',       # 9
132    'HOST_ANO_',      # 10
133    'NET_UNR_TOS_',   # 11
134    'HOST_UNR_TOS_',  # 12
135    'PKT_FILTERED_',  # 13 /* Packet filtered */
136    'PREC_VIOLATION_', # 14 /* Precedence violation */
137    'PREC_CUTOFF_',   # 15 /* Precedence cut off */
138    ] ],
139[ 'SOURCE_QUENCH' ], # 4 /* packet lost, slow down */
140[ 'REDIRECT',       # 5 /* shorter route, codes: */
141   ['NET_',           # 0 /* for network */
142    'HOST_',          # 1 /* for host */
143    'TOSNET_',        # 2 /* for tos and net */
144    'TOSHOST_'] ],    # 3 /* for tos and host */
145undef,              # 6
146undef,              # 7
147[ 'ECHO' ],         # 8 /* echo service */
148undef,              # 9
149undef,              # 10
150[ 'TIME_EXCEEDED',  # 11 /* time exceeded, code: */
151   ['INTRANS_',       # 0 /* ttl==0 in transit */
152    'REASS_'] ],      # 1 /* ttl==0 in reass */
153[ 'PARAMPROB' ],    # 12 /* ip header bad */
154[ 'TIMESTAMP' ],    # 13 /* timestamp request */
155[ 'TIMESTAMPREPLY' ], # 14 /* timestamp reply */
156[ 'INFO_REQUEST' ], # 15 /* information request */
157[ 'INFO_REPLY' ],   # 16 /* information reply */
158[ 'MASKREQ' ],      # 17 /* address mask request */
159[ 'MASKREPLY' ],    # 18 /* address mask reply */
160);
161
162sub TIESCALAR {
163   my $class = shift;
164   die unless $class;
165   my $this = shift;
166   die unless ref($this);
167   bless $this, $class
168}
169
170sub FETCH {
171   my $this = shift;
172   return '' unless (1 == $Cflow::protocol);
173   return '' unless defined $typecode[$Cflow::ICMPType]->[0];
174   return $typecode[$Cflow::ICMPType]->[0]
175      unless defined $typecode[$Cflow::ICMPType]->[1]->[$Cflow::ICMPCode];
176   return $typecode[$Cflow::ICMPType]->[1]->[$Cflow::ICMPCode]
177      . $typecode[$Cflow::ICMPType]->[0]
178}
179
180package Cflow::InetNtoA; #######################################################
181use Carp;
182
183sub TIESCALAR {
184   my $class = shift;
185   die unless $class;
186   my $this = shift;
187   die unless ref($this);
188   bless \$this, $class
189}
190
191sub FETCH {
192   my $this = shift;
193   join('.', unpack('C4', pack('N', $$$this))) # inet_ntoa was too slow
194}
195
196sub STORE {
197   croak "Can't modify read-only value in scalar assignment"
198}
199
200package Cflow::LocalTime; ######################################################
201use Carp;
202use POSIX; # for strftime
203
204sub TIESCALAR {
205   my $class = shift;
206   die unless $class;
207   my $this = shift;
208   die unless ref($this);
209   bless $this, $class
210}
211
212sub FETCH {
213   my $this = shift;
214   strftime("%Y/%m/%d %H:%M:%S", localtime($$this))
215}
216
217sub STORE {
218   croak "Can't modify read-only value in scalar assignment"
219}
220
221package Cflow::PerSecond; ######################################################
222use Carp;
223
224sub TIESCALAR {
225   my $class = shift;
226   die unless $class;
227   my $this = shift;
228   die unless ref($this);
229   bless $this, $class
230}
231
232sub FETCH {
233   my $this = shift;
234   my $seconds = "${Cflow::duration_secs}.${Cflow::duration_msecs}";
235   $$this / (($seconds > 0.)? $seconds : 1)
236}
237
238sub STORE {
239   croak "Can't modify read-only value in scalar assignment"
240}
241
242package Cflow::Octets2BitsPerSecond; ###########################################
243use Carp;
244
245sub TIESCALAR {
246   my $class = shift;
247   die unless $class;
248   my $this = shift;
249   die unless ref($this);
250   bless $this, $class
251}
252
253sub FETCH {
254   my $this = shift;
255   my $seconds = "${Cflow::duration_secs}.${Cflow::duration_msecs}";
256   $$this / (($seconds > 0.)? $seconds : 1)
257}
258
259sub STORE {
260   croak "Can't modify read-only value in scalar assignment"
261}
262
263package Cflow::ReRaw; ##########################################################
264use Carp;
265
266sub TIESCALAR {
267   my $class = shift;
268   die unless $class;
269   my $scalar;
270   my $this = \$scalar;
271   die unless ref($this);
272   bless $this, $class
273}
274
275sub FETCH {
276   my $this = shift;
277   pack($Cflow::entry,
278        $Cflow::index, $Cflow::exporter,
279        $Cflow::srcaddr, $Cflow::dstaddr,
280        $Cflow::input_if, $Cflow::output_if,
281        $Cflow::srcport, $Cflow::dstport,
282        $Cflow::pkts, $Cflow::bytes,
283        $Cflow::nexthop,
284        $Cflow::startime, $Cflow::endtime,
285        $Cflow::protocol, $Cflow::tos,
286        $Cflow::src_as, $Cflow::dst_as,
287        $Cflow::src_mask, $Cflow::dst_mask,
288        $Cflow::tcp_flags,
289        $Cflow::engine_type, $Cflow::engine_id);
290}
291
292sub STORE {
293   croak "Can't modify read-only value in scalar assignment"
294}
295
296package Cflow::KludgeSecs; #####################################################
297use Carp;
298
299sub TIESCALAR {
300   my $class = shift;
301   die unless $class;
302   my $this = shift;
303   die unless ref($this);
304   bless \$this, $class
305}
306
307sub FETCH {
308   my $this = shift;
309   ($$$this & 0xffff0000) >> 16
310}
311
312sub STORE {
313   croak "Can't modify read-only value in scalar assignment"
314}
315
316package Cflow::KludgeMsecs; ####################################################
317use Carp;
318
319sub TIESCALAR {
320   my $class = shift;
321   die unless $class;
322   my $this = shift;
323   die unless ref($this);
324   bless \$this, $class
325}
326
327sub FETCH {
328   my $this = shift;
329   $$$this & 0xffff
330}
331
332sub STORE {
333   croak "Can't modify read-only value in scalar assignment"
334}
335
336package Cflow; #################################################################
337
338# convert the RCS revision to a reasonable Exporter VERSION:
339'$Revision: 1.53 $' =~ m/(\d+)\.(\d+)/ && (( $Cflow::VERSION ) = sprintf("%d.%03d", $1, $2));
340
341require Exporter;
342require DynaLoader;
343require AutoLoader;
344@Cflow::ISA = qw(Exporter DynaLoader);
345%Cflow::EXPORT_TAGS = (
346
347flowvars => [qw(
348$unix_secs
349$exporter
350$exporterip
351$srcaddr
352$srcip
353$dstaddr
354$dstip
355$input_if
356$output_if
357$srcport
358$dstport
359$pkts
360$bytes
361$nexthop
362$nexthopip
363$startime
364$start_msecs
365$endtime
366$end_msecs
367$protocol
368$tos
369$src_as
370$dst_as
371$src_mask
372$dst_mask
373$tcp_flags
374$engine_type
375$engine_id
376$localtime
377$raw
378$reraw
379$Bps
380$pps
381$TCPFlags
382$ICMPTypeCode
383$ICMPType
384$ICMPCode
385$duration_secs
386$duration_msecs
387)],
388
389tcpflags => [qw(
390$TH_FIN
391$TH_SYN
392$TH_RST
393$TH_PUSH
394$TH_ACK
395$TH_URG
396)],
397
398icmptypes => [qw(
399$ICMP_ECHOREPLY
400$ICMP_DEST_UNREACH
401$ICMP_SOURCE_QUENCH
402$ICMP_REDIRECT
403$ICMP_ECHO
404$ICMP_TIME_EXCEEDED
405$ICMP_PARAMETERPROB
406$ICMP_TIMESTAMP
407$ICMP_TIMESTAMPREPLY
408$ICMP_INFO_REQUEST
409$ICMP_INFO_REPLY
410$ICMP_ADDRESS
411$ICMP_ADDRESSREPLY
412)],
413
414icmpcodes => [qw(
415$ICMP_NET_UNREACH
416$ICMP_HOST_UNREACH
417$ICMP_PROT_UNREACH
418$ICMP_PORT_UNREACH
419$ICMP_FRAG_NEEDED
420$ICMP_SR_FAILED
421$ICMP_NET_UNKNOWN
422$ICMP_HOST_UNKNOWN
423$ICMP_HOST_ISOLATED
424$ICMP_NET_ANO
425$ICMP_HOST_ANO
426$ICMP_NET_UNR_TOS
427$ICMP_HOST_UNR_TOS
428$ICMP_PKT_FILTERED
429$ICMP_PREC_VIOLATION
430$ICMP_PREC_CUTOFF
431$ICMP_UNREACH
432$ICMP_REDIR_NET
433$ICMP_REDIR_HOST
434$ICMP_REDIR_NETTOS
435$ICMP_REDIR_HOSTTOS
436$ICMP_EXC_TTL
437$ICMP_EXC_FRAGTIME
438)]
439
440);
441
442@Cflow::EXPORT_OK = qw(find verbose);
443# add the symbols for the specified tag(s) to @EXPORT_OK:
444Exporter::export_ok_tags(qw(flowvars tcpflags icmptypes icmpcodes));
445
446bootstrap Cflow $Cflow::VERSION;
447
448=head1 NAME
449
450Cflow::find - find "interesting" flows in raw IP flow files
451
452=head1 SYNOPSIS
453
454   use Cflow;
455
456   Cflow::verbose(1);
457   Cflow::find(\&wanted, <*.flows*>);
458
459   sub wanted { ... }
460
461or:
462
463   Cflow::find(\&wanted, \&perfile, <*.flows*>);
464
465   sub perfile {
466      my $fname = shift;
467      ...
468   }
469
470=head1 BACKROUND
471
472This module implements an API for processing IP flow accounting
473information which as been collected from routers and written into flow
474files by one of the various flow collectors listed below.
475
476It was originally conceived and written for use by FlowScan:
477
478   http://net.doit.wisc.edu/~plonka/FlowScan/
479
480=head1 Flow File Sources
481
482This package is of little use on its own.  It requires input in the
483form of time-stamped raw flow files produced by other software
484packages.  These "flow sources" either snoop a local ethernet (via
485libpcap) or collect flow information from IP routers that are
486configured to export said information.  The following flow sources are
487supported:
488
489=over 4
490
491=item argus by Carter Bullard:
492
493   http://www.qosient.com/argus/
494
495=item flow-tools by Mark Fullmer (with NetFlow v1, v5, v6, or v7):
496
497   http://www.splintered.net/sw/flow-tools/
498
499=item CAIDA's cflowd (with NetFlow v5):
500
501   http://www.caida.org/tools/measurement/cflowd/
502   http://net.doit.wisc.edu/~plonka/cflowd/
503
504=item lfapd by Steve Premeau (with LFAPv4):
505
506   http://www.nmops.org/
507
508=back
509
510=head1 DESCRIPTION
511
512Cflow::find() will iterate across all the flows in the specified
513files.  It will call your wanted() function once per flow record.  If
514the file name argument passed to find() is specified as "-", flows will
515be read from standard input.
516
517The wanted() function does whatever you write it to do.  For instance,
518it could simply print interesting flows or it might maintain byte,
519packet, and flow counters which could be written to a database after
520the find subroutine completes.
521
522Within your wanted() function, tests on the "current" flow can be
523performed using the following variables:
524
525=over 4
526
527=item $Cflow::unix_secs
528
529secs since epoch (deprecated)
530
531=item $Cflow::exporter
532
533Exporter IP Address as a host-ordered "long"
534
535=item $Cflow::exporterip
536
537Exporter IP Address as dotted-decimal string
538
539=item $Cflow::localtime
540
541$Cflow::unix_secs interpreted as localtime with this strftime(3) format:
542
543   %Y/%m/%d %H:%M:%S
544
545=item $Cflow::srcaddr
546
547Source IP Address as a host-ordered "long"
548
549=item $Cflow::srcip
550
551Source IP Address as a dotted-decimal string
552
553=item $Cflow::dstaddr
554
555Destination IP Address as a host-ordered "long"
556
557=item $Cflow::dstip
558
559Destination IP Address as a dotted-decimal string
560
561=item $Cflow::input_if
562
563Input interface index
564
565=item $Cflow::output_if
566
567Output interface index
568
569=item $Cflow::srcport
570
571TCP/UDP src port number or equivalent
572
573=item $Cflow::dstport
574
575TCP/UDP dst port number or equivalent
576
577=item $Cflow::ICMPType
578
579high byte of $Cflow::dstport
580
581Undefined if the current flow is not an ICMP flow.
582
583=item $Cflow::ICMPCode
584
585low byte of $Cflow::dstport
586
587Undefined if the current flow is not an ICMP flow.
588
589=item $Cflow::ICMPTypeCode
590
591symbolic representation of $Cflow::dstport
592
593The value is a the type-specific ICMP code, if any, followed by the ICMP type.
594E.g.
595
596   ECHO
597   HOST_UNREACH
598
599Undefined if the current flow is not an ICMP flow.
600
601=item $Cflow::pkts
602
603Packets sent in Duration
604
605=item $Cflow::bytes
606
607Octets sent in Duration
608
609=item $Cflow::nexthop
610
611Next hop router's IP Address as a host-ordered "long"
612
613=item $Cflow::nexthopip
614
615Next hop router's IP Address as a dotted-decimal string
616
617=item $Cflow::startime
618
619secs since epoch at start of flow
620
621=item $Cflow::start_msecs
622
623fractional portion of startime (in milliseconds)
624
625This will be zero unless the source is flow-tools or argus.
626
627=item $Cflow::endtime
628
629secs since epoch at last packet of flow
630
631=item $Cflow::end_msecs
632
633fractional portion of endtime (in milliseconds)
634
635This will be zero unless the source is flow-tools or argus.
636
637=item $Cflow::protocol
638
639IP protocol number (as is specified in F</etc/protocols>, i.e.
6401=ICMP, 6=TCP, 17=UDP, etc.)
641
642=item $Cflow::tos
643
644IP Type-of-Service
645
646=item $Cflow::tcp_flags
647
648bitwise OR of all TCP flags that were set within packets in the flow;
6490x10 for non-TCP flows
650
651=item $Cflow::TCPFlags
652
653symbolic representation of $Cflow::tcp_flags
654The value will be a bitwise-or expression.
655E.g.
656
657   PUSH|SYN|FIN|ACK
658
659Undefined if the current flow is not a TCP flow.
660
661=item $Cflow::raw
662
663the entire "packed" flow record as read from the input file
664
665This is useful when the "wanted" subroutine wants to
666write the flow to another FILEHANDLE. E.g.:
667
668   syswrite(FILEHANDLE, $Cflow::raw, length $Cflow::raw)
669
670Note that if you're using a modern version of perl that supports PerlIO
671Layers, be sure that FILEHANDLE is using something appropriate like the
672":bytes" layer.  This can be activated on open, or with:
673
674   binmode(FILEHANDLE, ":bytes");
675
676This will prevent the external LANG setting from causing perl to do
677such things as interpreting your raw flow records as UTF-8 characters
678and corrupting the record.
679
680=item $Cflow::reraw
681
682the entire "re-packed" flow record formatted like $Cflow::raw.
683
684This is useful when the "wanted" subroutine wants to write a modified flow
685to another FILEHANDLE. E.g.:
686
687   $srcaddr = my_encode($srcaddr);
688   $dstaddr = my_encode($dstaddr);
689   syswrite(FILEHANDLE, $Cflow::reraw, length $Cflow::raw)
690
691These flow variables are packed into $Cflow::reraw:
692
693   $Cflow::index, $Cflow::exporter,
694   $Cflow::srcaddr, $Cflow::dstaddr,
695   $Cflow::input_if, $Cflow::output_if,
696   $Cflow::srcport, $Cflow::dstport,
697   $Cflow::pkts, $Cflow::bytes,
698   $Cflow::nexthop,
699   $Cflow::startime, $Cflow::endtime,
700   $Cflow::protocol, $Cflow::tos,
701   $Cflow::src_as, $Cflow::dst_as,
702   $Cflow::src_mask, $Cflow::dst_mask,
703   $Cflow::tcp_flags,
704   $Cflow::engine_type, $Cflow::engine_id
705
706Note that if you're using a modern version of perl that supports PerlIO
707Layers, be sure that FILEHANDLE is using something appropriate like the
708":bytes" layer.  This can be activated on open, or with:
709
710   binmode(FILEHANDLE, ":bytes");
711
712This will prevent the external LANG setting from causing perl to do
713such things as interpreting your raw flow records as UTF-8 characters
714and corrupting the record.
715
716=item $Cflow::Bps
717
718the minimum bytes per second for the current flow
719
720=item $Cflow::pps
721
722the minimum packets per second for the current flow
723
724=back
725
726The following variables are undefined if using NetFlow v1 (which does
727not contain the requisite information):
728
729=over 4
730
731=item $Cflow::src_as
732
733originating or peer AS of source address
734
735=item $Cflow::dst_as
736
737originating or peer AS of destination address
738
739=back
740
741The following variables are undefined if using NetFlow v1 or LFAPv4
742(which do not contain the requisite information):
743
744=over 4
745
746=item $Cflow::src_mask
747
748source address prefix mask bits
749
750=item $Cflow::dst_mask
751
752destination address prefix mask bits
753
754=item $Cflow::engine_type
755
756type of flow switching engine
757
758=item $Cflow::engine_id
759
760ID of the flow switching engine
761
762=back
763
764Optionally, a reference to a perfile() function can be passed to
765Cflow::find as the argument following the reference to the wanted()
766function.  This perfile() function will be called once for each flow
767file.  The argument to the perfile() function will be name of the flow
768file which is about to be processed.  The purpose of the perfile()
769function is to allow you to periodically report the progress of
770Cflow::find() and to provide an opportunity to periodically reclaim
771storage used by data objects that may have been allocated or maintained
772by the wanted() function.  For instance, when counting the number of
773active hosts IP addresses in each time-stamped flow file, perfile() can
774reset the counter to zero and clear the search tree or hash used to
775remember those IP addresses.
776
777Since Cflow is an Exporter, you can request that all those scalar
778flow variables be exported (so that you need not use the "Cflow::"
779prefix):
780
781   use Cflow qw(:flowvars);
782
783Also, you can request that the symbolic names for the TCP flags,
784ICMP types, and/or ICMP codes be exported:
785
786   use Cflow qw(:tcpflags :icmptypes :icmpcodes);
787
788The tcpflags are:
789
790   $TH_FIN $TH_SYN $TH_RST $TH_PUSH $TH_ACK $TH_URG
791
792The icmptypes are:
793
794   $ICMP_ECHOREPLY     $ICMP_DEST_UNREACH $ICMP_SOURCE_QUENCH
795   $ICMP_REDIRECT      $ICMP_ECHO         $ICMP_TIME_EXCEEDED
796   $ICMP_PARAMETERPROB $ICMP_TIMESTAMP    $ICMP_TIMESTAMPREPLY
797   $ICMP_INFO_REQUEST  $ICMP_INFO_REPLY   $ICMP_ADDRESS
798   $ICMP_ADDRESSREPLY
799
800The icmpcodes are:
801
802   $ICMP_NET_UNREACH  $ICMP_HOST_UNREACH $ICMP_PROT_UNREACH
803   $ICMP_PORT_UNREACH $ICMP_FRAG_NEEDED  $ICMP_SR_FAILED
804   $ICMP_NET_UNKNOWN  $ICMP_HOST_UNKNOWN $ICMP_HOST_ISOLATED
805   $ICMP_NET_ANO      $ICMP_HOST_ANO     $ICMP_NET_UNR_TOS
806   $ICMP_HOST_UNR_TOS $ICMP_PKT_FILTERED $ICMP_PREC_VIOLATION
807   $ICMP_PREC_CUTOFF  $ICMP_UNREACH      $ICMP_REDIR_NET
808   $ICMP_REDIR_HOST   $ICMP_REDIR_NETTOS $ICMP_REDIR_HOSTTOS
809   $ICMP_EXC_TTL      $ICMP_EXC_FRAGTIME
810
811Please note that the names above are not necessarily exactly the same
812as the names of the flags, types, and codes as set in the values of the
813aforemented $Cflow::TCPFlags and $Cflow::ICMPTypeCode flow variables.
814
815Lastly, as is usually the case for modules, the subroutine names can be
816imported, and a minimum version of Cflow can be specified:
817
818   use Cflow qw(:flowvars find verbose 1.031);
819
820Cflow::find() returns a "hit-ratio".  This hit-ratio is a string
821formatted similarly to that of the value of a perl hash when taken in a
822scalar context.  This hit-ratio indicates ((# of "wanted" flows) / (#
823of scanned flows)).  A flow is considered to have been "wanted" if your
824wanted() function returns non-zero.
825
826Cflow::verbose() takes a single scalar boolean argument which indicates
827whether or not you wish warning messages to be generated to STDERR when
828"problems" occur.  Verbose mode is set by default.
829
830=head1 EXAMPLES
831
832Here's a complete example with a sample wanted function.  It will print
833all UDP flows that involve either a source or destination port of 31337
834and port on the other end that is unreserved (greater than 1024):
835
836   use Cflow qw(:flowvars find);
837
838   my $udp = getprotobyname('udp');
839   verbose(0);
840   find(\&wanted, @ARGV? @ARGV : <*.flows*>);
841
842   sub wanted {
843      return if ($srcport < 1024 || $dstport < 1024);
844      return unless (($srcport == 31337 || $dstport == 31337) &&
845		      $udp == $protocol);
846
847      printf("%s %15.15s.%-5hu %15.15s.%-5hu %2hu %10u %10u\n",
848	     $localtime,
849	     $srcip,
850	     $srcport,
851	     $dstip,
852	     $dstport,
853	     $protocol,
854	     $pkts,
855	     $bytes)
856   }
857
858Here's an example which demonstrates a technique which can be used to
859pass arbitrary arguments to your wanted function by passing a reference
860to an anonymous subroutine as the wanted() function argument to
861Cflow::find():
862
863   sub wanted {
864      my @params = @_;
865      # ...
866   }
867
868   Cflow::find(sub { wanted(@params) }, @files);
869
870=head1 ARGUS NOTES
871
872Argus uses a bidirectional flow model.  This means that some argus
873flows represent packets not only in the forward direction (from
874"source" to "destination"), but also in the reverse direction (from the
875so-called "destination" to the "source").  However, this module uses a
876unidirection flow model, and therfore splits some argus flows into two
877unidirectional flows for the purpose of reporting.
878
879Currently, using this module's API there is no way to determine if two
880subsequently reported unidirectional flows were really a single argus
881flow.  This may be addressed in a future release of this package.
882
883Furthermore, for argus flows which represent bidirectional ICMP
884traffic, this module presumes that all the reverse packets were
885ECHOREPLYs (sic).  This is sometimes incorrect as described here:
886
887   http://www.theorygroup.com/Archive/Argus/2002/msg00016.html
888
889and will be fixed in a future release of this package.
890
891Timestamps ($startime and $endtime) are sometimes reported incorrectly
892for bidirectional argus flows that represent only one packet in each
893direction.  This will be fixed in a future release.
894
895Argus flows sometimes contain information which does not map directly
896to the flow variables presented by this module.  For the time being,
897this information is simply not accessible through this module's API.
898This may be addressed in a future release.
899
900Lastly, argus flows produced from observed traffic on a local ethernet
901do not contain enough information to meaningfully set the values of all
902this module's flow variables.  For instance, the next-hop and
903input/output ifIndex numbers are missing.  For the time being, all
904argus flows accessed throught this module's API will have both the
905$input_if and $output_if as 42.  Althought 42 is the answer to life,
906the universe, and everthing, in this context, it is just an arbitrary
907number.  It is important that $output_if is non-zero, however, since
908existing FlowScan reports interpret an $output_if value of zero to mean
909that the traffic represented by that flow was not forwarded (i.e.
910dropped).  For similar reasons, the $nexthopip for all argus flows is
911reported as "127.0.0.1".
912
913=head1 BUGS
914
915Currently, only NetFlow version 5 is supported when reading
916cflowd-format raw flow files.
917
918When built with support for flow-tools and attempting to read
919a cflowd format raw flow file from standard input, you'll get the
920error:
921
922   open "-": No such file or directory
923
924For the time being, the workaround is to write the content to a file
925and read it from directly from there rather than from standard input.
926(This happens because we can't close and re-open file descriptor zero
927after determining that the content was not in flow-tools format.)
928
929When built with support for flow-tools and using verbose mode,
930Cflow::find will generate warnings if you process a cflowd format raw
931flow file.  This happens because it will first attempt to open the file
932as a flow-tools format raw flow file (which will produce a warning
933message), and then revert to handling it as cflowd format raw flow
934file.
935
936Likewise, when built with support for argus and attempting to read a
937cflowd format raw flow file from standard input, you'll get this
938warning message:
939
940   not Argus-2.0 data stream.
941
942This is because argus (as of argus-2.0.4) doesn't seem to have a mode
943in which such warning messages are supressed.
944
945The $Cflow::raw flow variable contains the flow record in cflowd
946format, even if it was read from a raw flow file produced by flow-tools
947or argus.  Because cflowd discards the fractional portion of the flow
948start and end time, only the whole seconds portion of these times will
949be retained.  (That is, the raw record in $Cflow::raw does not contain
950the $start_msecs and $end_msecs, so using $Cflow::raw to convert to
951cflowd format is a lossy operation.)
952
953When used with cflowd, Cflow::find() will generate warnings if the flow
954data file is "invalid" as far as its concerned.  To avoid this, you
955must be using Cisco version 5 flow-export and configure cflowd so that
956it saves all flow-export data.  This is the default behavior when
957cflowd produces time-stamped raw flow files after being patched as
958described here:
959
960   http://net.doit.wisc.edu/~plonka/cflowd/
961
962=head1 NOTES
963
964The interface presented by this package is a blatant ripoff of
965File::Find.
966
967=head1 AUTHOR
968
969Dave Plonka <plonka@doit.wisc.edu>
970
971Copyright (C) 1998-2005  Dave Plonka.
972This program is free software; you can redistribute it and/or modify it
973under the terms of the GNU General Public License as published by the
974Free Software Foundation; either version 2 of the License, or (at your
975option) any later version.
976
977=head1 VERSION
978
979The version number is the module file RCS revision number (B<$Revision: 1.53 $>)
980with the minor number printed right justified with leading zeroes to 3
981decimal places.  For instance, RCS revision 1.1 would yield a package
982version number of 1.001.
983
984This is so that revision 1.10 (which is version 1.010), for example,
985will test greater than revision 1.2 (which is version 1.002) when you
986want to B<require> a minimum version of this module.
987
988=head1 SEE ALSO
989
990perl(1), Socket, Net::Netmask, Net::Patricia.
991
992=cut
993
994$Cflow::verbose = 1; # issue warnings to STDERR by default
995
996$Cflow::entry = '';
997$Cflow::entry .= ' N'; # _index
998$Cflow::entry .= ' N'; # _router
999$Cflow::entry .= ' N'; # _srcIpAddr
1000$Cflow::entry .= ' N'; # _dstIpAddr
1001$Cflow::entry .= ' n'; # _inputIfIndex
1002$Cflow::entry .= ' n'; # _outputIfIndex
1003$Cflow::entry .= ' n'; # _srcPort
1004$Cflow::entry .= ' n'; # _dstPort
1005$Cflow::entry .= ' N'; # _pkts
1006$Cflow::entry .= ' N'; # _bytes
1007$Cflow::entry .= ' N'; # _ipNextHop
1008$Cflow::entry .= ' N'; # _startTime
1009$Cflow::entry .= ' N'; # _endTime
1010$Cflow::entry .= ' C'; # _protocol
1011$Cflow::entry .= ' C'; # _tos
1012$Cflow::entry .= ' n'; # _srcAs
1013$Cflow::entry .= ' n'; # _dstAs
1014$Cflow::entry .= ' C'; # _srcMaskLen
1015$Cflow::entry .= ' C'; # _dstMaskLen
1016$Cflow::entry .= ' C'; # _tcpFlags
1017$Cflow::entry .= ' C'; # _engineType
1018$Cflow::entry .= ' C'; # _engineId
1019$Cflow::entry_len = length(pack($Cflow::entry));
1020
1021# As we go, we'll build an associative array of cached
1022# formats for the variable portion.  After a bit of time
1023# this should speed things up because we can do a direct
1024# lookup from the $Cflow::index to the format rather than
1025# having to construct the format from scratch each time.
1026%Cflow::cached_formats = ();
1027
1028# tie these scalars so that we don't have to call some functions (such as
1029# strftime(3) and inet_ntoa(3)) unnecessarily.  (As tied scalars those
1030# functions will only be called if the values of these variables are actually
1031# fetched - i.e. if they're referred to by the callers "wanted" subroutine.
1032# This speeds things up (when possible) by saving a bit of processing time
1033# with each flow, which can add up to quite a bit for lots of flows.):
1034die unless tie($Cflow::localtime, 'Cflow::LocalTime', \$Cflow::endtime);
1035die unless tie($Cflow::exporterip, 'Cflow::InetNtoA', \$Cflow::exporter);
1036die unless tie($Cflow::srcip, 'Cflow::InetNtoA', \$Cflow::srcaddr);
1037die unless tie($Cflow::dstip, 'Cflow::InetNtoA', \$Cflow::dstaddr);
1038die unless tie($Cflow::nexthopip, 'Cflow::InetNtoA', \$Cflow::nexthop);
1039# Bytes per second, Packets per second:
1040die unless tie($Cflow::Bps, 'Cflow::PerSecond', \$Cflow::bytes);
1041die unless tie($Cflow::pps, 'Cflow::PerSecond', \$Cflow::pkts);
1042# TCPFlags:
1043die unless tie($Cflow::TCPFlags, 'Cflow::SymbolicTCPFlags', \$Cflow::tcp_flags);
1044# ICMPTypeCode:
1045die unless tie($Cflow::ICMPTypeCode, 'Cflow::SymbolicICMPTypeCode', \$Cflow::dstport);
1046
1047die unless tie($Cflow::reraw, 'Cflow::ReRaw');
1048
1049sub verbose {
1050   $Cflow::verbose = $_[0]
1051}
1052
1053sub wanted {
1054   return if ($Cflow::srcport < 1024 || $Cflow::dstport < 1024);
1055   return unless ((($Cflow::srcport == 31337 || $Cflow::dstport == 31337) &&
1056		   17 == $Cflow::protocol) ||
1057		  (($Cflow::srcport == 12345 || $Cflow::srcport == 12346 ||
1058		    $Cflow::dstport == 12345 || $Cflow::dstport == 12346) &&
1059		   6 == $Cflow::protocol));
1060
1061   my $when = POSIX::strftime("%Y/%m/%d %H:%M:%S", localtime($Cflow::unix_secs));
1062
1063   printf "$when %15.15s.%-5hu %15.15s.%-5hu %2hu %10u %10u\n", $Cflow::srcip, $Cflow::srcport, $Cflow::dstip, $Cflow::dstport, $Cflow::protocol, $Cflow::pkts, $Cflow::bytes
1064}
1065
10661;
1067__END__
1068