|
Packit |
fcad23 |
Improved Error Reporting and Thread-Safe Use of the SNMP Library
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
There is a need in some environments to support multiple threads
|
|
Packit |
fcad23 |
in a single application. The SNMP Library provides the Single Session
|
|
Packit |
fcad23 |
functions which support thread-safe operation when certain precautions
|
|
Packit |
fcad23 |
are taken. This document describes the operation of the SNMP Library
|
|
Packit |
fcad23 |
with a focus on its session management functions. The Traditional API
|
|
Packit |
fcad23 |
and the Single API functions are compared and contrasted.
|
|
Packit |
fcad23 |
A working understanding of the CMU or UCD SNMP Library
|
|
Packit |
fcad23 |
API is recommended to fully appreciate the concepts discussed.
|
|
Packit |
fcad23 |
The document ends with a list of restrictions for using the Single API
|
|
Packit |
fcad23 |
in a multi-threaded application.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Unfortunately, the SNMPv3 support was added about the same time as
|
|
Packit |
fcad23 |
the thread support and since they occurred in parallel the SNMPv3
|
|
Packit |
fcad23 |
support was never checked for multi-threading correctness. It is
|
|
Packit |
fcad23 |
most likely that it is not thread-safe at this time.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
***** IMPORTANT ANNOUNCEMENT *****
|
|
Packit |
fcad23 |
To the point, no resource locks are applied within the SNMP Library.
|
|
Packit |
fcad23 |
The APDU encoding and some session management functions can be used
|
|
Packit |
fcad23 |
in thread-safe manners. The MIB file parsing is not thread-safe.
|
|
Packit |
fcad23 |
The Single Session API was made available in November 1998.
|
|
Packit |
fcad23 |
Existing applications use the Traditional API, which is not thread-safe.
|
|
Packit |
fcad23 |
The thread-safe considerations are discussed throughout this document.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The research and development of the Single Session API that I've completed
|
|
Packit |
fcad23 |
was wholly funded by my employer, Internet Security Systems, Inc.
|
|
Packit |
fcad23 |
and is distributed freely to the Internet community.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
-Mike Slifcak, 23 April 1999
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
09 July 1999 Removed references to snmp_synch_setup and snmp_synch_reset
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Availability
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The Single Session API is integrated into the currently available
|
|
Packit |
fcad23 |
versions of the CMU SNMP library and the UC-Davis SNMP package.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
ftp://ftp.net.cmu.edu/pub/snmp/cmu-snmp-V1.13.tar.gz and later
|
|
Packit |
fcad23 |
Read : snmp_sess_api.3, Changes.SingleSession
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
ftp://ucd-snmp.ucdavis.edu/ucd-snmp-3.6.tar.gz and later
|
|
Packit |
fcad23 |
Read : snmp_sess_api.3, README.thread (after version 3.6.1)
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Both libraries work equally well in Windows NT and various
|
|
Packit |
fcad23 |
UNIX platforms. Please read this document and refer to
|
|
Packit |
fcad23 |
the snmp_sess_api section 3 manual page.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Glossary of Terms
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
APDU Application Protocol Data Unit
|
|
Packit |
fcad23 |
API Application Programming Interface
|
|
Packit |
fcad23 |
CMU Carnegie-Mellon University, Pittsburgh, PA.
|
|
Packit |
fcad23 |
Library The SNMP library; Both CMU and UCD versions are applicable.
|
|
Packit |
fcad23 |
Session Concept embodying the management of transacting SNMP APDUS.
|
|
Packit |
fcad23 |
SNMP Simple Network Management Protocol
|
|
Packit |
fcad23 |
UCD University of California at Davis, CA.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Introduction
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The Library extends the UNIX file concept (open, close, read, write) to a Session.
|
|
Packit |
fcad23 |
Opening a Session binds a local socket to a well-known port and creates internal
|
|
Packit |
fcad23 |
structures to help with controlling the transaction of SNMP APDUs. Closing a
|
|
Packit |
fcad23 |
Session releases the memory and system resources used for these purposes.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Since the mid-1980s, many SNMP applications have used the Traditional Session
|
|
Packit |
fcad23 |
API to transact SNMP APDUs between the local host and SNMP-enabled devices.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The Traditional Session API does not support multi-threaded applications:
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
1) There are no resource locks to prevent exposing the Library's
|
|
Packit |
fcad23 |
global data resources to corruption in a multi-threaded application;
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
2) The Traditional API functions that receive SNMP APDUs
|
|
Packit |
fcad23 |
do not provide an interface for one of many sessions;
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
3) Errors discovered by the Library are communicated through global
|
|
Packit |
fcad23 |
data structures and are not associated with the session
|
|
Packit |
fcad23 |
in which the error occurred.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The Single Session API provides these capabilities:
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
1) Manage a single SNMP session safely, in multi-threaded or
|
|
Packit |
fcad23 |
non-threaded applications, by avoiding access to data structures
|
|
Packit |
fcad23 |
that the Traditional Session API may share between Sessions;
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
2) Associate errors with the session context for threaded
|
|
Packit |
fcad23 |
and non-threaded applications.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Contrasting and Comparing Traditional API and Single API
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The Traditional API uses the struct snmp_session pointer returned
|
|
Packit |
fcad23 |
from snmp_open() to identify one SNMP session. The Single API uses
|
|
Packit |
fcad23 |
the opaque pointer returned from snmp_sess_open() to identify one
|
|
Packit |
fcad23 |
SNMP session.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Helpful Hint : The Library copies the contents of the
|
|
Packit |
fcad23 |
structure which is input to snmp_open() and snmp_sess_open().
|
|
Packit |
fcad23 |
Once copied, changing that input structure's data
|
|
Packit |
fcad23 |
has no effect on the opened SNMP Session.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The Traditional API uses the snmp_error() function to identify any
|
|
Packit |
fcad23 |
library and system errors that occurred during the processing for
|
|
Packit |
fcad23 |
one SNMP session. The Single API uses snmp_sess_error() for the
|
|
Packit |
fcad23 |
same purpose.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The Traditional API manages the private Sessions list structure;
|
|
Packit |
fcad23 |
adding to the list during snmp_open(), removing during snmp_close.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
With few exceptions, the Traditional API calls the Single API
|
|
Packit |
fcad23 |
for each session that appears on the Sessions list.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The Traditional API reads from all Sessions on the Sessions list;
|
|
Packit |
fcad23 |
The Single API does not use the Sessions list.
|
|
Packit |
fcad23 |
The Single API can read from only one Session.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Helpful Hint :
|
|
Packit |
fcad23 |
This is the basis for thread-safe-ness of the Library.
|
|
Packit |
fcad23 |
There are no resource locks applied.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Using the Single API
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
A multi-threaded application that deploys the SNMP Library should
|
|
Packit |
fcad23 |
should complete all MIB file parsing before additional threads
|
|
Packit |
fcad23 |
are activated. Drawing from the parsed contents of the MIB does
|
|
Packit |
fcad23 |
not incur any data corruption exposure once the internal MIB structures
|
|
Packit |
fcad23 |
are initialised.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The application may create threads such that a single thread may manage
|
|
Packit |
fcad23 |
a single SNMP session. The thread should call snmp_sess_init()
|
|
Packit |
fcad23 |
to prepare a struct snmp_session structure. The thread can adjust
|
|
Packit |
fcad23 |
session parameters such as the remote UDP port or the local UDP port,
|
|
Packit |
fcad23 |
which must be set prior to invoking snmp_sess_open().
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The first call to snmp_sess_init() initialises the SNMP Library,
|
|
Packit |
fcad23 |
including the MIB parse trees, before any SNMP sessions are created.
|
|
Packit |
fcad23 |
Applications that call snmp_sess_init() do not need to read MIBs
|
|
Packit |
fcad23 |
nor setup environment variables to utilize the Library.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
After the struct snmp_session is setup, the thread must call
|
|
Packit |
fcad23 |
snmp_sess_open() to create an SNMP session. If at any time
|
|
Packit |
fcad23 |
the thread must change the Session configuration,
|
|
Packit |
fcad23 |
snmp_sess_session() returns the pointer to the internal configuration
|
|
Packit |
fcad23 |
structure (a struct snmp_session, copied from snmp_sess_open).
|
|
Packit |
fcad23 |
The thread can adjust parameters such as the session timeout
|
|
Packit |
fcad23 |
or the community string with this returned struct snmp_session pointer.
|
|
Packit |
fcad23 |
Changes to the remote or local port values have no effect on an opened Session.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The thread can build PDUs and bind variables to PDUs, as it performs its duties.
|
|
Packit |
fcad23 |
The thread then calls snmp_sess_send() or snmp_sess_async_send() to build and send
|
|
Packit |
fcad23 |
an SNMP APDU to the remote device. If a Get-Response-PDU is expected, the thread
|
|
Packit |
fcad23 |
should call snmp_sess_synch_response() instead.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
When the thread is finished using the session, it must free the resources
|
|
Packit |
fcad23 |
that the Library used to manage the session.
|
|
Packit |
fcad23 |
Finally, the thread must call snmp_sess_close() to end the Session.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Snmp_sess_init(), snmp_open(), and snmp_sess_open()
|
|
Packit |
fcad23 |
must use the same calling parameter for a given Session.
|
|
Packit |
fcad23 |
Other methods should use only the returned parameter from
|
|
Packit |
fcad23 |
snmp_open() and snmp_sess_open() to access the opened SNMP Session.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Error Processing
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Two calls were added : snmp_error() and snmp_sess_error() return the
|
|
Packit |
fcad23 |
"errno" and "snmp_errno" values from the per session data, and a string
|
|
Packit |
fcad23 |
that describes the errors that they represent. The string must be freed
|
|
Packit |
fcad23 |
by the caller.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Use snmp_error() to process failures after Traditional API calls,
|
|
Packit |
fcad23 |
or snmp_sess_error() to process failure after Single API calls.
|
|
Packit |
fcad23 |
In the case where an SNMP session could not be opened,
|
|
Packit |
fcad23 |
call snmp_error() using the struct snmp_session supplied to either snmp_open()
|
|
Packit |
fcad23 |
or snmp_sess_open().
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The following variables and functions are obsolete and may create problems
|
|
Packit |
fcad23 |
in a multi-threaded application :
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
int snmp_errno
|
|
Packit |
fcad23 |
char * snmp_detail
|
|
Packit |
fcad23 |
snmp_set_detail()
|
|
Packit |
fcad23 |
snmp_api_errstring()
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Function Summary
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
The functions in the following table are functionally equivalent,
|
|
Packit |
fcad23 |
with the exception of these behaviors:
|
|
Packit |
fcad23 |
- The Traditional API manages many sessions
|
|
Packit |
fcad23 |
- The Traditional API passes a struct snmp_session pointer,
|
|
Packit |
fcad23 |
and touches the Sessions list
|
|
Packit |
fcad23 |
- The Single API manages only one session
|
|
Packit |
fcad23 |
- The Single API passes an opaque pointer, and does not use Sessions list
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Traditional Single Comment
|
|
Packit |
fcad23 |
=========== ============== =======
|
|
Packit |
fcad23 |
snmp_sess_init snmp_sess_init Call before either open
|
|
Packit |
fcad23 |
snmp_open snmp_sess_open Single not on Sessions list
|
|
Packit |
fcad23 |
snmp_sess_session Exposes snmp_session pointer
|
|
Packit |
fcad23 |
snmp_send snmp_sess_send Send one APDU
|
|
Packit |
fcad23 |
snmp_async_send snmp_sess_async_send Send one APDU with callback
|
|
Packit |
fcad23 |
snmp_select_info snmp_sess_select_info Which session(s) have input
|
|
Packit |
fcad23 |
snmp_read snmp_sess_read Read APDUs
|
|
Packit |
fcad23 |
snmp_timeout snmp_sess_timeout Check for timeout
|
|
Packit |
fcad23 |
snmp_close snmp_sess_close Single not on Sessions list
|
|
Packit |
fcad23 |
snmp_synch_response snmp_sess_synch_response Send/receive one APDU
|
|
Packit |
fcad23 |
snmp_error snmp_sess_error Get library,system errno
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Example 1 : Traditional API use.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
#include "snmp_api.h"
|
|
Packit |
fcad23 |
...
|
|
Packit |
fcad23 |
int liberr, syserr;
|
|
Packit |
fcad23 |
char *errstr;
|
|
Packit |
fcad23 |
struct snmp_session Session, *sptr;
|
|
Packit |
fcad23 |
...
|
|
Packit |
fcad23 |
snmp_sess_init(&Session);
|
|
Packit |
fcad23 |
Session.peername = "foo.bar.net";
|
|
Packit |
fcad23 |
sptr = snmp_open(&Session);
|
|
Packit |
fcad23 |
if (sptr == NULL) {
|
|
Packit |
fcad23 |
/* Error codes found in open calling argument */
|
|
Packit |
fcad23 |
snmp_error(&Session, &liberr, &syserr, &errstr);
|
|
Packit |
fcad23 |
printf("SNMP create error %s.\n", errstr);
|
|
Packit |
fcad23 |
free(errstr);
|
|
Packit |
fcad23 |
return 0;
|
|
Packit |
fcad23 |
}
|
|
Packit |
fcad23 |
/* Pass sptr to snmp_error from here forward */
|
|
Packit |
fcad23 |
...
|
|
Packit |
fcad23 |
/* Change the community name */
|
|
Packit |
fcad23 |
free(sptr->community);
|
|
Packit |
fcad23 |
sptr->community = strdup("public");
|
|
Packit |
fcad23 |
sptr->community_len = strlen("public");
|
|
Packit |
fcad23 |
...
|
|
Packit |
fcad23 |
if (0 == snmp_send(sptr, pdu)) {
|
|
Packit |
fcad23 |
snmp_error(sptr, &liberr, &syserr, &errstr);
|
|
Packit |
fcad23 |
printf("SNMP write error %s.\n", errstr);
|
|
Packit |
fcad23 |
free(errstr);
|
|
Packit |
fcad23 |
return 0;
|
|
Packit |
fcad23 |
}
|
|
Packit |
fcad23 |
snmp_close(sptr);
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Example 2 : Single API use.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
#include "snmp_api.h"
|
|
Packit |
fcad23 |
...
|
|
Packit |
fcad23 |
int liberr, syserr;
|
|
Packit |
fcad23 |
char *errstr;
|
|
Packit |
fcad23 |
void *sessp; /* <-- an opaque pointer, not a struct pointer */
|
|
Packit |
fcad23 |
struct snmp_session Session, *sptr;
|
|
Packit |
fcad23 |
...
|
|
Packit |
fcad23 |
snmp_sess_init(&Session);
|
|
Packit |
fcad23 |
Session.peername = "foo.bar.net";
|
|
Packit |
fcad23 |
sessp = snmp_sess_open(&Session);
|
|
Packit |
fcad23 |
if (sessp == NULL) {
|
|
Packit |
fcad23 |
/* Error codes found in open calling argument */
|
|
Packit |
fcad23 |
snmp_error(&Session, &liberr, &syserr, &errstr);
|
|
Packit |
fcad23 |
printf("SNMP create error %s.\n", errstr);
|
|
Packit |
fcad23 |
free(errstr);
|
|
Packit |
fcad23 |
return 0;
|
|
Packit |
fcad23 |
}
|
|
Packit |
fcad23 |
sptr = snmp_sess_session(sessp); /* <-- get the snmp_session pointer */
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
/* Pass sptr to snmp_sess_error from here forward */
|
|
Packit |
fcad23 |
...
|
|
Packit |
fcad23 |
/* Change the community name */
|
|
Packit |
fcad23 |
free(sptr->community);
|
|
Packit |
fcad23 |
sptr->community = strdup("public");
|
|
Packit |
fcad23 |
sptr->community_len = strlen("public");
|
|
Packit |
fcad23 |
...
|
|
Packit |
fcad23 |
if (0 == snmp_sess_send(sessp, pdu)) {
|
|
Packit |
fcad23 |
snmp_sess_error(sessp, &liberr, &syserr, &errstr);
|
|
Packit |
fcad23 |
printf("SNMP write error %s.\n", errstr);
|
|
Packit |
fcad23 |
free(errstr);
|
|
Packit |
fcad23 |
return 0;
|
|
Packit |
fcad23 |
}
|
|
Packit |
fcad23 |
snmp_sess_close(sessp);
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Example 3. Differences Between Traditional API and Single API Usage
|
|
Packit |
fcad23 |
5a6
|
|
Packit |
fcad23 |
> void *sessp; /* <-- an opaque pointer, not a struct pointer */
|
|
Packit |
fcad23 |
11,13c12,14
|
|
Packit |
fcad23 |
< sptr = snmp_open(&Session);
|
|
Packit |
fcad23 |
< if (sptr == NULL) {
|
|
Packit |
fcad23 |
---
|
|
Packit |
fcad23 |
> sessp = snmp_sess_open(&Session);
|
|
Packit |
fcad23 |
> if (sessp == NULL) {
|
|
Packit |
fcad23 |
19c20,22
|
|
Packit |
fcad23 |
< /* Pass sptr to snmp_error from here forward */
|
|
Packit |
fcad23 |
---
|
|
Packit |
fcad23 |
> sptr = snmp_sess_session(sessp); /* <-- get the snmp_session pointer */
|
|
Packit |
fcad23 |
>
|
|
Packit |
fcad23 |
> /* Pass sptr to snmp_sess_error from here forward */
|
|
Packit |
fcad23 |
26,27c29,30
|
|
Packit |
fcad23 |
< if (0 == snmp_send(sptr, pdu)) {
|
|
Packit |
fcad23 |
< snmp_error(sptr, &liberr, &syserr, &errstr);
|
|
Packit |
fcad23 |
---
|
|
Packit |
fcad23 |
> if (0 == snmp_sess_send(sessp, pdu)) {
|
|
Packit |
fcad23 |
> snmp_sess_error(sessp, &liberr, &syserr, &errstr);
|
|
Packit |
fcad23 |
33c36
|
|
Packit |
fcad23 |
< snmp_close(sptr);
|
|
Packit |
fcad23 |
---
|
|
Packit |
fcad23 |
> snmp_sess_close(sessp);
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
Restrictions on Multi-threaded Use of the SNMP Library
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
1. Invoke SOCK_STARTUP or SOCK_CLEANUP from the main thread only.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
2. The MIB parsing functions use global shared data and are not
|
|
Packit |
fcad23 |
multi-thread safe when the MIB tree is under construction.
|
|
Packit |
fcad23 |
Once the tree is built, the data can be safely referenced from
|
|
Packit |
fcad23 |
any thread. There is no provision for freeing the MIB tree.
|
|
Packit |
fcad23 |
Suggestion: Read the MIB files before an SNMP session is created.
|
|
Packit |
fcad23 |
This can be accomplished by invoking snmp_sess_init from the main
|
|
Packit |
fcad23 |
thread and discarding the buffer which is initialised.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
3. Invoke the SNMPv2p initialisation before an SNMP session is created,
|
|
Packit |
fcad23 |
for reasons similar to reading the MIB file.
|
|
Packit |
fcad23 |
The SNMPv2p structures should be available to all SNMP sessions.
|
|
Packit |
fcad23 |
CAUTION: These structures have not been tested in a multi-threaded
|
|
Packit |
fcad23 |
application.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
4. Sessions created using the Single API do not interact with other
|
|
Packit |
fcad23 |
SNMP sessions. If you choose to use Traditional API calls, call
|
|
Packit |
fcad23 |
them from a single thread. The Library cannot reference an SNMP
|
|
Packit |
fcad23 |
session using both Traditional and Single API calls.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
5. Using the callback mechanism for asynchronous response PDUs
|
|
Packit |
fcad23 |
requires additional caution in a multi-threaded application.
|
|
Packit |
fcad23 |
This means a callback function probably should probably not use
|
|
Packit |
fcad23 |
Single API calls to further process the session.
|
|
Packit |
fcad23 |
|
|
Packit |
fcad23 |
6. Each call to snmp_sess_open() creates an IDS. Only a call to
|
|
Packit |
fcad23 |
snmp_sess_close() releases the resources used by the IDS.
|
|
Packit |
fcad23 |
|