README.SNMP_util
README for SNMP_util.pm Copyright (c) 1998-2002, Mike Mitchell All rights reserved This program is free software; you can redistribute it under the "Artistic License" included in this distribution (file "Artistic"). Author: Mike Mitchell <Mike.Mitchell@.sas.com> Contributors: Tobias Oetiker <tobi@oetiker.ch> Simon Leinen <simon@switch.ch> Jeff Allen <jeff.allen@acm.org> Johannes Demel <demel@zid.tuwien.ac.at> Laurent Girod <girod.laurent@pmintl.ch> Ian Duplisse <i.duplisse@cablelabs.com> I was using Simon Leinen's SNMP tools in various perl scripts, and I found that I was using the same functions over and over. I grouped the common routines into a separate perl module so that I would only have to make changes in one place, rather than track down all the different perl scripts that included the code. The result is the 'SNMP_utils.pm' module. Thanks goes to Tobias Oetiker (tobi@oetiker.ch) of MRTG fame for the basic layout of the functions. The SNMP_utils.pm module contains the functions snmpmapOID(text, OID, [text, OID ...]) snmpMIB_to_OID(filename) snmpLoad_OID_Cache(filename) snmpQueue_MIB_File(filename, [filename]) snmpget(community@host:port:timeout:retries:backoff:version, OID, [OID...]) snmpgetnext(community@host:port:timeout:retries:backoff:version, OID, [OID...]) snmpwalk(community@host:port:timeout:retries:backoff:version, OID, [OID...]) snmpgetbulk(community@host:port:timeout:retries:backoff:version, non_repeat, max_repeat, OID, [OID...]) snmpset(community@host:port:timeout:retries:backoff:version, OID, type, value, [OID, type, value ...]) snmptrap(community@host:port:timeout:retries:backoff:version, enterpriseOID, agent, generalID, specificID, OID, type, value, [OID, type, value ...]) snmpmaptable(community@host:port:timeout:retries:backoff:version, function, OID, [OID...]) snmpmaptable4(community@host:port:timeout:retries:backoff:version, function, max_repetitions, OID, [OID...]) snmpwalkhash(community@host:port:timeout:retries:backoff:version, function, OID, [OID...], [hash ref]) The functions have a small mapping table for commonly used OIDs. The OIDs from RFC1213 (MIB-II) and RFC1315 (Frame Relay) are preloaded. It is much easier to say "ifInOctets.4" instead of "1.3.6.1.2.1.2.2.1.10.4". The snmpmapOID() function will let you add your own entries to the mapping table. It doesn't return anything. Be sure to leave off any instance number from the OID passed to snmpmapOID()! The example above would be &snmpmapOID("ifInOctets", "1.3.6.1.2.1.2.2.1.10"). Don't use &snmpmapOID("ifInOctets.4", "1.3.6.1.2.1.2.2.1.10.4"). The trailing ".4" is interpreted as an instance number, and not the entire OID. The snmpmapOID function will ignore the attempt to add a mapping entry that includes an instance number. The call &snmpmapOID("ifInOctets.four", "1.3.6.1.2.1.2.2.1.10.4") would be accepted, because the text ".four" is interpreted differently than the number 4. The snmpMIB_to_OID() function will open the passed-in MIB file name and read it. It will create text mappings for the appropriate OID number. It returns the number of text mappings added, so a zero or negative return indicates an error. The snmpLoad_OID_Cache() function will open the passed-in file name and read the file. It is expecting lines with a text string in the first column and an OID number in the second column, like ifInOctets 1.3.6.1.2.1.2.2.1.10 ifOutOctets 1.3.6.1.2.1.2.2.1.16 It will add the text to OID mappings in the file to the internal list by calling the "snmpmapOID()" function. This way the extra overhead of parsing a MIB file can be avoided if you have a pre-parsed version of the MIB handy. The snmpQueue_MIB_File() function queues up file names for use by the "snmpMIB_to_OID()" function. If there are filenames passed into "snmpQueue_MIB_File()", when an OID can't be found in the internal table, the queued MIB files are loaded one after another until the OID can be found (or the list is exhausted). This delays the MIB parsing until the OID value is actually needed. A cache file with the looked up text-to-OID mappings is maintained. It's name is "OID_cache.txt", and can be changed by setting the variable $SNMP_util::CacheFile to the name of the file you desire. This cache file is automatically loaded before the queued MIB files are parsed. If the OID is found in the cache file, the MIB file doesn't have to be parsed. The rest of the functions require a hostname/IP address as the first argument. The community string, port number, timeout, retries, backoff, and version parameters are all optional. If the community string isn't specified, "public" is used. If the port number isn't specified, the default value from SNMP_Sesssion.pm (port 161) is used for everything but snmptrap(). snmptrap() uses port 162 as its default. The port parameter was recently augmented to allow the specification of the IP address (or hostname) and port of the machine doing the query in addition to the IP address (or hostname) and port of the machine being queried. Some machines have additional security features that only allow SNMP queries to come from certain IP addresses. If the host doing the query has multiple interface, it may be necessary to specify the interface the query should come from. The port parameter is further broken down into "remote_port!local_address!local_port". Here are some examples: somehost somehost:161 somehost:161!192.168.2.4!4000 use 192.168.2.4 and port 4000 as source somehost:!192.168.2.4 use 192.168.2.4 as source somehost:!!4000 use port 4000 as source Most people will only need to use the first form ("somehost"). The timeout, retries, and backoff parameters default to whatever SNMP_Session.pm uses. For SNMP_Session.pm version 0.83 they are 2 seconds, 5 retries, and a 1.0 backoff factor. The backoff factor is used as a multiplier to increase the timeout after every retry. With a backoff factor of 1.0 the timeout stays the same for every retry. The version parameter defaults to SNMP version 1. Some SNMP values such as 64-bit counters have to be queried using SNMP version 2. Specifying "2" or "2c" as the version parameter will accomplish this. The snmpgetbulk routine is only supported in SNMP version 2 and higher. Several parameters internal to SNMP_Session can be set by passing a hash as the first OID. The keys to the hash are the parameters to modify. Here is a list of parameters and their default values in SNMP_Session version 0.91: 'community' => "public" 'timeout' => 2.0 'retries' => 5 'backoff' => 1.0 'debug' => 0 'default_max_repetitions' => 12 'use_getbulk' => 1 'lenient_source_address_matching' => 1 'lenient_source_port_matching' => 1 Consult the documentation and/or source code for SNMP_Session for further information of these parameters. The snmpget function returns an array with the results of the 'get' operation. The value associated with each OID is returned as a separate value in the array. The snmpgetnext function returns an array with the results of the 'getnext' operation. The OID number is added to the result as a prefix with a colon separator, like '1.3.6.1.2.1.2.2.1.2.1:ethernet' The snmpwalk function returns an array with all the OID numbers and values, like the 'snmpgetnext' function. If only one OID is specified for the walk, only the instance part of the OID number is added as a prefix. If multiple OID are specified for the walk, the entire OID number is added as a prefix. For instance, a walk of just '1.3.6.1.2.1.2.2.1.2' will return values like '1:ethernet', '2:ethernet', '3:fddi'. A walk multiple OIDs will return values like '1.3.6.1.2.1.2.2.1.2.1:ethernet'. The snmpwalk function will use a 'getbulk' query for efficiency if the SNMP version is 2 or higher. The snmpgetbulk function, like the snmpgetnext function, returns an array with the results of the 'getbulk' operation. The OID number is added to the result as a prefix with a colon separator, like '1.3.6.1.2.1.2.2.1.2.1:ethernet' The 'non_repeat' argument is the number of OID arguments that should be retrieved no more than once. The 'max_repeat' argument is the number of times that other variables beyond those specified by the 'non_repeat' argument should be retrieved. The getbulk query is only supported at SNMP version 2 or higher. The snmpset function is passed OID, type, and value triplets. It returns an array with the result of the set. The snmpmaptable function can be used for walking tables. The OID arguments are the columns of the table sharing the same index, and the passed-in function is called once per row. The passed-in function will be given the row index as a partial OID in dotted notation, e.g. "1.3", or "10.0.1.34", and values of the requested table columns in that row. The snmpmaptable4 function is just like snmpmaptable, only the third argument is the number of table rows to request in a single SNMP query. The snmpmaptable function uses the default of 12 rows. The snmpwalkhash acts like snmpwalk, but will call the passed-in function once per returned value. The function is passed a reference to a hash, the hostname, the textual OID, the dotted-numeric OID, the instance, the value, and the textual OID you requested. That function can customize the result you want, in a hash of hashes, so you can extract the value later by hosts, by oid_names, by oid_numbers, by instances... like these: $hash{$host}{$name}{$inst} = $value; $hash{$host}{$oid}{$inst} = $value; $hash{$name}{$inst} = $value; $hash{$oid}{$inst} = $value; $hash{$oid . '.' . $ints} = $value; $hash{$inst} = $value; ... If the last argument to snmpwalkhash is a reference to a hash, that hash reference is passed to the passed-in function instead of a local hash reference. That way your function can look up other objects unrelated to the current invocation of snmpwalkhash. Here is a simple example of using the functions: #! /usr/local/bin/perl5 BEGIN { ### # Finally, SNMPGet fully written in PERL5. # Thanks to Simon Leinen <simon@switch.ch> # More on: http://www.switch.ch/misc/leinen/snmp/perl/ #### # There older perls tend to behave peculiar with # large integers ... require 5.004; use SNMP_util "0.89"; } use strict; sub printfun { my($ind, $desc, $phy) = @_; my($a, $b, $c, $d, $e, $f, $mac); ($a, $b, $c, $d, $e, $f) = unpack("C6", $phy); $mac = sprintf("%02x-%02x-%02x-%02x-%02x-%02x", $a, $b, $c, $d, $e, $f); print "interface $ind: MAC $mac $desc\n"; } sub my_hash_with_host { my($h_ref, $host, $name, $oid, $inst, $value, $tree) = @_; $inst =~ s/^\.+//; if ($name =~ /ifPhysAddress/) { my $mac = ''; map { $mac .= sprintf("%02X", $_) } unpack "CCCCCC", $value; $value = $mac; } $h_ref->{$host}->{$name}->{$inst} = $value; } sub main { my($oid, $host, $response, $cont); my($desc, @ret, $nrows); $host = "127.0.0.1"; $cont = "Your Name"; # This snmpmapOID() isn't necessary, as it is already in # the internal map table. It is just an example... &snmpmapOID("ifDescr", "1.3.6.1.2.1.2.2.1.2"); print "Trying 'getnext' on $host\n"; @ret = &snmpgetnext($host, "ifDescr"); foreach $desc (@ret) { ($oid, $desc) = split(':', $desc, 2); print "$oid = $desc\n"; } print "Trying 'getnext' on $host with different timeout and retries\n"; @ret = &snmpgetnext($host, { 'timeout' => 4, 'retries' => 2 }, "ifDescr"); foreach $desc (@ret) { ($oid, $desc) = split(':', $desc, 2); print "$oid = $desc\n"; } print "Trying 'walk' on $host\n"; @ret = &snmpwalk($host, "ifDescr"); foreach $desc (@ret) { ($oid, $desc) = split(':', $desc, 2); print "$oid = $desc\n"; } print "Trying 'walkhash' on $host\n"; my %ret_hash = &snmpwalkhash($host, \&my_hash_with_host, "ifEntry"); foreach $oid (sort keys %{$ret_hash{$host}}) { foreach my $inst (sort { $a <=> $b } keys %{$ret_hash{$host}{$oid}}) { printf("%20s\t: %-15s %3s = %s\n", $host, $oid, $inst, $ret_hash{$host}{$oid}{$inst}); } } print "Trying 'walkhash' on $host, using own hash\n"; my(%myhash); %ret_hash = &snmpwalkhash($host, \&my_hash_with_host, "ifEntry", \%myhash); foreach $oid (sort keys %{$myhash{$host}}) { foreach my $inst (sort { $a <=> $b } keys %{$myhash{$host}{$oid}}) { printf("%20s\t: %-15s %3s = %s\n", $host, $oid, $inst, $myhash{$host}{$oid}{$inst}); } } print "Before set:\n"; $oid = "sysContact"; ($response) = &snmpget($host, $oid); if ($response) { print "GET $oid : $response\n"; } else { warn "$host did not respond to SNMP query\n"; } my $oldContact = $response; print "setting contact to $cont\n"; ($response) = &snmpset("security\@$host", $oid, 'string', $cont); if ($response) { print "SET: $oid : $response\n"; } else { die "$host did not respond to SNMP set\n"; } print "After set:\n"; ($response) = &snmpget($host, $oid); if ($response) { print "GET $oid : $response\n"; } else { die "$host did not respond to SNMP query\n"; } print "Setting contact back to $oldContact\n"; ($response) = &snmpset("security\@$host", $oid, 'string', $oldContact); if ($response) { print "SET: $oid : $response\n"; } else { die "$host did not respond to SNMP set\n"; } print "After 2nd set:\n"; ($response) = &snmpget($host, $oid); if ($response) { print "GET $oid : $response\n"; } else { die "$host did not respond to SNMP query\n"; } print "Walking table of interface description and physical address\n"; $nrows = &snmpmaptable($host, \&printfun, "ifDescr", "ifPhysAddress"); print "walked $nrows rows in the table\n"; } main; exit(0); ----------------------------------------------------- Here is an example using the MIB parsing functions. First create a file with a simple MIB: cat > dummy.mib <