Drawing some bandwidth usage graphs
-----------------------------------

First off, an <a href="12maytraffic.png">example</a>. This is what my implementation looks like (actually, the image is a bit bigger, this one is scaled down). If you can live with something similar to this, feel free to read on.


Getting the statistics
----------------------

First off, i knew there had to be a way to get pretty accurate measurements on how much traffic one or another host generates. It didn't take me very long to remember iptables having counters for the chains. So, i figured it would work and so it did. I added two new chains, one for outbound and one for inbound traffic and into each chain a rule that would detect one ip. It was done like this:

iptables -N STATS-IN
iptables -N STATS-OUT

iptables -A STATS-IN -d 194.204.48.52 -j RETURN
iptables -A STATS-IN -d 192.168.0.2 -j RETURN
iptables -A STATS-IN -d 192.168.0.3 -j RETURN
iptables -A STATS-IN -d 192.168.0.4 -j RETURN

iptables -A STATS-OUT -s 194.204.48.52 -j RETURN
iptables -A STATS-OUT -s 192.168.0.2 -j RETURN
iptables -A STATS-OUT -s 192.168.0.3 -j RETURN
iptables -A STATS-OUT -s 192.168.0.4 -j RETURN

Also, so that rules would be traversed, the chains obviously had to be used somehow. If you have a router, you can propably add them to the FORWARD chain and be done with it. I, however, use the router as a server as well, so I needed to add them to other places too (WAN NIC is eth0 and LAN NIC is eth2):

iptables -I FORWARD -i eth0 -o eth2 -j STATS-IN
iptables -I FORWARD -i eth2 -o eth0 -j STATS-OUT
iptables -I OUTPUT -o eth0 -j STATS-OUT
iptables -I INPUT -i eth0 -j STATS-IN

This allowed me to see how much someone has been recieving, like this:

teener:/home/windo# iptables -vL STATS-IN
Chain STATS-IN (2 references)
pkts bytes target prot opt in out source destination
628 215K RETURN all -- any any anywhere mannavaht.windolik
0 0 RETURN all -- any any anywhere windolik
0 0 RETURN all -- any any anywhere bigbrat.windolik
5 706 RETURN all -- any any anywhere dsl48-52.uninet.ee

Reaping the statistics
----------------------

So, we have the information we need available, now we need to reap it at regular intervals. For this, I set up a cron job to get the information every minute and write it to a log file. Also, for them to mantain a manageable size, another cronjob was added to "rotate" them each day. I now know that logrotate should have been used for this, but... Which ever.

teener:~# crontab -l
PATH="/sbin:/usr/sbin:/bin:/usr/bin"

* * * * * /root/trafficlog.sh
0 0 * * * /root/trafficlogrot.sh

Not very informative, is it? Here are the scripts mentioned:

teener:~# cat trafficlog.sh
#!/bin/bash
#
# writes traffic logs (iptables STATS-IN & STATS-OUT chains)
#

# currently, the fields are: 192.168.0.2, .3, .4, 194.204.48.52
iptables -vnxL -Z STATS-IN | grep RETURN | awk '{ printf("%s ", $2)}; END { print " " };' >> /var/log/traffic-in
iptables -vnxL -Z STATS-OUT | grep RETURN | awk '{ printf("%s ", $2)}; END { print " " };' >> /var/log/traffic-out

teener:~# cat trafficlogrot.sh
#!/bin/bash
#
# "Rotate" traffic logs
#

bzip2 --stdout /var/log/traffic-in > /var/log/traffic/traffic-in-`date +%d-%m-%Y`.bz2
rm /var/log/traffic-in

bzip2 --stdout /var/log/traffic-out > /var/log/traffic/traffic-out-`date +%d-%m-%Y`.bz2
rm /var/log/traffic-out

Does this make sense? You can see me reading the counters while zeroing them, getting the lines with RETURN in them, then using awk to print the counter to the logfile. The rotation just bz2izes the files and dumps them in a directory with a nice date in the name of the file.


Drawing them pictures
---------------------

I use gnuplot to draw the graphs. Mostly because it was the first (and last) I got to work like i wanted. Or got to work at all, I think. Anyways, by trying stuff out, I came to write these two gnuplot batch files (called out by the third one). They are pretty similar, as you may observe:

teener:/var/www/plot# cat gnuplot-in
set title "Inbound traffic"
set xlabel "Time (min)"
set ylabel "Bandwidth (kbps)"
set style data lines
plot "traffic-in" u ($1/1024.0/60.0) t "192.168.0.2"
replot "traffic-in" u ($2/1024.0/60.0) t "192.168.0.3"
replot "traffic-in" u ($3/1024.0/60.0) t "192.168.0.4"
set terminal png
set output "traffic-in.png"
replot "traffic-in" u ($4/1024.0/60.0) t "194.204.48.52"

teener:/var/www/plot# cat gnuplot-out
set title "Outbound traffic"
set xlabel "Time (min)"
set ylabel "Bandwidth (kbps)"
set style data lines
plot "traffic-out" u ($1/1024.0/60.0) t "192.168.0.2"
replot "traffic-out" u ($2/1024.0/60.0) t "192.168.0.3"
replot "traffic-out" u ($3/1024.0/60.0) t "192.168.0.4"
set terminal png
set output "traffic-out.png"
replot "traffic-out" u ($4/1024.0/60.0) t "194.204.48.52"

teener:/var/www/plot# cat gnuplot-traffic
load "gnuplot-in"
load "gnuplot-out"
exit

Im not entirely sure myself anymore. First it sets a bunch of lables and graph style. Then it plots 'traffic-out' using the first column, which it divides by 1024.0 and 60.0 to get kilobytes per second (from iptables, we got bytes per minute - minute because our cron job reaps every minute) giving this line the title "192.168.0.2". Then it draws the other lines on the same graph and finally outputs it to a png file.

If I looked at my bandwidth usage all the time, i would set up the cron job to reap and also draw the graphs, but I dont. So, i have this php script and this shell script:

teener:/var/www/plot# cat index.php
<?php

exec('./plot.sh');

?>
<html>
<body>
<img src="traffic-in.png">
<br>
<img src="traffic-out.png">
</body>
</html>

teener:/var/www/plot# cat plot.sh
#!/bin/bash
#
# Grab the logs and plot shit
#

gnuplot ./gnuplot-traffic

So here you go. Some mad mofo could really DOS my poor server by requesting this page a millon times(HINT, HINT)! Oh come on. Don't do that. It's so easy to fix and and totally pointless thing to do. Fuck off bitch!


04.06.2004