(Updated) Perl script that collects IP, MAC, interface and VLAN information from a Cisco switch using "plink" command-line SSH calls and then exports to a CSV file.

Got this from a buddy:

This Perl script collects IP, MAC, interface and VLAN information from a Cisco switch using "plink" command-line SSH calls.

Download OUI list from IEEE http://standards-oui.ieee.org/bopid/opid.csv which when put in the same folder as the script, translates the first half of the MAC address to the vendor name.

The output will be in CSV format, with columns showing the interface, MAC address, vendor, IP address (or "Trunk"), and VLAN(s).

#
# ports.pl - list IP addresses and VLANs associated with switch ports
# Usage: ports.pl [username] [password] <switch-ip-address>
# You will be prompted for username and password if not included
# on the command line
#

use strict;
use Term::ReadKey;
my %oui;  # IEEE list of Organizational Unit Identifiers
my $vendorCode;  # First half of MAC address
my $vendor;  # Vendor name from IEEE table
my $username;  # Username to login to host
my $yymmdd;  # Timestamp for output files
my $pw;  # Login password (entered manually at script startup)
my $rec;  # Inpute record from file
my $ipAddress;  # IP address of host
my $macAddress;
my $vlan;
my $interface;
my $found = 0;
my %ipAddresses;  # Hash of IP addresses indexed by MAC address
my %interfaces;  # Hash of interfaces indexed my MAC address
my %macAddresses;  # Hash of MAC addresses indexed by interface
my %vlans;  # Hash of VLANs indexed by interface
my %descs;  # Has of descriptions indexed by interface
my $desc;

($#ARGV == 0) || ($#ARGV == 2) || die("\nUsage: $0 [username] [password] <switch-ip-address>\n\nExamples:\n\n $0 192.168.1.1\n $0 admin Pa55W0rd 192.168.1.1\n\n");
&ReadOuiFile;  # Read in OUI file and build hash
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
$year -= 100;
$mon++;
($mon < 10) && ($mon = "0$mon");
($mday < 10) && ($mday = "0$mday");
($hour < 10) && ($hour = "0$hour");
($min < 10) && ($min = "0$min");
($sec < 10) && ($sec = "0$sec");
$yymmdd = "$year$mon$mday";

if ($#ARGV < 2) {
  $ipAddress = $ARGV[0];
  print "\n\nEnter username: ";
  $username = <STDIN>;
  chomp $username;
  print "\n\nEnter password: ";
  ReadMode('noecho');
  $pw = <STDIN>;
  ReadMode(0);
  print "\n";
  chomp $pw;
}

else {
  $username = $ARGV[0];
  $pw = $ARGV[1];
  $ipAddress = $ARGV[2];
}

system("plink -ssh -pw $pw $username\@$ipAddress show arp > arp-$yymmdd.txt");
system("plink -ssh -pw $pw $username\@$ipAddress show mac-address-table > mac-$yymmdd.txt");
system("plink -ssh -pw $pw $username\@$ipAddress show interface trunk > trunk-$yymmdd.txt");
system("plink -ssh -pw $pw $username\@$ipAddress show interface desc > desc-$yymmdd.txt");

open(INFILE, "desc-$yymmdd.txt") || die("$0: Cannot open desc-$yymmdd.txt");
while ($rec = <INFILE>) {
  chomp $rec;
  if (substr($rec, 0, 12) =~ /\//) {  # Includes interface?
    $interface = substr($rec, 0, 12);
    $interface =~ s/\s+//g;
    $desc = substr($rec, 55);
    $vlans{$interface} = "";
    $descs{$interface} = $desc;
  }  # end if (includes MAC address)
}
close INFILE;
system("del desc-$yymmdd.txt");

open(INFILE, "trunk-$yymmdd.txt") || die("$0: Cannot open trunk-$yymmdd.txt");
while ($rec = <INFILE>) {
  ($rec =~ /allow/) && ($found = 1);
  if (($found) && (substr($rec, 0, 12) =~ /\//)) {  # Includes interface?
    chomp $rec;
    $interface = substr($rec, 0, 10);
    $interface =~ s/\s+$//;
    $vlan = substr($rec, 12);
    $vlan =~ s/\,/ /g;
    $ipAddresses{$interface} = "Trunk";
    ($vlans{$interface} eq "") && ($vlans{$interface} = $vlan);
  }  # end if (includes MAC address)
}
close INFILE;
system("del trunk-$yymmdd.txt");

open(INFILE, "arp-$yymmdd.txt") || die("$0: Cannot open arp-$yymmdd.txt");
while ($rec = <INFILE>) {
  if (substr($rec, 42, 1) eq "\.") {  # Includes MAC address?
    chomp $rec;
    $ipAddress = substr($rec, 10, 15);
    $ipAddress =~ s/\s+$//;
    $macAddress = substr($rec, 38, 14);
    $vlan = substr($rec, 61, 8);
    $vlan =~ s/\s+$//;
    $vlan =~ s/Vlan//;
    $ipAddresses{$macAddress} = $ipAddress;
  }  # end if (includes MAC address)
}
close INFILE;
system("del arp-$yymmdd.txt");

if (&ReadMacFile() == 0) {
  system("plink -ssh -pw $pw $username\@$ipAddress show mac address-table > mac-$yymmdd.txt");  # Re-send using older command syntax
  &ReadMacFile();
}
system("del mac-$yymmdd.txt");


print "\n\nInterface,MAC Address,Vendor,IP Address,VLAN,Description\n";
foreach $interface (sort byint (keys %vlans)) {
  $macAddress = $macAddresses{$interface};
  $vendorCode = substr($macAddress, 0, 7);
  $vendor = $oui{$vendorCode};
  ($ipAddresses{$interface}) && ($ipAddress = $ipAddresses{$interface}) ||
  ($ipAddress = $ipAddresses{$macAddress});
  $vlan = $vlans{$interface};
  $desc = $descs{$interface};
  ($vlan > 0) && ($vlan < 4096) && print "$interface,$macAddress,$vendor,$ipAddress,$vlan,$desc\n";
}

# end of main program, subroutines follow
#
# ReadMacFile reads the "show-mac-address" output file and returns 1 if
# it contains valid data, 0 if not
#
sub ReadMacFile {
  my $found = 0;
  open(INFILE, "mac-$yymmdd.txt") || die("$0: Cannot open mac-$yymmdd.txt");
  while ($rec = <INFILE>) {
    if (substr($rec, 12, 1) eq "\.") {  # Includes MAC address?
      $found = 1;
      chomp $rec;
      $macAddress = substr($rec, 8, 14);
      $vlan = substr($rec, 0, 4);
      $vlan =~ s/^\s+//;
      $interface = substr($rec, 38);
      $interfaces{$macAddress} = $interface;
      ($vlans{$interface}) || ($vlans{$interface} = $vlan);
      $macAddresses{$interface} = $macAddress;
    }  # end if (includes MAC address)
  }
  close INFILE;
  return $found;
}  # end sub ReadMacFile
#
# Subroutine to sort by interface number
#
sub byint {
  my $numa;
  my $numb;
  my $stra;
  my $strb;
  my @fields;
  my $field;
  my $index;
  ($numa = $a) =~ s/[A-Z][a-z]//g;
  ($numb = $b) =~ s/[A-Z][a-z]//g;
  @fields = split(/\//, $numa);
  foreach $index (0 .. $#fields) {
    $field = $fields[$index];
    (length($field) < 2) && ($field = "0$field");
    (length($field) < 3) && ($field = "0$field");
    $stra .= $field;
  }
  $numa = @fields;
  @fields = split(/\//, $numb);
  $strb = "";
  foreach $index (0 .. $#fields) {
    $field = $fields[$index];
    (length($field) < 2) && ($field = "0$field");
    (length($field) < 3) && ($field = "0$field");
    $strb .= $field;
  }
  return ($stra <=> $strb);
}  # end sub byint
   
#
# Subroutine to read in Organizational Unit Identifier (OUI) entries
# from IEE file
#
sub ReadOuiFile {
  my @fields;
  my $oui;
  my $firstQuad;
  my $secondQuad;
  my $vendor;
  my $vendorCode;
 
  open(INFILE, "oui.txt") || return 0;
 
  while ($rec = <INFILE>) {
    if ($rec =~ /^  ..-..-../) {  # OUI record?
      chomp $rec;
      @fields = split(/\s+/, $rec);
      $oui = $fields[1];
      $oui =~ s/-//g;  # Remove hyphens
      $firstQuad = substr($oui, 0, 4);
      $secondQuad = substr($oui, 4, 2);
      $vendorCode = $firstQuad . "." . $secondQuad;
      $vendor = $fields[3];
      $vendorCode =~ tr/A-Z/a-z/;  # Lower case
      $oui{$vendorCode} = $vendor;
    }  # end if (OUI record)
  }  # end while
  close INFILE;
  return 1;
}  # end sub ReadOuiFile

Comments