#!/usr/bin/perl
######################################################################
# getMacAddress.pl
#
# see also:
# - https://www.cisco.com/c/en/us/support/docs/ip/simple-network-management-protocol-snmp/44800-mactoport44800.html
#
# version 2021-04-07
#
# example:
# getMacAddress.pl -ip=10.20.49.250 -type=hpe -community=mypub
# result:
# switch;10.20.49.250;vlan;VLAN27;mac;7c:5a:1c:11:3d:d8;ip;192.168.1.10
# switch;10.20.49.250;vlan;VLAN27;mac;7c:5a:1c:11:2f:3c;ip;192.168.1.11
# switch;10.20.49.250;vlan;VLAN27;mac;7c:5a:1c:11:3f:e0;ip;192.168.1.12
# switch;10.20.49.250;vlan;VLAN27;mac;7c:5a:1c:11:44:b8;ip;192.168.1.13
# switch;10.20.49.250;vlan;VLAN30;mac;94:40:c9:4a:31:1c;ip;192.168.1.14
# switch;10.20.49.250;vlan;VLAN30;mac;52:54:00:4e:cd:c4;ip;192.168.1.15
#
#
# (c) m.wendig
#
######################################################################
use Data::Dumper;
use strict;
use DBI;

my $num_args = $#ARGV;
if ($#ARGV == -1 ){
usage();
}
#my $ip="172.20.12.50";
my $ip='';
if ($ARGV[0]=~/-ip=(.*)$/){
$ip=$1;
}
print usage() if $ip eq '';

my $updatedb=0;
if (($ARGV[2]=~/-db/) || ($ARGV[3]=~/-db/)){
$updatedb=1;
}

my $type= "";
if ($ARGV[1]=~/-type=(.*)$/){
$type=$1;
}
if (($type eq 'hpe') || ($type eq 'cisco') || ($type eq 'watchguard')){
}else{usage();}

my $community = "public";
if ($ARGV[2]=~/-community=(.*)$/){
$community=$1;
}

my $dbname="mactable";
my $dbuser="root",
my $dbpwd="";
my $dbhost="localhost";

my $debug=0;
my $snmpwalk = '/usr/bin/snmpwalk';
my $line;
my @vlans=();

my $dbh;
if ($updatedb){
$dbh = DBI->connect("DBI:mysql:$dbname;host=$dbhost", "$dbuser", "$dbpwd") || die "Could not connect to database: $DBI::errstr";
}

#######################################
#1 retrieve vlan
#######################################
my $cmd ='';

if ($type eq "cisco"){
$cmd= "$snmpwalk -v 2c -c $community $ip .1.3.6.1.4.1.9.9.46.1.3.1.1.2";
open(IN, "$cmd |");
while(<IN>){
$line=$_;
chomp($line);
print "$line\n" if $debug;
#we expect someting like: SNMPv2-SMI::enterprises.9.9.46.1.3.1.1.2.1.41 = INTEGER: 1
if ($line =~ /(\d*)\s\=/){
my $vlan = $1;
print "vlan=$vlan.\n" if $debug;
#print "$line\n";
push @vlans,$vlan;
}
}
close(IN);
}

if ($type eq "hpe"){
#get all interface type 53 = vlan
$cmd= "$snmpwalk -v 2c -c $community $ip iso.3.6.1.2.1.2.2.1.3";
print "$cmd\n" if $debug;
open(IN, "$cmd |");
while(<IN>){
$line=$_;
chomp($line);
print "$line\n" if $debug;
#we expect something like: iso.3.6.1.2.1.2.2.1.3.2249 = INTEGER: 53
if ($line =~ /(\d*)\s\=\sINTEGER: 53/){
my $vlan = $1;
print "vlan=$vlan.\n" if $debug;
$cmd= "$snmpwalk -v 2c -c $community $ip iso.3.6.1.2.1.2.2.1.2.$vlan";
open(IN2, "$cmd |");
my $vlanname='';
while(<IN2>){
my $line2=$_;
chomp($line2);
#we expect something like: iso.3.6.1.2.1.2.2.1.2.2249 = STRING: "VLAN1000"
if ($line2 =~ /STRING:\s\"(.*)\"$/){
$vlanname=$1;
}
print ">>$line2: vlanname=$vlanname\n" if $debug;
}
close(IN2);


#print "$line\n";
push @vlans,[$vlan,$vlanname];
}
}
close(IN);
}


if ($type eq "watchguard"){
#get all interface type 6 = vlan
$cmd= "$snmpwalk -v 2c -c $community $ip iso.3.6.1.2.1.2.2.1.3";
print "$cmd\n" if $debug;
open(IN, "$cmd |");
while(<IN>){
$line=$_;
chomp($line);
print "$line\n" if $debug;
#we expect something like: iso.3.6.1.2.1.2.2.1.3.2249 = INTEGER: 6
if ($line =~ /(\d*)\s\=\sINTEGER: 6/){
my $vlan = $1;
print "vlan=$vlan.\n" if $debug;
$cmd= "$snmpwalk -v 2c -c $community $ip iso.3.6.1.2.1.2.2.1.2.$vlan";
open(IN2, "$cmd |");
my $vlanname='';
while(<IN2>){
my $line2=$_;
chomp($line2);
#we expect something like: iso.3.6.1.2.1.2.2.1.2.2249 = STRING: "VLAN1000"
if ($line2 =~ /STRING:\s\"(.*)\"$/){
$vlanname=$1;
}
print ">>$line2: vlanname=$vlanname\n" if $debug;
}
close(IN2);


#print "$line\n";
push @vlans,[$vlan,$vlanname];
}
}
close(IN);
}

#we should have a datastructure like the following now:
#$VAR46 = [
# '2249',
# 'VLAN1000'
# ];
#$VAR47 = [
# '3249',
# 'VLAN2000'
# ];
#print Dumper(@vlans);


####################################
#2 foreach vlan do something
####################################
if ($type eq "cisco"){
foreach my $vlanelem (@vlans){
my $vlan = @$vlanelem[0];
my $vlanname = @$vlanelem[1];
#print "check vlan $vlan.\n";
next if $vlan > 1000;
my $cmd = "$snmpwalk -v 2c -c $community\@$vlan $ip .1.3.6.1.2.1.17.4.3.1.1";
open(IN, "$cmd |");
while(<IN>){
$line=$_;
chomp($line);
print "$line\n" if $debug;
#we expect someting like: SNMPv2-SMI::mib-2.17.4.3.1.1.254.175.11.155.132.164 = Hex-STRING: FE AF 0B 9B 84 A4
if ($line =~ /\.(\d*\.\d*.\d*\.\d*) = Hex-STRING: (.*)$/){
my $macip = $1;
my $mac = $2;
$mac =~s/\s*$//g;
$mac =~s/\s/:/g;
$mac =lc($mac);
print "switch;$ip;vlan;$vlanname;mac;$mac;ip;$macip\n";
updateDatabase($ip,$vlanname,$mac,$macip)if $updatedb;
}
}
close(IN);
}
}

####################################
if (($type eq "hpe") || ($type eq "watchguard")) {
foreach my $vlanelem (@vlans){
my $vlan = @$vlanelem[0];
my $vlanname = @$vlanelem[1];
#print "check vlan $vlan.\n";
#next if $vlan > 1000;
my $cmd = "$snmpwalk -v 2c -c $community $ip .1.3.6.1.2.1.4.22.1.2.$vlan ";
open(IN, "$cmd |");
while(<IN>){
$line=$_;
chomp($line);
print "$line\n" if $debug;
#we expect someting like: SNMPv2-SMI::mib-2.17.4.3.1.1.254.175.11.155.132.164 = Hex-STRING: FE AF 0B 9B 84 A4
if ($line =~ /\.(\d*\.\d*.\d*\.\d*) = Hex-STRING: (.*)$/){
my $macip = $1;
my $mac = $2;
$mac =~s/\s*$//g;
$mac =~s/\s/:/g;
$mac =lc($mac);
print "switch;$ip;vlan;$vlanname;mac;$mac;ip;$macip\n";
updateDatabase($ip,$vlanname,$mac,$macip)if $updatedb;
}
}
close(IN);
}
}


if ($updatedb){
$dbh->disconnect();
}


########
# updateDatabase(switch,vlan,mac)
########
sub updateDatabase($$$){
my $switch=$_[0];
my $vlan=$_[1];
my $mac=$_[2];
my $ip=$_[3];
print "run db update for vlan $vlan and mac $mac and ip $ip.\n" if $debug;
#my $sth = $dbh->prepare('select id, count from macs where vlan like \''.$vlan.'\' and mac like \''.$mac.'\' and switch like \''.$switch.'\'');
my $sth = $dbh->prepare('select id, count from macs where vlan like \''.$vlan.'\' and mac like \''.$mac.'\' and ip like \''.$ip.'\'');
$sth->execute();
my $result =$sth->fetchrow_hashref();
my $rows = $sth->rows;
#print "Value returned: $result->{id}. rows: $rows.\n";

if ($rows > 0){
#update
my $count = $result->{count} + 1;
my $sqlstr = 'update macs set count='.$count.' where id='.$result->{id}.' ';
print "sqlstr=$sqlstr\n" if $debug;
$dbh->do($sqlstr);
}else{
#insert
$dbh->do('insert into macs (switch,vlan,mac,ip,count,firstseen) values (\''.$switch.'\',\''.$vlan.'\',\''.$mac.'\',\''.$ip.'\',1,now() )');
}



}

sub usage(){
print "usage:\n";
print "\n";
print "getMacAddress -ip=<IP-Address> -type=<hpe|cisco|watchguard> -community=<SNMP-community> <-db>\n";
print "\n";
print " -ip: IP Address of switch to query\n";
print " -type: supported type = hpe or cisco or watchguard\n";
print " -community: SNMP community if unspecified default is public\n";
print " -db: if specified update database\n";
print "\n";
exit(1);
}


#########################################
##### needed database schema
#########################################

=sqlschema
CREATE TABLE IF NOT EXISTS `macs` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`switch` char(50) NOT NULL,
`vlan` char(50) NOT NULL,
`mac` char(50) NOT NULL,
`ip` char(50) NOT NULL,
`count` int(11) NOT NULL DEFAULT '0',
`firstseen` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`lastseen` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

=END

computer2know :: thank you for your visit :: have a nice day :: © 2021