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.

Showing posts with label perl. Show all posts
Showing posts with label perl. Show all posts

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!

Wednesday, January 9, 2008

ERS 8600 (ipNetToMediaIfIndex)

There was a recent comment about a Usenet positing I made back in 2002 in comp.protocols.snmp.

In the post I was responding to someone looking for information on how to decode the value returned from the ipNetToMediaIfIndex when querying an ERS 8600 switch. Thankfully Shane (Nortel) was able to help me come up with the forumla.

card = ( $value AND 62914560 ) / 4194304
port = (( $value AND 4128768) / 65536 ) + 1
With that formula you could now walk the ipNetToMediaTable and retreieve the entire ARP table providing you the card and port number, MAC address, and IP address for each entry in the table.

The next issue was how to deal with MultiLink Trunk interfaces. In this case (and with my current software code) I build a table of all the MLT interfaces prior to polling the ipNetToMediaTable. I still use Perl but it shouldn't be very hard to convert to PHP.
# rcMltNumMlts
$nummlts = $sess->get("rcMltNumMlts.0");

for ($i = 1; $i <= $nummlts; $i++) { # rcMltName $mltname[$i] = $sess->get("rcMltName.$i");
# rcMltId
$mltindex[$i] = $sess->get("rcMltId.$i");
# rcMltIfIndex
$mltifindex[$i] = $sess->get("rcMltIfIndex.$i");
print "DEBUG: MltId = $i and MltName = $mltname[$i] and MltIndex = $mltindex[$i] and MltIfIndex = $mltifindex[$i]
\n" if ($DEBUG);
};
Now that we have the rcMltTable in an array we can walk the ipNetToMediaTable and match up any entries. Here's the code I use (again it's Perl but you should be able to convert to PHP);
# Evaulate with bitwise operation
$card = (($vals[0] & 62914560) / 4194304);
$port = (($vals[0] & 4128768) / 65536) + 1;

# Evaulate to determine if port is a MLT
if ($card != 0) {
$intf = (((64 * $card) + $port) - 1);
print "DEBUG: $vals[1] address found on card $card port $port\n";
} else {
$mlt = 1;
print "DEBUG: $vals[1] address found on MLT $mltname[$port]\n";
} # end else
Hopefully that doesn't look too complicated. The important piece here is that you need to merge the rcMltTable with the ipNetToMediaTable to get your results. If you name the MLT with something meaningful you can then return that string to the application that is making the query.

I wrote a Perl application that would search the ARP table of an Ethernet Routing Switch 8600 dynamically for a specific IP address entry. Here's an example of the output;
Nortel Passport 8600 Gigabit Switch IP ARP Table Search

Initializing query for sw-ccr-8600.datacenter.acme.org for IP address 1.1.1.10...

sysDescr = ERS-8610 (4.1.3.0)
sysObjectID = .1.3.6.1.4.1.2272.30
sysUpTime = 169 Days 6 Hours 43 mins 11 secs
sysContact = Acme Network Infrastructure Team
sysName = sw-ccr-8600.datacenter.acme.org
sysLocation = USA

Please be patient it may take a while to complete the search...

DEVICE FOUND

1.1.1.10 (000AE4753FC9) address found on MLT SMLT-5500

We searched through 1183 forwarding records...

That's all folks!
I will look to publish the complete code on my website sometime in the near future.

Cheers!

Saturday, October 27, 2007

Perl Scripting

I really like using Perl because of the Net-SNMP Perl libraries that make it really easy to write code to interact with devices that support SNMP.

Hopefully everyone out there is backing up their network switch configurations in the unlikely event that if their hardware dies they only need to worry about replacing the hardware and not about re-configuring the entire switch.

Quite a few years back I wrote a Perl script that would send the proper SNMP commands to instruct a network switch to copy it's configuration to a TFTP server. This script essentially became known as "switchtftpbackup.pl" It's nothing fancy or pretty but it gets the job done.

I've posted this Perl script on my webiste under the Perl section.

I run this script from Cron one of our CentOS Linux servers at work every week. The same server also acts as a central TFTP server for the entire organization. I also run other scripts that then archive the weekly backups, in the event that I need to go to a backup that's more than a week old.

I believe both Nortel's Optivity NMS and Cisco's Cisco Works both have options to backup switch configurations these days.

What are you using?

Cheers!