1####################################################################
2#
3# SnmpAgent module for use with the code generated by mib2c with the
4# embedded perl configuration file mib2c.perl.conf
5#
6# Copyright Tripleplay Services Limited 2005
7# All rights reserved.
8#
9# Use is subject to license terms specified in the COPYING file
10# distributed with the Net-SNMP package.
11#
12####################################################################
13
14package NetSNMP::agent::Support;
15require Exporter;
16
17use NetSNMP::OID (':all');
18use NetSNMP::agent (':all');
19use NetSNMP::ASN (':all');
20use NetSNMP::default_store (':all');
21use Data::Dumper;
22
23
24use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);
25
26@ISA       = qw(Exporter getLeaf);
27@EXPORT    = qw(registerAgent getOidElement setOidElement);
28@EXPORT_OK = qw();
29$VERSION = '5.09';
30
31use strict;
32use warnings;
33
34BEGIN {
35    print STDERR "Module SnmpAgent.pm loaded ok\n";
36}
37
38# set to 1 to get extra debugging information
39my $debugging = 0;
40
41# if we're not embedded, this will get auto-set below to 1
42my $subagent = 0;
43
44################################################################
45# The oidtable is used for rapid random access to elements with known
46# oids, such as when doing gets/sets. It is not so good for
47# ordered access. For that we build a tree (see below) and setup
48# next links in the oidtable to aid in evaluating where to go next
49# for GETNEXT requests.
50################################################################
51my $oidtable;
52my $oidtree;
53
54# oidroot holds the top most oid record in the table.
55my $oidroot = "";
56
57################################################################
58# Build a tree for handling getnext requests
59# Some parts borrowed from the perl cookbook
60################################################################
61sub buildTree {
62    my ($new_oidtable) = @_;
63    foreach my $key (keys %$new_oidtable) {
64	insert($oidtree, $key);
65    }
66}
67
68sub printTree {
69    my ($tree) = @_;
70    return unless $tree;
71    printTree($tree->{LEFT});
72    print $tree->{VALUE}. "\n";
73    printTree($tree->{RIGHT});
74}
75
76my $prev="";
77##############################################################
78# Walk the sorted oid tree and set the 'next' links in the
79# oidtable.
80##############################################################
81sub doLinks {
82    my ($tree) = @_;
83    return unless $tree;
84    doLinks($tree->{LEFT});
85    if($oidroot eq "") {
86	$oidroot = $tree->{VALUE};
87#	print "Setting oidroot to $oidroot\n";
88    }
89    if($prev ne "") {
90	$oidtable->{$prev}->{next} = $tree->{VALUE};
91    }
92    $prev = $tree->{VALUE};
93    my $node = $oidtable->{$tree->{VALUE}};
94    doLinks($tree->{RIGHT});
95}
96
97################################################################
98# Insert a node into the tree
99################################################################
100sub insert {
101    my ($tree, $value) = @_;
102    unless ($tree) {
103	$tree = {};  # Allocate memory
104	$tree->{VALUE} = $value;
105	$tree->{LEFT} = undef;
106	$tree->{RIGHT} = undef;
107	$_[0] = $tree;
108	return ;
109    }
110    my $tv = new NetSNMP::OID($tree->{VALUE});
111    my $mv = new NetSNMP::OID($value);
112    if ($tv > $mv) {
113	insert($tree->{LEFT}, $value);
114    } elsif ($tv < $mv) {
115	insert($tree->{RIGHT}, $value);
116    } else {
117	print "ERR: Duplicate insert of $value\n";
118    }
119}
120
121#################################################################
122## Main interface.
123## registerAgent(oid);
124#################################################################
125sub registerAgent {
126    my $agent = shift;
127    my $regat = shift;
128    my $new_oidtable = shift;
129
130    foreach (keys %$new_oidtable) { $oidtable->{$_} = $new_oidtable->{$_}; }
131
132    # This is necessary to avoid problems traversing trees where the
133    # IID index is range-limited to not include .0, which is used as a
134    # placeholder in the oidtable.
135     netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
136                            NETSNMP_DS_LIB_DONT_CHECK_RANGE, 1);
137
138    print STDERR "Building OID tree\n";
139    buildTree($new_oidtable);
140    doLinks($oidtree);
141
142#    print Dumper($oidtable);
143
144    # Debug. Print the list of oids in their next ordering
145    my $node = $oidroot;
146    while($node) {
147#	print $node . "\n";
148	$node = $oidtable->{$node}->{next};
149    }
150
151# where we are going to hook onto
152    my $regoid = new NetSNMP::OID($regat);
153    print "registering at ",$regoid,"\n" if ($debugging);
154
155# If we're not running embedded within the agent, then try to start
156# our own subagent instead.
157    if (! $agent) {
158	$agent = new NetSNMP::agent('Name' => 'test', # reads test.conf
159			    'AgentX' => 1);   # make us a subagent
160	$subagent = 1;
161	print STDERR "started us as a subagent ($agent)\n"
162	}
163
164# we register ourselves with the master agent we're embedded in.  The
165# global $agent variable is how we do this:
166    print Dumper($agent) if ($debugging);
167    $agent->register('myname',$regoid, \&my_snmp_handler);
168}
169
170
171######################################################################
172# The subroutine to handle the incoming requests to our
173# part of the OID tree.  This subroutine will get called for all
174# requests within the OID space under the registration oid made above.
175######################################################################
176sub my_snmp_handler {
177    my ($handler, $registration_info, $request_info, $requests) = @_;
178
179    my $request;
180    my $reqnum=1;
181
182#    print STDERR "refs: ",join(", ", ref($handler), ref($registration_info),
183	#		       ref($request_info), ref($requests)),"\n";
184
185    print "==============================================\n" if ($debugging);
186
187    print STDERR "processing a request of type " .
188	$request_info->getMode() . "\n" if ($debugging) ;
189    #
190    # Process each varbind in teh list of requests
191    #
192    for($request = $requests; $request; $request = $request->next()) {
193      my $oid = $request->getOID();
194      print STDERR "--  processing request of $oid (request $reqnum) \n"  if ($debugging);
195
196      #
197      # Handle the different request types
198      #
199      my $mode = $request_info->getMode();
200      if ($mode == MODE_GET) {
201	  getLeaf($oid, $request, $request_info);
202      } elsif ($mode == MODE_GETNEXT) {
203	  getNextOid($oid, $request, $request_info);
204      } else {
205	  print STDERR "Request type $mode not supported\n";
206      }
207
208      $reqnum++;
209    }
210
211    print STDERR "  finished processing\n"
212	if ($debugging);
213}
214
215##########################################################
216# Given an oid see if there is an entry in the oidtable
217# and get the record if there is.
218#
219# Passed the oid as a NetSNMP oid
220#
221# Returns the record if found
222#
223##########################################################
224sub findOid {
225    my $oid = shift;
226
227    # Convert the OID to a string
228    # The index items are temporarily set to zero to cater for tables
229    my @indexes = $oid->to_array();
230
231    my $idxoffset = $oid->length() - 1;
232
233    # Locate the record in the table if it exists
234    # If no match then try setting index values to zero until
235    # we have a match of we exhaust the oid
236    while($idxoffset) {
237	my $oidstr="." . join ".", @indexes;
238	my $rec = $oidtable->{$oidstr};
239
240	# Return the record if found and the repaired index array
241	if($rec) {
242	    print "Found OID $oid ($oidstr) in the table\n"  if ($debugging);
243	    return ($rec);
244	} else {
245	    # Not found. Set the next potential index to zero and
246	    # try again
247	    $indexes[$idxoffset] = 0;
248	    $idxoffset--;
249	}
250    }
251    return (0);
252}
253
254
255##########################################################
256# Sub to return an element of an OID
257# This is commonly used to get an index item from
258# an OID for table accesses.
259##########################################################
260sub getOidElement {
261    my ($oid, $idx) = @_;
262
263    my @idx = $oid->to_array();
264    my $len = $oid->length();
265    my $val = $idx[$idx];
266    return $val;
267}
268##########################################################
269# Sub to set an element of an OID
270# Returns a new NetSNMP::OID object
271##########################################################
272sub setOidElement {
273    my ($oid, $offset, $val) = @_;
274
275    my @idx = $oid->to_array();
276    $idx[$offset] = $val;
277    my $str = "." . join ".", @idx;
278    return new NetSNMP::OID($str);;
279}
280
281
282##########################################################
283# Given scalar record in the oidtable get the value.
284# Passed the record and the request.
285##########################################################
286sub getScalar {
287    my ($rec, $request) = @_;
288
289    # Got a scalar node from the table
290    my $type = $rec->{type};
291
292    # Call the GET function
293    my $val = $rec->{func}();
294
295    $request->setValue($type, $val);
296}
297
298############################################################
299# Given a record in the OID table that is a columnar object
300# locate any objects that have the required index.
301#
302# Passed the record, the oid object and the request
303############################################################
304sub getColumnar {
305    my ($rec, $oid, $request) = @_;
306
307    print "Get Columnar $oid\n"   if ($debugging);
308
309    my $type = $rec->{type};
310    my $args = $rec->{args};
311
312    # Check the index is in range and exists
313    if($rec->{check}($oid)) {
314
315	# Call the handler function with the oid
316	my $val = $rec->{func}($oid);
317
318	# Set the value found in the request
319	$request->setValue($type, $val);
320    }
321}
322
323######################################################################
324#
325# If the oid is in range then set the data in the supplied request
326# object.
327#
328# Tries to get a scalar first then checks the coumnar second
329#
330# Return 1 if successful or 0 if not
331#
332#######################################################################
333sub getLeaf {
334    my $oid          = shift;
335    my $request      = shift;
336    my $request_info = shift;
337
338    print "getLeaf: $oid\n"  if ($debugging);
339
340    # Find an oid entry in the table
341    my ($rec) = findOid($oid);
342    if($rec) {
343
344	# Record found. Use the istable flag to pass control to the
345	# scalar or coulmnar handler
346	if($rec->{istable} == 1) {
347	    return getColumnar($rec, $oid, $request);
348	} else {
349	    return getScalar($rec, $request);
350	}
351    }
352}
353
354#####################################################
355#
356# getNextOid
357#
358# The workhorse for GETNEXT.
359# Given an OID, locates the next oid and if valid does
360# a getLeaf on that OID. It does this by walking the list of
361# OIDs. We try to otimise the walk by first looking for an oid
362# in the list as follows:
363#
364# 1. Try to locate an oid match in the table.
365#    If that succeeds then look for the next object in the table
366#    using the next attribute and get that object.
367#
368# 2. If the OID found is a table element then use the table
369#    specific index handler to see if there is an item with the
370#    next index.This will retutn either an oid which we get, or 0.
371#    If there is not then we continue our walk along the tree
372#
373#  3.If the supplied oid is not found, but is in the range of our  oids
374#    then we start at the root oid and walk the list until we either
375#    drop of the end, or we fnd an OID that is greater than the OID supplied.
376#    In all cases if we sucessfully find an OID to retrieve,
377#    then we set the next OID in the resposnse.
378#
379######################################################
380sub getNextOid {
381    my $oid          = shift;
382    my $request      = shift;
383    my $request_info = shift;
384
385    my $curoid = new NetSNMP::OID($oidroot); # Current OID in the walk
386
387    # Find the starting position if we can
388    my $current = findOid($oid);
389#    print Dumper($current);
390    if($current) {
391	# Found the start in the table
392	$curoid = new NetSNMP::OID($oid);
393
394        # If the node we found is not a table then start at the
395	# next oid in the table
396	unless($current->{istable}) {
397
398	    my $nextoid = $current->{next};
399	    $curoid = new NetSNMP::OID($nextoid);
400#	    print "Not a table so using the next $nextoid\n";
401	    $current = $oidtable->{$nextoid};
402	}
403    }
404
405    # If we cannot find the starting point in the table, then start
406    # at the top and
407    # walk along the list until we drop off the end
408    # or we get to an oid that is >= to the one we want.
409    else {
410
411#	print "Not found so using the top ($oidroot)\n";
412	$current = $oidtable->{$oidroot};
413
414	while($current && $curoid <= $oid) {
415	    my $nextoid = $current->{next};
416	    print "Trying $nextoid\n"   if ($debugging);
417	    $current = $oidtable->{$nextoid};
418	    $curoid = $current ? new NetSNMP::OID($nextoid) : undef;
419	}
420    }
421
422    ##
423    ## Got a starting point
424    ## $current points to the node in the table
425    ## $curoid is a NetSNMP::OID object with the oid we are trying
426    ##
427    print "Got a startng point of " . Dumper($current)   if ($debugging);
428    while($current) {
429	if($current->{istable}) {
430
431	    # Got a table oid. See if the next is valid for the table
432#	    print "Trying table\n";
433
434	    my $nextoid = $current->{nextoid}($curoid);
435
436#	    print Dumper($nextoid);
437	    if($nextoid) {
438		getColumnar($current, $nextoid, $request);
439		$request->setOID($nextoid);
440		return;
441	    }
442
443	    # No not this one so try the next
444	    $nextoid = $current->{next};
445	    $current = $oidtable->{$nextoid};
446	    $curoid = $current ? new NetSNMP::OID($nextoid) : undef;
447	    print "Trying next $curoid $nextoid\n"   if ($debugging);
448	} else {
449
450	    # Must be the current node
451	    if(getScalar($current, $request)) {
452		$request->setOID($curoid);
453		return 1;
454	    }
455	}
456    }
457}
458
459
460# Return true from this module
4611;
462