Last active
September 30, 2016 20:46
-
-
Save scottchiefbaker/971d2927925a00e474bb09b54db1fa38 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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