#################################################################### # # SnmpAgent module for use with the code generated by mib2c with the # embedded perl configuration file mib2c.perl.conf # # Copyright Tripleplay Services Limited 2005 # All rights reserved. # # Use is subject to license terms specified in the COPYING file # distributed with the Net-SNMP package. # #################################################################### package NetSNMP::agent::Support; require Exporter; use NetSNMP::OID (':all'); use NetSNMP::agent (':all'); use NetSNMP::ASN (':all'); use NetSNMP::default_store (':all'); use Data::Dumper; use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION); @ISA = qw(Exporter getLeaf); @EXPORT = qw(registerAgent getOidElement setOidElement); @EXPORT_OK = qw(); $VERSION = '5.0901'; use strict; use warnings; BEGIN { print STDERR "Module SnmpAgent.pm loaded ok\n"; } # set to 1 to get extra debugging information my $debugging = 0; # if we're not embedded, this will get auto-set below to 1 my $subagent = 0; ################################################################ # The oidtable is used for rapid random access to elements with known # oids, such as when doing gets/sets. It is not so good for # ordered access. For that we build a tree (see below) and setup # next links in the oidtable to aid in evaluating where to go next # for GETNEXT requests. ################################################################ my $oidtable; my $oidtree; # oidroot holds the top most oid record in the table. my $oidroot = ""; ################################################################ # Build a tree for handling getnext requests # Some parts borrowed from the perl cookbook ################################################################ sub buildTree { my ($new_oidtable) = @_; foreach my $key (keys %$new_oidtable) { insert($oidtree, $key); } } sub printTree { my ($tree) = @_; return unless $tree; printTree($tree->{LEFT}); print $tree->{VALUE}. "\n"; printTree($tree->{RIGHT}); } my $prev=""; ############################################################## # Walk the sorted oid tree and set the 'next' links in the # oidtable. ############################################################## sub doLinks { my ($tree) = @_; return unless $tree; doLinks($tree->{LEFT}); if($oidroot eq "") { $oidroot = $tree->{VALUE}; # print "Setting oidroot to $oidroot\n"; } if($prev ne "") { $oidtable->{$prev}->{next} = $tree->{VALUE}; } $prev = $tree->{VALUE}; my $node = $oidtable->{$tree->{VALUE}}; doLinks($tree->{RIGHT}); } ################################################################ # Insert a node into the tree ################################################################ sub insert { my ($tree, $value) = @_; unless ($tree) { $tree = {}; # Allocate memory $tree->{VALUE} = $value; $tree->{LEFT} = undef; $tree->{RIGHT} = undef; $_[0] = $tree; return ; } my $tv = new NetSNMP::OID($tree->{VALUE}); my $mv = new NetSNMP::OID($value); if ($tv > $mv) { insert($tree->{LEFT}, $value); } elsif ($tv < $mv) { insert($tree->{RIGHT}, $value); } else { print "ERR: Duplicate insert of $value\n"; } } ################################################################# ## Main interface. ## registerAgent(oid); ################################################################# sub registerAgent { my $agent = shift; my $regat = shift; my $new_oidtable = shift; foreach (keys %$new_oidtable) { $oidtable->{$_} = $new_oidtable->{$_}; } # This is necessary to avoid problems traversing trees where the # IID index is range-limited to not include .0, which is used as a # placeholder in the oidtable. netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_CHECK_RANGE, 1); print STDERR "Building OID tree\n"; buildTree($new_oidtable); doLinks($oidtree); # print Dumper($oidtable); # Debug. Print the list of oids in their next ordering my $node = $oidroot; while($node) { # print $node . "\n"; $node = $oidtable->{$node}->{next}; } # where we are going to hook onto my $regoid = new NetSNMP::OID($regat); print "registering at ",$regoid,"\n" if ($debugging); # If we're not running embedded within the agent, then try to start # our own subagent instead. if (! $agent) { $agent = new NetSNMP::agent('Name' => 'test', # reads test.conf 'AgentX' => 1); # make us a subagent $subagent = 1; print STDERR "started us as a subagent ($agent)\n" } # we register ourselves with the master agent we're embedded in. The # global $agent variable is how we do this: print Dumper($agent) if ($debugging); $agent->register('myname',$regoid, \&my_snmp_handler); } ###################################################################### # The subroutine to handle the incoming requests to our # part of the OID tree. This subroutine will get called for all # requests within the OID space under the registration oid made above. ###################################################################### sub my_snmp_handler { my ($handler, $registration_info, $request_info, $requests) = @_; my $request; my $reqnum=1; # print STDERR "refs: ",join(", ", ref($handler), ref($registration_info), # ref($request_info), ref($requests)),"\n"; print "==============================================\n" if ($debugging); print STDERR "processing a request of type " . $request_info->getMode() . "\n" if ($debugging) ; # # Process each varbind in teh list of requests # for($request = $requests; $request; $request = $request->next()) { my $oid = $request->getOID(); print STDERR "-- processing request of $oid (request $reqnum) \n" if ($debugging); # # Handle the different request types # my $mode = $request_info->getMode(); if ($mode == MODE_GET) { getLeaf($oid, $request, $request_info); } elsif ($mode == MODE_GETNEXT) { getNextOid($oid, $request, $request_info); } else { print STDERR "Request type $mode not supported\n"; } $reqnum++; } print STDERR " finished processing\n" if ($debugging); } ########################################################## # Given an oid see if there is an entry in the oidtable # and get the record if there is. # # Passed the oid as a NetSNMP oid # # Returns the record if found # ########################################################## sub findOid { my $oid = shift; # Convert the OID to a string # The index items are temporarily set to zero to cater for tables my @indexes = $oid->to_array(); my $idxoffset = $oid->length() - 1; # Locate the record in the table if it exists # If no match then try setting index values to zero until # we have a match of we exhaust the oid while($idxoffset) { my $oidstr="." . join ".", @indexes; my $rec = $oidtable->{$oidstr}; # Return the record if found and the repaired index array if($rec) { print "Found OID $oid ($oidstr) in the table\n" if ($debugging); return ($rec); } else { # Not found. Set the next potential index to zero and # try again $indexes[$idxoffset] = 0; $idxoffset--; } } return (0); } ########################################################## # Sub to return an element of an OID # This is commonly used to get an index item from # an OID for table accesses. ########################################################## sub getOidElement { my ($oid, $idx) = @_; my @idx = $oid->to_array(); my $len = $oid->length(); my $val = $idx[$idx]; return $val; } ########################################################## # Sub to set an element of an OID # Returns a new NetSNMP::OID object ########################################################## sub setOidElement { my ($oid, $offset, $val) = @_; my @idx = $oid->to_array(); $idx[$offset] = $val; my $str = "." . join ".", @idx; return new NetSNMP::OID($str);; } ########################################################## # Given scalar record in the oidtable get the value. # Passed the record and the request. ########################################################## sub getScalar { my ($rec, $request) = @_; # Got a scalar node from the table my $type = $rec->{type}; # Call the GET function my $val = $rec->{func}(); $request->setValue($type, $val); } ############################################################ # Given a record in the OID table that is a columnar object # locate any objects that have the required index. # # Passed the record, the oid object and the request ############################################################ sub getColumnar { my ($rec, $oid, $request) = @_; print "Get Columnar $oid\n" if ($debugging); my $type = $rec->{type}; my $args = $rec->{args}; # Check the index is in range and exists if($rec->{check}($oid)) { # Call the handler function with the oid my $val = $rec->{func}($oid); # Set the value found in the request $request->setValue($type, $val); } } ###################################################################### # # If the oid is in range then set the data in the supplied request # object. # # Tries to get a scalar first then checks the coumnar second # # Return 1 if successful or 0 if not # ####################################################################### sub getLeaf { my $oid = shift; my $request = shift; my $request_info = shift; print "getLeaf: $oid\n" if ($debugging); # Find an oid entry in the table my ($rec) = findOid($oid); if($rec) { # Record found. Use the istable flag to pass control to the # scalar or coulmnar handler if($rec->{istable} == 1) { return getColumnar($rec, $oid, $request); } else { return getScalar($rec, $request); } } } ##################################################### # # getNextOid # # The workhorse for GETNEXT. # Given an OID, locates the next oid and if valid does # a getLeaf on that OID. It does this by walking the list of # OIDs. We try to otimise the walk by first looking for an oid # in the list as follows: # # 1. Try to locate an oid match in the table. # If that succeeds then look for the next object in the table # using the next attribute and get that object. # # 2. If the OID found is a table element then use the table # specific index handler to see if there is an item with the # next index.This will retutn either an oid which we get, or 0. # If there is not then we continue our walk along the tree # # 3.If the supplied oid is not found, but is in the range of our oids # then we start at the root oid and walk the list until we either # drop of the end, or we fnd an OID that is greater than the OID supplied. # In all cases if we sucessfully find an OID to retrieve, # then we set the next OID in the resposnse. # ###################################################### sub getNextOid { my $oid = shift; my $request = shift; my $request_info = shift; my $curoid = new NetSNMP::OID($oidroot); # Current OID in the walk # Find the starting position if we can my $current = findOid($oid); # print Dumper($current); if($current) { # Found the start in the table $curoid = new NetSNMP::OID($oid); # If the node we found is not a table then start at the # next oid in the table unless($current->{istable}) { my $nextoid = $current->{next}; $curoid = new NetSNMP::OID($nextoid); # print "Not a table so using the next $nextoid\n"; $current = $oidtable->{$nextoid}; } } # If we cannot find the starting point in the table, then start # at the top and # walk along the list until we drop off the end # or we get to an oid that is >= to the one we want. else { # print "Not found so using the top ($oidroot)\n"; $current = $oidtable->{$oidroot}; while($current && $curoid <= $oid) { my $nextoid = $current->{next}; print "Trying $nextoid\n" if ($debugging); $current = $oidtable->{$nextoid}; $curoid = $current ? new NetSNMP::OID($nextoid) : undef; } } ## ## Got a starting point ## $current points to the node in the table ## $curoid is a NetSNMP::OID object with the oid we are trying ## print "Got a startng point of " . Dumper($current) if ($debugging); while($current) { if($current->{istable}) { # Got a table oid. See if the next is valid for the table # print "Trying table\n"; my $nextoid = $current->{nextoid}($curoid); # print Dumper($nextoid); if($nextoid) { getColumnar($current, $nextoid, $request); $request->setOID($nextoid); return; } # No not this one so try the next $nextoid = $current->{next}; $current = $oidtable->{$nextoid}; $curoid = $current ? new NetSNMP::OID($nextoid) : undef; print "Trying next $curoid $nextoid\n" if ($debugging); } else { # Must be the current node if(getScalar($current, $request)) { $request->setOID($curoid); return 1; } } } } # Return true from this module 1;