The Simple Network Management Protocol (SNMP) is an application layer protocol that allows remote management and data collection from networked devices. A networked device can be anything that is connected to the network, such as a router, a bridge, or a host.
A managed networked device contains software that acts as the SNMP agent for the device. It handles the application layer protocol for SNMP and carries out the management commands. These commands consist of getting information and setting of operational parameters.
There are also network management application programs (usually running on a host somewhere on the network) that send SNMP commands to the various managed devices on the network to perform the management tasks. These tasks can consist of configuration management, network traffic monitoring and network trouble shooting.
The Extensible Simple Network Management Protocol (eSNMP) is the SNMP agent architecture for Tru64 UNIX or earlier versions of DIGITAL UNIX. It includes a master agent process and multiple related processes containing eSNMP subagents. The master agent performs the SNMP protocol handling and the subagents perform the requested management commands. This chapter assumes you are familiar with the following:
SNMP protocol
Management Information Base (MIB) definitions and Request For Comments (RFCs)
Object Identifiers (OIDs) and the International Standards Organization (ISO) registration hierarchy (1.3.6.1.2.1, and so on)
The C programming language
This chapter provides the following information:
Overview of eSNMP
Overview of the eSNMP application programming interface (API)
Detailed information on the eSNMP routines
The following sections describe the components and architecture of the eSNMP agent. It contains information on the following:
Components of eSNMP
Architecture
SNMP Versions
The eSNMP components are as follows:
/usr/sbin/snmpd
-- The master agent
daemon.
/usr/sbin/os_mibs
-- The host MIB
and networking subagent daemon.
/usr/sbin/svrMgt_mib
-- A server management
subagent daemon.
/usr/sbin/svrSystem_mib
-- A server
system subagent daemon.
/usr/sbin/mosy
-- The MIB compiler.
/usr/sbin/snmpi
-- The object table
code generator.
/usr/shlib/libesnmp.so
-- The eSNMP
library.
/usr/include/esnmp.h
-- eSNMP definitions.
/usr/examples/esnmp/*
-- Example code.
The Management Information Base (MIB) defines a set of data elements that relate to network management. Many of these are standardized in the RFCs that are produced as a result of the Internet Engineering Task Force (IETF) working group standardization effort of the Internet Society.
The data elements defined in the RFCs are identified using a naming scheme with a hierarchical structure. Each name at each level of the hierarchy has a number associated with it. You can refer to the data elements in the MIB definitions by name or by their corresponding sequence of numbers, which are called the Object Identifier (OID). You can extend an OID for a specific data element further by adding more numbers to identify a specific instance of the data element. The entire collection of managed data elements is called the MIB tree.
Each SNMP agent implements those MIB elements that pertain to the device being managed, plus a few common MIB elements. These are the supported MIB tree elements. An extensible SNMP agent is one that permits its supported MIB tree to be distributed among various processes and change dynamically.
The eSNMP consists of a single master agent and any number of subagents.
The master agent handles the SNMP protocols, supports MIBs related to SNMP
itself, and maintains a registry of connected subagents and the MIB subtrees
they support.
The master agent for eSNMP is the daemon process
/usr/sbin/snmpd
.
Tru64 UNIX provides subagents that implement several different MIBs,
both IETF standard and proprietary to Compaq.
Refer to the Software
Product Description (SPD),
Technical Overview, and the
snmpd
(8)
reference page for more information.
These subagents (and any third-party
subagents) together with the master agent function as a single SNMP agent
for the host.
6.1.2 Architecture
The master agent listens on the preassigned User Datagram
Protocol (UDP) port for an incoming SNMP request.
When the master agent receives
an SNMP request, it authenticates it against the local security database and
handles any authentication or protocol errors.
If the request is valid, the
snmpd
daemon consults its MIB registry.
(See
snmpd
(8)
for more
information.) For each MIB object contained in the request it determines which
registered MIB could contain that object and which subagent has registered
that MIB.
The master agent then builds a series of messages; one for each
subagent that will be involved in this SNMP request.
Each subagent program is linked with the shareable library
libesnmp.so
.
This library contains the protocol implementation
that enables communication between the master agent and the subagent.
This
code parses the master agent's message and consults its local object table.
The object table is a data structure that is defined and initialized
in code emitted by the
snmpi
and
mosy
MIB compiler tools.
It contains an entry for each MIB object that is contained
in the MIBs implemented in that subagent.
One part of an object table entry
is the address of a function that services requests for the MIB object.
These
functions are called method routines.
The eSNMP library code calls into the indicated method routine for each of the MIB variables in the master agent's message. The eSNMP library code creates a response packet based on the function return values and sends it back to the master agent.
The master agent starts a timer and assembles the response packets from
all involved subagents.
The master agent may rebuild and resend a new set
of subagent messages, depending on the specific request; for example, a
GetNext
request.
When the master agent has all required data or
error responses or has timed out waiting for a response from a subagent, it
builds an SNMP response message and sends it to the originating SNMP application.
The interaction between the master agent and subagent is invisible to the
requesting SNMP management application.
Subagent programs are linked against the
libesnmp.so
shareable library, which performs all protocol handling and dispatching.
Subagent developers need to code method routines for their defined MIB objects
only.
6.1.3 SNMP Versions
Extensible SNMP support for SNMPv2c exists in the following areas. This is based on RFCs 1901 through 1908, inclusive:
The MIB tools (the
mosy
and
snmpi
programs) support SNMPv2c Structure of Management Information for
SNMPv2 (SMIv2) and textual conventions.
The eSNMP library API supports SNMPv2c, variable binding exceptions, and error codes.
The master agent currently supports SNMPv1 and SNMPv2c in a bilingual manner. All SNMPv2c-specific information from the subagent is mapped, when necessary, into SNMPv1-adherent data according to RFC 2089. For example, if a management application makes a request using SNMPv1 PDUs, the master agent replies using SNMPv1 PDUs, mapping any SNMPv2c SMI items received from subagents. This means that subagents created with a previous version of the eSNMP API do not require any code changes and do not have to be recompiled.
All communication by the
libesnmp.so
library to and
from the master agent is done through an implementation of
RFC 2257,
Agent Extensibility (AgentX) Version 1.
RFC 2257 defines a standard protocol
for communications between extensible agent components, a master agent and
multiple subagents.
This means that subagents that use the eSNMP API will
function correctly with no modification if a different vendor's master agent
(one that conforms to RFC 2257) is running on the Tru64 UNIX host.
6.2 Overview of the Extensible SNMP Application Programming Interface
The subagent's functions are to establish communications with the master agent, register the MIB subtrees that it intends to handle, and process requests from the master agent. It must also be able to send SNMP traps on behalf of the host application.
The subagent consists of the following:
A main function written by the developer
The eSNMP library routines that perform the AgentX protocol work
The method routines written by the developer that handle specific MIB elements
The object table structures generated from MIB definition
files using the
mosy
and
snmpi
programs
The subagent is usually embedded within an application, such as a router daemon. Subagent processing is only a small part of the work performed by the process. In this case, the main event loop of the application makes the calls into the eSNMP library. In other cases, the subagent is a standalone daemon process that has its own main routine.
While processing a packet received from the master agent, the eSNMP
library calls the specified method routine for each requested MIB variable.
Each defined MIB variable in the subagent's object table has a pointer to
the method routine for handling requests on that MIB variable.
Since the
object tables are generated by the
mosy
and
snmpi
programs, the method routine names are static.
The eSNMP developer's kit provided with the operating system consists of the following:
/usr/sbin/mosy
-- MIB compiler utility
/usr/sbin/snmpi
-- Object table code
generator utility
/usr/examples/esnmp/mib-converter.sh
--
MIB text extraction tool
/usr/shlib/libesnmp.so
-- eSNMP shared
library
/usr/include/esnmp.h
-- eSNMP definitions
file
/usr/examples/esnmp/*
-- Subagent
example source code
The eSNMP shared library (libesnmp.so
) provides the
following services:
Master-agent to subagent protocol handling routines
Routines for communicating with the master agent on behalf of the subagent, as follows:
esnmp_init
-- Initializes the protocol
(performs a handshake with the master agent)
esnmp_allocate
-- Requests the master
agent to allocate a value for one or more specific index objects (OIDs)
esnmp_deallocate
-- Requests the master
agent to deallocate a value or values for one or more index objects (OIDs)
esnmp_register
-- Registers a MIB
subtree with the master agent
esnmp_poll
-- Processes a packet from
the master agent
esnmp_trap
-- Requests the master
agent to generate an SNMP trap
esnmp_are_you_there
-- Pings the master
agent
esnmp_unregister
-- Unregisters a
MIB subtree
esnmp_term
-- Ends communication with
the master agent and terminates extensible SNMP
esnmp_sysuptime
-- Time handling and
synchronization
Support routines useful for developing method routines. See Section 6.3 for a complete list and description of each eSNMP support routine.
The
esnmp.h
header file is associated with
the eSNMP library.
This file defines all data structures, constants, and
function prototyes required to implement subagents to this API.
Understanding MIB subtrees is crucial to understanding the eSNMP API and how your subagent will work.
Note
This section assumes that you understand the OID naming structure used in SNMP. If not, refer to RFC 1902: Structure of Management Information for Version 2 of the Simple Network Management Protocol (SNMPv2).
The information in SNMP is structured hierarchically like an inverted tree. In this hierarchy, data is only associated with leaf nodes. Each node has a name and a number. Each node can also be identified by an OID, which is a concatenation of the non-negative numbers, called sub-identifiers, that exist on the path from the root node down to that node in the tree. An OID must be at least two sub-identifiers in length, and can be at most 128 sub-identifiers in length. Valid sub-identifier 1 values range from 0 to 2, inclusive; sub-identifier 2 values range from 0 to 39, inclusive; and the remaining sub-identifier values can be any non-negative number.
For example, the
chess
MIB provided with the sample
code in the
/usr/examples/esnmp
directory has an element
with the name
chess
.
The OID for the element
chess
is
1.3.6.1.4.1.36.2.15.2.99
, which is derived
from its position in the hierarchy of the tree:
iso(1) org(3) dod(6) internet(1) private(4) enterprise(1) digital(36) ema(2) sysobjects(15) decosf(2) chess(99)
Any node in the MIB hierarchy can define a MIB subtree.
All elements
within the subtree have an OID that starts with the OID of the subtree base.
For example, if we define
chess
to be a MIB subtree base,
the elements with the same prefix as the
chess
OID are
all within the MIB subtree:
chess 1.3.6.1.4.1.36.2.15.2.99 chessProductID 1.3.6.1.4.1.36.2.15.2.99.1 chessMaxGames 1.3.6.1.4.1.36.2.15.2.99.2 chessNumGames 1.3.6.1.4.1.36.2.15.2.99.3 gameTable 1.3.6.1.4.1.36.2.15.2.99.4 gameEntry 1.3.6.1.4.1.36.2.15.2.99.4.1 gameIndex 1.3.6.1.4.1.36.2.15.2.99.4.1.1 gameDescr 1.3.6.1.4.1.36.2.15.2.99.4.1.2 gameNumMoves 1.3.6.1.4.1.36.2.15.2.99.4.1.3 gameStatus 1.3.6.1.4.1.36.2.15.2.99.4.1.4 moveTable 1.3.6.1.4.1.36.2.15.2.99.5 moveEntry 1.3.6.1.4.1.36.2.15.2.99.5.1 moveIndex 1.3.6.1.4.1.36.2.15.2.99.5.1.1 moveByWhite 1.3.6.1.4.1.36.2.15.2.99.5.1.2 moveByBlack 1.3.6.1.4.1.36.2.15.2.99.5.1.3 moveStatus 1.3.6.1.4.1.36.2.15.2.99.5.1.4 chessTraps 1.3.6.1.4.1.36.2.15.2.99.6 moveTrap 1.3.6.1.4.1.36.2.15.2.99.6.1
It is this MIB subtree base that is registered with the master agent to tell it that this subagent handles all requests related to the elements within the subtree.
The master agent expects a subagent to handle all objects subordinate to the registered MIB subtree. This principle guides your choice of MIB subtrees.
For example, registering a subtree of
chess
is reasonable
because it is realistic to assume that the subagent could handle all requests
for elements in this subtree.
Registering an entire application-specific
MIB usually makes sense because the particular application expects to handle
all objects defined in the application-specific MIB.
Registering a subtree of
transmission
(under MIB-2)
would be a mistake, because it is unlikely that the subagent is prepared to
handle every defined MIB object subordinate to transmission (FDDI, Token Ring,
and so on).
A subagent may register as many MIB subtrees as it wants. It can register OIDs that overlap with other registrations by itself or other subagents; however, it cannot register the same OID more than once. Subagents can register and unregister MIB subtrees at any time after communication with the master agent is established.
Normally it is the non-leaf nodes that are registered as a subtree with the master agent. However, leaf nodes (those representing a MIB variable that can have an instance and an associated value), or even specific instances, can be registered as a subtree.
The master agent delivers requests to the subagent that has the MIB
subtree with the longest prefix and the best priority.
6.2.2 Object Tables
The
mosy
and
snmpi
utilities are used to generate the C language code that defines
the object tables from the MIBs.
The object tables are defined in the emitted
files
subtree_tbl.h
and
subtree_tbl.c
, files that are compiled into your subagent.
These modules are created by the utilities; it is recommended that you
do not edit them.
If the MIBs change or a future version of the eSNMP development
utilities require your object tables to be rebuilt, it is easier to rebuild
the files and recompile them if you did not edit the files.
6.2.2.1 The subtree_tbl.h File
The
subtree_tbl.h
file contains the following information:
A declaration of the MIB subtree structure
Index definitions for each MIB variable in the MIB subtree
Enumeration definitions for MIB variables with enumerated values
MIB group data structure definitions
Method routine function prototypes
Declaration of the MIB Subtree Structure
The MIB subtree is automatically initialized by code in the
subtree_tbl.c
file.
A pointer to this structure is
passed to the
esnmp_register
routine to register a MIB
subtree with the master agent.
All access to the object table for this MIB
subtree is through this pointer.
The declaration has the following form:
extern
SUBTREE
subtree_subtree;
Index Definitions
Index definitions for each MIB variable in the
SUBTREE
have the following:
#define I_mib-variable nnnn
These values are unique for each MIB variable within a MIB subtree and
are the index into the object table for this MIB variable.
These values are
also generally used to differentiate between variables that are implemented
in the same method routine so they can be used in a switch operation.
Enumeration Definitions
Enumeration definitions for those MIB variables that are defined with "SYNTAX INTEGER" and enumerated values have the following form:
#define D_mib-variable_enumeration-name value
Enumeration definitions are useful since they describe the architected value that enumerated integer MIB variables may take on; for example:
/* enumerations for gameEntry group */ #define D_gameStatus_complete 1 #define D_gameStatus_underway 2 #define D_gameStatus_delete 3
Data Structure Definitions
MIB group data structure definitions have the following form:
typedef struct xxx
{type mib-variable;
.
.
.char mib-variable_mark;
.
.
.
}mib-group_type
A data structure is emitted for each MIB group within the MIB subtree.
Each structure definition contains a field representing each MIB variable
within the group.
If the MIB variable name is not unique within the pool
of MIBs presented to the
snmpi
program at the time the
subtree_tbl.h
file is built,
the
snmpi
program does not qualify the name with the name
of its parent variable (group name) to make it unique.
In addition to the
MIB variable fields, the structure includes a 1-byte
mib-variable_mark
field for each variable.
You can
use these for maintaining status of a MIB variable; for example, the following
is the group structure for the
chess
MIB:
typedef struct _chess_type { OID chessProductID; int chessMaxGames; int chessNumGames; char chessProductID_mark; char chessMaxGames_mark; char chessNumGames_mark; } chess_type;
MIB group structures are provided for convenience, but are not mandatory.
You can use whatever structure is easiest for you in your method routine.
Method Routine Function Prototypes
Each MIB group within the MIB subtree has a method routine prototype defined. A MIB group is a collection of MIB variables that are leaf nodes and share a common parent node.
There is always a function prototype for the method routine that handles
the
Get
,
GetNext
, and
GetBulk
operations.
If the group contains any writable variables, there
is also a function prototype for the method routine that handles
Set
operations.
Pointers to these routines appear in the MIB subtree's
object table, which is initialized in the
subtree_tbl.c
module.
You must write method routines for each prototype that
is defined, as follows:
extern int mib-group_get(
METHOD *method
);
extern int mib-group_set(
METHOD *method
);
For example:
extern int chess_get(METHOD *method); extern int chess_set(METHOD *method);
Method routines are discussed in more detail in
Section 6.3.2.3.
6.2.2.2 The subtree_tbl.c File
The
subtree_tbl.c
file contains the following information:
An array of integers representing the OIDs for each MIB variable
An array of
OBJECT
structures.
(See
esnmp.h
.)
The initialized
SUBTREE
structure
Array of Integers
The array of integers used as the OIDs of each MIB variable in the MIB subtree has the following form:
static unsigned int elems[] = { ...
OBJECT Structures
There is one
OBJECT
for each MIB variable within
the MIB subtree.
(See
esnmp.h
.)
An
OBJECT
represents a MIB variable and has the following
fields:
object_index
-- The constant
I_mib-variable
from the
subtree_tbl.h
file.
oid
-- The variable's OID (points
to a part of
elems[]
).
type
-- The variable's data type.
getfunc
-- The address of method routine
to call for
Get
operations.
setfunc
-- The address of method routine
to call for
Set
operations.
The master agent has no knowledge of object tables or MIB variables.
It only maintains a registry of MIB subtrees.
When a request for a particular
MIB variable arrives, it is processed as follows.
In the following procedure,
the MIB variable is
mib_var
and the MIB subtree is
subtree_1
:
The master agent finds
subtree_1
as the
authoritative region for the
mib_var
in the registry of
MIB subtrees.
The authoritative region is determined as the registered MIB
subtree that has the longest prefix and the best priority.
The master agent sends an eSNMP message to the subagent that
registered
subtree_1
.
The subagent consults its list of registered MIB subtrees
and locates
subtree_1
.
It searches the object table of
subtree_1
and locates the following:
mib_var
(for
Get
and
Set
requests)
The first object lexicographically after
mib_var
(for
GetNext
or
GetBulk
requests)
The subagent calls the appropriate method routine.
If the
method routine completes successfully, the data is returned to the master
agent.
If not, for
Get
or
Set
, an error
is returned.
For
GetNext
or
GetBulk
,
the
libesnmp
library code keeps trying subsequent objects
in the object table of
subtree_1
until a method routine
returns success or the table is exhausted.
In either case an appropriate
response is returned.
If the master agent detects
subtree_1
could
not return data on a
GetNext
or
GetBulk
routine, it iteratively tries the MIB subtree lexicographically after
subtree_1
until a subagent returns a value or the registry of MIB
subtrees is exhausted.
Initialized SUBTREE Structure
A pointer to the
SUBTREE
structure is passed to the
esnmp_register
eSNMP library routine to register the MIB subtree.
It is through this pointer that the library routines find the object structures.
The following is an example of the
chess
subtree structure:
SUBTREE chess_subtree = { "chess", "1.3.6.1.4.1.36.2.15.2.99", { 11, &elems[0] }, objects, I_moveStatus};
The
SUBTREE
structure has the following elements:
name -- The name of the base node of the MIB subtree.
dots -- The ASCII string representation of the MIB subtree's OID; it is what actually gets registered.
oid -- The OID of the base node of the subtree; it points back to the array of integers.
object_tbl
-- A pointer to
the array of objects in the object table.
It is indexed by the
I_xxxx
definitions found in the
subtree_tbl.h
file.
last
-- The index of the last
object in the
object_tbl
file.
It is used to determine
when the end of the table has been reached.
The final section of the
subtree_tbl.c
contains short routines for allocating and freeing the
mib-group_type
structures.
These are provided as
a convenience and are not a required part of the API.
6.2.3 Implementing a Subagent
As a subagent developer,
you are usually presented with a UNIX application, daemon, or driver (such
as the
gated
daemon or ATM driver) and need to implement
an SNMP interface.
The following steps explain how you do this:
Obtain a MIB specification.
MIB development starts with a MIB specification, usually in the form of RFCs. For SNMPv1, the specifications are written in concise MIB format according to RFC 1212. For SNMPv2c, the specifications are written in SMIv2 and the textual conventions as specified in RFC 1902 and RFC 1903, respectively. Designing and specifying a MIB is beyond the scope of this document; it is assumed you have a MIB specification.
The standard RFCs can be obtained from the RFC editor at the following URL:
http://www.rfc-editor.org/rfc.html
If you have to build your own MIB specification, you can look at a similar MIB written by another vendor. One source for public MIBs is in the archives section of the Network Management page at the following URL:
http://smurfland.cit.buffalo.edu/NetMan/index.html
You need MIBs for all of the elements you are implementing
in the subagent and for any elements referenced by these MIBs (such that all
element names resolve to the OID numbers).
As a minimum you will need the
SMIv2 MIB,
snmp-smi.my
, and the textual conventions,
snmp-tc.my
.
These are in the
/usr/examples/esnmp
directory.
Compile your MIBs.
Once you obtain MIB definitions, use them to generate the object tables for your new subagent. The objective is to take the MIB specification text for each of the MIBs, extract the ASN.1 specifications, and compile them into C language modules that contain the local object tables.
Compile your MIBs using the following tools:
mib-converter.sh
The
mib-converter.sh
is an
awk
shell script that extracts the MIB ASN.1 definitions from the RFC text.
This
step removes the text before and after the MIB definition and removes page
headings and footings.
The
mib-converter.sh
script may not remove everything
that needs to be removed; therefore, you may need to remove some things manually,
using a text editor.
The following is an example of how to use the
mib-converter.sh
script:
#
/usr/examples/esnmp/mib-converter.sh mib-def.txt > \ mib-def.my
Be careful; some
RFCs contain more than one MIB definition.
You can only use the
mib-converter.sh
script shell on RFCs that contain a single MIB
definition.
The
mosy
compiler may not handle it either.
If you use an RFC that contains more than one MIB definition, make each one
into a separate input file.
The resulting output files containing the extracted
MIB ASN.1 definitions should be in the following form:
mib-def.my
mosy
The
mosy
compiler parses
.my
files
created by the
mib-converter.sh
script and compiles them
into
.defs
files.
The
.defs
files describe
the object hierarchy within the MIB.
The
.defs
files are
front-ends to several tools.
The following is an example of how to use the
mosy
compiler:
#
mosy mib-def.my
The
mosy
compiler produces
mib-def.defs
files.
The
mosy
program is taken from ISODE 8.0 (distributed
with the 4BSD/ISODE SNMPv2 package).
snmpi
The MIB data initializer creation program (snmpi
)
reads a concatenation of the
.def
files compiled by the
mosy
compiler and generates the C code to define the static structures
of the object table for a specified MIB subtree.
Note
The
snmpi
program supplied with this operating system is different from thesnmpi
program in 4BSD/ISODE SMUX.
Concatenate the
.def
files the
mosy
compiler compiles into the
objects.defs
file.
Be sure
to include the compiled versions of
snmp-smi.my
and
snmp-tc.my
.
The
objects.defs
file must contain
enough MIBs to resolve all MIB names, even if they are not used by your subtrees.
Then generate the object table files using the following command:
#
/usr/sbin/snmpi objects.defs subtree
The
snmpi
program
has a print option that allows you to dump the contents of the entire tree
generated as a result of the objects it finds into the
objects.defs
file.
If you are having trouble with the subtrees you may find
this to be helpful.
Use the following command to generate a listing:
#
/usr/sbin/snmpi -p objects.defs > objects.txt
The
snmpi
program outputs the
subtree_tbl.c
and
subtree_tbl.h
;
subtree
is the name of the base MIB
variable name for a MIB subtree.
These two files are C code used to initialize
the MIB object table for the specified subtree.
(This is the local object
table.) Repeat this process for each MIB subtree being implemented in your
subagent.
Note that the
snmpi
program defaults to using
MIB groups as the level of granularity for method routines; that is, the assumption
is made that all MIB variables within a group should be serviced by the same
method routine.
(It also provides the
mib-group_type
data structure to help do this.)
The
mib-group_type
structure
is not part of the API; it is provided as a convenience.
It is helpful to
use the
mib-group
organization of the object table.
This is because, generally, those objects are logically related and usually
accessed as a group; for example,
ipRoutes
are returned
more or less complete from the kernel routing tables.
Code the method routines and the API calls.
Write the code that calls the eSNMP library API to initialize communications
with the master agent (snmpd
), and register your MIBs.
(See
Section 6.2.4.)
Write the code for the required method routines.
(See
Section 6.3.)
Usually you need one
Get
method routine and one
Set
method routine for each MIB group within your registered MIB
subtree.
The
subtree_tbl.h
files generated in the previous step define the names and function prototype
for each method routine you need.
Build the subagent.
An example Makefile,
chess.mk
, is provided in the
/usr/examples/esnmp
directory.
Execute and test your subagent.
Run your subagent like any other program or daemon.
There are trace
facilities built into the eSNMP library routines to assist in the debugging
process.
Use the
set_debug_level
routine in the
main
section to enable the trace.
Once the subagent has initialized and successfully registered a MIB
subtree, you can send SNMP requests using standard applications.
For example, Compaq
Insight Manager, HP Openview, or any MIB browser.
If you do not have access
to SNMP applications, you can use the
snmp_request
,
snmp_traprcv
, and
snmp_trapsnd
programs to help
debug subagents.
Note that if you interactively debug, your subagent will probably cause SNMP requests to timeout.
Normally all error and warning messages are recorded in the system's
daemon log.
When running the sample
chess
subagent and
the
os_mibs
subagent, you specify a trace run-time argument,
as follows:
os_mibs -trace
With
the trace option active, the program does not run as a daemon and all trace
output goes to
stdout
; it displays each message that is
processed.
You can use this feature in your own subagents by calling the
set_debug_level
routine and pass it the
TRACE
parameter.
Anything passed in the debug macro is sent to
stdout
,
as follows:
ESNMP_LOG
((TRACE, ("message_text \n"));
To send everything to the daemon log, call
the
set_debug_level
routine and pass it the
WARNING
|| DAEMON_LOG
parameter or the
set_debug_level
routine and pass it the
ERROR || DAEMON_LOG
parameter to
suppress warning messages.
6.2.4 Subagent Protocol Operations
The eSNMP API provides for autonomous subagents that
are not closely tied to the master agent (snmpd
).
Subagents
can be part of other subsystems or products and have primary functions not
related to SNMP.
For instance, the
gated
daemon is primarily
concerned with Internet routing; however it also functions as a subagent.
In particular, the
snmpd
daemon does not start or
stop any subagent daemons during its startup or shutdown procedures.
It also
does not maintain any on-disk configuration information about subagents.
Whenever the
snmpd
daemon starts, it has no knowledge of
previous subagent or MIB subtree registrations.
Typically all daemons on the operating system system are started or
stopped together, as the system changes run levels.
But subagents should
correctly handle situations where they start before the
snmpd
daemon, or are running while the
snmpd
daemon is restarted
to reload information from its configuration file.
In these situations subagents
need to restart the eSNMP protocol as described in the following sections.
6.2.4.1 Order of Operations
The sequence of subagent protocol operations are as follows:
Initialization (esnmp_init
)
Allocate any needed indexes for tables shares across multiple
subagents (esnmp_allocate
)
Registration (esnmp_register [esnmp_register ...]
)
Data communication
The following loop happens continuously:
{ determine sockets with data pending if the eSNMP socket has data pending esnmp_poll periodically call esnmp_are_you_there as required during periods of inactivity }
Termination (esnmp_term
)
Note that it is very important that subagents call the
esnmp_term
function when they are stopping.
This enables eSNMP to free system
resources being used by the subagent.
The example subagent in the
/usr/examples/esnmp
directory
shows how to code subagent protocol operations.
6.2.4.2 Function Return Values
The eSNMP API function return values indicate to a subagent both the success or failure of the requested operation and the state of the master agent. The following list provides a description of each return value and the indicated subagent actions:
ESNMP_LIB_OK
The operation was successful.
ESNMP_LIB_NO_CONNECTION
The connection between the subagent and the master agent could not be
initiated.
This value is returned by the
esnmp_init
function.
Causes -- The master agent is not running or is not responding.
Action -- Restart the protocol by calling the
esnmp_init
function again after a suitable delay.
ESNMP_LIB_BAD_ALLOC
Cannot allocate the index or indexes.
This value is returned by the
esnmp_allocate
function.
Causes are as follows:
The
esnmp_init
function has not been successfully
called prior to calling the
esnmp_allocate
function.
One of the parameters in the
esnmp_allocate
function is invalid.
See the log file to determine which parameter is invalid.
Action -- Call the
esnmp_allocate
function in the proper sequence and with correct arguments.
ESNMP_LIB_BAD_DEALLOC
Cannot deallocate the index or indexes.
This value is returned by the
esnmp_deallocate
function.
Causes are as follows:
The
esnmp_init
function has not been successfully
called prior to calling the
esnmp_allocate
function.
One of the parameters in the
esnmp_allocate
function is invalid.
See the log file to determine which parameter is invalid.
Action -- Call the
esnmp_deallocate
function in the proper sequence and with correct arguments.
ESNMP_LIB_LOST_CONNECTION
Lost communications with the master agent.
This value is returned by
the
esnmp_register
,
esnmp_poll
,
esnmp_are_you_there
,
esnmp_unregister
, and
esnmp_trap
functions.
Causes -- An attempt to send a packet to the master agent's socket failed; this is normally due to the master agent terminating abnormally.
Action -- Restart the protocol by calling the
esnmp_init
function after a suitable delay.
ESNMP_LIB_BAD_REG
The attempt to send a registration failed.
This value is returned by
the
esnmp_register
,
esnmp_unregister
,
and
esnmp_poll.
functions.
Causes are as follows:
The
esnmp_init
function has not been successfully
called prior to calling the
esnmp_register
function.
The
timeout
parameter in the
esnmp_register
function is invalid.
The subtree passed to the
esnmp_register
function has already been queued for registration or has been registered by
this subagent.
A previous registration was failed by the master agent (when
returned by the
esnmp_poll
function).
See the log file
to determine the details regarding why it failed and which subtree was at
fault.
Trying to unregister a subtree that was not registered (esnmp_unregister
).
Action -- Call the
esnmp_register
function in the proper sequence and with correct arguments.
ESNMP_LIB_CLOSE
The master agent is stopping.
This value is returned by the
esnmp_poll
function.
Causes -- The master agent is beginning an orderly shutdown.
Action -- Restart the protocol with the
esnmp_init
function as suited by the subagent.
ESNMP_LIB_NOTOK
An eSNMP protocol error occurred and the packet was discarded.
This
value is returned by the
esnmp_poll
and
esnmp_trap
functions.
Causes -- This indicates a packet-level protocol error within eSNMP, probably due to lack of memory resources within the subagent.
Action -- Continue.
6.3 Extensible SNMP Application Programming Interface
The following sections provide detailed information on the SNMP Application Programming Interface, which consists of the following:
Calling interface
Method routine calling interface
The
libesnmp
support routines
The calling interface contains the following routines:
esnmp_init
esnmp_allocate
esnmp_deallocate
esnmp_register
esnmp_register2
esnmp_unregister
esnmp_unregister2
esnmp_capabilities
esnmp_uncapabilities
esnmp_poll
esnmp_are_you_there
esnmp_trap
esnmp_term
esnmp_sysuptime
6.3.1.1 The esnmp_init Routine
The
esnmp_init
routine locally initializes the eSNMP
subagent, and initiates communication with the master agent.
This call does not block waiting for a response from the master agent.
After calling the
esnmp_init
routine, call the
esnmp_register
routine for each MIB subtree that is to be handled
by this subagent.
Call this routine during program initialization or to restart the eSNMP
protocol.
If you are restarting, the
esnmp_init
routine
clears all registrations so each subtree must be reregistered.
You should attempt to create a unique
subagent_identifier, perhaps using the program name (argv[0]
)
and additional descriptive text.
The syntax for the
esnmp_init
routine is as follows:
int esnmp_init(
int *socket,
char *subagent_identifier
);
The arguments are defined as follows:
The address of the integer that receives the socket descriptor used by eSNMP.
The address of a null-terminated string that identifies this subagent (usually program name).
The return values are as follows:
Could not initialize or communicate with the master agent. Try again after a delay.
The
esnmp_init
routine has completed successfully.
Could not allocate memory for the subagent.
The following is an example of the
esnmp_init
routine:
#include <esnmp.h> int socket; status = esnmp_init(&socket, "gated");
6.3.1.2 The esnmp_allocate Routine
The
esnmp_allocate
routine requests the allocation
of a value for one or more specific index objects.
Index allocation is a service provided by an AgentX-compliant master agent. It provides generic support for sharing MIB conceptual tables among subagents who are assumed to have no knowledge of each other. The master agent maintains a database of index objects (OIDs) and the values that have been allocated for each index. The master agent is unaware of what MIB variables (if any) the index objects represent. By convention, subagent developers should use the MIB variable listed in the INDEX clause as the index object for which values must be allocated.
For tables indexed by multiple variables, values may be allocated for each index although this is frequently unnecessary. The subagent may request one of the following types of allocation:
A specific index value
An index value that is not currently allocated
An index value that has never been allocated
The last two alternatives reflect the uniqueness and constancy
requirements present in many MIB specifications for arbitrary integer indexes;
for example,
ifIndex
in the IF-MIB (RFC 2233),
snmpFddiSMTIndex
in the FDDI MIB (RFC 1285), and
sysApplInstallPkgIndex
in the System Application MIB (RFC 2287).
The need
for subagents to share tables using such indexes is the main motivation for
an index allocation mechanism.
Index allocation and MIB region registration are not coupled in the master agent. The master agent does not consider its current state of index allocations when processing registration requests and does not consider its current registry when processing index allocation requests. This is mainly to accommodate non-AgentX subagents.
Subagent developers should request the allocation of an index first. Then, they should register the corresponding region. When done in this order, a successful index allocation request gives a subagent a good hint (but no guarantee) of what it should be able to register. The registration might fail because some other subagent has already registered that row of the table.
Subagents should register conceptual rows in a shared table in the following order:
Allocate an index value successfully.
Use the allocated index value or values in the
instance
field in the
ESNMP_REG
structure
that is passed to the
esnmp_register2
routine.
In this
way, the eSNMP subagent developer can fully qualify the MIB region or regions
specified by the subtree and any
range_subid
and
range_upper_bound
fields in that
ESNMP_REG
structure.
Subagent developers should set the
priority
field to 255 when attempting registrations with instances.
If the registration fails with a result code of ESNMP_REG_STATE_REGDUP,
deallocate the previously allocated index value or values for this row with
the
esnmp_deallocate
routine and begin the process again at step 1.
Note that index allocation is necessary only when an index is an arbitrary
value and the subagent developer cannot determine which index values to use.
When index values have intrinsic meaning, subagents should not allocate their
index values.
For example, in
RFC 1514
the table of running software processes (hrSWRunTable
) is indexed by the system's native process identifier (pid).
A
subagent implementing the row of
hrSWRunTable
corresponding
to its own process would register the region defining that row's object instances;
it is not necessary to allocate index values.
The syntax for the
esnmp_allocate
routine is as follows:
int esnmp_allocate(
ESNMP_ALLOC *alloc_parm
);
The arguments are as follows:
Is a pointer
to a
ESNMP_ALLOC
structure.
The caller must keep this structure
and its referenced
VARBIND
list persistent (in memory).
This is necessary in order for the eSNMP runtime library to update fields
with the result of the index allocation request provided by the master agent.
The structure contains the following fields:
A pointer to a varable
binding list containing one or more
VARBINDs
.
Each
VARBIND
in the list contains the name of an index object to be allocated
a value.
You must always supply a value for each varbind.
When neither the ESNMP_ALLOC_ANY_INDEX nor the ESNMP_ALLOC_NEW_INDEX
flags are specified, the master agent uses all supplied values when processing
the index allocation request.
When either the ESNMP_ALLOC_ANY_INDEX or the
ESNMP_ALLOC_NEW_INDEX flag is specified, the master agent ignores all supplied
values and generates an appropriate value for each
VARBIND
when processing the index allocation request.
A bitmask of the following values:
The index allocation request is for the cluster context.
This index allocation request
is for a new value.
The master agent will generate a value for each
VARBIND
that has not been used since it started running.
These
values will be passed back in the successful response.
This index allocation request
is for an unused value.
The master agent will generate a value for each
VARBIND
that may or may not have been used since it started running.
These values will be passed back in the successful response.
One of the following
integer values that provides the caller with asynchronous updates of the state
of the index allocation request.
After the return of the
esnmp_poll
routine, the caller can inspect this parameter.
For the following
status codes, the
alloc->error_index
field contains a value
of zero (0):
The index allocation request is currently held locally while waiting for a connection to the master agent to become established.
The routine sent the index allocation request to the master agent (final status still pending).
The master agent successfully
processed and acknowledged the index allocation request.
The subagent can
now use the allocated index value or values in the
esnmp_reg->instance
field that is passed to the
esnmp_register2
call.
For the following status codes, the
alloc->error_index
field contains a non-zero value:
The master agent rejected the index allocation request because of a wrong index type or wrong options.
The master agent rejected the index allocation request because the supplied index object value or values are currently in use.
The master agent rejected the index allocation request because it had no available value or values for the requested index object. This status is returned only if you specify the ESNMP_ALLOC_NEW_INDEX or ESNMP_ALLOC_ANY_INDEX option.
The master agent rejected the index allocation request because the cluster context option is not supported.
The master agent rejected the index allocation request because of other reasons.
Identifies
the
VARBIND
(starting from 1) in the
alloc.vb
varbind list to which the status code applies.
After the return
of the
esnmp_poll
routine, the caller can inspect this
parameter.
The return values are as follows:
The alloc_parm argument was not specified.
The
esnmp_allocate
routine has completed successfully.
The subagent lost communication with the master agent.
The subagent has not established
a connection with the master agent or no valid
VARBIND
list was dereferenced by the
alloc_parm
argument.
A message is also in the log file.
Note that the return value indicates only initiation of an index allocation
request.
The actual status code returned in the master agent's response will
be returned in a subsequent call to the
esnmp_poll
routine
in the
alloc->status
and
alloc->error_index
fields.
The following is an example of the
esnmp_allocate
routine:
#define INDENT_SIZE 4 #define RESPONSE_TIMEOUT 0 /* use the default time set in esnmp_init message */ #define REGISTRATION_PRIORITY 128 /* priority at which the MIB subtree will register */ #define RANGE_SUBID 10 /* the identifier position in oid->elements just after ifEntry */ #define RANGE_UPPER_BOUND 22 /* the identifier for ifSpecific, under ifEntry */ int rc, status; unsigned int our_ifIndex_instance = 0; int ready_to_register = 0; int have_a_good_registration = 0; extern SUBTREE ifEntry_subtree; /* generated by /usr/sbin/snmpi -r ifEntry */ OBJECT *ifIndex_object = &ifEntry_subtree.object_tbl[I_ifIndex]; static ESNMP_ALLOC esnmp_alloc_for_ifIndex; /* retain this structure to obtain status code and index object values. Also, retain this structure for a subsequent call to esnmp_deallocate */ static ESNMP_REG esnmp_reg_for_ifEntry; /* retain this structure for a subsequent call to esnmp_unregister2 */ static OID ifEntry_instance_oid; VARBIND *vb; \* * initialize the ESNMP_ALLOC structure *\ memset(&esnmp_alloc_for_ifIndex, 0, sizeof(ESNMP_ALLOC)); esnmp_alloc_for_ifIndex.options = ESNMP_ALLOC_NEW_INDEX; esnmp_alloc_for_ifIndex.vb = vb = (VARBIND *)malloc(sizeof(VARBIND)); bzero((char *)vb, sizeof(VARBIND)); clone_oid(&vb->name, &ifIndex_object->oid); o_integer(vb, ifIndex_object, (unsigned long)0); /* master will return actual value assigned */ while(!have_a_good_registration) { status = esnmp_allocate(&esnmp_alloc_for_ifIndex ); if (status != ESNMP_LIB_OK) { printf("Could not queue the 'ifIndex' \n"); printf("index object for index allocation\n") }
.
.
.
rc = esnmp_poll();
.
.
.
if (esnmp_alloc_for_ifIndex.status > ESNMP_ALLOC_STATE_SENT) { /* * esnmp_alloc_for_ifIndex.status nows contain the final * status from the master agent. */ switch(esnmp_alloc_for_ifIndex.status) { case ESNMP_ALLOC_STATE_DONE our_ifIndex_instance = esnmp_alloc_for_ifIndex.vb->value.ul; ready_to_register = 1; printf("\n*** Successful index allocation. Our conceptual row in the"); printf("\n*** interfaces table was allocated. ifIndex value is %i\n\n", our_ifIndex_instance); break; case ESNMP_ALLOC_STATE_ALLOCTYPE: printf("\n*** Failed index allocation - 'allocation type'"); printf("\n*** associated with supplied varbind #%i.\n\n", esnmp_alloc_for_ifIndex.error_index); break; case ESNMP_ALLOC_STATE_ALLOCINUSE: printf("\n*** Failed index allocation - 'allocation in use'"); printf("\n*** associated with supplied varbind #%i.\n\n", esnmp_alloc_for_ifIndex.error_index); break; case ESNMP_ALLOC_STATE_ALLOCAVAIL: printf("\n*** Failed index allocation - 'no available values'"); printf("\n*** associated with supplied varbind #%i.\n\n", esnmp_alloc_for_ifIndex.error_index); break; case ESNMP_ALLOC_STATE_ALLOCNOCLU: printf("\n*** Failed index allocation - 'cluster context not supported'"); printf("\n*** associated with supplied varbind #%i.\n\n", esnmp_alloc_for_ifIndex.error_index); esnmp_alloc_for_ifIndex.options &= ~ESNMP_ALLOC_CLUSTER; break; case ESNMP_ALLOC_STATE_REJ: printf("\n*** failed index allocation - 'other reasons'"); printf("\n*** associated with supplied varbind #%i.\n\n", esnmp_alloc_for_ifIndex.error_index); break; } /* End switch */ } /* End if */ | | if (ready_to_register) { vb = esnmp_alloc_for_ifIndex.vb; memset(&esnmp_reg_for_ifEntry, 0, sizeof(ESNMP_REG)); esnmp_reg_for_ifEntry.subtree = &ifEntry_subtree; esnmp_reg_for_ifEntry.priority = REGISTRATION_PRIORITY; esnmp_reg_for_ifEntry.timeout = RESPONSE_TIMEOUT; esnmp_reg_for_ifEntry.range_subid = RANGE_SUBID; esnmp_reg_for_ifEntry.range_upper_bound = RANGE_UPPER_BOUND; ifEntry_instance_oid.nelem = 1; ifEntry_instance_oid.elements = &our_ifIndex_instance; esnmp_reg_for_ifEntry.instance = (OID *)malloc(sizeof(OID)); esnmp_reg_for_ifEntry.instance = clone_oid(esnmp_reg_for_ifEntry.instance, &ifEntry_instance_oid); status = esnmp_register2(&esnmp_reg_for_ifEntry); if (status != ESNMP_LIB_OK) { printf("Could not queue the registration for 'ifEntry'\n"); } else {
.
.
.
rc = esnmp_poll();
.
.
.
if (esnmp_reg_for_ifEntry.state > ESNMP_REG_STATE_SENT) { /* * esnmp_reg_for_ifEntry.status nows contain the final * status from the master agent. */ switch(esnmp_reg_for_ifEntry.state) { case ESNMP_REG_STATE_DONE: printf("\n*** Successful registration for conceptual row in"); printf("\n*** the interfaces table indexed by an ifIndex"); printf("\n*** value of %i.\n\n", our_ifIndex_instance); have_a_good_registration = 1; break; case ESNMP_REG_STATE_REGDUP: printf("\n*** Failed registration - Duplicate registration."); printf("\n*** We need to deallocate this ifIndex value and"; printf("\n*** allocate a new value\n\n"); break; case ESNMP_REG_STATE_REGNOCLU: printf("\n*** Failed registration - Cluster context not supported."); printf("\n*** Need to deallocate this ifIndex value and to"); printf("\n*** allocate a new value in the default context\n\n"); esnmp_alloc_for_ifIndex.options &= ~ESNMP_ALLOC_CLUSTER; break; case ESNMP_REG_STATE_REJ: printf("\n*** Failed registration - Other reasons\n\n"); break; } if (!have_a_good_registration) { esnmp_deallocate(&esnmp_alloc_for_ifIndex); ready_to_register = 0; rc = esnmp_poll(); free_oid(esnmp_reg_for_ifEntry.instance); free(esnmp_reg_for_ifEntry.instance); esnmp_reg_for_ifEntry.instance = NULL; } } } } }
6.3.1.3 The esnmp_deallocate Routine
The
esnmp_deallocate
routine requests the master
agent to deallocate a value or values previously allocated to this subagent
for one or more index objects.
Upon receiving an index deallocation request, the master agent updates its database of index objects (OIDs) by marking the specified values as deallocated. These released values are considered available for assignment by subsequent index allocation requests.
The syntax for the
esnmp_deallocate
routine is as
follows:
int esnmp_deallocate(
ESNMP_ALLOC *alloc_parm
);
The arguments are as follows:
Is a pointer
to an
ESNMP_ALLOC
structure.
Typically, this is the same
structure used in a previous call to
esnmp_allocate
.
The
caller must keep this structure and its referenced
VARBIND
list persistent (in memory).
This is necessary in order for the eSNMP runtime
library to update fields with the result of the index allocation request provided
by the master agent.
The structure contains the following fields:
A pointer to a varable
binding list containing one or more
VARBIND
s.
Each
VARBIND
in the list contains the name and the value of an index
object previously allocated.
A bitmask of the following values:
The index allocation request is for the cluster context.
One of the following
integer values that provides the caller with asynchronous updates of the state
of the index deallocation request.
After the return of the
esnmp_poll
routine, the caller can inspect this parameter.
For the following
status codes, the
alloc->error_index
field contains a value
of zero (0):
The index deallocation request was sent to the master agent (final status still pending).
The master agent successfully processed and acknowledged the index deallocation request. The master agent can now reuse the index value or values when processing a subsequent index allocation request.
For the following status codes, the
alloc->error_index
filed contains a non-zero value:
The master agent rejected the index deallocation request because of an unknown allocation.
The master agent rejected the index deallocation request because of other reasons.
Identifies
the
VARBIND
(starting from 1) in the
alloc.vb
list to which the status code applies.
After the return of the
esnmp_poll
routine, the caller can inspect this parameter.
The return values are as follows:
The alloc_parm argument was not specified.
The
esnmp_allocate
routine has completed successfully.
The subagent lost communication with the master agent.
The subagent has not established
a connection with the master agent or no valid
VARBIND
list was dereferenced by the
alloc_parm
argument.
See the log file.
Note that the return value indicates only initiation of an index deallocation
request.
The actual status code returned in the master agent's response will
be returned in a subsequent call to the
esnmp_poll
routine
in the
alloc->status
and
alloc->error_index
fields.
The following is an example of the
esnmp_deallocate
routine:
#include <esnmp.h> int status; static ESNMP_ALLOC esnmp_alloc_for_ifIndex; /* structure retained from a previous call to esnmp_allocation(). Retain this structure for update with final status from the master agent */ /* call to unregister2() goes here */ esnmp_alloc_for_ifIndex.options = 0; /* clear options */ status = esnmp_deallocate( &esnmp_alloc_for_ifIndex ); if (status != ESNMP_LIB_OK) { printf("Could not queue the 'ifIndex' \n"); printf("index object for index allocation\n"); }
.
.
.
esnmp_poll();
.
.
.
if (esnmp_alloc_for_ifIndex.status > ESNMP_ALLOC_STATE_SENT) { /* * the final status from the master agent is available */ switch(esnmp_alloc_for_ifIndex.status) { case ESNMP_ALLOC_STATE_DONE: printf("Successful index deallocation for value(s) associated\n"); printf("with the 'ifIndex' index object.\n"); free_varbind(esnmp_alloc_forifIndex.vb); break; case ESNMP_ALLOC_STATE_DEALLOC_REJ: printf("Failed index deallocation due to 'unknown allocation'\n"); printf("associated with supplied varbind #%i.\n", esnmp_alloc_for_ifIndex.error_index); break; case ESNMP_ALLOC_STATE_REJ: printf("Failed index deallocation due to 'other reasons'\n"); printf("associated with supplied varbind #%i.\n", esnmp_alloc_for_ifIndex.error_index); break; } }
6.3.1.4 The esnmp_register Routine
The
esnmp_register
routine requests registration
of a single MIB subtree.
This indicates to the master agent that the subagent
instantiates MIB variables within the registered MIB subtree.
The initialization routine (esnmp_init
) must be called
prior to calling the
esnmp_register
routine.
The
esnmp_register
function must be called for each
SUBTREE
structure corresponding to each MIB subtree that it will be handling.
At any time MIB subtrees can be unregistered by calling
esnmp_unregister
and then be reregistered by calling
esnmp_register
.
When restarting the eSNMP protocol by calling
esnmp_init
,
all MIB subtree registrations are cleared.
All MIB subtrees must be reregistered.
A MIB subtree is identified by the base MIB variable name and its corresponding
OID.
This tuple represents the parent of all MIB variables that are contained
in the MIB subtree; for example, the MIB-2
tcp
subtree
has an OID of
1.3.6.1.2.1.6
.
All MIB variables subordinate
to this (those that have the same first 7 identifiers) are included in the
subtree's region.
A MIB subtree can also be a single MIB variable (a leaf
node) or even a specific instance.
By registering a MIB subtree, the subagent indicates that it will process SNMP requests for all MIB variables (or OIDs) within that MIB subtree's region. Therefore, a subagent should register the most fully qualified (longest) MIB subtree that still contains its instrumented MIB variables.
The master agent requires that a subagent cannot register the same MIB subtree more than once. Other than this one restriction, a subagent may register MIB subtrees that overlap the OID range of MIB subtrees that it previously registered or those of MIB subtrees registered by other subagents.
For example, consider the two Tru64 UNIX daemons,
os_mibs
and
gated
.
The
os_mibs
daemon
registers the
ip
MIB subtree (1.3.6.1.2.1.4) and the
gated
daemon registers the
ipRouteEntry
MIB subtree
(1.3.6.1.2.1.4.21.1).
Requests for
ip
MIB variables within
ipRouteEntry
, such as
ipRouteIfIndex
(1.3.6.1.2.1.4.21.1.2),
are passed to the
gated
subagent.
Requests for other
ip
variables, such as
ipNetToMediaIfIndex
(1.3.6.1.2.1.4.22.1.1),
are passed to the
os_mibs
subagent.
If the
gated
process should terminate or unregister the
ipRouteEntry
MIB subtree, subsequent requests for
ipRouteIfIndex
will go to the
os_mibs
subagent because the
ip
MIB subtree, which includes all
ipRouteEntry
MIB variables, would now be the authoritative region of requests for
ipRouteIfIndex
.
When the master agent receives a SIGUSR1 signal, it puts its MIB registry
in to the
/var/tmp/snmpd_dump.log
file.
See
snmpd
(8)
for more information.
The syntax for the
esnmp_register
routine is as follows:
int esnmp_register(
SUBTREE *subtree,
int timeout,
int priority
);
The arguments are defined as follows:
A pointer to
a
SUBTREE
structure corresponding to the MIB subtree to
be handled.
The
SUBTREE
structures are externally declared
and initialized in the code emitted by the
mosy
and
snmpi
utilities (xxx_tbl.c
and
xxx_tbl.h
, where
xxx
is the name of the MIB subtree) taken directly from the
MIB document.
Note
All memory pointed to by the subtree fields must have permanent storage since it is referenced by
libesnmp
for the duration of the program. You should use the data declarations emitted by thesnmpi
utility.
The number of seconds the master agent should wait for responses when requesting data in this MIB subtree. This value must be between zero (0) and three hundred (300). If the value is zero (0), the default timeout is used (3 seconds). You should use the default.
The registration priority. The entry with the largest number has the highest priority. The range is 1 to 255. The subagent that has registered a MIB subtree that has the highest priority over a range of Object Identifiers (OIDs) gets all requests for that range of OIDs.
MIB subtrees that are registered with the same priority are considered duplicates, and the registration is rejected by the master agent.
The priority argument is a mechanism for cooperating subagents to handle different configurations.
The return values are as follows:
The
esnmp_register
routine has completed successfully.
The
esnmp_init
routine has not been called, the timeout parameter is invalid, or this MIB
subtree has already been queued for registration.
The subagent lost communication with the master agent.
Note that the status indicates only the initiation of the request.
The actual status returned in the master agent's response will be returned
in a subsequent call to the
esnmp_poll
routine.
The following is an example of the
esnmp_register
routine:
#include <esnmp.h> #define RESPONSE_TIMEOUT 0 /* use the default time set in esnmp_init message */ #define REGISTRATION_PRIORITY 10 /* priority at which subtrees will register */ int status; extern SUBTREE ipRouteEntry_subtree; status = esnmp_register( &ipRouteEntry_subtree, RESPONSE_TIMEOUT, REGISTRATION_PRIORITY ); if (status != ESNMP_LIB_OK) { printf ("Could not queue the 'ipRouteEntry' \n"); printf ("subtree for registration\n"); }
6.3.1.5 The esnmp_unregister Routine
The
esnmp_unregister
routine unregisters a MIB subtree
with the master agent.
This routine can be called by the application code to tell the eSNMP
subagent not to process requests for variables in this MIB subtree anymore.
You can later reregister a MIB subtree, if needed, by calling the
esnmp_register
routine.
The syntax for the
esnmp_unregister
routine is as
follows:
int esnmp_unregister(
SUBTREE *subtree
);
The arguments are as follows:
A pointer to
the
SUBTREE
structure for the MIB subtree to be unregistered.
The return values are as follows:
The routine completed successfully.
The MIB subtree was not registered.
The request to unregister the MIB subtree could not be sent. You should restart the protocol.
The following is an example of the
esnmp_unregister
routine:
#include <esnmp.h> int status extern SUBTREE ipRouteEntry_subtree; status = esnmp_unregister( &ipRouteEntry_subtree ); switch (status) { case ESNMP_LIB_OK: printf ("The esnmp_unregister routine completed successfully.\n"); break; case ESNMP_LIB_BAD_REG: printf ("The MIB subtree was not registered.\n"); break; case ESNMP_LIB_LOST_CONNECTION: printf("%s%s%s\n", "The request to unregister the ", "MIB subtree could not be sent. ", "You should restart the protocol.\n"); break; }
6.3.1.6 The esnmp_register2 Routine
The
esnmp_register2
routine offers extensions to
the
esnmp_register
routine.
The initialization routine (esnmp_init
) must be called
prior to calling the
esnmp_register2
routine.
The
esnmp_register2
function must be called for each subtree structure
corresponding to each MIB subtree that it will be handling.
At any time MIB
subtrees can be unregistered by calling
esnmp_unregister2
and then be reregistered by calling
esnmp_register2
.
When restarting the eSNMP protocol by calling
esnmp_init
,
all MIB subtree registrations are cleared.
All MIB subtrees must be reregistered.
A MIB subtree is identified by the base MIB variable name and its corresponding
OID.
This tuple represents the parent of all MIB variables that are contained
in the MIB subtree; for example, the MIB-2
tcp
subtree
has an OID of
1.3.6.1.2.1.6
.
All elements subordinate
to this (those that have the same first 7 identifiers) are included in the
subtree's object table.
A MIB subtree can also be a single MIB object (a
leaf node) or even a specific instance.
By registering a MIB subtree, the subagent indicates that it will process SNMP requests for all MIB variables (or OIDs) within that MIB subtree's region. Therefore, a subagent should register the most fully qualified (longest) MIB subtree that still contains its instrumented MIB variables.
A subagent using the
esnmp_register2
routine can
register the same MIB subtree for the local node and for a cluster.
To register
the MIB subtree for both, you must call the
esnmp_register2
routine twice: once with the ESNMP_REG_OPT_CLUSTER bit set in the
options
parameter and once with the ESNMP_REG_OPT_CLUSTER bit
clear in the
options
parameter.
Alternatively,
you can register a MIB subtree for the cluster only or for the local node
only, by setting or clearing the ESNMP_REG_OPT_CLUSTER bit, respectively,
in the
options
parameter.
A subagent may also register MIB subtrees that overlap the OID range of MIB subtrees that it previously registered or those of MIB subtrees registered by other subagents.
For example, consider the two Tru64 UNIX daemons,
os_mibs
and
gated
.
The
os_mibs
daemon
registers the
ip
MIB subtree (1.3.6.1.2.1.4) and the
gated
daemon registers the
ipRouteEntry
MIB subtree
(1.3.6.1.2.1.4.21.1).
Both of these registrations are made with the ESNMP_REG_OPT_CLUSTER
bit set in the
options
parameter.
Requests for
ip
MIB variables within
ipRouteEntry
, such as
ipRouteIfIndex
(1.3.6.1.2.1.4.21.1.2), are passed to the
gated
subagent.
Requests for other
ip
variables,
such as
ipNetToMediaIfIndex
(1.3.6.1.2.1.4.22.1.1), are
passed to the
os_mibs
subagent.
If the
gated
process should terminate or unregister the
ipRouteEntry
MIB subtree, subsequent requests for
ipRouteIfIndex
will go to the
os_mibs
subagent because the
ip
MIB subtree, which includes all
ipRouteEntry
MIB variables, would now be the authoritative region of requests for
ipRouteIfIndex
.
The syntax for the
esnmp_register2
routine is as
follows:
int esnmp_register2(
ESNMP_REG *reg
);
The arguments are defined as follows:
A pointer to a
ESNMP_REG
structure, which contains the following fields:
A pointer to
a
SUBTREE
structure corresponding to the MIB subtree to
be handled.
The
SUBTREE
structures are externally declared
and initialized in the code emitted by the
mosy
and
snmpi
utilities (xxx_tbl.c
and
xxx_tbl.h
, where
xxx
is the name of the MIB subtree) taken directly from the
MIB document.
Note
All memory pointed to by this field must have permanent storage since it is referenced by
libesnmp
for the duration of the program. You should use the data declarations emitted by thesnmpi
utility.
The registration priority. The entry with the largest number has the highest priority. The range is 1 to 255. The subagent that has registered a MIB subtree that has the highest priority over a range of Object Identifiers (OIDs) gets all requests for that range of OIDs.
MIB subtrees that are registered with the same priority are considered duplicates, and the registration is rejected by the master agent.
The priority field is a mechanism for cooperating subagents to handle different configurations.
The number of seconds the master agent should wait for responses when requesting data in this MIB subtree. This value must be between zero (0) and three hundred (300). If the value is zero (0), the default timeout is used (3 seconds). You should use the default.
An integer value that when non-zero and together with the range_upper_bound field specifies a range instead of one of the MIB subtree's OID sub-identifiers. The range_subid field specifies the OID sub-identifier modified by the range_upper_bound field.
An integer value that, in conjunction with a non-zero range_subid field specifies a range instead of one of the MIB subtree's OID sub-identifiers. The range_upper_bound field provides the upper bound of the range and the range_subid field provides the lower bound of the range, which is the MIB subtree's OID sub-identifier.
An integer value that when set to ESNMP_REG_OPT_CLUSTER indicates that the registration is valid cluster-wide and when set to zero indicates that the registration is valid for the local node.
One of the following
integer values that provides the caller with asynchronous updates of the state
of registration of this MIB subtree.
After the return of the
esnmp_poll
routine, the caller can inspect this parameter.
The registration is currently held locally while waiting for connection to the master agent.
The registration was sent to the master agent.
The wegistration was successfully ackowledged by the master agent.
The registration was rejected by the master agent because it was a duplicate.
The master agent does not support cluster registrations.
The master agent rejected the registration for other reasons.
When non-null,
this input parameter specifies a partial or fully qualified instance for the
MIB subtree or subtrees in the registration.
Use this parameter when registering
a row in a table.
See
Section 6.3.1.2
and
snmpi
(8)for
additional information on registering rows in a table.
The return values are as follows:
The
esnmp_register2
routine has completed successfully.
The
esnmp_init
routine has not been called, the timeout parameter is invalid, a registration
slot is not available, or this MIB subtree has already been queued for registration.
A message is also in the log file.
The subagent lost communication with the master agent.
Note that the status indicates only the initiation of the request.
The actual status returned in the master agent's response will be returned
in a subsequent call to the
esnmp_poll
routine in the
reg->state
field.
The following is an example of the
esnmp_register2
routine:
#include <esnmp.h> #define RESPONSE_TIMEOUT 0 /* use the default time set in esnmp_init message */ #define REGISTRATION_PRIORITY 10 /* priority at which the MIB subtree will register */ #define RANGE_SUBID 7 /* the identifier position in oid->elements just after mib-2 */ #define RANGE_UPPER_BOUND 8 /* the identifier for egp, under mib-2 */ int status; extern SUBTREE ip_subtree; static ESNMP_REG esnmp_reg_for_ip2egp; /* retain this structure for a subsequent call to esnmp_unregister2 */ /* * initialize the ESNMP_REG structure */ memset(&esnmp_reg_for_ip2egp, 0, sizeof(ESNMP_REG)); esnmp_reg_for_ip2egp.subtree = &ip_subtree; esnmp_reg_for_ip2egp.priority = REGISTRATION_PRIORITY; esnmp_reg_for_ip2egp.timeout = RESPONSE_TIMEOUT; esnmp_reg_for_ip2egp.range_subid = RANGE_SUBID; esnmp_reg_for_ip2egp.range_upper_bound = RANGE_UPPER_BOUND; status = esnmp_register2( &esnmp_reg_for_ip2egp ); if (status != ESNMP_LIB_OK) { printf("Could not queue the 'ipRouteEntry' \n"); printf("subtree for registration\n"); }
6.3.1.7 The esnmp_unregister2 Routine
The
esnmp_unregister2
routine unregisters a MIB subtree
with the master agent.
Use this routine only when the MIB subtree was registered
using the
esnmp_register2
routine.
This routine can be called by the application code to tell the eSNMP
subagent not to process requests for variables in this MIB subtree any more.
You can later reregister a MIB subtree, if needed, by calling the
esnmp_register2
routine.
The syntax for the
esnmp_unregister2
routine is as
follows:
int esnmp_unregister2(
ESNMP_REG *reg
);
The arguments are as follows:
A pointer to the
ESNMP_REG
structure that was used when the
esnmp_register2
routine was called.
The return values are as follows:
The routine completed successfully.
The MIB subtree was not registered.
The request to unregister the MIB subtree could not be sent. You should restart the protocol.
The following is an example of the
esnmp_unregister2
routine:
#include <esnmp.h> int status extern ESNMP_REG esnmp_reg_for_ip2egp; status = esnmp_unregister2( &esnmp_reg_for_ip2egp ); switch(status) { case ESNMP_LIB_OK: printf("The esnmp_unregister2 routine completed successfully.\n"); break; case ESNMP_LIB_BAD_REG: printf("The MIB subtree was not registered.\n"); break; case ESNMP_LIB_LOST_CONNECTION: printf("%s%s%s\n", "The request to unregister the ", "MIB subtree could not be sent. ", "You should restart the protocol.\n"); break; }
6.3.1.8 The esnmp_capabilities Routine
The
esnmp_capabilities
routine adds a subagent's
capabilities to the master agent's sysORTable.
The sysORTable is a conceptual
table that contains an agent's object resources, and is described in
RFC 1907.
This routine is called at any point after initializing eSNMP by a call
to the
esnmp_init
routine.
The syntax for the
esnmp_capabilities
routine is
as follows:
void esnmp_capabilities(
OID *agent_cap_id,
char *agent_cap_descr
);
The arguments are as follows:
A pointer to an object identifier that represents an authoritative agent capabilities identifier. This value is used for the sysORID object in the sysORTable for the managed node.
A pointer to a null-terminated character string describing agent_cap_id. This value is used for the sysORDescr object in the sysORTable for the managed node.
6.3.1.9 The esnmp_uncapabilities Routine
The
esnmp_uncapabilities
routine removes a subagent's
capabilities from the master agent's sysORTable.
This routine is called if a subagent alters its capabilities dynamically. When a logical connection for a subagent is closed, the master agent removes the related entries in sysORTable.
The syntax for the
esnmp_uncapabilities
routine is
as follows:
void esnmp_uncapabilities(
OID *agent_cap_id
);
The arguments are as follows:
A pointer to an object identifier of the agent capabilities statement to be removed from the sysORTable.
6.3.1.10 The esnmp_poll Routine
The
esnmp_poll
routine processes a pending message
that has been sent by the master agent.
This routine is called after the
user's
select()
call has indicated data is ready on the
eSNMP socket.
(This socket was returned from the call to the
esnmp_init
routine).
If no message is pending on the socket, the
esnmp_poll
routine blocks until one is received.
If a received message indicates a problem, the routine makes an entry
in the
syslog
file and returns an error status.
If the received message is a request for SNMP data, the routine consults the object table and calls the appropriate method routine or routines.
The syntax for the
esnmp_poll
routine is as follows:
int esnmp_poll(
void
);
The return values are as follows:
The
esnmp_poll
routine has completed successfully.
A previous registration was failed by the master agent. See the log file.
A duplicate subagent identifier
has already been received by the master agent.
This is an
esnmp_init
error.
The master agent failed
to initiate an
esnmp_init
request.
Restart after a delay.
See the log file.
A CLOSE message was received.
An eSNMP protocol error occurred. The packet was discarded.
Communication with master agent was lost. Restart the connection.
6.3.1.11 The esnmp_are_you_there Routine
The
esnmp_are_you_there
routine requests the master
agent to report immediately that it is up and functioning.
This call does
not block waiting for a response.
The response is processed by calling the
esnmp_poll
routine.
If no response is received within the timeout period, the application
code should restart the eSNMP protocol by calling the
esnmp_init
routine.
There are no timers maintained by the eSNMP library.
The syntax for the
esnmp_are_you_there
routine is
as follows:
int esnmp_are_you_there(
void
);
The return values are as follows:
The request was sent.
Cannot send the request because the master agent is down.
6.3.1.12 The esnmp_trap Routine
The
esnmp_trap
routine sends a trap message to the
master agent.
This function can be called at anytime.
If the master agent
is not running the eSNMP protocol, traps are queued and sent when communication
is possible.
The trap message is actually sent to the master agent after the master
agent's response to the
esnmp_init
call has been processed.
This processing happens within any API call, in most cases during subsequent
calls to the
esnmp_poll
routine.
The quickest way to send
traps to the master agent is to call the
esnmp_init
,
esnmp_poll
, and
esnmp_trap
routines.
The master agent formats the trap into an SNMP trap message and sends
it to management stations based on its current configuration.
For information
on configuring the master agent, see
snmpd
(8)
and
snmpd.conf
(4).
There is no response returned from the master agent for a trap.
The syntax for the
esnmp_trap
routine is as follows:
int esnmp_trap(
int generic_trap,
int specific_trap,
char *enterprise,
VARBIND *vb
);
The arguments are as follows:
A generic trap code. Set to 0 (zero) for SNMPv2 traps.
A specific trap code. Set to 0 (zero) for SNMPv2 traps.
An enterprise
OID string in dot notation.
Set to the object identifier defined by the NOTIFICATION-TYPE
macro in the defining MIB specification.
This value is passed as the value
of
SnmpTrapOID.0
in the SNMPv2-Trap-PDU.
A
VARBIND
list of data (a NULL pointer indicates no data)
The return values are as follows:
The routine completed successfully.
The routine could not send the trap message to the master agent.
Something failed and a message could not be generated.
6.3.1.13 The esnmp_term Routine
The
esnmp_term
routine sends a close message to the
master agent and shuts down the eSNMP protocol.
All subagents must call this
routine when terminating, so that the master agent can update its MIB registry
more quickly and that system resources used by eSNMP on the behalf of the
subagents can be released.
The syntax for the
esnmp_term
routine is as follows:
void esnmp_term(
void
);
The return value is:
The
esnmp_term
routine always returns ESNMP_LIB_OK, even if the packet could not be sent.
6.3.1.14 The esnmp_sysuptime Routine
The
esnmp_sysuptime
routine converts UNIX system
time obtained from
gettimeofday
into a value with the same
timebase as
sysUpTime
.
This can be used as a
TimeTicks
data type (the time since the master agent started) in
units of 1/100 seconds.
The time base is obtained from the master agent in
response to the
esnmp_init
routine, so calls to this function
before that time will not be accurate.
This provides a general purpose mechanism to convert UNIX timestamps
into SNMP
TimeTicks
.
The function returns the appropriate
value of
sysUpTime
for a given UNIX.
Passing a null timestamp
returns the current value of
sysUpTime
.
The syntax is as follows:
unsigned int esnmp_sysuptime(
struct timeval *timestamp
);
The arguments are as follows:
Is a pointer
to a
struct timeval
containing a value obtained from the
gettimeofday
system call.
The structure is defined in
include/sys/time.h
.
A NULL pointer means return the current
sysUpTime
.
The following is an example of the
esnmp_sysuptime
routine:
#include <sys/time.h> #include <esnmp.h> struct timeval timestamp; gettimeofday(×tamp, NULL); o_integer(vb, object, esnmp_sysuptime(×tamp));
The return is as follows:
0
Indicates an error (gettimeofday
failed); otherwise,
timestamp
contains the time in 1/100ths seconds since the master agent protocol started.
6.3.2 Method Routine Calling Interface
SNMP requests may contain many
VariableBindings
(encoded MIB variables).
The
libsnmp
code executing in a subagent matches each
VariableBinding
with an object table entry.
The object table's method routine
is then called.
Therefore, a method routine is called to service a single
MIB variable.
Since a single method method routine can handle a number of
MIB variables, the same method routine may be called several times during
a single SNMP request.
The method routine calling interface contains the following functions:
*_get
*_set
Section 6.3.2.3
provides additional information
on method routines.
6.3.2.1 The *_get Routine
The
*_get
routine is a method routine for the specified
MIB item, which is typically a MIB group (for example,
system
in MIB-2) or a table entry (for example,
ifEntry
in MIB-2).
However, it is up to your discretion.
See
snmpi
(8)
for more
information.
The
libesnmp
routines call whatever routine is specified
for
Get
operations in the object table identified by the
registered subtree.
This function is pointed to by some number of elements of the subagent
object table.
When a request arrives for an object, its method routine is
called.
The
*_get
method routine is called in response
to a
Get
SNMP request.
The syntax for the
*_get
routine is as follows:
int mib-group_get(
METHOD *method
);
The arguments are:
A pointer to
a
METHOD
structure, which contains the following fields:
One of ESNMP_ACT_GET, ESNMP_ACT_GETNEXT, or ESNMP_ACT_GETBULK.
An integer number that is unique to this SNMP request. Each method routine called while servicing a single SNMP request receives the same value of serial_num. New SNMP requests are indicated by a new value of serial_num.
Used for
GetBulk
only.
This value indicates the current iteration number
of a repeating
VARBIND.
This number increments
from 1 to
max_repetitions
, and is 0 for nonrepeating
VARBIND
structures.
For
GetBulk
.
The maximum number of repetitions to perform.
This will
be 0 for nonrepeating
VARBIND
structures.
You can optimize
subsequent processing by knowing the maximum number repeat calls will be made.
A pointer to
the
VARBIND
structure for which we must fill in the
OID
and data fields.
Upon entry of the method routine, the
method->varbind->name
field is the OID that was requested.
Upon exit of the method routine, the
method->varbind
field contains the requested data, and the
method->varbind->name
field is updated to reflect the actual instance OID for the returned
VARBIND
.
The
libsnmp
routines (o_integer
,
o_string,
o_oid,
and
o_octet
)
are generally used to load data.
The
libsnmp instance2oid
routine is used to update the OID in
method->varbind->name
field.
A pointer to
the object table entry for the MIB variable being referenced.
The
method->object->object_index
field is this object's unique index
within the object table (useful when one method routine services many objects).
The
method->object->oid
field is the OID defined
for this object in the MIB.
The instance requested is derived by comparing
this
OID
with the
OID
in the request
found in the
method->varbind->name
field.
The
oid2instance
function is useful for this.
The possible return values for the
*_get
method routine
are as follows:
The routine completed successfully.
The requested object cannot be returned or does not exist.
The requested instance of an object cannot be returned or does not exist.
A general processing error.
6.3.2.2 The *_set Method Routine
The
*_set
method routine for a specified MIB item,
which is typically a MIB group (for example,
system
in
MIB-2) or a table entry (for example,
ifEntry
in MIB-2).
However, it is up to your discretion.
See
snmpi
(8)
for more information.
The
libesnmp
routines call whatever routine is specified
for
Set
operations in the object table identified by the
registered subtree.
This function is pointed to by some number of elements of the subagent
object table.
When a request arrives for an object, its method routine is
called.
The
*_set
method routine is called in response
to a
Set
SNMP request.
The syntax for the
*_set
method routine is as follows:
int mib-group_set(
METHOD *method
);
The arguments are as follows:
A pointer to
a
METHOD
structure, which contains the following fields:
The action value can be one of the following: ESNMP_ACT_SET, ESNMP_ACT_COMMIT, ESNMP_ACT_UNDO, or ESNMP_ACT_CLEANUP
An integer number that is unique to this SNMP request. Each method routine called while servicing a single SNMP request receives the same value of serial_num. New SNMP requests are indicated by a new value of serial_num.
A pointer to
the
VARBIND
structure that contains the MIB variable's
supplied data value and name (OID).
The instance information has already
been extracted from the OID and placed in
method->row->instance
field.
A pointer to
the object table entry for the MIB variable being referenced.
The
method->object->object_index
field is
this object's unique index within the object table (useful when one method
routine services many objects).
The
method->object->oid
field is the OID defined for this object in the MIB.
A read-only integer
bitmask set by
libesnmp
.
If set, the ESNMP_FIRST_IN_ROW
bit indicates that this call is the first object to be set in the row.
If
set, the ESNMP_LAST_IN_ROW bit indicates that this call is the last object
to be set in the row.
Only
METHOD
structures with the
ESNMP_LAST_IN_ROW bit set are passed to the method routines for commit, undo,
and cleanup phases.
A pointer to a
ROW_CONTEXT
structure (defined in the
esnmp.h
header file).
All
Set
calls to the method routine that
refer to the same group and have the same instance number will be presented
with the same row structure.
The method routines can accumulate information
in the row structures during
Set
calls for use during the
commit and undo phases.
The accumulated data can be released by the method
routines during the cleanup phase.
The
ROW_CONTEXT
structure contains the following
fields:
An address
of an array containing the instance OID for this conceptual row.
The
libesnmp
routine builds this array by subtracting the
object oid
from the requested variable
binding oid
.
The size
of the
method->row->instance
field.
A pointer to be used privately by the method routine to reference data needed to process this request.
A pointer to be used privately by the method routine to reference data needed to potentially undo this request.
An integer to be used privately by the method routine to hold any state information it requires.
The possible returns for the
*_set
method routine
are as follows:
The routine completed successfully.
The requested object cannot be set or was not implemented.
The data type for the requested value is the wrong type.
The requested value is the wrong length.
The requested value is represented incorrectly.
The requested value is out of range.
The requested instance can never be created.
The requested instance cannot currently be created.
The requested value is not consistent.
A failure due to some resource constraint.
A general processing error.
The commit phase failed.
The undo phase failed.
Overall Processing of the *_set Routine
Every variable binding is parsed and its object is located in the object
table.
A
METHOD
structure is created for each
VARBIND
structure.
These
METHOD
structures point
to a
ROW_CONTEXT
structure, which is useful for handling
these phases.
Objects in the same conceptual row all point to the same
ROW_CONTEXT
structure.
This determination is made by checking the
following:
The referenced objects are in the same MIB group.
The
VARBIND
structures have the same instance
OIDs.
Each
ROW_CONTEXT
structure is loaded with the instance
information for that conceptual row.
The
ROW_CONTEXT
structure
context
and
save
fields are set to
NULL, and the
state
field is set to ESNMP_SET_UNKNOWN
structure.
The method routine for each object is called, being passed its
METHOD
structure with an action code of ESNMP_ACT_SET.
If all method routines return success, a single method routine (the
last one called for the row) is called for each row, with
method->action
== ESNMP_ACT_COMMIT
.
If any row reports failure, all rows that were successfully committed
are told to undo the phase.
This is accomplished by calling a single method
routine for each row (the same one that was called for the commit phase),
with a
method->action == ESNMP_ACT_UNDO
.
Finally, each row is released.
The same single method routine for each
row is called with a
method->action == ESNMP_ACT_CLEANUP
.
This occurs for every row, regardless of the results of previous processing.
The following list describes the action codes:
ESNMP_ACT_SET
Each object's method routine is called during the
Set
phase, until all objects are processed or a method routine returns an error
status value.
(This is the only phase during which each object's method routine
is called.) For variable bindings in the same conceptual row,
method->row
points to a common
ROW_CONTEXT
.
The
method->flags
bitmask has the ESNMP_LAST_IN_ROW
bit set, if this is the last object being called for this
ROW_CONTEXT
.
This enables you to do a final consistency check, because you
have seen every variable binding for this conceptual row.
The method routine's job in this phase is to determine if the
SetRequest
will work, return the correct SNMP error code if not,
and prepare any context data it needs to actually perform the
Set
during the commit phase.
The
method->row->context
is private to the method
routine;
libesnmp
does not use it.
A typical use is to
store the address of an emitted
foo_type
structure that has been loaded with the data from the
VARBIND
for the conceptual row.
ESNMP_ACT_COMMIT
Even though several variable bindings may be in a conceptual row, only
the last one in order of the
SetRequest
is processed.
So, for all the method routines that point to a common row, only the last
method routine is called.
This method routine must have available to it all necessary data and
context to perform the operation.
It must also save a snapshot of current
data or whatever it needs to undo the
Set
if required.
The
method->row->save
field is intended to hold a pointer
to whatever data is needed to accomplish this.
A typical use is to store
the address of an
xxx
structure that has been loaded
with the current data for the conceptual row.
The
xxx
structure is one that has been automatically generated by the
snmpi
program.
The
method->row->save
field is also private to the
method routine;
libesnmp
does not use it.
If the set operation succeeds, return ESNMP_MTHD_noError; otherwise, back out the commit as best you can and return a value of ESNMP_MTHD_commitFailed.
If any errors were returned during the commit phase,
libesnmp
enters the undo phase; if not, it enters the cleanup phase.
Note
The undo phase may occur even if the
Set
operation in your subagent is successful because theSetRequest
spanned subagents and some other subagent failed.
ESNMP_ACT_UNDO
For each conceptual row that was successfully committed, the same method
routine is called with
method->action == ESNMP_ACT_UNDO
.
The
ROW_CONTEXT
structures that have not yet been called
for the commit phase are not called for the undo phase; they are called for
cleanup phase.
The method routine should attempt to restore conditions to what they
were before it executed the commit phase.
(This is typically done using the
data pointed to by the
method->row->save
field.)
If successful, return ESNMP_MTHD_noError; otherwise, return ESNMP_MTHD_undoFail.
ESNMP_ACT_CLEANUP
Regardless of what else has happened, at this point each
ROW_CONTEXT
participates in cleanup phase.
The same method routine that was
called for commit phase is called with
method->action == ESNMP_ACT_CLEANUP
.
This indicates the end of processing for the
SetRequest
.
The method routine should perform whatever cleanup is required; for instance,
freeing dynamic memory that might have been allocated and stored in
method->row->context
and
method->row->save
fields,
and so on.
The function return status value is ignored for the cleanup phase.
6.3.2.3 Method Routine Applications Programming
You must write the code for the method routines declared
in the
subtree_tbl.h
file.
Each method routine has one argument, which is a pointer to the
METHOD
structure, as follows:
int mib_group_get(
METHOD *method
int mib_group_set(
METHOD *method
);
The
Get
method routines are used to perform
Get
,
GetNext
, and
GetBulk
operations.
Get
method routines perform the following tasks:
Extract the instance portion of the requested OID.
You can
do this manually by comparing the
method->object->oid
field (the object's base OID) to the
method->varbind->name
field (the requested OID).
You can use the
oid2instance
libesnmp
routine to do this.
Determine the instance validity. The instance OID may be null or any length, depending on what was requested and how your object was selected. You may be able to reject the request immediately by checking on the instance OID.
Extract the data. Based on the instance OID and method->action field, determine what data, if any, is to be returned.
Load the response OID back into the method routine's
VARBIND
structure.
Set the
method->varbind
field with the OID of the actual MIB variable instance you are returning.
This is usually accomplished by loading an array of integers with the instance
OID you wish to return and calling the
instance2OID
libesnmp
routine.
Load the response data back into the method routine's
VARBIND
structure.
Use one of the
libesnmp
library routine with the
corresponding data type to load the
method->varbind
field with the data to return:
o_integer
o_string
o_octet
o_oid
These routines make a copy of the data you specify.
The
libesnmp
function manages any memory associated with copied data.
The method routine must manage the original data's memory.
The routine does any necessary conversions to the type defined in the object table for the MIB variable and copies the converted data into the method->varbind field.
See the Value Representation section for information on data value representation.
Return the correct status value, as follows:
ESNMP_MTHD_noError -- The routine completed successfully or no errors were found.
ESNMP_MTHD_noSuchInstance -- There is no such instance of the requested object.
ESNMP_MTHD_noSuchObject -- No such object exists.
ESNMP_MTHD_ genErr -- An error occurred and the routine did not complete successfully.
The values in a
VARBIND
structure
for each data type are represented as follows.
(Refer to the
esnmp.h
file for a definition of the
OCT
and
OID
structures.)
ESNMP_TYPE_Integer32 (varbind->value.sl
field)
This is a 32-bit signed integer.
Use the
o_integer
routine to insert an integer value into the
VARBIND
.
Note
that the prototype for the value argument is unsigned long, so you may need
to cast this to a
signed int
.
ESNMP_TYPE_DisplayString, ESNMP_TYPE_Opaque, ESNMP_TYPE_OctetString
(varbind->value.oct
field)
This is an octet string.
It is contained in the
VARBIND
structure as an
OCT
structure that contains a length and
a pointer to a dynamically allocated character array.
The
DisplayString
is different only in that the character
array can be interpreted as ASCII text where the
OctetString
can be anything.
If the
OctetString
contains bits or a
bitstring, the
OCT
structure contains the following:
A length equal to the number of bytes needed to contain the value that is ((qty-bits - 1)/8 + 1)
A pointer to a buffer containing the bits of the
bitstring
in the form
bbbbb..bb, where
the
bb
octets represent the bit string itself,
where bit zero (0) comes first and so on.
Any unused bits in the last octet
are set to zero (0).
Use the
o_string
routine to insert a value into the
VARBIND
structure, which is a buffer and a length.
New space will
be allocated and the buffer copied into the new space.
Use the
o_octet
routine to insert a value into the
VARBIND
structure, which is a pointer to an
OCT
structure.
New space is allocated and the buffer pointed to by the
OCT
structure is copied.
ESNMP_TYPE_ObjectId (varbind->value.oid
and the
varbind->name
fields)
This is an object identifier.
It is contained in the
VARBIND
structure as an
OID
structure that contains
the number of elements and a pointer to a dynamically allocated array of unsigned
integers, one for each element.
The
varbind->name
field is used to hold the object identifier and instance information that
identifies MIB variable.
Use the
OID2Instance
function
to extract the instance elements from an incoming OID on a request.
Use the
instance2oid
function to combine the instance elements with the
MIB variable's base OID to set the
VARBIND
structure's
name
field when building a response.
Use the
o_oid
function to insert an object identifier
into the
VARBIND
structure when the OID value to be returned
as data is in the form of a pointer to an
OID
structure.
Use the
o_string
function to insert an object ID
into the
VARBIND
structure when the OID value to be returned
as data is in the form of a pointer to an ASCII string containing the OID
in dot format; for example
1.3.6.1.2.1.3.1.1.2.0
.
ESNMP_TYPE_NULL
This is the NULL or empty type.
This is used to indicate that there
is no value.
The length is 0 and the value union in the
VARBIND
structure is zero-filled.
The incoming
VARBIND
structures on a
Get
,
GetNext
, and
GetBulk
will
have this data type.
A method routine should never return such a value.
An incoming
Set
request never has such a value in a
VARBIND
structure.
ESNMP_TYPE_IpAddress (varbind->value.oct
field)
This is an IP address.
It is contained in the
VARBIND
structure in an
OCT
structure that has a length of 4 and
a pointer to a dynamically allocated buffer containing the 4 bytes of the
IP address in network byte order.
Use the
o_integer
function to insert an IP address
into the
VARBIND
structure when the value is an unsigned
integer in network byte order.
Use the
o_string
function to insert an IP address
into the
VARBIND
structure when the value is a byte array
(in network byte order).
Use a length of 4.
ESNMP_TYPE_UInteger32 ESNMP_TYPE_Counter32 ESNMP_TYPE_Gauge32
(varbind->value.ul
field)
The 32-bit counter and 32-bit gauge data types are stored in the
VARBIND
structure as an
unsigned int
.
Use the
o_integer
function to insert an unsigned
value into the
VARBIND
structure.
ESNMP_TYPE_TimeTicks
(varbind->value.ul
field)
The 32-bit timeticks type values are stored in the
VARBIND
structure as an
unsigned int
.
Use the
o_integer
function to insert an unsigned
value into the
VARBIND
structure.
ESNMP_TYPE_Counter64 (varbind->value.ul64
)
The 64-bit counter is stored in a
VARBIND
structure
as an
unsigned long
, which on an Alpha machine has a 64-bit
value.
Use the
o_integer
function to insert an unsigned
long value (64 bits) into the
VARBIND
structure.
6.3.3 The libsnmp Support Routines
The following sections provide information on the
libsnmp
support routines, which consist of the following:
o_integer
o_octet
o_oid
o_string
str2oid
sprintoid
instance2oid
oid2instance
inst2ip
cmp_oid
cmp_oid_prefix
clone_oid
free_oid
clone_buf
mem2oct
cmp_oct
clone_oct
free_oct
free_varbind_data
set_debug_level
is_debug_level
ESNMP_LOG
The
o_integer
routine loads an integer value into
the
VARBIND
structure with the appropriate type.
The syntax is as follows:
int o_integer(
VARBIND *vb,
OBJECT *obj,
unsigned long value
);
The arguments are as follows:
A pointer to the
VARBIND
structure that is to receive the data.
This function does
not allocate the
VARBIND
structure.
A pointer to the
OBJECT
structure for the MIB variable associated with the
OID
in the
VARBIND
structure.
The value to
be inserted into the
VARBIND
structure.
The real type as defined in the object structure must be one of the following; otherwise, an error is returned.
If the real type is
IpAddress
, the assumption is
that the 4-byte integer is in network byte order.
32-bit INTEGER
32-bit Counter (unsigned)
32-bit Gauge (unsigned)
32-bit TimeTicks (unsigned)
32-bit INTEGER (unsigned)
64-bit Counter (unsigned)
IMPLICIT OCTET STRING (4)
The following is an example of the
o_integer
routine:
#include <esnmp.h> #include "ip_tbl.h" <-- for ipNetToMediaEntry_type definition VARBIND *vb = method->varbind; OBJECT *object = method->object; ipNetToMediaEntry_type *data; : : assume buffer and structure member assignments occur here : switch(arg) { case I_atIfIndex: return o_integer(vb, object, data->ipNetToMediaIfIndex);
The following are the return values:
The routine completed successfully.
An error has occurred.
The
o_octet
routine loads an octet value into the
VARBIND
structure with the appropriate type.
The syntax is as follows:
int o_octet(
VARBIND *vb,
OBJECT *obj,
OCT *oct
);
The arguments are as follows:
A pointer to the
VARBIND
structure that is to receive the data.
This function does
not allocate the
VARBIND
structure.
Note
If the original value in the vb field is not NULL, this routine attempts to free it. So if you issue the
malloc
command to allocate your own vb structure, be sure to fill it with zeros before using it.
A pointer to the
OBJECT
structure for the MIB variable associated with the
OID
in the
VARBIND
structure.
The value to
be inserted into the
VARBIND
structure.
The real type as defined in the object structure must be one of the following; otherwise, an error is returned:
OCTET STRING
IMPLICIT OCTET STRING (4) -- in octet form, network byte order
DisplayString (Textual Convention)
IMPLICIT OCTET STRING
The following is an example of the
o_octet
routine:
#include <esnmp.h> #include "ip_tbl.h" <-- for ipNetToMediaEntry_type definition VARBIND *vb = method->varbind; OBJECT *object = method->object; ipNetToMediaEntry_type *data; : : assume buffer and structure member assignments occur here : switch(arg) { case I_atPhysAddress: return o_octet(vb, object, &data->ipNetToMediaPhysAddress);
The returns are as follows:
The routine completed successfully.
An error condition has occurred.
The
o_oid
routine loads an OID value into the
VARBIND
structure with the appropriate type.
The syntax is as follows:
int o_oid(
VARBIND *vb,
OBJECT *obj,
OID *oid
);
The arguments are as follows:
A pointer to the
VARBIND
structure that is to receive the data.
This function does
not allocate the
VARBIND
structure.
Note
If the original value in the vb field is not NULL, this routine attempts to free it; therefore, if you issue the
malloc
command to allocate your own vb structure, fill it with zeros (0s) before using it.
A pointer to the
OBJECT
structure for the MIB variable associated with the OID in
the
VARBIND
structure.
The value to
be inserted into the
VARBIND
structure as data.
See
Section 6.2.1
for OID length and values.
The real type as defined in the object structure must be the following; otherwise, an error is returned:
OBJECT IDENTIFIER
The following is an example of the
o_oid
routine:
#include <esnmp.h> #include "ip_tbl.h" <-- for ipNetToMediaEntry_type definition VARBIND *vb = method->varbind; OBJECT *object = method->object; ipNetToMediaEntry_type *data; : : assume buffer and structure member assignments occur here : switch(arg) { case I_atObjectID: return o_oid(vb, object, &data->ipNetToMediaObjectID);
The returns are as follows:
The routine completed successfully.
An error condition has occurred.
The
o_string
routine loads a string value into the
VARBIND
structure with the appropriate type.
The syntax is as follows:
int o_string(
VARBIND *vb,
OBJECT *obj,
unsigned char *ptr,
int len
);
The arguments are as follows:
A pointer to the
VARBIND
structure that is to receive the data.
This function does
not allocate the
VARBIND
structure.
Note
If the original value in the vb field is not NULL, this routine attempts to free it; therefore, if you issue the
malloc
command to allocate your own vb structure, fill it with zeros (0s) before using it.
A pointer to the
OBJECT
structure for the MIB variable associated with the
oid
in the
VARBIND
structure.
A pointer to the
buffer containing data to be inserted into the
VARBIND
structure as data.
The length of the data in buffer to which ptr field points.
The real type as defined in the object structure must be one of the following; otherwise, an error is returned:
OCTET STRING
IMPLICIT OCTET STRING (4) -- in octet form, network byte order
DisplayString (Textual Convention)
IMPLICIT OCTET STRING
OBJECT IDENTIFIER -- in dot notation, 1.3.6.1.4.1.3.6
The following is an example of the
o_string
routine:
#include <esnmp.h> #include "ip_tbl.h" <-- for ipNetToMediaEntry_type definition VARBIND *vb = method->varbind; OBJECT *object = method->object; ipNetToMediaEntry_type *data; : : assume buffer and structure member assignments occur here : switch(arg) { case I_atPhysAddress: return o_string(vb, object, data->ipNetToMediaPhysAddress.ptr, data->ipNetToMediaPhysAddress.len);
The return values are as follows:
The routine completed successfully.
An error condition has occurred.
The
str2oid
routine converts a null-terminated OID
string (in dot notation) to an
OID
structure.
It dynamically allocates the elements buffer and inserts its pointer
into the
OID
structure passed in.
It is the responsibility
of the caller to free this buffer.
The OID can have a maximum of 128 elements.
A null string or empty string returns an OID structure that has one element
of zero (0).
Note that the
str2oid
routine does not allocate an
OID
structure.
The syntax is as follows:
OID * str2oid(
OID *oid,
char *s
);
The following is an example of the
str2oid
routine:
#include <esnmp.h> OID abc; if (str2oid(&abc, "1.2.5.4.3.6") == NULL) DPRINTF((WARNING,"It did not work...\n"));
The returns are as follows:
An error has occurred; otherwise, the pointer
to the
OID
structure (its first argument) is returned.
The
sprintoid
routine converts an
OID
into a null-terminated string in dot notation.
An
OID
structure can have up to 128 elements.
A full sized
OID
structure can require a large buffer.
The syntax is as follows:
char * sprintoid(
char *buffer,
OID *oid
);
The following is an example of the
sprintoid
routine:
#include <esnmp.h> #define SOMETHING_BIG 1024 OID abc; char buffer[SOMETHING_BIG]; : : assume abc gets initialized with some value : printf("dots=%s\n", sprintoid(buffer, &abc));
The return value points to its first argument.
6.3.3.7 The instance2oid Routine
The
instance2oid
routine makes a copy of the object's
base OID and appends a copy of the instance array to make a complete OID for
a value.
The
instance
is an array of integers and
len
is the number of elements.
The instance array may be created
by
oid2instance
or constructed from key values as a result
of a
get_next
search.
The routine dynamically allocates the elements buffer and inserts its
pointer into the
OID
structure passed in the call.
The
calling program or module is responsible for freeing this buffer.
To use this routine, point to the
OID
structure that
is to receive the new
OID
values and call this routine.
Any previous value in the
OID
structure is freed (it calls
free_oid
first) and the new values are dynamically allocated and
inserted.
Be sure the initial value of the new
OID
structure
is all zeros, if you do not want it to be freed.
Note that the
instance2oid
routine does not allocate
an
OID
structure, only the array containing the elements.
The syntax is as follows:
OID * instance2oid(
OID *new,
OBJECT *obj,
unsigned int *instance,
int len
);
The arguments are as follows:
A pointer to the
OID
structure that is to receive the new OID value.
A pointer to the object table entry for the MIB variable being obtained. The first part of the new OID is the OID from this MIB object table entry.
A pointer to an array of instance values. These values are appended to the base OID obtained from the MIB object table entry to construct the new OID.
The number of elements in the instance array.
The following is an example of the
instance2oid
routine:
#include <esnmp.h> VARBIND *vb; <-- filled in OBJECT *object; <-- filled in unsigned int instance[6]; -- Construct the outgoing OID in a GETNEXT -- -- Instance is N.1.A.A.A.A where A's are IP address -- instance[0] = data->ipNetToMediaIfIndex; instance[1] = 1; for (i = 0; i < 4; i++) { instance[i+2]=((unsigned char *)(&data->ipNetToMediaNetAddress))[i]; } instance2oid(&vb->name, object, instance, 6);
The returns are as follows:
An error has occurred; otherwise, the pointer
to the
OID
structure (its first argument) is returned.
6.3.3.8 The oid2instance Routine
The
oid2instance
routine extracts the instance values
from an
OID
structure and copies them to the specified
array of integers.
It then returns the number of elements in the array.
The instance is the elements of an OID beyond those elements that identify
the MIB variable.
They are used as indexes to identify a specific instance
of a MIB value.
If the
OID
structure contains more elements than
expected (more than specified by the
max_len
parameter),
the function copies the number of elements specified by
max_len
only and returns the total number of elements that would have
been copied had there been space.
The syntax is as follows:
int oid2instance(
OID *oid,
OBJECT *obj,
unsigned int *instance,
int max_len
);
The arguments are as follows:
A pointer to an
incoming
OID
structure containing an instance or part of
an instance.
A pointer to the object table entry for the MIB variable.
A pointer to an array of unsigned integers where the index will be placed.
The number of elements available in the instance array.
#include <esnmp.h> OID *incoming = &method->varbind->name; OBJECT *object = method->object; int instLength; unsigned int instance[6]; -- in a GET operation -- -- Expected Instance is N.1.A.A.A.A where A's are IP address -- instLength = oid2instance(incoming, object, instance, 6); if (instLength != 6) return ESNMP_MTHD_noSuchInstance;
The
N
will be in
instance[0]
and
the IP address will be in
instance[2]
,
instance[3]
,
instance[4]
, and
instance[5]
.
The returns are as follows:
<0 -- An error occurred.
This is not returned if
the object was obtained by looking at this
oid
.
0 -- There are no instance elements.
>0 -- The number of elements in the index. (This could be larger than the max_len parameter).
The
inst2ip
routine returns an IP address derived
from an OID instance.
For evaluation of an instance for
Get
and
Set
operations use the EXACT mode.
For
GetNext
and
GetBulk
operations use the NEXT mode.
When
using the NEXT mode, this routine's logic assumes that the search for data
will be performed using greater than or equal to matches.
The syntax is as follows:
int inst2ip(
unsigned int *inst,
int length,
unsigned int *ipAddr,
int exact,
int carry
);
The arguments are as follows:
A pointer to an
array of
unsigned int
containing the instance numbers returned
by the
oid2instance
routine to be converted to an IP address.
Each element is in the range 0 to 255.
Using the EXACT mode, the routine
returns 1 if an element is out of range.
Using NEXT mode, a value greater
than 255 causes that element to overflow.
It is set to 0 and the next most
significant element is incremented, so it returns a lexically equivalent value
of the next possible
ipAddress
.
The number of
elements in the instance array.
Instances beyond the fourth are ignored.
If the length is less than 4, the missing values are assumed to be 0.
A
negative length results in an
ipAddr
value of 0.
For an exact match (such as
Get
) there must be at exactly
four elements.
A pointer to where to return the IP address value. It is in network byte order; that is, the most significant element is first.
Either TRUE or FALSE.
TRUE
means do an EXACT match.
If any element is
greater than 255 or if there are not exactly 4 elements, return 1.
The carry
argument is ignored.
FALSE
means do a NEXT match.
That is, return the
lexically next IP address if the carry is set and the length is at least 4.
If there are fewer than 4 elements, assume the missing values are 0.
If
any one element contains a value greater than 255, then zero the value and
increment the next most significant element.
Return 1 only in the case where
there is a carry from the more significant (the first) value.
Is the carry to add to the IP address on a NEXT match. If you are trying to determine the next possible IP address, pass in a 1; otherwise, pass in a 0. A length of less than 4 cancels the carry.
The following are examples of the
inst2ip
routine.
The following example converts an instance to an IP address for a
Get
operation, which is an
EXACT
match.
#include <esnmp.h> OID *incoming = &method->varbind->name; OBJECT *object = method->object; int instLength; unsigned int instance[6]; unsigned int ip_addr; int iface; -- The instance is N.1.A.A.A.A where A.A.A.A is the IP address-- instLength = oid2instance(incoming, object, instance, 6); if (instLength == 6 && !inst2ip(&instance[2], 4, &ip_addr, TRUE,0)) { iface = (int) instance[0]; } else return ESNMP_MTHD_noSuchInstance;
The following example shows a
GetNext
operation where
there is only one key or that the
ipAddr
value
is the least significant part of the key.
This is a NEXT match; therefore,
a 1 is passed in for
carry
value.
#include <esnmp.h> OID *incoming = &method->varbind->name; OBJECT *object = method->object; int instLength; unsigned int instance[6]; unsigned int ip_addr; int iface; -- The instance is N.1.A.A.A.A where A.A.A.A is the IP address-- instLength = oid2instance(incoming, object, instance, 6); iface = (instLength < 1) ? 0 :(int) instance[0]; iface += inst2ip(&instance[2], instLength - 2, &ip_addr, FALSE, 1);
In the following example, if there is more than one part to a search
key and you are doing a
GetNext
operation, you want to
find the next possible value for the search key so you can do a cascaded greater-than
or equal-to search.
If you have a search key of a number and two
ipAddr
values that are represented in the instance part of the OID as
N.A.A.A.A.B.B.B.B
with
N
as single
valued integer and the
A.A.A.A
portion making up
one IP address and the
B.B.B.B
portion making up
a second IP address and a total length of 9 if all elements are given, you
start by converting the least significant part of the key, (that would be
the
B.B.B.B
portion).
You do that by calling the
inst2ip
routine passing in a 1 for the carry and (length-5)
for the length.
If the conversion of the B.B.B.B portion generated a carry (returned 1), you will pass it on to the next most significant part of the key.
Therefore, convert the
A.A.A.A
portion by
calling the
inst2ip
routine, passing in (length-1)
for the length and the carry returned from the conversion of the
B.B.B.B
portion.
The most significant element
N
is a number; therefore, add the carry from the
A.A.A.A
conversion to the number.
If that also overflows, then
this is not a valid search key.
#include <esnmp.h> OID *incoming = &method->varbind->name; OBJECT *object = method->object; int instLength; unsigned int instance[9]; unsigned int ip_addrA; unsigned int ip_addrB; int iface; int carry; -- The instance is N.A.A.A.A.B.B.B.B -- instLength = oid2instance(incoming, object, instance, 9); iface = (instLength < 1) ? 0 :(int) instance[0]; carry = inst2ip(&instance[5],instLength - 5,&ip_addrB,FALSE,1); carry = inst2ip(&instance[1],instLength - 1,&ip_addrA,FALSE,carry); iface += carry; if (iface > carry) { -- a carry caused an overflow in the most significant element return ESNMP_MTHD_noSuchInstance; }
The returns are as follows:
If the carry is 0, the routine ended successfully.
If the carry equals 1, it indicates an error if EXACT match or there was a carry for a NEXT match. If there was a carry, the returned ipAddr is 0.
The
cmp_oid
routine compares two
OID
structures.
This routine does an element-by-element comparison starting with
the most significant element (element 0) and working toward the least significant
element.
If all other elements are equal, the
OID
structure
with the fewest elements is considered less.
The syntax is as follows:
int cmp_oid(
OID *q,
OID *p
);
The returns are as follows:
+1 -- The
oid
q
is greater than
oid
p
.
0 -- The
oid
q
is in
oid
p
.
-1 -- The
oid
q
is less than
oid
p
.
6.3.3.11 The cmp_oid_prefix Routine
The
cmp_oid_prefix
routine compares an
OID
structure against a prefix.
A prefix could be the OID on an
object in the object table.
The elements beyond the prefix are the instance
information.
This routine does an element-by-element comparison, starting with the
most significant element (element 0) and working toward the least significant
element.
If all elements of the prefix OID match exactly with corresponding
elements of
OID
q
structure,
it is considered a match even if the
OID
q
structure contains additional elements.
The
OID
q
structure is considered greater than the OID prefix if the first nonmatching
element is larger.
It is considered smaller if the first nonmatching element
is less.
The syntax is as follows:
int cmp_oid_prefix(
OID *q,
OID *prefix
);
The following is an example of the
cmp_oid_prefix
routine:
#include <esnmp.h> OID *q; OBJECT *object; if (cmp_oid_prefix(q, &object->oid) == 0) printf("matches prefix\n");
The returns are as follows:
-1 -- The
oid
is less than the
prefix.
0 -- The
oid
is in the prefix.
+1 -- The
oid
is greater than the
prefix.
6.3.3.12 The clone_oid Routine
The
clone_oid
routine makes a copy of the
OID
structure.
It dynamically allocates the elements buffer and inserts its pointer
into the
OID
structure passed in.
To use this routine, pass in a pointer to the old
OID
structure to be cloned and a pointer to the new
OID
structure
that is to receive the duplicated
OID
values.
The calling program or module has the responsibility to free this buffer.
Note that any previous elements buffer pointed to by the new
OID
structure will be freed and pointers to the new, dynamically
allocated, buffer will be inserted.
Be sure to initialize the new
OID
structure with zeroes (0), unless it contains an element buffer
that can be freed.
Also note that this routine does not allocate an
OID
structure.
If the old
OID
structure is NULL or contains a NULL
pointer to its elements buffer, a new
OID
of {0.0} is generated.
The syntax is as follows:
OID *clone_oid(
OID *new,
OID *oid
);
The arguments are as follows:
A pointer to the
OID
structure that is to receive the copy.
A pointer to the
OID
structure where the data is to be obtained.
The following is an example of the
clone_oid
routine:
#include <esnmp.h> OID oid1; OID oid2; : : assume oid1 gets assigned a value : memset(&oid2, 0, sizeof(OID)); if (clone_oid(&oid2, &oid1) == NULL) ESNMP_LOG((WARNING, "It did not work\n"));
The returns are as follows:
An error occurred; otherwise, the pointer to the new OID (its first argument) is returned.
The
free_oid
routine frees an
OID
structure's elements buffer.
It frees the buffer pointed to by
oid->elements
field then zeros that field and the
oid->nelem
field.
Note that this routine does not deallocate the
OID
structure itself, only the elements buffer attached to it.
The syntax is as follows:
void free_oid(
OID *oid
);
The following is an example of the
free_oid
routine:
#include <esnmp.h> OID oid; : : assume oid was assigned a value (perhaps with clone_oid() : and we are now finished with it. : free_oid(&oid);
6.3.3.14 The clone_buf Routine
The
clone_buf
routine duplicates a buffer in a dynamically
allocated space.
One extra byte is always allocated on end and filled with
\0
.
If the
len
parameter is less than
0, the duplicate buffer length is set to 0.
The routine always returns a
buffer pointer, unless there is a
malloc
error.
The caller has the responsibility to free the allocated buffer.
The syntax is as follows:
char *clone_buf(
char *str,
int len
);
The arguments are as follows:
A pointer to the buffer to be duplicated.
The number of bytes to copy.
The following is an example of the
clone_buf
routine:
#include <esnmp.h> char *str = "something nice"; char *copy; copy = clone_buf(str, strlen(str));
The returns are as follows:
A
malloc
error occurred;
otherwise, the pointer to allocated buffer containing a copy of the original
buffer is returned.
The
mem2oct
routine converts a string, (a buffer
and length) to an
OCT
structure.
It dynamically allocates a new buffer, copies the indicated data into
it, and updates the
OCT
structure with the new buffer's
address and length.
The caller has the responsibility to free the allocated buffer.
Note this routine does not allocate an
OCT
structure
and that it does not free data previously pointed to in the
OCT
structure before making the assignment.
The syntax is as follows:
OCT * mem2oct(
OCT *new,
char *buffer,
int len
);
The following is an example of the
mem2oct
routine:
#include <esnmp.h> char buffer; int len; OCT abc; : : buffer and len are initialized to something : memset(&abc, 0, sizeof(OCT)); if (mem2oct(&abc, buffer, len) == NULL) ESNMP_LOG((WARNING,"It did not work...\n"));
The following are the return values:
An error occurred; otherwise, the pointer
to the
OCT
structure (its first argument) is returned.
The
cmp_oct
routine compares two octet strings.
The two octet strings are compared byte-by-byte for the length of the shortest
octet string.
If all bytes are equal, the lengths are compared.
An octet
with a null pointer is considered the same as a zero-length octet.
The syntax is as follows:
int cmp_oct(
OCT *oct1,
OCT *oct2
);
The following is an example of the
cmp_oct
routine:
#include <esnmp.h> OCT abc, efg; : : abc and efg are initialized to something : if (cmp_oct(&abc, &efg) > 0) ESNMP_LOG((WARNING,"octet abc is larger than efg...\n"));
The returns are as follows:
-1 -- The string to which the first parameter points is less than the second.
0 -- The string to which the first parameter points is equal to the second.
+1 -- The string to which the first parameter points is greater than the second.
6.3.3.17 The clone_oct Routine
The
clone_oct
routine makes a copy of an
OCT
structure.
The caller passes in a pointer to the old
OCT
structure
to be cloned and a pointer to the new
OCT
structure that
is to receive the duplicate
OCT
structure values.
It dynamically allocates the buffer, copies the data, and updates the
new
OCT
structure with the buffer's address and length.
The caller has the responsibility to free this allocated buffer.
Note that any previous buffer to which the new
OCT
structure points is freed and pointers to the new, dynamically allocated buffer
are inserted.
Be sure to initialize the new
OCT
structure
with zeros (0), unless it contains a buffer that can be freed.
Also note that this routine does not allocate an
OCT
structure, only the elements buffer pointed to by the
OCT
structure.
The syntax is as follows:
OCT *clone_oct(
OCT *new,
OCT *old
);
The arguments are as follows:
A pointer to the
OCT
structure that is to receive the copy.
A pointer to the
OCT
structure where the data is to be obtained.
The following is an example of the
clone_oct
routine:
#include <esnmp.h> OCT octet1; OCT octet2; : : assume octet1 gets assigned a value : memset(&octet2, 0, sizeof(OCT)); if (clone_oct(&octet2, &octet1) == NULL) ESNMP_LOG((WARNING, "It did not work\n"));
The return values are as follows:
An error occurred; otherwise, the pointer
to the
OCT
structure (its first argument) is returned.
The
free_oct
routine frees the buffer attached to
an
OCT
structure.
It frees a dynamically allocated buffer to which the
OCT
structure points, then zeros (0) the pointer and length fields in the
OCT
structure.
If the
OCT
structure is already
NULL, this routine does nothing.
If the buffer attached to the
OCT
structure is already NULL, this routine sets the
length
field of the
OCT
structure to zero (0).
Note that this routine does not deallocate the
OCT
structure, only the buffer to which it points.
The syntax is as follows:
void free_oct(
OCT *oct
);
The following is an example of the
free_oct
routine:
#include <esnmp.h> OCT octet; : : assume octet was assigned a value (perhaps with clone_oct() : and we are now finished with it. : free_oct(&octet);
6.3.3.19 The free_varbind_data Routine
The
free_varbind_data
routine frees the dynamically
allocated fields within the
VARBIND
structure.
The routine performs a
free_oid
(vb->name
) operation.
If the
vb->type
field indicates, it then frees the
vb->value
data using either the
free_oct
or the
free_oid
routine.
It does not deallocate the
VARBIND
structure itself;
only the name and data buffers to which it points.
The syntax is as follows:
void free_varbind_data(
VARBIND *vb
);
The following is an example of the
free_varbind_data
routine:
#include <esnmp.h> VARBIND *vb; : : assume oid and data are declared and : assigned appropriate values : vb = (VARBIND*)malloc(sizeof(VARBIND)); clone_oid(&vb->name, oid); clone_oct(&vb->value.oct, data); : : some processing that uses vb occurs here : free_varbind_data(vb); free(vb);
6.3.3.20 The set_debug_level Routine
The
set_debug_level
routine sets the logging level,
which dictates what log messages are generated.
The program or module calls
the routine during program initialization in response to run-time options.
If not called, WARNING and ERROR messages are sent to
stdout
as the default.
The syntax is as follows:
void set_debug_level(
int stat,
LOG_CALLBACK_ROUTINE callback_routine
);
The arguments are as follows:
The log level. The following values can be set individually or in combination:
For when a bad error occurred, requiring a restart.
For when a packet cannot be handled; this also implies ERROR.
For when tracing all packets; this also implies ERROR and WARNING.
Causes output to go to
syslog
rather than to standard output.
Causes the callback function to be called to output log messages. If this bit is set, you must provide the second argument, which is a pointer to a user supplied external callback function. If DAEMON_LOG and EXTERN_LOG are not specified, output goes to standard output.
A user-supplied external callback function. For example:
void callback_function(
int level,
char *message
);
The
level
will be
ERROR
,
WARNING
, or
TRACE
.
If the EXTERN_LOG bit is
set in
stat, the
callback
function
will be called whenever an
ESNMP_LOG
macro is executed
and the log level indicates that a log message is to be generated.
This facility allows an implementer to control where eSNMP library functions
output log messages.
If the
EXTERN_LOG
bit will not be
set, pass in a NULL pointer for the callback function argument.
The following is an example of the
set_debug_level
routine:
#include <esnmp.h> extern void log_handler(int level, char *message); if (daemonize) set_debug_level(EXTERN_LOG | WARNING, log_handler); else set_debug_level(TRACE, NULL);
6.3.3.21 The is_debug_level Routine
The
is_debug_level
routine tests the logging level
to see if the specified level is set.
You can test the levels as follows:
ERROR -- For when a bad error occurred, requiring restart.
WARNING -- For when a packet cannot be handled; this also implies ERROR.
TRACE -- For when tracing all packets; this also implies ERROR and WARNING.
DAEMON_LOG -- For output going to
syslog
.
EXTERN_LOG -- For the callback function to be called to output log messages.
The syntax is as follows:
int is_debug_level(
int type
);
The return values are as follows:
The requested level is set and the
ESNMP_LOG
will generate output, or output will go to the specified
destination.
The logging level is not set.
The following is an example of the
is_debug_level
routine:
#include <esnmp.h> if (is_debug_level(TRACE)) dump_packet();
6.3.3.22 The ESNMP_LOG Routine
The
ESNMP_LOG
routine is an error declaration C macro
defined in the
<esnmp.h>
header file.
It gathers the
information that it can obtain and sends it to the log.
If
DAEMON_LOG
is set, log messages are sent to the daemon log.
If
EXTERN_LOG
is set, log messages are sent to the
callback
function; otherwise, log messages go to standard output.
Note
The
esnmp_log
routine is called using the ESNMP_LOG macro, which uses the helper routineesnmp_logs
to format part of the text. Do not use these functions without theESNMP_LOG
macro.
#define ESNMP_LOG(level, x) if (is_debug_level(level)) { \ esnmp_log(level, esnmp_logs x, __LINE__, __FILE__);}
Where
x
is
(text)
:
text -
format, arguments, ....
For example a
printf
statement.
Can be one of the following:
Declares an error condition.
Declares a warning.
Puts in log file if trace is active.
The syntax is as follows:
ESNMP_LOG(level,(format,
...))
The following is an example of the
ESNMP_LOG
routine:
#include <esnmp.h> ESNMP_LOG( ERROR, ("Cannot open file %s\n", file));