#**************************************************************************************** #Date: 01/12/2008 * #Program: SearchSNMPmac v2.0.1 Beta * #Author: Ariel M. Liguori. * #Descr.: Find a target Mac Addr in a list of L2 devices using SNMP Queries. * #**************************************************************************************** use Getopt::Std; use Net::MacMap; use Net::SNMP v5.1.0 qw(:snmp DEBUG_ALL); use vars qw($SCRIPT $VERSION %OPTS $ip $s $if $iface); require "getopts.pl"; # To avoid Undefined subroutine &main::Getopts Error. $SCRIPT = 'SSMAC'; $VERSION = '2.0.2 Beta'; # Validate the command line options if (!getopts('a:A:c:D:E:h:m:n:p:r:t:u:v:x:X:M:F:T:', \%OPTS)) { _usage(); } # This order of comparisons is not random, it's reflect the best sequence to detect errors. if ( $#ARGV != '-1'){ _usage(); } if ( exists($OPTS{h})){ _usage(); } if ( !exists($OPTS{M}) || !exists($OPTS{c}) || !exists($OPTS{v}) || !exists($OPTS{T}) ){ _usage(); } if ( $OPTS{v} eq '3') { _usage() unless ( exists($OPTS{u}) ); # Youu _MUST_ define the username in a SNMP v3 request. } # convert the mac address to the standar form defined by the RFC my $mac_conv = convert_mac_to_hex(\$OPTS{M}); open(DB_FILE, "<", "$OPTS{T}"); foreach $ip (){ # Create the SNMP session ($s, my $e) = Net::SNMP->session( -hostname => $ip, exists($OPTS{a}) ? (-authprotocol => $OPTS{a}) : (), exists($OPTS{A}) ? (-authpassword => $OPTS{A}) : (), exists($OPTS{c}) ? (-community => $OPTS{c}) : (), exists($OPTS{D}) ? (-domain => $OPTS{D}) : (), exists($OPTS{d}) ? (-debug => DEBUG_ALL) : (), exists($OPTS{m}) ? (-maxmsgsize => $OPTS{m}) : (), exists($OPTS{p}) ? (-port => $OPTS{p}) : (), exists($OPTS{r}) ? (-retries => $OPTS{r}) : (), exists($OPTS{t}) ? (-timeout => $OPTS{t}) : (), exists($OPTS{u}) ? (-username => $OPTS{u}) : (), exists($OPTS{v}) ? (-version => $OPTS{v}) : (), exists($OPTS{x}) ? (-privprotocol => $OPTS{x}) : (), exists($OPTS{X}) ? (-privpassword => $OPTS{X}) : () ); # Was the session created? if (!defined($s)) { _exit($e); } $iface = get_iface($mac_conv); $numOfMacs = get_macsOnIface(); if ( get_iface_info() ){ printData(\$iface); last; } $s->close(); }; close DB_FILE; exit 0; #**************************************************************************************** # End of the main * #**************************************************************************************** # Conver the MAC Addr. to the format established in the RFC. sub convert_mac_to_hex{ my $mac_new; my $i; my @test; chomp $OPTS{M}; split(/-/,$OPTS{M}); foreach(@_){ @test[$i]= hex($_[$i]); $i++; }; $mac_new = join('.',@test); return $mac_new; } # Get the iface index. sub get_iface{ my $mac_conv = $_[0]; my $nameifuno, $descarto, $linea; $linea = get("1.3.6.1.2.1.17.4.3.1.2.$mac_conv"); ($descarto, $nameifuno)= split /\INTEGER: /,$linea; chomp $nameifuno; return $nameifuno; } # Determine how many mac are learned in an iface, if this is more tha one this is a trunk port. sub get_macsOnIface{ my @MIB_buffer; my $counter, $c, $compMib_line; @MIB_buffer = walk("1.3.6.1.2.1.17.4.3.1.2"); $MIB_buffer = join('.',@MIB_buffer); @MIB_buffer = split /\n/,$MIB_buffer; $compMib_line = join(' ','INTEGER:',$iface); $counter = 0; $c=0; foreach(@MIB_buffer){ if( @MIB_buffer[$c]=~/$compMib_line/ ){ $counter++; } if ($counter > 1){ last; } $c++; }; return $counter; } # Extract the correct iface name, not the index, something like "FastEtherne 0/7" is most cute. sub get_iface_info{ my $useless; my @MIB_buffer; my $status = 0; if($numOfMacs eq '1'){ $MIB_buffer = get("1.3.6.1.2.1.17.1.4.1.2.$iface"); ($useless, $nameif)= split /INTEGER: /,$MIB_buffer; $MIB_buffer= get("1.3.6.1.2.1.31.1.1.1.1.$nameif"); ($useless, $if)= split /STRING: /,$MIB_buffer; $status = 1; } chomp $if; return $status; } #Exits. sub _exit{ if (defined($s)) { $s->close(); } printf join('', sprintf("%s: ", $SCRIPT), shift(@_), ".\n"), @_; exit 1; } #Print the usage data oof the app. sub _usage { print << "USAGE"; $SCRIPT v$VERSION Usage: $SCRIPT [options] [oid] Options: -v 1|2c|3 SNMP version -M Mac Address to find in format XX-XX-XX-XX-XX-XX SNMPv1/SNMPv2c: -c Community name SNMPv3: -u Username (required) -E Context Engine ID -n Context Name -a Authentication protocol -A Authentication password -x Privacy protocol -X Privacy password Transport Layer: -D Domain -m Maximum message size -p Destination port -r Number of retries -t Timeout period Database: -s * IP Address of the SQL DB (in further versions) -P * Port of the SQL DB (in further versions) -U * Username of the SQL DB (in further versions) -C * Password of the SQL DB (in further versions) -T TXT file used as DB in plain-text Output: -F TXT file to print the output (By default prints on stdout) USAGE exit 1; } # Print the info obtained by the script. sub printData{ my $vendor = Net::MacMap::vendor($OPTS{M}); if ( !exists($OPTS{F}) ){ print "\nThe Mac Address $OPTS{M} is in the port $if of the switch $ip and correspond to a $vendor device."; }else{ open(FILE_H,">","$OPTS{F}"); print FILE_H "\nThe Mac Address $OPTS{M} is in the port $if of the switch $ip and correspond to a $vendor device."; close FILE_H; } } # This is the code for a SNMP walk request. sub walk{ my $myoid =$_[0]; my @args = ( exists($OPTS{E}) ? (-contextengineid => $OPTS{E}) : (), exists($OPTS{n}) ? (-contextname => $OPTS{n}) : (), -varbindlist => [$myoid] ); my @buffer; my $c = 0, $temp_1, $temp_2; if ($s->version eq '1'){ my $oid; while (defined($s->get_next_request(@args))) { $oid = ($s->var_bind_names())[0]; if (!oid_base_match($myoid, $oid)) { last; } $temp_1 = snmp_type_ntop($s->var_bind_types()->{$oid}); $temp_2 = $s->var_bind_list()->{$oid}; $buffer[$c] = "$oid = $temp_1: $temp_2\n"; $c++; @args = (-varbindlist => [$oid]); } } else { push(@args, -maxrepetitions => 25); outer: while (defined($s->get_bulk_request(@args))) { my @oids = oid_lex_sort(keys(%{$s->var_bind_list()})); foreach (@oids) { if (!oid_base_match($myoid, $_)) { last outer; } $temp_1 = snmp_type_ntop($s->var_bind_types()->{$_}); $temp_2 = $s->var_bind_list()->{$_}; $buffer[$c] = "$_ = $temp_1: $temp_2\n"; $c++; # Make sure we have not hit the end of the MIB if ($s->var_bind_list()->{$_} eq 'endOfMibView') { last outer; } } # Get the last OBJECT IDENTIFIER in the returned list @args = (-maxrepetitions => 25, -varbindlist => [pop(@oids)]); } } # Let the user know about any errors if ($s->error() ne '') { _exit($s->error()); } return @buffer; } # This is the code for a SNMP get request. sub get{ my $myoid =$_[0]; my @args = ( exists($OPTS{E}) ? (-contextengineid => $OPTS{E}) : (), exists($OPTS{n}) ? (-contextname => $OPTS{n}) : (), -varbindlist => [$myoid], ); # Send the SNMP message if (!defined($s->get_request(@args))) { _exit($s->error()); } # Print the results foreach ($s->var_bind_names()) { $temp_1 = snmp_type_ntop($s->var_bind_types()->{$_}); $temp_2 = $s->var_bind_list()->{$_}; $buffer = "$_ = $temp_1: $temp_2\n"; } return $buffer; }