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