1
Improved Error Reporting and Thread-Safe Use of the SNMP Library
3
There is a need in some environments to support multiple threads
4
in a single application. The SNMP Library provides the Single Session
5
functions which support thread-safe operation when certain precautions
6
are taken. This document describes the operation of the SNMP Library
7
with a focus on its session management functions. The Traditional API
8
and the Single API functions are compared and contrasted.
9
A working understanding of the CMU or UCD SNMP Library
10
API is recommended to fully appreciate the concepts discussed.
11
The document ends with a list of restrictions for using the Single API
12
in a multi-threaded application.
14
***** IMPORTANT ANNOUNCEMENT *****
15
To the point, no resource locks are applied within the SNMP Library.
16
The APDU encoding and some session management functions can be used
17
in thread-safe manners. The MIB file parsing is not thread-safe.
18
The Single Session API was made available in November 1998.
19
Existing applications use the Traditional API, which is not thread-safe.
20
The thread-safe considerations are discussed throughout this document.
22
The research and development of the Single Session API that I've completed
23
was wholly funded by my employer, Internet Security Systems, Inc.
24
and is distributed freely to the Internet community.
26
-Mike Slifcak, 23 April 1999
28
09 July 1999 Removed references to snmp_synch_setup and snmp_synch_reset
33
The Single Session API is integrated into the currently available
34
versions of the CMU SNMP library and the UC-Davis SNMP package.
36
ftp://ftp.net.cmu.edu/pub/snmp/cmu-snmp-V1.13.tar.gz and later
37
Read : snmp_sess_api.3, Changes.SingleSession
39
ftp://ucd-snmp.ucdavis.edu/ucd-snmp-3.6.tar.gz and later
40
Read : snmp_sess_api.3, README.thread (after version 3.6.1)
42
Both libraries work equally well in Windows NT and various
43
UNIX platforms. Please read this document and refer to
44
the snmp_sess_api section 3 manual page.
48
APDU Application Protocol Data Unit
49
API Application Programming Interface
50
CMU Carnegie-Mellon University, Pittsburgh, PA.
51
Library The SNMP library; Both CMU and UCD versions are applicable.
52
Session Concept embodying the management of transacting SNMP APDUS.
53
SNMP Simple Network Management Protocol
54
UCD University of California at Davis, CA.
58
The Library extends the UNIX file concept (open, close, read, write) to a Session.
59
Opening a Session binds a local socket to a well-known port and creates internal
60
structures to help with controlling the transaction of SNMP APDUs. Closing a
61
Session releases the memory and system resources used for these purposes.
63
Since the mid-1980s, many SNMP applications have used the Traditional Session
64
API to transact SNMP APDUs between the local host and SNMP-enabled devices.
66
The Traditional Session API does not support multi-threaded applications:
68
1) There are no resource locks to prevent exposing the Library's
69
global data resources to corruption in a multi-threaded application;
71
2) The Traditional API functions that receive SNMP APDUs
72
do not provide an interface for one of many sessions;
74
3) Errors discovered by the Library are communicated through global
75
data structures and are not associated with the session
76
in which the error occurred.
78
The Single Session API provides these capabilities:
80
1) Manage a single SNMP session safely, in multi-threaded or
81
non-threaded applications, by avoiding access to data structures
82
that the Traditional Session API may share between Sessions;
84
2) Associate errors with the session context for threaded
85
and non-threaded applications.
88
Contrasting and Comparing Traditional API and Single API
90
The Traditional API uses the struct snmp_session pointer returned
91
from snmp_open() to identify one SNMP session. The Single API uses
92
the opaque pointer returned from snmp_sess_open() to identify one
95
Helpful Hint : The Library copies the contents of the
96
structure which is input to snmp_open() and snmp_sess_open().
97
Once copied, changing that input structure's data
98
has no effect on the opened SNMP Session.
100
The Traditional API uses the snmp_error() function to identify any
101
library and system errors that occurred during the processing for
102
one SNMP session. The Single API uses snmp_sess_error() for the
105
The Traditional API manages the private Sessions list structure;
106
adding to the list during snmp_open(), removing during snmp_close.
108
With few exceptions, the Traditional API calls the Single API
109
for each session that appears on the Sessions list.
111
The Traditional API reads from all Sessions on the Sessions list;
112
The Single API does not use the Sessions list.
113
The Single API can read from only one Session.
116
This is the basis for thread-safe-ness of the Library.
117
There are no resource locks applied.
122
A multi-threaded application that deploys the SNMP Library should
123
should complete all MIB file parsing before additional threads
124
are activated. Drawing from the parsed contents of the MIB does
125
not incur any data corruption exposure once the internal MIB structures
128
The application may create threads such that a single thread may manage
129
a single SNMP session. The thread should call snmp_sess_init()
130
to prepare a struct snmp_session structure. The thread can adjust
131
session parameters such as the remote UDP port or the local UDP port,
132
which must be set prior to invoking snmp_sess_open().
134
The first call to snmp_sess_init() initialises the SNMP Library,
135
including the MIB parse trees, before any SNMP sessions are created.
136
Applications that call snmp_sess_init() do not need to read MIBs
137
nor setup environment variables to utilize the Library.
139
After the struct snmp_session is setup, the thread must call
140
snmp_sess_open() to create an SNMP session. If at any time
141
the thread must change the Session configuration,
142
snmp_sess_session() returns the pointer to the internal configuration
143
structure (a struct snmp_session, copied from snmp_sess_open).
144
The thread can adjust parameters such as the session timeout
145
or the community string with this returned struct snmp_session pointer.
146
Changes to the remote or local port values have no effect on an opened Session.
148
The thread can build PDUs and bind variables to PDUs, as it performs its duties.
149
The thread then calls snmp_sess_send() or snmp_sess_async_send() to build and send
150
an SNMP APDU to the remote device. If a Get-Response-PDU is expected, the thread
151
should call snmp_sess_synch_response() instead.
153
When the thread is finished using the session, it must free the resources
154
that the Library used to manage the session.
155
Finally, the thread must call snmp_sess_close() to end the Session.
157
Snmp_sess_init(), snmp_open(), and snmp_sess_open()
158
must use the same calling parameter for a given Session.
159
Other methods should use only the returned parameter from
160
snmp_open() and snmp_sess_open() to access the opened SNMP Session.
165
Two calls were added : snmp_error() and snmp_sess_error() return the
166
"errno" and "snmp_errno" values from the per session data, and a string
167
that describes the errors that they represent. The string must be freed
170
Use snmp_error() to process failures after Traditional API calls,
171
or snmp_sess_error() to process failure after Single API calls.
172
In the case where an SNMP session could not be opened,
173
call snmp_error() using the struct snmp_session supplied to either snmp_open()
177
The following variables and functions are obsolete and may create problems
178
in a multi-threaded application :
188
The functions in the following table are functionally equivalent,
189
with the exception of these behaviors:
190
- The Traditional API manages many sessions
191
- The Traditional API passes a struct snmp_session pointer,
192
and touches the Sessions list
193
- The Single API manages only one session
194
- The Single API passes an opaque pointer, and does not use Sessions list
196
Traditional Single Comment
197
=========== ============== =======
198
snmp_sess_init snmp_sess_init Call before either open
199
snmp_open snmp_sess_open Single not on Sessions list
200
snmp_sess_session Exposes snmp_session pointer
201
snmp_send snmp_sess_send Send one APDU
202
snmp_async_send snmp_sess_async_send Send one APDU with callback
203
snmp_select_info snmp_sess_select_info Which session(s) have input
204
snmp_read snmp_sess_read Read APDUs
205
snmp_timeout snmp_sess_timeout Check for timeout
206
snmp_close snmp_sess_close Single not on Sessions list
207
snmp_synch_response snmp_sess_synch_response Send/receive one APDU
208
snmp_error snmp_sess_error Get library,system errno
211
Example 1 : Traditional API use.
213
#include "snmp_api.h"
217
struct snmp_session Session, *sptr;
219
snmp_sess_init(&Session);
220
Session.peername = "foo.bar.net";
221
sptr = snmp_open(&Session);
223
/* Error codes found in open calling argument */
224
snmp_error(&Session, &liberr, &syserr, &errstr);
225
printf("SNMP create error %s.\n", errstr);
229
/* Pass sptr to snmp_error from here forward */
231
/* Change the community name */
232
free(sptr->community);
233
sptr->community = strdup("public");
234
sptr->community_len = strlen("public");
236
if (0 == snmp_send(sptr, pdu)) {
237
snmp_error(sptr, &liberr, &syserr, &errstr);
238
printf("SNMP write error %s.\n", errstr);
245
Example 2 : Single API use.
247
#include "snmp_api.h"
251
void *sessp; /* <-- an opaque pointer, not a struct pointer */
252
struct snmp_session Session, *sptr;
254
snmp_sess_init(&Session);
255
Session.peername = "foo.bar.net";
256
sessp = snmp_sess_open(&Session);
258
/* Error codes found in open calling argument */
259
snmp_error(&Session, &liberr, &syserr, &errstr);
260
printf("SNMP create error %s.\n", errstr);
264
sptr = snmp_sess_session(sessp); /* <-- get the snmp_session pointer */
266
/* Pass sptr to snmp_sess_error from here forward */
268
/* Change the community name */
269
free(sptr->community);
270
sptr->community = strdup("public");
271
sptr->community_len = strlen("public");
273
if (0 == snmp_sess_send(sessp, pdu)) {
274
snmp_sess_error(sessp, &liberr, &syserr, &errstr);
275
printf("SNMP write error %s.\n", errstr);
279
snmp_sess_close(sessp);
281
Example 3. Differences Between Traditional API and Single API Usage
283
> void *sessp; /* <-- an opaque pointer, not a struct pointer */
285
< sptr = snmp_open(&Session);
286
< if (sptr == NULL) {
288
> sessp = snmp_sess_open(&Session);
289
> if (sessp == NULL) {
291
< /* Pass sptr to snmp_error from here forward */
293
> sptr = snmp_sess_session(sessp); /* <-- get the snmp_session pointer */
295
> /* Pass sptr to snmp_sess_error from here forward */
297
< if (0 == snmp_send(sptr, pdu)) {
298
< snmp_error(sptr, &liberr, &syserr, &errstr);
300
> if (0 == snmp_sess_send(sessp, pdu)) {
301
> snmp_sess_error(sessp, &liberr, &syserr, &errstr);
305
> snmp_sess_close(sessp);
308
Restrictions on Multi-threaded Use of the SNMP Library
310
1. Invoke SOCK_STARTUP or SOCK_CLEANUP from the main thread only.
312
2. The MIB parsing functions use global shared data and are not
313
multi-thread safe when the MIB tree is under construction.
314
Once the tree is built, the data can be safely referenced from
315
any thread. There is no provision for freeing the MIB tree.
316
Suggestion: Read the MIB files before an SNMP session is created.
317
This can be accomplished by invoking snmp_sess_init from the main
318
thread and discarding the buffer which is initialised.
320
3. Invoke the SNMPv2p initialisation before an SNMP session is created,
321
for reasons similar to reading the MIB file.
322
The SNMPv2p structures should be available to all SNMP sessions.
323
CAUTION: These structures have not been tested in a multi-threaded
326
4. Sessions created using the Single API do not interact with other
327
SNMP sessions. If you choose to use Traditional API calls, call
328
them from a single thread. The Library cannot reference an SNMP
329
session using both Traditional and Single API calls.
331
5. Using the callback mechanism for asynchronous response PDUs
332
requires additional caution in a multi-threaded application.
333
This means a callback function probably should probably not use
334
Single API calls to further process the session.
336
6. Each call to snmp_sess_open() creates an IDS. Only a call to
337
snmp_sess_close() releases the resources used by the IDS.