Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/usr/bin/perl
########################################################################
# This script runs tcpdump on a specific NIC (defaults to eth1) and
# parses the output from that to calculate the top talked, both send
# and receive. After X second it will output who the top talkers were
# and continue to calculate
#
# Scott Baker - 2009-07-31
########################################################################
use strict;
use English;
use Time::HiRes qw(time usleep);
use Getopt::Long;
$| = 1; # Disable output buffering
# Capture control+c
$SIG{TERM} = $SIG{INT} = \&myend;
my $eth = get_first_interface();
my $sec = 15;
my $ok = GetOptions(
'interface=s' => \$eth,
'seconds=i' => \$sec,
'help' => \&show_usage,
);
if ($UID != 0) {
print color(9); # Red
print "Warning: script will probably need root access\n\n";
print color(); # Reset
}
# Show the top X talkers
my $topx = 8;
my $start = time();
my $last = time();
print "Capturing on $eth, Outputting stats every $sec seconds\n\n";
# Open TCPDUMP and read through it line by line live
my $cmd = "/usr/sbin/tcpdump -nn -i $eth ip 2>/dev/null";
my $pid = open(CAPTURE,"$cmd |");
my $stats;
my $tcount = 0;
my $last_spin = time();
while (my $line = <CAPTURE>) {
my @p = split(/\s/,$line);
my $src = $p[2];
my $dst = $p[4];
# Remove everything after the last . which is the port number
#$src =~ s/(.+)\.(.*)/$1/g;
#$dst =~ s/(.+)\.(.*)/$1/g;
$src = extract_ip($src);
$dst = extract_ip($dst);
if (!$src || !$dst) { next; }
$stats->{'src'}->{$src}++;
$stats->{'dst'}->{$dst}++;
$stats->{'total_dst'}++;
$stats->{'total_src'}++;
# If it's been X seconds since the last update output the stats
if ((time() - $last) > $sec) {
output_stats($stats);
# Reset the last output and stats
$last = time();
$stats = {};
}
# Show the spinner every 1/10 seconds so we know the program is doing something
if (time() - $last_spin > .1) {
spinner();
$last_spin = time();
}
$tcount++;
}
close CAPTURE;
sub extract_ip {
my $str = shift();
my $ret = '';
# It's a 4 octet IP already
if (char_count(".",$str) == 3) {
my $ret = $str;
$ret =~ s/://;
return $ret;
# It's a 5 octet IP/Port pairing
} else {
#65.182.224.39.80
my $len = length($str);
my $count = 0;
for (my $i = 0; $i < $len ; $i++) {
my $char = substr($str,$i,1);
if ($char eq ".") { $count++; }
if ($char eq "." && $count > 3) {
return $ret;
}
$ret .= $char;
}
return $ret;
}
}
sub char_count {
my $needle = shift();
my $haystack = shift();
my $len = length($haystack);
my $count = 0;
for (my $i = 0; $i < $len ; $i++) {
if (substr($haystack,$i,1) eq $needle) {
$count++;
}
}
return $count;
}
# Custom end function
sub myend {
my $stop = time();
my $total = sprintf("%.2f",$stop - $start);
print "\b\b";
print "Processed $tcount packets in $total seconds\n";
}
#########################################################################################
sub output_stats {
my $stats = shift();
# Sort the src/dst arrays to get the top talker for each
my @topsrc = sort{ $stats->{'src'}->{$b} <=> $stats->{'src'}->{$a} } keys %{$stats->{'src'}};
my @topdst = sort{ $stats->{'dst'}->{$b} <=> $stats->{'dst'}->{$a} } keys %{$stats->{'dst'}};
print "\b" . mysql_date(time(),1) . "\n\n";
my $count = 0;
print "Top source by packet count\n";
print "--------------------------\n";
foreach my $i(@topsrc) {
# Print out the IP address and the number of packets
my $query_count = $stats->{'src'}->{$i};
my $per = ($query_count / $stats->{'total_src'}) * 100;
my $qps = $query_count / $sec;
printf("%15s = %-6i (%5.2f%% / %i pps)\n",$i,$query_count,$per,$qps);
$count++;
if ($count > $topx) { last; } # Only show the topx records
}
print "\n";
###########################################################
$count = 0;
print "Top destination by packet count\n";
print "-------------------------------\n";
foreach my $i(@topdst) {
# Print out the IP address and the number of packets
my $query_count = $stats->{'dst'}->{$i};
my $per = ($query_count / $stats->{'total_dst'}) * 100;
my $qps = $query_count / $sec;
printf("%15s = %-6i (%5.2f%% / %i pps)\n",$i,$stats->{'dst'}->{$i},$per,$qps);
$count++;
if ($count > $topx) { last; } # Only show the topx records
}
print "\n";
print "=" x 31;
print "\n\n";
}
sub mysql_date() {
my ($epoch,$inc_time) = @_;
$epoch ||= time();
my @date = localtime();
my $ret = sprintf("%04d-%02d-%02d", $date[5] + 1900, $date[4] + 1, $date[3]);
if ($inc_time) { $ret .= sprintf(" %02d:%02d:%02d", $date[2], $date[1], $date[0]); }
return $ret;
}
my $cur_key = 0;
sub spinner() {
my @chars = ("-", "\\", "|", "/");
my $backsp = "\b";
my $spinner_lag = $_[0] if $_[0];
if ($cur_key == @chars) {
$cur_key = 0;
}
if ($spinner_lag) {
my $i;
if ($i == $spinner_lag) {
print $backsp . $chars[$cur_key];
$i = 1;
}
else { $i++; }
}
else { print $backsp . $chars[$cur_key]; }
$cur_key++;
}
sub get_first_interface {
my $cmd = 'ip link list';
my @out = `$cmd`;
# Look for the first interface that's NOT 'lo' (loopback)
foreach (@out) {
if ($_ =~ /^(\d+): (\w+):/) {
my $ifc = $2;
if ($ifc ne 'lo') {
return $ifc;
}
}
}
}
# String format: '115', '165bold', '10_on_140', 'reset', 'on_173'
sub color {
my $str = shift();
# If we're NOT connected to a an interactive terminal don't do color
if (-t STDOUT == 0) { return ''; }
# No string sent in, so we just reset
if (!$str || $str eq 'reset') {
return "\e[0m";
}
# Get foreground and bold
my ($fc,$bold) = $str =~ /^(\d+)(b|bold)?/g;
# Get the background color (if present)
my ($bc) = $str =~ /on_?(\d+)$/g;
my $ret = '';
if ($bold) { $ret .= "\e[1m"; }
if ($fc) { $ret .= "\e[38;5;${fc}m"; }
if ($bc) { $ret .= "\e[48;5;${bc}m"; }
return $ret;
}
sub show_usage {
print "$0 [--interface eth2] [--seconds 10]\n";
exit;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment