Skip to content

Instantly share code, notes, and snippets.

@agramajo
Created April 29, 2015 19:16
Show Gist options
  • Save agramajo/216ac69d507428b6d13d to your computer and use it in GitHub Desktop.
Save agramajo/216ac69d507428b6d13d to your computer and use it in GitHub Desktop.
display stats by ip accounting with iptables
#!/usr/bin/perl
use strict;
# Quick display stats by ip accounting with iptables chains
# Only for linux kernel and netfilter
#
# CONFIG
# Read the source!
#
# SUMMARY
# - Parse the output of arp command, so harvest ip addresses
# - Create chains for accounting (in main forward chain)
# - Run iptables -L and parse this (twice):
#
# Chain stats (1 references)
# num pkts bytes target prot opt in out source destination
# 1 501 43099 RETURN all -- * * 10.0.0.10 0.0.0.0/0
# 2 0 0 RETURN all -- * * 0.0.0.0/0 10.0.0.10
# 3 2 1524 RETURN all -- * * 0.0.0.0/0 10.0.0.10
# 4 101 82146 RETURN all -- * * 10.0.0.109 0.0.0.0/0
# 5 2 1540 RETURN all -- * * 0.0.0.0/0 10.0.0.109
# 6 239 110995 RETURN all -- * * 10.0.0.108 0.0.0.0/0
# 7 28786 5831340 RETURN all -- * * 10.0.0.110 0.0.0.0/0
# 8 1596 97792 RETURN all -- * * 0.0.0.0/0 10.0.0.110
#
# - Calculate rate transfer of each ip address
# - Generate output sorted by download rate (ip, rate down, rate up):
#
# HOST DOWNLOAD UPLOAD
# 172.21.0.56 22.55K ( 180.44K) 13.58K ( 108.67K)
# 10.0.0.105 9.77K ( 78.12K) 9.80K ( 78.38K)
# 10.0.0.128 5.88K ( 47.07K) 112.6 ( 900.8)
# 10.0.0.109 76.9 ( 615.2) 81.4 ( 651.2)
# 10.0.0.110 76.8 ( 614.4) 81.3 ( 650.4)
# 10.0.0.130 89.3 ( 714.4) 50.7 ( 405.6)
# 10.0.0.10 6 ( 48) 6 ( 48)
# TOTAL 31.16K ( 249.30K) 22.56K ( 180.44K)
#
# - Flush and clean on exit (ctrl-c to break)
#
# DISCLAIMER
# Use at your own risk!
#
# AUTHOR
# Alejandro Gramajo ( agramajo at gmail dot com )
# 20060504
#
# config
#
my $ipt = "/sbin/iptables";
my $arp = "/usr/sbin/arp";
my $chain = "stats" . $$;
my $main_chain = "FORWARD";
my $sleep_time = 5; # in seconds
my $ipt_list = "$ipt -L $chain -n -v -x --line-numbers";
my $arp_list = "$arp -n";
my %data1;
my %data2;
my $cont = 1;
check();
#
# capture signals for ending, clean all rules
#
$SIG{'INT'} = \&end;
$SIG{'TERM'} = \&end;
#
# capture arp output, build rules
#
start();
print "Waiting for $sleep_time seconds...\n\n";
#
# main loop
#
while ($cont) {
my @data = `$ipt_list 2>&1`;
foreach (@data) {
next unless /^\d+/; # skip not numbers
# bytes == pos 2
# ip source == pos 8
# ip destination == pos 9
my ($bytes, $ip_src, $ip_dst) = (split /\s+/)[2,8,9];
if ($ip_dst =~ /0\.0\.0\.0/) { $data1{ $ip_src } = $bytes }
elsif ($ip_src =~ /0\.0\.0\.0/) { $data2{ $ip_dst } = $bytes }
}
sleep $sleep_time;
my @data = `$ipt_list 2>&1`;
foreach (@data) {
next unless /^\d+/; # skip not numbers
# bytes == pos 2
# ip source == pos 8
# ip destination == pos 9
my ($bytes, $ip_src, $ip_dst) = (split /\s+/)[2,8,9];
#
# calculate difference in bytes
#
if ($ip_dst =~ /0\.0\.0\.0/) {
$data1{ $ip_src } = $bytes - $data1{ $ip_src };
}
elsif ($ip_src =~ /0\.0\.0\.0/) {
$data2{ $ip_dst } = $bytes - $data2{ $ip_dst };
}
}
my $total1 = 0;
my $total2 = 0;
printf("\t%-16s %19s %19s\n", "HOST", "DOWNLOAD", "UPLOAD");
# sorted by rate of ip destination (download)
foreach (sort { $data2{ $b } <=> $data2{ $a } } keys %data1) {
# speed = bytes / time
my $rate_src = $data1{ $_ } / $sleep_time;
my $rate_dst = $data2{ $_ } / $sleep_time;
# show only rate > 0
next if ($rate_src == 0 and $rate_dst == 0);
$total1 += $rate_src;
$total2 += $rate_dst;
printf("\t%-16s %8s (%8s) %8s (%8s)\n", $_,
size2human($rate_dst), size2human($rate_dst*8),
size2human($rate_src), size2human($rate_src*8),
);
}
printf("\t%-16s %8s (%8s) %8s (%8s)\n", "TOTAL",
size2human($total2),size2human($total2*8),
size2human($total1),size2human($total1*8),
);
print "-" x 70, "\n";
}
#
# functions
#
sub size2human {
# XXX max giga (G)
my $data = shift;
my $tmp;
if ($data > 1024**3) { $tmp = sprintf "%0.2fG", $data / 1024**3; }
elsif ($data > 1024**2) { $tmp = sprintf "%0.2fM", $data / 1024**2; }
elsif ($data > 1024) { $tmp = sprintf "%0.2fK", $data / 1024; }
else { $tmp = $data; }
return $tmp;
}
sub start {
system("$ipt -N $chain");
system("$ipt -I $main_chain -j $chain");
open ARP, "$arp_list |";
while (<ARP>) {
next unless /^\d+/; # skip not numbers
my $ip = (split /\s+/)[0];
system("$ipt -A $chain -s $ip -j RETURN");
system("$ipt -A $chain -d $ip -j RETURN");
}
close ARP;
}
sub end {
system("$ipt -F $chain");
system("$ipt -D $main_chain -j $chain");
system("$ipt -X $chain");
$cont = 0;
}
sub check {
die "\nOnly run as root\n\n" if ($> != 0);
die "\nWhere is $ipt\n\n" unless (-e $ipt);
die "\nWhere is $arp\n\n" unless (-e $arp);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment