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