diff --git a/lib/SNMP/Info.pm b/lib/SNMP/Info.pm index 7303ba3b..b188d71f 100644 --- a/lib/SNMP/Info.pm +++ b/lib/SNMP/Info.pm @@ -634,7 +634,7 @@ See documentation in L for details. =item SNMP::Info::Layer2::Ubiquiti -SNMP Interface to Ubiquiti Access Points +SNMP Interface to Ubiquiti Access Points and other devices See documentation in L for details. @@ -1550,6 +1550,7 @@ sub device_type { 30065 => 'SNMP::Info::Layer3::Arista', 35098 => 'SNMP::Info::Layer3::Pica8', 41112 => 'SNMP::Info::Layer2::Ubiquiti', + 4413 => 'SNMP::Info::Layer2::Ubiquiti', ); my %l2sysoidmap = ( diff --git a/lib/SNMP/Info/Layer2/Ubiquiti.pm b/lib/SNMP/Info/Layer2/Ubiquiti.pm index 751623b0..97008f30 100644 --- a/lib/SNMP/Info/Layer2/Ubiquiti.pm +++ b/lib/SNMP/Info/Layer2/Ubiquiti.pm @@ -8,6 +8,9 @@ use strict; use Exporter; use SNMP::Info::IEEE802dot11; use SNMP::Info::Layer2; +use SNMP::Info::Layer3; # only used in sub mac() + + @SNMP::Info::Layer2::Ubiquiti::ISA = qw/SNMP::Info::IEEE802dot11 SNMP::Info::Layer2 Exporter/; @@ -35,7 +38,30 @@ $VERSION = '3.38'; %MUNGE = ( %SNMP::Info::Layer2::MUNGE, %SNMP::Info::IEEE802dot11::MUNGE, ); sub os { - return 'Ubiquiti'; + my $ubnt = shift; + + my $names = $ubnt->dot11_prod_name(); + + foreach my $iid ( keys %$names ) { + my $prod = $names->{$iid}; + next unless defined $prod; + # Product names that match AirOS products + if((lc $prod) =~ /station/ or (lc $prod) =~ /beam/ or (lc $prod) =~ /grid/){ + return 'AirOS'; + # Product names that match UAP + }elsif((lc $prod) =~ /uap/){ + return 'UniFi'; + }else{ + # Continue below to find OS name + } + } + + ## EdgeMAX OS (EdgeSwitch and EdgeRouter) name is first field split by space + my $ver = $ubnt->description() || ''; + + my @myver = split(/ /, $ver); + + return $myver[0]; } sub os_ver { @@ -46,13 +72,24 @@ sub os_ver { foreach my $iid ( keys %$versions ) { my $ver = $versions->{$iid}; next unless defined $ver; - return $ver; + return $ver; + ## Not sure what this function does, it seems to be extraneous being in the same code block after a return statement? if ( $ver =~ /([\d\.]+)/ ) { return $1; } } + my $ver = $dot11->description() || ''; + if($ver =~ /^edgeswitch/){ + ## EdgeSwitch OS version is second field split by comma + my @myver = split(/, /, $ver); - return; + return $myver[1]; + } + + ## EdgeRouter OS version is second field split by space + my @myver = split(/ /, $ver); + + return $myver[1]; } sub vendor { @@ -60,18 +97,115 @@ sub vendor { } sub model { - my $dot11 = shift; + my $ubnt = shift; - my $names = $dot11->dot11_prod_name(); + my $names = $ubnt->dot11_prod_name(); foreach my $iid ( keys %$names ) { my $prod = $names->{$iid}; next unless defined $prod; return $prod; } - return; + + my $desc = $ubnt->description() || ''; + + ## Pull Model from beginning of description, separated by comma (EdgeSwitch) + if((lc $desc) =~ /^edgeswitch/){ + my @mydesc = split(/, /, $desc); + return $mydesc[0]; + } + + if(!((lc $desc) =~ /edgeos/)){ + # Not sure what type of device this is to get Model + # Wireless devices report dot11_prod_name + # EdgeSwitch includes mode directly and edgeos logic is in else statement + return ; + }else{ + ## do some logic to determine ER model based on tech specs from ubnt: + ## https://help.ubnt.com/hc/en-us/articles/219652227--EdgeRouter-Which-EdgeRouter-Should-I-Use-#tech%20specs + ## Would be nice if UBNT simply adds the model string to their SNMP daemon directly + my $ethCount = 0; + my $switchCount = 0; + #my $sfpCount = 0; + #my $poeCount = 0; + my $memTotalReal = $ubnt->memTotalReal; + my $cpuLoad = $ubnt->hrProcessorLoad; + my $cpuCount = 0; + ## My perl is lacking. Not sure if there's a more efficient way to find the cpu count + foreach my $iid ( keys %$cpuLoad ) { + $cpuCount++; + } + + my $ifDescs = $ubnt->ifDescr; + foreach my $iid ( keys %$ifDescs ) { + my $ifDesc = $ifDescs->{$iid}; + next unless defined $ifDesc; + + if((lc $ifDesc) =~ /^eth\d+$/){ # exclude vlan interfaces. Ex: eth1.5 + $ethCount++; + }elsif((lc $ifDesc) =~ /^switch/){ + $switchCount++; + } + } + + ## If people have other models to further fine-tune this logic that would be great. + if($ethCount eq 9){ + ## Should be ER Infinity + return "EdgeRouter Infinity" + }if($ethCount eq 8){ + ## Could be ER-8 Pro, ER-8, or EP-R8 + return "EdgeRouter 8-Port" + }elsif($ethCount eq 5 and $cpuCount eq 4){ + ## Could be ER-X or ER-X-SFP + return "EdgeRouter X 5-Port" + }elsif($ethCount eq 5){ + return "EdgeRouter PoE 5-Port" + }elsif($ethCount eq 3 and $cpuCount eq 2){ + return "EdgeRouter LITE 3-Port" + }else{ + ## failback string + return "EdgeRouter eth-$ethCount switch-$switchCount mem-$memTotalReal cpuNum-$cpuCount"; + } + + } } +## simply take the MAC and clean it up +sub serial { + my $ubnt = shift; + + my $serial = $ubnt->mac(); + if($serial){ + $serial =~ s/://g; + return uc $serial; + } + return ; +} + +## UBNT doesn't put the primary-mac interface at index 1 +sub mac { + my $ubnt = shift; + my $ifDescs = $ubnt->ifDescr; + + foreach my $iid ( keys %$ifDescs ) { + my $ifDesc = $ifDescs->{$iid}; + next unless defined $ifDesc; + ## CPU Interface will have the primary MAC for EdgeSwitch + ## eth0 will have primary MAC for linux-based UBNT devices + if($ifDesc =~ /CPU/ or $ifDesc eq 'eth0'){ + my $mac = $ubnt->ifPhysAddress->{$iid}; + + # syntax stolen from sub munge_mac in SNMP::Info + $mac = lc join( ':', map { sprintf "%02x", $_ } unpack( 'C*', $mac ) ); + return $mac if $mac =~ /^([0-9A-F][0-9A-F]:){5}[0-9A-F][0-9A-F]$/i; + + } + } + + # MAC malformed or missing + return; + +} 1; __END__ @@ -141,15 +275,15 @@ Returns 'Ubiquiti Networks, Inc.' =item $ubnt->model() -Returns the model extracted from C. +Returns the model extracted from C, with failback to some complex logic for EdgeMax devices =item $ubnt->os() -Returns 'Ubiquiti' +Returns 'Ubiquiti Networks, Inc.' =item $ubnt->os_ver() -Returns the software version extracted from C. +Returns the software version extracted from C, with failback to description splitting for EdgeMax devices =back