#!/usr/bin/perl -w

#************************** Begin Copyright ****************************
#
# (C) Copyright IBM Corp. 2009, All Rights Reserved 
#
#************************** End Copyright ******************************

#
# Author: Vasileios Pappas (vpappas@us.ibm.com)
#


use NetSNMP::OID (':all'); 
use NetSNMP::ASN (':all');
use NetSNMP::agent (':all'); 


my %oid_value;
my %oid_type;
my %oid_next;
$cache_timout=60;
$last_populated=0;
my $netdir="/sys/class/net/";

my $rootOID = ".1.3.6.1.2.1.17";
my $regoid = new NetSNMP::OID($rootOID);
$agent->register("DOT1QBRIDGE", $regoid, \&request_handler);

print STDERR "DOT1QBRIDGE AGENT STARTED\n";

sub request_handler{
	my ($handler, $registration_info, $request_info, $requests)=@_;

	populate_mib();
	for($request=$requests; $request; $request=$request->next()){
   		if($request_info->getMode()==MODE_GET){
			mode_get($request);
		}elsif($request_info->getMode()==MODE_GETNEXT){
			mode_get_next($request);
		}else{
	
		}	
	}
}

sub mode_get{
	my ($request)=@_;
	my $oid=$request->getOID();

	print STDERR "GET: $oid\n";

	reply($request, $oid) if(exists $oid_value{$oid});
}

sub mode_get_next{
	my ($request)=@_;
	my $oid=$request->getOID();

	print STDERR "GET_NEXT: $oid\n";

	if(defined($oid_next{$oid})){ 
		reply($request, $oid_next{$oid});
	}elsif($oid<=new NetSNMP::OID(".1.3.6.1.2.1.17.1.4.1.1")){ 
		reply($request, 
		$oid_next{new NetSNMP::OID(".1.3.6.1.2.1.17.1.4.1.1")});
	}elsif($oid<=new NetSNMP::OID(".1.3.6.1.2.1.17.1.4.1.2")){
		reply($request, 
		$oid_next{new NetSNMP::OID(".1.3.6.1.2.1.17.1.4.1.2")});
	}elsif($oid<=new NetSNMP::OID(".1.3.6.1.2.1.17.4.3.1.1")){
		reply($request, 
		$oid_next{new NetSNMP::OID(".1.3.6.1.2.1.17.4.3.1.1")});
	}elsif($oid<=new NetSNMP::OID(".1.3.6.1.2.1.17.4.3.1.2")){
		reply($request, 
		$oid_next{new NetSNMP::OID(".1.3.6.1.2.1.17.4.3.1.2")});
	}elsif($oid<=new NetSNMP::OID(".1.3.6.1.2.1.17.4.3.1.3")){
		reply($request, 
		$oid_next{new NetSNMP::OID(".1.3.6.1.2.1.17.4.3.1.3")});
	}else{

	}
}

sub reply{
	my ($request, $oid)=@_;
	my $type=$oid_type{$oid};
	my $value=$oid_value{$oid};
	
	$request->setOID($oid);
	$request->setValue($type, $value);
	print STDERR "REPLY: $oid -> $value ($type)\n";
} 

# Populated MIB OID 
#
# dot1dTpFdbAddress: 	1.3.6.1.2.1.17.4.3.1.1.<dot1dTpFdbAddress> 
#			OCTET STRING (SIZE (6))
#
# dot1dTpFdbPort: 	1.3.6.1.2.1.17.4.3.1.2.<dot1dTpFdbAddress>
#			INTEGER32 
#
# dot1dTpFdbStatus:	1.3.6.1.2.1.17.4.3.1.3.<dot1dTpFdbAddress>
#			INTEGER
# 	  		1 : other
#			2 : invalid
#			3 : learned
#			4 : self
#			5 : mgmt
#
# dot1dBasePort:	1.3.6.1.2.1.17.1.4.1.1.<dot1dBasePort>
#			INTEGER32
#
# dot1dBasePortIfIndex:	1.3.6.1.2.1.17.1.4.1.2.<dot1dBasePort>
#			INTEGER32	
#

sub populate_mib{
	my ($now, $oid, $baseport, @bridges);
	my (%indexes, %macs, %ages, %locals);

        $now=time;
        return if($now-$last_populated<=$cache_timout);
        $last_populated=$now;
	
	%oid_value=();
	%oid_type=();	
	%oid_next=();

	print STDERR "PUPULATING MIB\n";

	opendir(DIR, $netdir) or return -1;
	while(my $br=readdir(DIR)){
		next unless($br=~/^br\d+$/);
		push @bridges, $br;
	}
	close(DIR);

	foreach my $bridge (@bridges){
		my $brifdir=$netdir.$bridge."/brif/";
		opendir(DIR, $brifdir) or return -1;
		while(my $if=readdir(DIR)){
			next if($if eq "." or $if eq "..");
			my $port_no=$brifdir.$if."/port_no";
			my $ifindex=$netdir.$if."/ifindex";
			open(PRT, $port_no) or return -1;
			my $port=<PRT>;
			close(PRT);
			open(PRT, $ifindex) or return -1;
			my $index=<PRT>;
			close(PRT);
			chomp $port;
			chomp $index;
			$port=hex($port);
			$indexes{$bridge}{$port}=$index;
			$ifindex=$netdir.$bridge."/ifindex";
			open(PRT, $ifindex) or return -1;
			my $index=<PRT>;
			close(PRT);
			chomp $index;
			$indexes{$bridge}{$bridge}=$index;
		}
		close(DIR);

		my $fdb=$netdir.$bridge."/brforward";
		sysopen(FWD, $fdb, O_RDONLY) or return -1;
		while(sysread(FWD, $data, 20)){	
			my $mac;
			my ($b1,$b2,$b3,$b4,$b5,$b6,$port,$local,$age,$hi)=
			unpack("C6 C C L C x3", $data);
			$mac=sprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
						$b1,$b2,$b3,$b4,$b5,$b6);
			$age=$age/100;
			$macs{$mac}{$bridge}=$port;
			$ages{$mac}{$bridge}=$age;
			$locals{$mac}{$bridge}=3;
			next unless($local);
			$macs{$mac}{$bridge}=$bridge;
			$locals{$mac}{$bridge}=4;
		}
		close(FWD);
	}

	# 
	# TODO: fix the way OIDs are created (use only NetSNMP::OID);
	#
	$baseport=1;
	$oid=new NetSNMP::OID(".1.3.6.1.2.1.17.1.4.1");
	foreach my $bridge (keys %indexes){
		foreach my $port (keys %{$indexes{$bridge}}){
			unless(defined($first_baseport)){
				$first_baseport=$baseport;
				$oid_next{$oid.".1"}=$oid.".1.".$baseport;
				$oid_next{$oid.".2"}=$oid.".2.".$baseport;
			}
			my $index=$indexes{$bridge}{$port};
			$baseports{$bridge}{$port}=$baseport;
	
			$oid_value{$oid.".1.".$baseport}=$baseport;
			$oid_type{$oid.".1.".$baseport}=ASN_INTEGER;
			if(defined($prv_baseport)){
				$oid_next{$oid.".1.".$prv_baseport}=
					$oid.".1.".$baseport;
			}
		
			$oid_value{$oid.".2.".$baseport}=$index;
			$oid_type{$oid.".2.".$baseport}=ASN_INTEGER;
			if(defined($prv_baseport)){
				$oid_next{$oid.".2.".$prv_baseport}=
					$oid.".2.".$baseport;
			}
			
			$prv_baseport=$baseport;
			$baseport++;
		}
	}
	$oid_next{$oid.".1.".$prv_baseport}=$oid.".2.".$first_baseport;
	$oid_next{$oid.".2.".$prv_baseport}=0;

	$oid=new NetSNMP::OID(".1.3.6.1.2.1.17.4.3.1");
	foreach my $mac (sort {$a cmp $b} keys %macs){
		foreach my $bridge (keys %{$macs{$mac}}){
			my $mac_oid=mac2oid($mac);
			unless(defined($first_mac_oid)){
				$first_mac_oid=$mac_oid;
				$oid_next{$oid.".1"}=$oid.".1".$mac_oid;
				$oid_next{$oid.".2"}=$oid.".2".$mac_oid;
				$oid_next{$oid.".3"}=$oid.".3".$mac_oid;
			}
			my $port=$macs{$mac}{$bridge};
			my $baseport=$baseports{$bridge}{$port};
			my $status=$locals{$mac}{$bridge};
			my $age=$ages{$mac}{$bridge};

			$oid_value{$oid.".1".$mac_oid}=mac2hex($mac);
			$oid_type{$oid.".1".$mac_oid}=ASN_OCTET_STR;
			if(defined($prv_mac_oid)){
				$oid_next{$oid.".1".$prv_mac_oid}=
					$oid.".1".$mac_oid;
			}

			$oid_value{$oid.".2".$mac_oid}=$baseport;
			$oid_type{$oid.".2".$mac_oid}=ASN_INTEGER;
			if(defined($prv_mac_oid)){
				$oid_next{$oid.".2".$prv_mac_oid}=
					$oid.".2".$mac_oid;	
			}	

			$oid_value{$oid.".3".$mac_oid}=$status;
			$oid_type{$oid.".3".$mac_oid}=ASN_INTEGER;
			if(defined($prv_mac_oid)){
				$oid_next{$oid.".3".$prv_mac_oid}=
					$oid.".3".$mac_oid;
			}
	
			$prv_mac_oid=$mac_oid;
		}
	}
	$oid_next{$oid.".1".$prv_mac_oid}=$oid.".2".$first_mac_oid;
	$oid_next{$oid.".2".$prv_mac_oid}=$oid.".3".$first_mac_oid;	
	$oid_next{$oid.".3".$prv_mac_oid}=0;

	undef $first_baseport;
	undef $prv_baseport;
	undef $first_mac_oid;
	undef $prv_mac_oid;
	return 0;
}

sub mac2oid{
	my ($mac)=@_;
	my @octets=split(/:/,$mac);

	$mac="";
	foreach my $octet (@octets){
		$mac=$mac.".".hex($octet);
	}
	return $mac;
}

sub mac2hex{
        my ($mac)=@_;
	my @decimals;
	my @octets=split(/:/,$mac);
	
	foreach my $octet (@octets){
		push @decimals, hex($octet);
	}
	return pack("cccccc", @decimals);
}
