#!/usr/sbin/perl

# traffic-mon - Monitor ip traffic, and print high volume hosts
# erco 10/21/99 1.00

# Typical output from tcpdump (spaces added for alignment)
#
# 188.10.6.8.1013   > 188.10.6.14.513:   tcp 0 (DF) [tos 0x10]
# 188.10.6.14.513   > 188.10.6.8.1013:   tcp 118 (DF) [tos 0x10]
# 188.10.6.102.1842 > 188.10.2.33.53:    udp 48
# 188.10.2.33.53    > 188.10.6.102.1842: udp 114
# 188.10.6.102.18   > 188.10.2.33.53:    udp 44
# 188.10.6.8.1013   > 188.10.6.14.513:   tcp 0 (DF) [tos 0x10]
#

$| = 1;
use Socket;

my $cmd = "tcpdump -l -t -n -q";
my @arr;
my ($src, $dst, $proto);
my ($pcnt) = 0;
my $timemark = time();
my $samplesecs = ( 5 * 60 );	# secs between pkt count rotations

# OPEN TCPDUMP OUTPUT
unless ( ( open ( FD, "$cmd|" ) ) )
    { print STDERR "pipe failed for command '$cmd': $!"; exit(1); }

while ( <FD> )
{
    if ( (++$pcnt) % 50 == 0 )
    {
        my @out=sort{$traffic{$b}{pcnt}<=>$traffic{$a}{pcnt}}(keys(%traffic));

	print "\033[2J\033[1;1H";
        printf("%-8s %-8s %s\n", "Pkts", "5mins", "Hostname");
        printf("%-8s %-8s %s\n", "----", "-----", "------------");

	my $count = 0;
        foreach ( @out )
	{
	    printf("%-8d %-8d %-15s\n", $traffic{$_}{pcnt}, 
					$traffic{$_}{pcnt5min},
					IP2Hostname($_));
	    if ( ++$count > 20 ) { last; }
	}
    }

    # 5 MINS GONE BY?
    #     Rotate out the current values, reset to zero
    #
    if ( time() > ( $timemark + $samplesecs ) )
    {
	foreach ( keys ( %traffic ) )
	{
	    $traffic{$_}{pcnt5min} = $traffic{$_}{pcnt};
	    $traffic{$_}{pcnt}     = 0;
	}
	$timemark = time();
    }

    chomp();
    @arr = split();

    if ( $arr[0] !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)/ ) { next; }
    $src = sprintf("%d.%d.%d.%d", $1, $2, $3, $4);

    if ( $arr[2] !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)/ ) { next; }
    $dst = sprintf("%d.%d.%d.%d", $1, $2, $3, $4);

    $proto = $arr[3];

    $traffic{$src}{pcnt}++;
    $traffic{$dst}{pcnt}++;
}
close(FD);

# CONVERT IP ADDRESS TO HOSTNAME
#     $1 - ip address string, eg. "127.0.0.1"
#
sub IP2Hostname($)
{
    my ($addr) = @_;

    # CHECK LOOKUP CACHE
    #    If we already did the lookup, avoid the overhead from
    #    the gethostbyaddr() call.
    #
    if ( defined( $G_ip2host{$addr} ) ) { return($G_ip2host{$addr}); }

    # DO A DNS LOOKUP
    my $iaddr = inet_aton($addr);
    my $name = gethostbyaddr($iaddr, AF_INET);
    if ( ! defined ( $name ) ) { $name = $addr; }

    # CACHE LOOKUP FOR LATER
    $G_ip2host{$addr} = $name;
    return( $name );
}
