#!/usr/bin/perl 
# ============================================================================
# $Id$
# Derek Gassen 
# Script to monitor flow specification ( flow-spec ) route filters 
# via an snmp poll.
# parses the __dynamic_default_inet__ juniper filter using 
# the appropriate OIDs and totals up all active peer and transit
# DoS mitigations. Doesn't use Net::SNMP's non-block'g.
#
# Taken out of dtown's cpan table.pl example. Diff with 
# http://search.cpan.org/src/DTOWN/Net-SNMP-5.2.0/examples/table.pl
# to see changes from the original.

# Copyright (c) 2000-2002 David M. Town 
# All rights reserved.

# This program is free software; you may redistribute it and/or modify it
# under the same terms as Perl itself.
# ============================================================================

use strict;
use Data::Dumper;
use Net::SNMP;
use POSIX qw(strftime);

my ($fwPacketOid,$fwPacketCount,%rtrPacketCount,%totalCounts,$curRtr,$now);

$now=strftime "%y%m%e%H%M", localtime;
open (PEERTRAN, "< /pathname/juniper.peer")||die "router list is missing!: ", $!;
while (){
    chomp;
    $curRtr = lc($_);
    ($fwPacketCount) = getRtrCount($curRtr);
    foreach (keys %{$fwPacketCount}){
	$rtrPacketCount{$curRtr."|".$_."|".$now}+=$fwPacketCount->{$_};
	$totalCounts{$_."|".$now}+=$fwPacketCount->{$_};
    }
}
print "Total flowspec DDoS mitigation:\n";
foreach (keys %totalCounts){
    printf("%s:\t%10d\n", $_, $totalCounts{$_});
}
# dump some hashes for reference
open (BYRTR, ">> /pathname/FSCounts.o")||die "Can't write to FSCounts.o transaction log: ",$!;    
print BYRTR Dumper(\%totalCounts);
print BYRTR Dumper(\%rtrPacketCount);
close (BYRTR);

sub getRtrCount {
    my $router = $_[0];
    print "working on: $router\n";
    # Create the SNMP session 
    my ($session, $error) = Net::SNMP->session(
					       -hostname  => $router,
					       -community => 'public',
					       -port      => 161,
					       -maxmsgsize=> 16384,
					       -version   => 'snmpv2c'
					       );
    # Was the session created?
    if (!defined($session)) {
	printf("ERROR: %s.\n", $error);
	exit 1;
    }
    
    # show firewall family filter __dynamic_default_inet__
    
    my $ifTable = '1.3.6.1.4.1.2636.3.5.2.1.7.24.95.95.100.121.110.97.109.105.99.95.100.101.102.97.117.108.116.95.105.110.101.116.95.95';
    
    my ($result,$fwInvert,$fwPacket,%fwPacketOid,%fwFSName,%fsCount);
    
    if (defined($result = $session->get_table(-baseoid => $ifTable))) {
	%{$fwInvert}=reverse%{$result}; # invert hash to name->oid
	foreach (keys(%{$fwInvert})) {
	    # stick your flowspec routes to ignore filter list here.
	    next if m@^1\.2\.3.4,2\.3\.4\.5,proto=17@; # some weird route
	    next if m@proto=1,=6@; # don't care about this one
	    next if m@^[^.]+(?:\.[^.]+){3},\*,proto=6,dstport=80@; # more weirdness
	    $fwPacketOid{$_}=$fwInvert->{$_};
	    $fwPacketOid{$_}=~s@(2636\.3\.5\.2\.1)\.7@$1.4@;
	}
    } else {
	printf("ERROR: %s.\n\n", $session->error());
    }

    undef $session;
    # Create the SNMP session 
    my ($session, $error) = Net::SNMP->session(
					       -hostname  => $ARGV[0] || $router,
					       -community => $ARGV[1] || 'public',
					       -port      => $ARGV[2] || 161,
					       -maxmsgsize=> 16384,
					       -version   => 'snmpv2c'
					       );
    # Was the session created?
    if (!defined($session)) {
	printf("ERROR: %s.\n", $error);
	exit 1;
    }
    $ifTable=~s@(2636\.3\.5\.2\.1)\.7@$1.4@;
    if (!defined($result = $session->get_table(-baseoid => $ifTable))) {print "Error can't get packet counts!\n"};
    foreach (keys(%fwPacketOid)){
	printf("%s => %s\n", $_, $result->{$fwPacketOid{$_}});
	$fsCount{$_}=$result->{$fwPacketOid{$_}};
    }
    $session->close;
    return \%fsCount;
}