Blame README.thread

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