6    Extensible SNMP Application Programming Interface

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:

This chapter provides the following information:

6.1    Overview of eSNMP

The following sections describe the components and architecture of the eSNMP agent. It contains information on the following:

6.1.1    Components of eSNMP

The eSNMP components are as follows:

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:

6.1.4    AgentX

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:

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:

The eSNMP shared library (libesnmp.so) provides the following services:

6.2.1    MIB Subtrees

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:

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:

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:

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:

  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.

  2. The master agent sends an eSNMP message to the subagent that registered subtree_1.

  3. 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:

  4. 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.

  5. 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:

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:

  1. 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.

  2. 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:

  3. 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.

  4. Build the subagent.

    An example Makefile, chess.mk, is provided in the /usr/examples/esnmp directory.

  5. 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_trapsndprograms 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:

  1. Initialization (esnmp_init)

  2. Allocate any needed indexes for tables shares across multiple subagents (esnmp_allocate)

  3. Registration (esnmp_register [esnmp_register ...])

  4. 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
    }
    

  5. 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:

6.3    Extensible SNMP Application Programming Interface

The following sections provide detailed information on the SNMP Application Programming Interface, which consists of the following:

6.3.1    Calling Interface

The calling interface contains the following routines:

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:

socket

The address of the integer that receives the socket descriptor used by eSNMP.

subagent_identifier

The address of a null-terminated string that identifies this subagent (usually program name).

The return values are as follows:

ESNMP_LIB_NO_CONNECTION

Could not initialize or communicate with the master agent. Try again after a delay.

ESNMP_LIB_OK

The esnmp_init routine has completed successfully.

ESNMP_LIB_NOTOK

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:

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:

  1. Allocate an index value successfully.

  2. 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.

  3. 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:

alloc_parm

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:

vb

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.

options

A bitmask of the following values:

ESNMP_ALLOC_CLUSTER

The index allocation request is for the cluster context.

ESNMP_ALLOC_NEW_INDEX

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.

ESNMP_ALLOC_ANY_INDEX

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.

status

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):

ESNMP_ALLOC_STATE_PENDING

The index allocation request is currently held locally while waiting for a connection to the master agent to become established.

ESNMP_ALLOC_STATE_SENT

The routine sent the index allocation request to the master agent (final status still pending).

ESNMP_ALLOC_STATE_DONE

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:

ESNMP_ALLOC_STATE_ALLOCTYPE

The master agent rejected the index allocation request because of a wrong index type or wrong options.

ESNMP_ALLOC_STATE_ALLOCINUSE

The master agent rejected the index allocation request because the supplied index object value or values are currently in use.

ESNMP_ALLOC_STATE_ALLOCAVAIL

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.

ESNMP_ALLOC_STATE_ALLOCNOCLU

The master agent rejected the index allocation request because the cluster context option is not supported.

ESNMP_ALLOC_STATE_REJ

The master agent rejected the index allocation request because of other reasons.

error_index

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:

ESNMP_LIB_NOTOK

The alloc_parm argument was not specified.

ESNMP_LIB_OK

The esnmp_allocate routine has completed successfully.

ESNMP_LIB_LOST_CONNECTION

The subagent lost communication with the master agent.

ESNMP_LIB_BAD_ALLOC

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:

alloc_parm

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:

vb

A pointer to a varable binding list containing one or more VARBINDs. Each VARBIND in the list contains the name and the value of an index object previously allocated.

options

A bitmask of the following values:

ESNMP_ALLOC_CLUSTER

The index allocation request is for the cluster context.

status

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):

ESNMP_ALLOC_STATE_SENT

The index deallocation request was sent to the master agent (final status still pending).

ESNMP_ALLOC_STATE_DONE

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:

ESNMP_ALLOC_STATE_DEALLOC_REJ

The master agent rejected the index deallocation request because of an unknown allocation.

ESNMP_ALLOC_STATE_REJ

The master agent rejected the index deallocation request because of other reasons.

error_index

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:

ESNMP_LIB_NOTOK

The alloc_parm argument was not specified.

ESNMP_LIB_OK

The esnmp_allocate routine has completed successfully.

ESNMP_LIB_LOST_CONNECTION

The subagent lost communication with the master agent.

ESNMP_LIB_BAD_DEALLOC

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:

subtree

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 the snmpi utility.

timeout

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.

priority

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:

ESNMP_LIB_OK

The esnmp_register routine has completed successfully.

ESNMP_LIB_BAD_REG

The esnmp_init routine has not been called, the timeout parameter is invalid, or this MIB subtree has already been queued for registration.

ESNMP_LIB_LOST_CONNECTION

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:

subtree

A pointer to the SUBTREE structure for the MIB subtree to be unregistered.

The return values are as follows:

ESNMP_LIB_OK

The routine completed successfully.

ESNMP_LIB_BAD_REG

The MIB subtree was not registered.

ESNMP_LIB_LOST_CONNECTION

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:

reg

A pointer to a ESNMP_REG structure, which contains the following fields:

subtree

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 the snmpi utility.

priority

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.

timeout

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.

range_subid

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.

range_upper_bound

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.

options

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.

state

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.

ESNMP_REG_STATE_PENDING

The registration is currently held locally while waiting for connection to the master agent.

ESNMP_REG_STATE_SENT

The registration was sent to the master agent.

ESNMP_REG_STATE_DONE

The wegistration was successfully ackowledged by the master agent.

ESNMP_REG_STATE_REGDUP

The registration was rejected by the master agent because it was a duplicate.

ESNMP_REG_STATE_REGNOCLU

The master agent does not support cluster registrations.

ESNMP_REG_STATE_REJ

The master agent rejected the registration for other reasons.

instance

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:

ESNMP_LIB_OK

The esnmp_register2 routine has completed successfully.

ESNMP_LIB_BAD_REG

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.

ESNMP_LIB_LOST_CONNECTION

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:

reg

A pointer to the ESNMP_REG structure that was used when the esnmp_register2 routine was called.

The return values are as follows:

ESNMP_LIB_OK

The routine completed successfully.

ESNMP_LIB_BAD_REG

The MIB subtree was not registered.

ESNMP_LIB_LOST_CONNECTION

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:

agent_cap_id

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.

agent_cap_descr

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:

agent_cap_id

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:

ESNMP_LIB_OK

The esnmp_poll routine has completed successfully.

ESNMP_LIB_BAD_REG

A previous registration was failed by the master agent. See the log file.

ESNMP_LIB_DUPLICATE

A duplicate subagent identifier has already been received by the master agent. This is an esnmp_init error.

ESNMP_LIB_NO_CONNECTION

The master agent failed to initiate an esnmp_init request. Restart after a delay. See the log file.

ESNMP_LIB_CLOSE

A CLOSE message was received.

ESNMP_LIB_NOTOK

An eSNMP protocol error occurred. The packet was discarded.

ESNMP_LIB_LOST_CONNECTION

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:

ESNMP_LIB_OK

The request was sent.

ESNMP_LIB_LOST_CONNECTION

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:

generic_trap

A generic trap code. Set to 0 (zero) for SNMPv2 traps.

specific_trap

A specific trap code. Set to 0 (zero) for SNMPv2 traps.

enterprise

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.

vb

A VARBIND list of data (a NULL pointer indicates no data)

The return values are as follows:

ESNMP_LIB_OK

The routine completed successfully.

ESNMP_LIB_LOST_CONNECTION

The routine could not send the trap message to the master agent.

ESNMP_LIB_NOTOK

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:

ESNMP_LIB_OK

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:

timestamp

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(&timestamp, NULL);
o_integer(vb, object, esnmp_sysuptime(&timestamp));

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:

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:

method

A pointer to a METHOD structure, which contains the following fields:

action

One of ESNMP_ACT_GET, ESNMP_ACT_GETNEXT, or ESNMP_ACT_GETBULK.

serial_num

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.

repeat_cnt

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.

max_repetitions

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.

varbind

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.

object

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->namefield. The oid2instance function is useful for this.

The possible return values for the *_get method routine are as follows:

ESNMP_MTHD_noError

The routine completed successfully.

ESNMP_MTHD_noSuchObject

The requested object cannot be returned or does not exist.

ESNMP_MTHD_noSuchInstance

The requested instance of an object cannot be returned or does not exist.

ESNMP_MTHD_genErr

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:

method

A pointer to a METHOD structure, which contains the following fields:

action

The action value can be one of the following: ESNMP_ACT_SET, ESNMP_ACT_COMMIT, ESNMP_ACT_UNDO, or ESNMP_ACT_CLEANUP

serial_num

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.

varbind

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.

object

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.

flags

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.

row

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:

instance

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.

instance_len

The size of the method->row->instance field.

context

A pointer to be used privately by the method routine to reference data needed to process this request.

save

A pointer to be used privately by the method routine to reference data needed to potentially undo this request.

state

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:

ESNMP_MTHD_noError

The routine completed successfully.

ESNMP_MTHD_notWritable

The requested object cannot be set or was not implemented.

ESNMP_MTHD_wrongType

The data type for the requested value is the wrong type.

ESNMP_MTHD_wrongLength

The requested value is the wrong length.

ESNMP_MTHD_wrongEncoding

The requested value is represented incorrectly.

ESNMP_MTHD_wrongValue

The requested value is out of range.

ESNMP_MTHD_noCreation

The requested instance can never be created.

ESNMP_MTHD_inconsistentName

The requested instance cannot currently be created.

ESNMP_MTHD_inconsistentValue

The requested value is not consistent.

ESNMP_MTHD_resourceUnavailable

A failure due to some resource constraint.

ESNMP_MTHD_genErr

A general processing error.

ESNMP_MTHD_commitFailed

The commit phase failed.

ESNMP_MTHD_undoFailed

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:

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 the SetRequest 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:

  1. 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.

  2. 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.

  3. Extract the data. Based on the instance OID and method->action field, determine what data, if any, is to be returned.

  4. 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.

  5. 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:

    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.

  6. Return the correct status value, as follows:

Value Representation

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.)

6.3.3    The libsnmp Support Routines

The following sections provide information on the libsnmp support routines, which consist of the following:

6.3.3.1    The o_integer Routine

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:

vb

A pointer to the VARBIND structure that is to receive the data. This function does not allocate the VARBIND structure.

obj

A pointer to the OBJECT structure for the MIB variable associated with the OID in the VARBIND structure.

value

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.

ESNMP_TYPE_Integer32:

32-bit INTEGER

ESNMP_TYPE_Counter32:

32-bit Counter (unsigned)

ESNMP_TYPE_Gauge32:

32-bit Gauge (unsigned)

ESNMP_TYPE_TimeTicks:

32-bit TimeTicks (unsigned)

ESNMP_TYPE_UInteger32:

32-bit INTEGER (unsigned)

ESNMP_TYPE_Counter64:

64-bit Counter (unsigned)

ESNMP_TYPE_IpAddress:

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:

ESNMP_MTHD_noError

The routine completed successfully.

ESNMP_MTHD_genErr

An error has occurred.

6.3.3.2    The o_octet Routine

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:

vb

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.

obj

A pointer to the OBJECT structure for the MIB variable associated with the OID in the VARBIND structure.

value

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:

ESNMP_TYPE_OCTET_STRING

OCTET STRING

ESNMP_TYPE_IpAddress

IMPLICIT OCTET STRING (4) -- in octet form, network byte order

ESNMP_TYPE_DisplayString

DisplayString (Textual Convention)

ESNMP_TYPE_Opaque

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:

ESNMP_MTHD_noError

The routine completed successfully.

ESNMP_MTHD_genErr

An error condition has occurred.

6.3.3.3    The o_oid Routine

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:

vb

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.

obj

A pointer to the OBJECT structure for the MIB variable associated with the OID in the VARBIND structure.

value

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:

ESNMP_TYPE_OBJECT_IDENTIFIER

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:

ESNMP_MTHD_noError

The routine completed successfully.

ESNMP_MTHD_genErr

An error condition has occurred.

6.3.3.4    The o_string Routine

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:

vb

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.

obj

A pointer to the OBJECT structure for the MIB variable associated with the oid in the VARBIND structure.

ptr

A pointer to the buffer containing data to be inserted into the VARBIND structure as data.

len

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:

ESNMP_TYPE_OCTET_STRING

OCTET STRING

ESNMP_TYPE_IpAddress

IMPLICIT OCTET STRING (4) -- in octet form, network byte order

ESNMP_TYPE_DisplayString

DisplayString (Textual Convention)

ESNMP_TYPE_Opaque

IMPLICIT OCTET STRING

ESNMP_TYPE_OBJECT_IDENTIFIER

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:

ESNMP_MTHD_noError

The routine completed successfully.

ESNMP_MTHD_genErr

An error condition has occurred.

6.3.3.5    The str2oid Routine

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:

NULL

An error has occurred; otherwise, the pointer to the OID structure (its first argument) is returned.

6.3.3.6    The sprintoid Routine

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:

new

A pointer to the OID structure that is to receive the new OID value.

obj

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.

instance

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.

len

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:

NULL

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:

oid

A pointer to an incoming OID structure containing an instance or part of an instance.

obj

A pointer to the object table entry for the MIB variable.

instance

A pointer to an array of unsigned integers where the index will be placed.

max_len

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:

6.3.3.9    The inst2ip Routine

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:

inst

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.

length

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.

ipAddr

A pointer to where to return the IP address value. It is in network byte order; that is, the most significant element is first.

exact

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.

carry

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:

6.3.3.10    The cmp_oid Routine

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:

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:

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:

new

A pointer to the OID structure that is to receive the copy.

old

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:

NULL

An error occurred; otherwise, the pointer to the new OID (its first argument) is returned.

6.3.3.13    The free_oid Routine

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:

str

A pointer to the buffer to be duplicated.

len

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:

NULL

A malloc error occurred; otherwise, the pointer to allocated buffer containing a copy of the original buffer is returned.

6.3.3.15    The mem2oct Routine

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:

NULL

An error occurred; otherwise, the pointer to the OCT structure (its first argument) is returned.

6.3.3.16    The cmp_oct Routine

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:

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:

new

A pointer to the OCT structure that is to receive the copy.

old

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:

NULL

An error occurred; otherwise, the pointer to the OCT structure (its first argument) is returned.

6.3.3.18    The free_oct Routine

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:

stat

The log level. The following values can be set individually or in combination:

ERROR

For when a bad error occurred, requiring a 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

Causes output to go to syslog rather than to standard output.

EXTERN_LOG

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.

callback_routine

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:

The syntax is as follows:

int is_debug_level(
        int type );

The return values are as follows:

TRUE

The requested level is set and the ESNMP_LOG will generate output, or output will go to the specified destination.

FALSE

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 routine esnmp_logs to format part of the text. Do not use these functions without the ESNMP_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.

level

Can be one of the following:

ERROR

Declares an error condition.

WARNING

Declares a warning.

TRACE

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));