#!/usr/bin/perl -w # switchcheck.pl use strict; use Net::SNMP; my($error,$session,$seed_oid,$oid_root,$csvname,$community,$hostname,$seed_ip,$serial,$option,$logdir); my(@unique,@hostname); my($ifMIB) = "1.3.6.1.2.1.31.1.1.1"; my(%option)=( verbose => 0, csv => 0, log => 0, batch => 0, help => 0, hostname => 0, read => 0 ); die "\nusage: $0 option ip\n Use $0 -h for list of options\n" unless ( @ARGV >= 1 ); $logdir="/var/log/sc"; #Begin processing command line arguments #*************************************************************** while(@ARGV){ $option = $ARGV[0]; if($option eq "-v"){ $option{'verbose'} = 1; $option{'log'} = 0; $option{'read'} = 0; } elsif($option eq "-l"){ $option{'log'} = 1; $option{'verbose'} = 0; $option{'read'} = 0; } elsif($option eq "-r"){ $option{'read'} = 1; $option{'verbose'} = 0; $option{'log'} = 0; } elsif($option eq "-b"){ $option{'batch'} = 1; $community = $ARGV[1]; shift @ARGV; } elsif($option eq "-h"){ $option{'help'} = 1; die "\n\n usage: $0 options ip ... \n\n -h This help message -l Logging -v Verbose -c filename Send output to filename.csv -b community Batch, community is the snmp community name -r ip ... Display results from log files for specified IPs\n\n\n"; } elsif($option eq "-c"){ $option{'csv'} = 1; $option{'read'} = 0; $option{'log'} = 0; $csvname = ("$ARGV[1]".".csv"); shift @ARGV; } else{ die "usage: $0 ip\n" unless ($option =~ m{\d+\.\d+\.\d+\.\d+}); push @hostname , $option; $option{'hostname'} = 1; } shift @ARGV; } die "You need to give at least one IP address as an option\n" unless($option{'hostname'}); #End argument processing #************************************************************* #Prompt for SNMP community string unless($option{'batch'}){ print "community: "; chomp($community = ); } $oid_root = "1.3.6.1.2.1.2.2.1"; $seed_oid = ("$oid_root".".1"); unless($option{'csv'}){ foreach(@hostname){ undef @unique; $hostname=$_; #Open SNMP session ($session,$error) = Net::SNMP->session(Hostname => $hostname, Community => $community); die "$error\n" unless($session); get_oids($seed_oid); #Get the SNMP info for this target if($option{'verbose'}){ print "Port Speed Errors/Data In Errors/Data Out Uptime \n"; print "-----------------------------------------------------------------------------------------------------------------------------\n"; SNMP_Grocery_List(); } elsif($option{'log'}){ SNMP_Logging(); } elsif($option{'read'}){ Read_Log(); } else{ SNMP_Light(); } $session->close; } } if($option{'csv'}){ open(CSV,">$csvname")|| die "Can't open $csvname\n"; if($option{'verbose'}){ print CSV "Name,Speed,Errors_In,Data_in,Errors_out,Data_out,Uptime,Switch_IP\n"; foreach(@hostname){ undef @unique; $hostname=$_; #Open SNMP session ($session,$error) = Net::SNMP->session(Hostname => $hostname, Community => $community); die "$error\n" unless($session); get_oids($seed_oid); #Get the SNMP info for this target Make_CSV_Verbose(); } $session->close; } else{ print CSV "Switch_IP,Name,Total_Ports,Up,Down,Admin_Down\n"; foreach(@hostname){ undef @unique; $hostname=$_; #Open SNMP session ($session,$error) = Net::SNMP->session(Hostname => $hostname, Community => $community); die "$error\n" unless($session); get_oids($seed_oid); #Get the SNMP info for this target Make_CSV(); $session->close; } } close(CSV) || die "Can't close $csvname\n"; } #Subs below here #*************************************************************************** #This sub is what walks through and enumerates an oid tree #*************************************************************************** sub get_oids{ my($starting_oid , $new_oid , $unique_oid , $result , $crap); $starting_oid = $_[0]; $new_oid = $starting_oid ; while(Net::SNMP::oid_context_match($starting_oid,$new_oid)){ $result = $session->get_next_request(($new_oid)); return unless (defined $result); ($new_oid , $crap) = %$result; if (Net::SNMP::oid_context_match($starting_oid,$new_oid)){ $unique_oid = $new_oid; $unique_oid =~ s/$starting_oid//g; push @unique , $unique_oid ; get_oids($new_oid); } } #This is the sub for -v option #*************************************************************************** sub SNMP_Grocery_List{ my($unique , $oper , $admin , $name , $uptime , $speed , $datain , $dataout , $errorsin , $errorsout); my(%tally); %tally=( total => 0, up => 0, down => 0, admin => 0 ); foreach(@unique){ $unique = $_ ; $tally{'total'}++; $oper = (Get_SNMP_Info("$oid_root".".8"."$unique")); if($oper == 1){ $tally{'up'}++; $name = (Get_SNMP_Info("$ifMIB".".1"."$unique")); $uptime = (Get_SNMP_Info("$oid_root".".9"."$unique")); $speed = (Get_SNMP_Info("$ifMIB".".15"."$unique")); $datain = (Get_SNMP_Info("$oid_root".".10"."$unique")); $dataout = (Get_SNMP_Info("$oid_root".".16"."$unique")); $errorsin = (Get_SNMP_Info("$oid_root".".14"."$unique")); $errorsout = (Get_SNMP_Info("$oid_root".".20"."$unique")); $~="GROCERY"; write; } else { $tally{'down'}++; $admin = (Get_SNMP_Info("$oid_root".".7"."$unique")); if($admin == 2){ $tally{'admin'}++; } } } print "\nSummary: $hostname \n Total Ports: $tally{'total'} \n Ports Up: $tally{'up'} \n Ports Down: $tally{'down'} \n Admin Down $tally{'admin'} \n"; format GROCERY = @<<<<<<<<<< @<<<<<< @>>>>>>>>>>>>>>>>>/@<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>>>>>>/@<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $name,$speed,$errorsin,$datain,$errorsout,$dataout,$uptime . } # Sub for -l option #*************************************************************************** sub SNMP_Logging{ my($unique , $oper , $dbm); my(%log); $dbm=("$logdir"."sc"."$hostname"); dbmopen(%log, $dbm, 0644); foreach(@unique){ $unique = $_ ; $oper = (Get_SNMP_Info("$oid_root".".8"."$unique")); if($oper == 1){ $log{$unique}++; } } dbmclose(%log); } #Sub for -r option #*************************************************************************** sub Read_Log{ my($dbm,$name,$uptime,$unique,$counter); my(%log); $dbm=("$logdir"."sc"."$hostname"); dbmopen(%log, $dbm, 0644); print "\n\n Switch Check Log for $hostname\n"; print "---------------------------------------------------\n"; while (($unique, $counter) = each(%log)) { $name = (Get_SNMP_Info("$ifMIB".".1"."$unique")); $uptime = (Get_SNMP_Info("$oid_root".".9"."$unique")); $~="READ_LOG"; write; } dbmclose(%log); format READ_LOG = @<<<<<<<<<<<<<< @<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $name,$counter,$uptime . } #sub for default operation #*************************************************************************** sub SNMP_Light{ my($unique , $oper , $admin); my(%tally); %tally=( total => 0, up => 0, down => 0, admin => 0 ); foreach(@unique){ $unique = $_ ; $tally{'total'}++; $oper = (Get_SNMP_Info("$oid_root".".8"."$unique")); if($oper == 1){ $tally{'up'}++; } else { $tally{'down'}++; $admin = (Get_SNMP_Info("$oid_root".".7"."$unique")); if($admin == 2){ $tally{'admin'}++; } } } print "\nSummary: $hostname \n Total Ports: $tally{'total'} \n Ports Up: $tally{'up'} \n Ports Down: $tally{'down'} \n Admin Down $tally{'admin'} \n"; } #Sub that acually gets the value for a oid #*************************************************************************** sub Get_SNMP_Info{ #This sub gets the value of an oid my($crap , $value , $result); my($oid) = $_[0]; $result = $session->get_request("$oid"); return unless (defined $result); ($crap , $value) = %$result; return $value; } #sub for -c and -v #*************************************************************************** sub Make_CSV_Verbose{ my($unique , $oper , $admin , $name , $uptime , $speed , $datain , $dataout , $errorsin , $errorsout); foreach(@unique){ $unique = $_ ; $oper = (Get_SNMP_Info("$oid_root".".8"."$unique")); $name = (Get_SNMP_Info("$ifMIB".".1"."$unique")); $uptime = (Get_SNMP_Info("$oid_root".".9"."$unique")); $speed = (Get_SNMP_Info("$ifMIB".".15"."$unique")); $datain = (Get_SNMP_Info("$oid_root".".10"."$unique")); $dataout = (Get_SNMP_Info("$oid_root".".16"."$unique")); $errorsin = (Get_SNMP_Info("$oid_root".".14"."$unique")); $errorsout = (Get_SNMP_Info("$oid_root".".20"."$unique")); print CSV "$name,$speed,$errorsin,$datain,$errorsout,$dataout,$uptime,$hostname\n"; } } #sub for -c #*************************************************************************** sub Make_CSV{ my($unique, $sysname , $oper , $admin); my(%tally); %tally=( total => 0, up => 0, down => 0, admin => 0 ); $sysname = (Get_SNMP_Info("1.3.6.1.2.1.1.5.0")); foreach(@unique){ $unique = $_ ; $tally{'total'}++; $oper = (Get_SNMP_Info("$oid_root".".8"."$unique")); if($oper == 1){ $tally{'up'}++; } else { $tally{'down'}++; $admin = (Get_SNMP_Info("$oid_root".".7"."$unique")); if($admin == 2){ $tally{'admin'}++; } } } print CSV "$hostname,$sysname,$tally{'total'},$tally{'up'},$tally{'down'},$tally{'admin'}\n"; } #*************************************************************************** } =head1 Name switchcheck.pl =head1 Summary Usage: switchcheck.pl options ip { ip ip ... } Designed to get an overview or collect statisics on switchports usage using SNMP. -b option allows you to specify community name on the command line to allow being called in a cron job -l logs basic port usage stats to a dbm file -r is used to read files created by -l -v gives more detailed stats on ports that are up -c generates a csv file, can be used in conjunction with -v Some options are mutually exclusive. The later option takes precedence. =head1 Tested with: Perl 5.6.1 RedHat 7.1 against: Cisco 6509,2924XL,3508XL,3512XL,3524XL,3548XL switches =head1 Author fingers =head1 Credits Thanks to ybic for answering dumb perl questions and appreciating poorly written code =head1 Caveats When looking at the port counts it is important to keep in mind that vlans and other "interfaces" show up in this count. Look at the output from -v for each type of switch you are using to get an idea how many non-port interfaces each switch has. =head1 Todos Add in -i option to add a polling interval, to spread out the bandwidth impacts of running against remote equipment. Accept multiple RO strings for use in enviroments where switches may have different community names. Add in VLAN and MAC information to the -v reports. So far I haven't been able to identify the oids to use for these. Add in support to log to a real database instead of dbm. =head1 Updates 2001-07-19 Post to perlmonks =cut