HOWTO: Count network traffic using iptables

March 2005


Send errors, corrections, enhancements

This HOWTO explains a quick and easy way to count network traffic (bytes sent over an interface) using only iptables.

It is here mainly because this way I can cut'n'paste it when I need it somewhere, myself. :-)

If you don't know what iptables, netfilter or Linux networking is, check out the iptables web site, especially an iptables tutorial which has in-depth explanations of all concepts. (Hint: all of this is only useful for Linux starting at Kernel version 2.4 or newer.)

What do you get?

A log file with the number of bytes transfered over some interface.

What do you need to do?

Configuring iptables to keep track of the traffic

iptables automatically counts packages and bytes passing through each of its rules. It's easy to lose some bytes if there are rules matching more than one interface, so the following is added in front of all other rules. Feel free to do this for any other interface as well.

Firewall rules for accounting
iptables -N pppcount
iptables -A INPUT -i $PPP -j pppcount
iptables -A FORWARD -i $PPP -j pppcount
iptables -A FORWARD -o $PPP -j pppcount
iptables -A OUTPUT -o $PPP -j pppcount
iptables -A pppcount

Logging traffic

This trivial script logs all traffic passing through the "pppcount" rule and resets the count.

BYTES=`iptables -L pppcount -Z -vnx | tail -2 | head -1 | awk '{print $2}'`
DATE=`date "+%Y-%m-%d %H:%M:%S"`
echo $DATE $BYTES >> $LOG

The script is put into /etc/network/if-down.d and so is called whenever a network interface goes down.

It is safe to call that script at all times, not only if the interface goes down; if the interface did not see any traffic yet, it simply records 0 bytes. To make it less likely to lose traffic, it can also be called from anywhere related to network traffic (i.e., in your firewall setup script, at the beginning (before counts are reset); somewhere in /etc/ppp/ip-down.d, or wherever else).

Using the logged data

Here is a perl script to evaluate the log file; it prints the traffic per day.


my $log="/mnts/usbroot/var/log/dsltraffic.log";
open(LOG,$log) or die "$log: $!";
my %days;
my %months;
while (<LOG>)
  if (m/^(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+) (\d+)$/)
    $days{"$1$2$3"} += $7;
    $months{"$1$2"} += $7;
close LOG;

my $total=0;
my $count=0;
print "period  : traffic per day/month   | total traffic           | avg/31 days\n";
foreach my $day (sort (keys %days,keys %months))
  if (exists($days{$day}))
    my $traffic=$days{$day};
    $total += $traffic;
    my $avg=$total/$count*31;
    printf "%-8s: %9.2f MB, %6.2f GB | %9.2f MB, %6.2f GB | %6.2f GB\n",
    my $traffic=$months{$day};
    printf "%-8s: %9.2f MB, %6.2f GB |                         |\n",

Example output:

Output from eval_logtraffic.pl
period  : traffic per day/month   | total traffic           | avg/31 days
200503  :     77.28 MB,   0.08 GB |                         |
20050327:     77.28 MB,   0.08 GB |     77.28 MB,   0.08 GB |   2.34 GB