#!/usr/bin/perl -W
#
# OrinocoTOP v0x01a
# Licensed under GPL 
#
# Programik do wyciagania ciekawych informacji z ORINOCO AP2000 ;)
# Mozliwe ze dziala takze na AP600 oraz na AP2500 - nie testowane
# Zainspirowany outputem z snmpwalk oraz lektura orinoco.mib :)
# Firmware, na ktorym sprawdzalem: 2.4.3 oraz 2.4.5
#
# 0. To jest w zasadzie wersja Alfa/Beta...
# 1. Kod jest bardzo brzydko napisany i praktycznie nieczytelny ;)
# 2. Mialo byc napisane przy uzyciu SNMP_Session, ale niestety czas
#    naglil i nie mialem okazji zrobic tego "tak jakbym chcial" (tm) 
# 3. Jesli Orinoco zwroci przez SNMP adres "0.0.0.0" jako klienta 
#    ten programik potraktuje takiego klienta jako "WDS" co nie do 
#    konca jest prawda... - wlasciwie to wcale nie jest ;)
# 4. Prosilbym o informowanie mnie o wszystkich bledach/uwagach/opiniach/etc 
#    na adres email podany na koncu
# 5. Proxim spier.... implementacje SNMP i czesc danych jest niepoprawnie 
# 	 wyswietlana ( SNR i transfery zwlascza )
#
# TODO: typ stacji, statystyki per interface
# 
# Jakub Wartak [vnull@pcnet.com.pl] 2004
#

# sciezka do snmpwalk ( z net-snmp, dostepne z http://net-snmp.sf.net )
my $snmpwalk = "/usr/bin/snmpwalk";

# co ile odswiezac?
my $delay = 5;

use POSIX;
#use IPC::Open2;

my $OR = $ARGV[0];
my $comm = $ARGV[1];
if ( !defined $OR || !defined $comm) {
	printf "usage: ./ortop.pl <ip_orinoco> <snmp_community>\n\n";
	exit(0);
}

# zaczerpniete z orinoco.mib
my $oriStationStatTable = ".1.3.6.1.4.1.11898.2.1.33.1.1";
my $OID = $oriStationStatTable;
my $cmd = "$snmpwalk -On -v 2c -c $comm $OR -Cc $OID";

my %db_mac;
my %db_ip;
my %db_if;
my %db_input;
my %db_output;
my %db_snr;
my %db_type;

my %last_in;
my %last_out;

# glowna petla
while(1) {

%db_mac = ();
%db_ip = ();
%db_if = ();
%db_input = ();
%db_output = ();
%db_snr = ();
%db_type = ();

# czytamy to co nam snmpwalk zwroci...
open(S, "$cmd |") or die;
while(<S>) {
	chomp;
	&process_line($_);
}
close S;

print "\n\n\n\n";
printf STDOUT ("%-20s %-16s %-6s %-6s %-10s %-10s\n", 
    "MAC", "IP", "IF", "SNR", "IN(kbit/s)", "OUT(kbit/s)");

my $sum_rx = 0;
my $sum_tx = 0;

while ( ( $i, $mac ) = each %db_mac ) {
	my $ip = $db_ip{$i};
	my $snr = $db_snr{$i};
	my $if = $db_if{$i};

	my $desc = "${if}_${mac}_${i}";

	$rx = $last_in{$desc};
	$tx = $last_out{$desc};
	
	if ( ! defined $rx ) {
	    $rx = $tx = "0";
	    $diff_tx = $diff_rx = "0";
	} else {
		if($rx < 0 || $tx < 0 ) {
			print "ERROR: $rx $tx\n";
			die;
		}

		# kilka operacji :>
		$diff_rx = $db_input{$i} - $rx;
 	   	$diff_rx = $diff_rx * $delay;
		$diff_rx = $diff_rx / 1024;

		$diff_tx = $db_output{$i} - $tx;
		$diff_tx = $diff_tx * $delay;
		$diff_tx = $diff_tx / 1024;

		# nie wiem dlaczego ale czasami wyglada na to
		# ze Orinoco "przewija" countery :/
		# i roznica moze byc ujemna.... zapobiegamy temu
		# TODO: lepiej to zbadac...
		if($diff_rx < 0 ) {
			$diff_rx = 0;
			$db_input{$i}=0;
			$rx = 0;
		}
   		if($diff_tx < 0 ) {
       		        $diff_tx = 0;
	                $db_output{$i}=0;
       		 	$tx = 0;
		}

	}
	#print "drx=$diff_rx (rx=$rx) dtx=$diff_tx (tx=$tx)\n";

	my $p = sprintf("%-20s %-16s %-6s %-6s %-10.2f %-10.2f\n", 
	    $mac, $ip, $if, $snr, $diff_rx, $diff_tx);

	print "$p";

	$sum_rx += $diff_rx;
	$sum_tx += $diff_tx;

        $last_in{$desc} = $db_input{$i};
        $last_out{$desc} = $db_output{$i};
}

printf STDOUT "TOTAL IN: %.2f kbit/s TOTAL OUT: %.2f kbit/s\n", $sum_rx, $sum_tx;

sleep($delay);

} 

1;

sub process_line() {
	my $l = shift;
	# btw: wstydze sie tego kodu :P
	my $skip = "^$oriStationStatTable\.";	
	$l =~ s/$skip//;
	my @what = split(/\./, $l, 2);
	my $func = $what[0];
	my @tmp = split(/ /, $what[1]);
	my $index = $tmp[0];
	my $name = &func2name($func);
	my $data = $tmp[3];

	if ( $name eq "macaddr" ) {
		if(defined $db_mac{$index}) { return; }
		$data = "$tmp[3]:$tmp[4]:$tmp[5]:$tmp[6]:$tmp[7]:$tmp[8]";
		$db_mac{$index} = $data;
	} elsif ( $name eq "ip" ) { 
	    if(defined $db_ip{$index}) { return; }
	    if ($data eq "0.0.0.0") { $data = "( WDS )"; }
	    $db_ip{$index} = $data; 
	} elsif ( $name eq "if" ) { 
	    if(defined $db_if{$index}) { return; }
	    $db_if{$index} = &if2name($data); 
	} elsif ( $name eq "input" ) { 
	    if(defined $db_input{$index}) { return; }
	    $db_input{$index} = $data; 
	} elsif ( $name eq "output" ) { 
	    if(defined $db_output{$index}) { return; }
	    $db_output{$index} = $data; 
	} elsif ( $name eq "snr" ) { 
	    if(defined $db_snr{$index}) { return; }
	    $db_snr{$index} = $data; 
	}
	elsif ( $name eq "NIE" ) { return; } # :P :P :P

#print "$l\n";  
#	print "$name $index $data\n";
	

}

# Lame :/
sub if2name() {
	my $num = shift;

	if($num eq 1) { return "eth"; }
        elsif($num eq 2 || $num eq 0) { return "?"; }
        elsif($num eq 3) { return "wlan1"; }
        elsif($num eq 4) { return "wlan2"; }
}

# To tez jest Lame :/
# ostatnia czesc OIDa zamienia na nazwe
sub func2name() {
	my $f = shift;
	if($f == "1") { return "index"; }
	elsif($f == "2") { return "macaddr"; }
	elsif($f == "3") { return "ip"; }
	elsif($f == "4") { return "if"; }
	elsif($f == "12") { return "input"; }
	elsif($f == "16") { return "output"; }
	elsif($f == "20") { return "snr"; }
	else { return "NIE"; } # :P
}

# TheEND
