#!/usr/bin/perl # tcptool # profiles network traffic based on tcpdump output # March 2007 use Socket; use vars qw / %opt /; use Getopt::Std; my $me = `basename $0`; chomp( $me ); my $myself = `hostname`; chomp ( $myself ); my $opt_string = 'm:i:t:c:hp'; getopts( "$opt_string", \%opt ) or usage(); $opt{'i'} =~ /\S/ or usage(); defined($opt{'h'}) && usage(); my $int = $opt{'i'}; my $top = $opt{'t'} || 10; my $mask = $opt{'m'} || 24; my $count = $opt{'c'} || 100000; my $modmask = 2**(32-$mask); my (%protos, %proto_packets, %src_net_bytes, %dst_net_bytes, %src_net_packets, %dst_net_packets, %tcp_port_bytes, %udp_port_bytes, @port_table); my $total_bytes = 0; my $total_packets = 0; my $start = time; open IN, "tcpdump -lnntqv -i $int -c $count 2>/dev/null|"; while () { if (/proto\s(\d+),\slength:\s(\d+)\)\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\.(\d+)\s>\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\.(\d+)/) { ($prot, $len, $src, $spt, $dst, $dpt) = ($1, $2, $3, $4, $5, $6); if ($prot == 6) { if (defined($opt{'p'}) && ($spt > 1024)) { $tcp_port_bytes{'1024+'} += $len; } else { $tcp_port_bytes{$spt} += $len; } if (defined($opt{'p'}) && ($dpt > 1024)) { $tcp_port_bytes{'1024+'} += $len; } else { $tcp_port_bytes{$dpt} += $len; } } elsif ($prot == 17) { if (defined($opt{'p'}) && ($spt > 1024)) { $udp_port_bytes{'1024+'} += $len; } else { $udp_port_bytes{$spt} += $len; } if (defined($opt{'p'}) && ($dpt > 1024)) { $udp_port_bytes{'1024+'} += $len; } else { $udp_port_bytes{$dpt} += $len; } } @srcq = split /\./, $src; $srcdec = $srcq[3] + ($srcq[2] * 256) + ($srcq[1] * 65536) + ($srcq[0] * 16777216); $srcnet = $srcdec - ($srcdec % $modmask); $srcnetq = inet_ntoa( inet_aton( $srcnet ) ); $src_net_packets{$srcnetq}++; $src_net_bytes{$srcnetq} += $len; @dstq = split /\./, $dst; $dstdec = $dstq[3] + ($dstq[2] * 256) + ($dstq[1] * 65536) + ($dstq[0] * 16777216); $dstnet = $dstdec - ($dstdec % $modmask); $dstnetq = inet_ntoa( inet_aton( $dstnet ) ); $dst_net_packets{$dstnetq}++; $dst_net_bytes{$dstnetq} += $len; $protos{$prot} += $len; $proto_packets{$prot}++; $total_bytes += $len; $total_packets++; } elsif (/proto\s(\d+),\slength:\s(\d+)\)\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*?\s>\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) { ($prot, $len, $src, $dst) = ($1, $2, $3, $4); @srcq = split /\./, $src; $srcdec = $srcq[3] + ($srcq[2] * 256) + ($srcq[1] * 65536) + ($srcq[0] * 16777216); $srcnet = $srcdec - ($srcdec % $modmask); $srcnetq = inet_ntoa( inet_aton( $srcnet ) ); $src_net_packets{$srcnetq}++; $src_net_bytes{$srcnetq} += $len; @dstq = split /\./, $dst; $dstdec = $dstq[3] + ($dstq[2] * 256) + ($dstq[1] * 65536) + ($dstq[0] * 16777216); $dstnet = $dstdec - ($dstdec % $modmask); $dstnetq = inet_ntoa( inet_aton( $dstnet ) ); $dst_net_packets{$dstnetq}++; $dst_net_bytes{$dstnetq} += $len; $protos{$prot} += $len; $proto_packets{$prot}++; $total_bytes += $len; $total_packets++; } else { print "nonIP: $_"; } } close IN; my $elapsed = time - $start; my $pps = int( $count / $elapsed ); my $mbps = sprintf ( "%3.2f", ( ( $total_bytes * 8 ) / ( 1048576 * $elapsed ) ) ); my $avgpkt = int( $total_bytes / $total_packets ); $tb = nice_byte( $total_bytes ); print "\n$myself\n\n+++++++++++++++++++++++++++++++++++++++++++++++\n$tb seen in $elapsed seconds ($total_packets packets)\n$mbps mb/s, $pps pps, avg pkt size: $avgpkt\n+++++++++++++++++++++++++++++++++++++++++++++++\n\n"; print "prot:\n=====\n"; $counter = 0; foreach $n (sort {$protos{$b} <=> $protos{$a}} keys %protos) { last if ($counter == $top); $m = getprotobynumber( $n ); $pad = 24 - length( $m ); $pad2 = 10 - length( nice_byte( $protos{$n} ) ); print $m . " " x $pad . nice_byte( $protos{$n} ) . " " x $pad2 . "(" . $proto_packets{$n} . ")" . "\n"; $counter++; } print "\nsrc:\n====\n"; $counter = 0; foreach $n (sort {$src_net_bytes{$b} <=> $src_net_bytes{$a}} keys %src_net_bytes) { last if ($counter == $top); $pad = 21 - length( $n ); $pad2 = 10 - length( nice_byte( $src_net_bytes{$n} ) ); print $n . "/" . $mask . " " x $pad . nice_byte( $src_net_bytes{$n} ) . " " x $pad2 . "(" . $src_net_packets{$n} . ")" . "\n"; $counter++; } print "\ndst:\n====\n"; $counter = 0; foreach $n (sort {$dst_net_bytes{$b} <=> $dst_net_bytes{$a}} keys %dst_net_bytes) { last if ($counter == $top); $pad = 21 - length( $n ); $pad2 = 10 - length( nice_byte( $dst_net_bytes{$n} ) ); print $n . "/" . $mask . " " x $pad . nice_byte( $dst_net_bytes{$n} ) . " " x $pad2 . "(" . $dst_net_packets{$n} . ")" . "\n"; $counter++; } print "\nports:\n======\n"; $counter = 0; foreach $n (sort {$tcp_port_bytes{$b} <=> $tcp_port_bytes{$a}} keys %tcp_port_bytes) { last if ($counter == $top); $pad = 20 - length( $n ); $port_table[$counter] .= "tcp/" . $n . " " x $pad . nice_byte( $tcp_port_bytes{$n} ) . " " x (10 - length(nice_byte( $tcp_port_bytes{$n}))) . "|"; $counter++; } $counter = 0; foreach $n (sort {$udp_port_bytes{$b} <=> $udp_port_bytes{$a}} keys %udp_port_bytes) { last if ($counter == $top); $pad = 10 - length( $n ); $port_table[$counter] .= " udp/" . $n . " " x $pad . nice_byte( $udp_port_bytes{$n} ); $counter++; } for $line (@port_table) { print $line . "\n"; } print "\n"; # Eat fresh; every day. sub nice_byte { my $bytes = shift; my $bt = " b"; my $sb = $bytes; if ($bytes >= 1073741824) { $bt = " Gb"; $sb = $bytes / 1073741824; } elsif ($bytes >= 1048576 ) { $bt = " Mb"; $sb = $bytes / 1048576; } elsif ($bytes >= 1024 ) { $bt = " Kb"; $sb = $bytes / 1024; } $sb = sprintf( "%.1f", $sb ); return $sb . $bt; } sub usage { print STDERR < : interface to listen on -m mask : CIDR mask for subnets (default 24) -c count : number of packets to capture (default 100,000) -p : aggregate non-privileged ports (default off) -t N : print top N entries for each list (default 10) -h : print this help message EOF exit( 1 ); }