Search code examples
perlsnmp

Perl Net::SNMP returns wrong value for some OIDs


I'm trying to get data from a QLogic switch via SNMP. When I snmpwalk the connUnitPortStatCountError OID (.1.3.6.1.3.94.4.5.1.3) from the command line, I get a hex string result for each port on the switch:

# snmpwalk -v1 -On -c <community> <host> .1.3.6.1.3.94.4.5.1.3
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.1 = Hex-STRING: 00 00 00 00 00 00 00 04 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.2 = Hex-STRING: 00 00 00 00 00 00 00 04 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.3 = Hex-STRING: 00 00 00 00 00 00 00 04 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.4 = Hex-STRING: 00 00 00 00 00 00 00 01 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.5 = Hex-STRING: 00 00 00 00 00 00 00 04 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.6 = Hex-STRING: 00 00 00 00 00 00 00 2C 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.7 = Hex-STRING: 00 00 00 00 00 00 00 0E 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.8 = Hex-STRING: 00 00 00 00 00 00 00 09 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.9 = Hex-STRING: 00 00 00 00 00 00 00 38 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.10 = Hex-STRING: 00 00 00 00 00 00 00 1C 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.11 = Hex-STRING: 00 00 00 00 00 00 00 12 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.12 = Hex-STRING: 00 00 00 00 00 00 00 04 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.13 = Hex-STRING: 00 00 00 00 00 00 00 0A 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.14 = Hex-STRING: 00 00 00 00 00 00 00 21 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.15 = Hex-STRING: 00 00 00 00 00 00 00 04 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.16 = Hex-STRING: 00 00 00 00 00 00 00 04 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.17 = Hex-STRING: 00 00 00 00 00 00 00 04 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.18 = Hex-STRING: 00 00 00 00 00 00 00 04 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.19 = Hex-STRING: 00 00 00 00 00 00 00 08 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.20 = Hex-STRING: 00 00 00 00 00 00 00 0A 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.21 = Hex-STRING: 00 00 00 00 00 00 00 00 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.22 = Hex-STRING: 00 00 00 00 00 00 00 00 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.23 = Hex-STRING: 00 00 00 00 00 00 00 00 
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.24 = Hex-STRING: 00 00 00 00 00 00 00 00

But when I call Net::SNMP's get_table() subroutine in a Perl script with the same OID, the values for some of the ports do not match the results I got from snmpwalk:

# ./foo <host>
$VAR1 = {
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.1' => '0x0000000000000004',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.2' => '0x0000000000000004',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.3' => '0x0000000000000004',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.4' => '0x0000000000000001',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.5' => '0x0000000000000004',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.6' => ',',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.7' => '0x000000000000000e',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.8' => '  ',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.9' => '8',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.10' => '0x000000000000001c',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.11' => '0x0000000000000012',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.12' => '0x0000000000000004',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.13' => '',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.14' => '!',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.15' => '0x0000000000000004',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.16' => '0x0000000000000004',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.17' => '0x0000000000000004',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.18' => '0x0000000000000004',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.19' => ',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.20' => '',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.21' => '',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.22' => '',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.23' => '',
          '.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.24' => ''
        };

Notice that the results all match until the sixth port, where snmpwalk returns '00 00 00 00 00 00 00 2C' and my script returns ','. I see similar behavior with other OIDs from the connUnitPortStatTable: the results mostly agree with snmpwalk, but a few do not. Here is the Perl script itself:

#!/usr/bin/env perl

use strict;
use warnings;

use Data::Dumper;
use Net::SNMP;

# Custom sort order for OIDs. Only sorts on last number in OID because the rest don't matter in this example
$Data::Dumper::Sortkeys = sub { [ sort { (split '\.', $a)[-1] <=> (split '\.', $b)[-1] } keys %{$_[0]} ] };

my $switch = shift;
my $port = 161;
my $community = <community>; # Edited, obviously

my ($session, $error) = Net::SNMP->session(
                          -hostname      => $switch,
                          -port          => $port,
                          -timeout       => 7,
                          -retries       => 2,
                          -community     => $community,
                        );

if (!defined $session) {
  printf "ERROR: connection failed\n", $error;
  exit 1;
}

my $oid = '.1.3.6.1.3.94.4.5.1.3'; # connUnitPortStatCountError
my $result = $session->get_table(-baseoid => $oid);

if (!defined($result)) {
  printf "ERROR: %s.\n", $session->error();
  $session->close();
  exit 1;
}

$session->close();
print Dumper($result);

exit 0;

Edit: For perpetuity, here's my program updated per PerC's answer:

#!/usr/bin/env perl

use strict;
use warnings;

use Net::SNMP;

my $switch = shift;
my $port = 161;
my $community = <community>; # Edited, obviously

my ($session, $error) = Net::SNMP->session(
                          -hostname      => $switch,
                          -port          => $port,
                          -timeout       => 7,
                          -retries       => 2,
                          -community     => $community,
                          -translate     => [-octetstring => 0],
                        );

if (!defined $session) {
  printf "ERROR: connection failed\n", $error;
  exit 1;
}

my $oid = '.1.3.6.1.3.94.4.5.1.3'; # connUnitPortStatCountError
my $result = $session->get_table(-baseoid => $oid);

if (!defined($result)) {
  printf "ERROR: %s.\n", $session->error();
  $session->close();
  exit 1;
}

$session->close();

foreach my $key (sort { (split '\.', $a)[-1] <=> (split '\.', $b)[-1] } keys %$result) {
  printf "%s => %s\n", $key, unpack "H*", $result->{$key};
}

exit 0;

And the output:

# ./foo <host>
.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.1 => 0000000000000004

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.2 => 0000000000000004

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.3 => 0000000000000004

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.4 => 0000000000000001

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.5 => 0000000000000004

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.6 => 000000000000002c

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.7 => 000000000000000e

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.8 => 0000000000000009

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.9 => 0000000000000038

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.10 => 000000000000001c

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.11 => 0000000000000012

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.12 => 0000000000000004

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.13 => 000000000000000a

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.14 => 0000000000000021

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.15 => 0000000000000004

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.16 => 0000000000000004

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.17 => 0000000000000004

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.18 => 0000000000000004

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.19 => 0000000000000008

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.20 => 000000000000000a

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.21 => 0000000000000000

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.22 => 0000000000000000

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.23 => 0000000000000000

.1.3.6.1.3.94.4.5.1.3.16.0.0.192.221.18.202.254.0.0.0.0.0.0.0.0.24 => 0000000000000000

Solution

  • This is because Net::SNMP tries to translate any OCTET_STRING into a human readable string.

    See: https://metacpan.org/module/Net::SNMP#Non-blocking-SNMPv2c-get-bulk-request-for-ifTable for an explanation and how to work around this.