We've moved from Blogger to WordPress!

You should be automatically redirected in 5 seconds. If not, visit
http://blog.michaelfmcnamara.com
and update your bookmarks.

Monday, May 5, 2008

Perl Script to poll ARP Table

I've written a lot of Perl scripts to help make managing the network easier and more efficient. One of the scripts I've written allows me to dump the IP ARP table of the Nortel Ethernet Routing Switch 8600 to a file for later/additional processing. While the script was original written for the ERS 8600 switch it will also work on just about any router (Layer 3 device) that supports the RFC1213 (ipNetToMediaNetAddress).

The script has been tested and works on Nortel's BayRS routers (ARN, ASN, BLN, BCN). You just obviously need to be careful of how the script interprets the ipNetToMediaIfIndex value depending on the device you are polling.

The script get8600arp.pl is a very straight forward script. It simply polls various SNMP OIDs and then stores the results in a file. It does this for every switch (FQDN/IP Address) that is listed in the input file.

#!/usr/bin/perl
#
# Filename: /root/get8600arp.pl
#
# Purpose: Query Nortel Ethernet Routing Switch 8600 for the IP ARP
# table via SNMP. This script will poll a list of devices
# (input file) and dump the contents of the IP ARP table to
# and outputfile.
#
# Author: Michael McNamara
#
# Date: December 5, 2002
#
# Support Switches:
# - Nortel ERS 8600
# - Nortel ERS 1600
# - Nortel ERS 5500
# - Nortel BayRS Routers
#
# Requirements:
# - Net-SNMP
# - Net-SNMP Perl Module
# - SNMP-MIBS
#
# Changes:
#
# - May 5, 2007 (M.McNamara)
# clean up code and documentation for release to public
# - Oct 10, 2006 (M.McNamara)
# went back to SNMP v1 to support BayRS legacy routers
# - Sep 04, 2003 (M.McNamara)
# migrated from vendor specific MIB to RFC1213 (ipNetToMediaNetAddress)
#

# Load Modules
use strict;
use SNMP;
use Net::Ping;

# Declare constants
#use constant DEBUG => 0; # DEBUG settings
use constant RETRIES => 3; # SNMP retries
use constant TIMEOUT => 1000000; # SNMP timeout, in microseconds
use constant SNMPVER => 1; # SNMP version

# SNMP Settings
$SNMP::verbose = 0;
$SNMP::use_enums = 1;
$SNMP::use_sprint_value = 0;
&SNMP::initMib();
&SNMP::loadModules('RAPID-CITY');

# Declaration Variables
my ($sess, @vals);
my @devices;
my ($card, $port);
my $snmphost;
my $comm = "public"; # SNMP ReadOnly Community String
my %array;
my $switchfile;
my $datafile;

our $DEBUG; # DEBUG flag

undef @devices;

# Program and help information
my $program = "get8600arp.pl";
my $version = "v1.3";
my $author = "Michael McNamara";
my $purpose = "This Perl script is retreieve the IP ARP table from the ERS8600 Layer 3 switch/router and store it in file for later use.";
my $usage = "Usage: $program \[input\] \[output\] \[-help\] \[debug\]\n <input> = filename listing each switch to poll\n <output> = filename where to store output\n";

if (($#ARGV +1) <= 2) {
print "Program: $program \nVersion: $version \nWritten by: $author \n$purpose\n\n$usage\n";
print "DEBUG: ARGV = $#ARGV\n";
print "DEBUG: ARGV = $ARGV[0] $ARGV[1] $ARGV[2] $ARGV[3]\n";
exit;
}

my $arg1 = shift @ARGV;
my $arg2 = shift @ARGV;
my $arg3 = shift @ARGV;

if ($arg1 =~ /help/) {
print "Program: $program \nVersion: $version \nWritten by: $author \n$purpose\n\n$usage\n";
print "DEBUG: ARGV = @ARGV\n";
print "DEBUG: ARGV = $ARGV[0] $ARGV[1] $ARGV[2] $ARGV[3]\n";
exit;
}

$switchfile = $arg1;
$datafile = $arg2;
$DEBUG = $arg3;

# Test to see if inputifle exists
if (!-e $switchfile) {
die "ERROR: Unable to locate and/or open inputfile $switchfile...";
}

############################################################################
##### B E G I N M A I N ##################################################
############################################################################

&load_switches;

&collect_arp;

exit 0;

############################################################################
#### E N D M A I N #######################################################
############################################################################

############################################################################
# Subroutine collect_arp
#
# Purpose: collect ARP information from layer 3 switches/routers
############################################################################
sub collect_arp {

# Open output datafile for appending
open(DATAFILE, ">>$datafile");

# Loop over each Passport 8600 switch
foreach $snmphost (@devices) {

my $packet = Net::Ping->new('icmp');

$snmphost =~ s/\n//g; # remove CRLF

if ($packet->ping($snmphost)) {


$sess = new SNMP::Session ( DestHost => $snmphost,
Community => $comm,
Retry => RETRIES,
Timeout => TIMEOUT,
Version => SNMPVER );

my $vars = new SNMP::VarList(
['ipNetToMediaIfIndex', 0],
['ipNetToMediaPhysAddress', 0],
['ipNetToMediaNetAddress', 0],
['ipNetToMediaType', 0] );

while (1) {

@vals = $sess->getnext($vars); # retreive SNMP information

last unless ($vars->[0]->tag eq 'ipNetToMediaIfIndex');

$vals[1] = unpack('H12', $vals[1]);
$vals[1] =~ tr/a-z/A-Z/;

$card = (($vals[0] & 62914560) / 4194304);
$port = (($vals[0] & 4128768) / 65536) + 1;

print "$snmphost, $vals[0], ($card/$port), $vals[1], $vals[2], $vals[3]\n" if ($DEBUG);
print DATAFILE "$snmphost, $vals[0], $card, $port, $vals[1], $vals[2]\n";

$array{$snmphost}[$card][$port] = $vals[2];

} # end while

} else {

print ("ERROR: $snmphost not responding to ICMP ping skipping...\n");

} #end if $packet

} #end foreach

close(DATAFILE);

} #end sub collect_arp



############################################################################
# Subroutine load_switches
#
# Purpose: load list of switches
############################################################################
sub load_switches {

open(SWITCHLIST, "<$switchfile");

# Walk through data file
while (<SWITCHLIST>) {

# Skip blank lines
next if (/^\n$/);
# Skip comments
next if (/^#/);

#print "DEBUG: adding $_ to our list of devices \n" if ($DEBUG);

push (@devices, $_);

}

close(SWITCHLIST);

return 1;

} # end sub load_switches
############################################################################




The real magic that folks have always been searching for is the binary formula to turn the ipNetToMediaIfIndex into a location that denotes the card and port where that specific device is connected to.

  $card = (($vals[0] & 62914560) / 4194304);
$port = (($vals[0] & 4128768) / 65536) + 1;


While I still use flat files you could certainly adopt this code to dump the output into a database. I just haven't had the time although I've been playing with MySQL quite a bit lately.

Cheers!

No comments: