blob: ad4b1c92b7a5907a4fb12711a32621d8b9095328 [file] [log] [blame]
Dave Shield880692e2000-01-31 11:41:08 +00001Note, this is based on the text from a web page, which can be found in
Wes Hardaker6241ebf2001-04-06 19:12:28 +00002the documentation section of the http://www.net-snmp.org web page.
Wes Hardakerf4029451999-03-10 23:07:05 +00003
4Extending the UCD-SNMP agent
Dave Shield880692e2000-01-31 11:41:08 +00005============================
Wes Hardakerf4029451999-03-10 23:07:05 +00006
Dave Shield880692e2000-01-31 11:41:08 +00007This document describes the procedure for writing code to extend
Dave Shield1d652852003-05-27 15:56:46 +00008the functionality of the v4 UCD-SNMP network management agent.
Dave Shield658d54c2004-11-17 14:49:38 +00009Modules written using this procedure should also work with the v5
10Net-SNMP agent, though such modules would not take advantage of the
11new handler-based helper mechanism. See the on-line documentation
12for more information and examples of the newer approach.
Dave Shield880692e2000-01-31 11:41:08 +000013We would be very interested in comment and feedback about how useful
Dave Shield658d54c2004-11-17 14:49:38 +000014(or otherwise) you find this description, and ways in which it could
15be improved.
Wes Hardakerf4029451999-03-10 23:07:05 +000016
17The information is designed to be read in order - the structure being:
18
19 1. Overview & Introduction
20 2. MIB files, and how they relate to the agent implementation
21 3. Header files
22 4. The basic structure of module implementation code
23 5. The details of non-table based implementations
24 6. The details of simple table based implementations
25 7. The details of more general table based implementations
26 8. How to implement SET-able variables
27
Dave Shield880692e2000-01-31 11:41:08 +000028While the document is intended to be generally self-contained,
29it does occasionally refer to code files shipped with the main UCD
30distribution (in particular the example module), and it may prove
31useful to have these files available for reference.
32
331. How to write a Mib module
34============================
Wes Hardakerf4029451999-03-10 23:07:05 +000035
36Introduction
Dave Shield880692e2000-01-31 11:41:08 +000037------------
Wes Hardakerf4029451999-03-10 23:07:05 +000038
39The design of the UCD SNMP agent has always been shaped by the desire to be
40able to extend its functionality by adding new modules. One of the earliest
41developments from the underlying CMU code base was the ability to call
42external scripts, and this is probably the simplest method of extending the
43agent.
44However, there are circumstances where such an approach is felt to be
45inappropriate - perhaps from considerations of speed, access to the
46necessary data, reliability or elegance. In such cases, the obvious solution
47is to provide C code that can be compiled into the agent itself to implement
48the desired module. Many of the more recent developments in the code
49structure have been intended to ease this process. In particular, one of the
50more recent additions to the suite is the tool mib2c. This is designed to
51take a portion of the MIB tree (as defined by a MIB file) and generate the
52code skeleton necessary to implement this. This document will cover the use
53mib2c, as well as describing the requirements and functionality of the code
54in more detail.
55
56In order to implement a new MIB module, three files are necessary, and these
57will be considered in turn. Note that, by the very nature of the task, this
58document cannot cover the details of precisely how to obtain the necessary
59information from the operating system or application. Instead, it describes
60the code framework that is needed, freeing the implementer from needing to
61understand the detailed internals of the agent, and allowing them to
62concentrate on the particular problem in hand.
63
64It may prove useful to examine some of the existing module implementations
65and examples in the light of this description, and suitable examples will be
66referred to at the appropriate points. However, it should be remembered that
67the UCD agent seeks to support a wide variety of systems, often with
68dramatically differing implementations and interfaces, and this is reflected
69in the complexity of the code. Also, the agent has developed gradually over
70the years, and there is often some measure of duplication or redundancy as a
71result.
72As the FAQ states, the official slogan of the UCD-SNMP developers is
73
74 The current implementation is non-obvious and may need to be
75 improved.
76
77This document describes the ideal, straightforward cases - real life is
78rarely so simple, and the example modules may prove easier to follow at a
79first reading.
80It is also advisable to have a compiled and installed implementation
81available before starting to extend the agent. This will make debugging and
82testing the agent much easier.
83
84A note regarding terminology - the word "module" is widely used throughout
85this document, with a number of different meanings.
86
87 * support for a new MIB,
88 i.e. the whole of the functionality that is required. This is usually
89 termed a MIB module;
90 * a self-contained subset of this, implemented as a single unit.
91 This is usually termed an implementation module (or simply "a module");
Dave Shield880692e2000-01-31 11:41:08 +000092 * the combination of such subsets, usually termed a module group.
Wes Hardakerf4029451999-03-10 23:07:05 +000093
Dave Shield880692e2000-01-31 11:41:08 +000094Note that the first and third of these are often synonymous - the
Wes Hardakerf4029451999-03-10 23:07:05 +000095difference being that a MIB module refers to the view from outside the
96agent, regarding this as a seamless whole and hiding the internal
97implementation. A "module group" is used where the internal structure is of
98more relevance, and recognises the fact that the functionality may be
Dave Shield880692e2000-01-31 11:41:08 +000099provided by a number of co-operating implementation modules.
Wes Hardakerf4029451999-03-10 23:07:05 +0000100
101Anyway, enough waffle - on with the details: The three files needed are
102
103 * a MIB definition file;
104 * a C header file;
105 * a C implementation file.
106
107The next part looks at the MIB definition file, and how this impacts on the
108agent implementation.
109
Dave Shield880692e2000-01-31 11:41:08 +00001102. The MIB File
111===============
Wes Hardakerf4029451999-03-10 23:07:05 +0000112
113The first file needed is the MIB file that defines the MIB module to be
114implemented.
115Strictly speaking, this is not absolutely necessary, as the agent itself
116does not make any direct use of the MIB definitions. However, it is
117advisable to start with this for three reasons:
118
119 * It provides an initial specification for what is to be implemented.
120 Code development is always easier if you know what you are meant to be
121 writing!
122 * If the new MIB file is read in with the other MIB files,
123 this lets the applications provided with the suite be used to test the
124 new agent, and report (hopefully meaningful) symbolic OIDs and values,
125 rather than the bare numeric forms.
126 (N.B: Remember to tell the application to load the new MIB. See the
127 relevant question in the FAQ)
128 * The tool mib2c uses this description to produce the two code files.
129 This is by far the easiest way to develop a new module.
Dave Shield658d54c2004-11-17 14:49:38 +0000130 (Note that the v5 version of mib2c is generally similar, but does
131 not correspond exactly to the v4 version described here)
Wes Hardakerf4029451999-03-10 23:07:05 +0000132
133If the intention is to implement a 'standard' MIB module, or a
134vendor-specific one, then the construction of this file will have already
135been done for you. If the intention is to provide a totally new, private
136module, then you will need to write this yourself, in addition to the agent
137code files.
138A description of MIB file format and syntax is beyond the scope of this
139document, and most books on SNMP management should provide some information
140on this subject. One book which concentrates on this is
141
142 Understanding SNMP MIBS
143 (Perkins & McGinnis, Prentice Hall, ISBN 0-13-437708-7).
144
145This blatant plug is wholly unrelated to the fact that David Perkins is an
146active member of the development group, and is regarded as our resident
Dave Shield880692e2000-01-31 11:41:08 +0000147"protocol guru and policeman". (In fact, this book concentrates on MIB
148files in rather more detail than is appropriate in more general SNMP works).
149Information on other books covering SNMP and Network Management more generally
150is available on the SimpleWeb site (among other places).
151See the FAQ for more details.
Wes Hardakerf4029451999-03-10 23:07:05 +0000152
153Assigned OID numbers
Dave Shield880692e2000-01-31 11:41:08 +0000154--------------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000155
156One word of advice - even if you are developing a totally private MIB
157module, you will still need to position this somewhere within the overall
158MIB tree. Please do NOT simply choose a location "at random". Any such is
159likely to have either been assigned to some other organisation, or may be so
160assigned some time in the future. However much you may regard your project
161as a totally internal affair, such projects have a tendency to exceed their
162expected scope, both in terms of lifetime and distribution (not to mention
163the potential OID clash if you subsequently need to use elements from the
164legitimate owner's tree).
165It is simple and cheap (i.e. free!) to obtain your own official segment of
166the MIB tree (see http://www.iana.org for an application form), and having
167done so, you then have complete global authority over it. If you have
168problems with this, it's worth contacting the development team (email:
Wes Hardaker36091e52000-12-02 00:35:27 +0000169net-snmp-coders@lists.sourceforge.net) for advice. Please do think to the
Wes Hardakerf4029451999-03-10 23:07:05 +0000170future, and be a good Net citizen by using a legitimately assigned OID as
171the root of your new MIB.
172
173MIB division
Dave Shield880692e2000-01-31 11:41:08 +0000174------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000175
176The next point to consider, whether writing by hand or using mib2c,
177implementing an existing MIB, or writing a new one, is whether and how to
178divide up the MIB tree. This is a purely internal implementation decision,
179and will not be visible to management applications querying the agent. A
180sensible choice of partitioning will result in a simpler, clearer
181implementation, which should ease both the initial development and
182subsequent maintenance of the module.
183Unfortunately, this choice is one of the module-specific decisions, so must
184be made on a case-by-case basis. For a simple, self-contained module, it may
185well be reasonable to implement the module as a single block (examples
186include the SNMP statistics subtree RFC 1907 or the TCP subtree RFC 2011).
187More complex and diverse modules (such as the Host Resources MIB - RFC 1514)
188are more naturally considered as a number of individual sub-modules.
189Some guidelines to bear in mind when deciding on this division:
190
Dave Shield880692e2000-01-31 11:41:08 +0000191 * A MIB sub-tree consisting purely of scalar objects with a common
192 OID prefix would normally be handled in a single implementation module;
193 * Separate scalar subtrees would normally be in different implementation
Wes Hardakerf4029451999-03-10 23:07:05 +0000194 modules;
Dave Shield880692e2000-01-31 11:41:08 +0000195 * A table can either be handled within the same implementation module
196 as related scalar objects in the same subtree, or in a separate
197 implementation module;
Wes Hardakerf4029451999-03-10 23:07:05 +0000198 * Variables that rely on the same underlying data structure to retrieve
199 their values, should probably be in the same implementation module (and
Dave Shield880692e2000-01-31 11:41:08 +0000200 conversely, (though less so) those that don't, shouldn't).
Wes Hardakerf4029451999-03-10 23:07:05 +0000201
202As an initial rule of thumb, a good initial division is likely to be
203obtained by treating each table and each scalar sub-tree separately. This
204can be seen in the current agent, where most of the MIB-II modules (RFC
Dave Shield880692e2000-01-31 11:41:08 +00002051213) are implemented in separate files (see the files under mibgroup/mibII).
206Note that many of these combine scalar and table handling in the same file,
207though they are implemented using separate routines.
208 This is also the approach used by mib2c, which constructs a single pair of
209code files, but uses a separate routine for each table (and another for all
210the scalar variables).
211 Ultimately, the final consideration (concerning the underlying data) is
212the most important, and should guide the basic division. For example, the
213Host Resources Running Software and Running Software Performance modules,
214while separate in the MIB tree, use the same underlying kernel data and so
215are implemented together.
Wes Hardakerf4029451999-03-10 23:07:05 +0000216
217MIB name
Dave Shield880692e2000-01-31 11:41:08 +0000218--------
Wes Hardakerf4029451999-03-10 23:07:05 +0000219
220The final requirement at this stage is to choose a name for each
221implementation module. This should be reasonably short, meaningful, unique
222and unlikely to clash with other (existing or future) modules. Mib2c uses
223the label of the root node of the MIB sub-tree as this name, and this is a
224reasonable choice in most cases.
225Recent changes to the agent code organisation have introduced the idea of
226module groups of related implementation modules. This is used, for example,
227to identify the constituent modules of a 'split' MIB (such as the Host
228Resources MIB), or those relating to a particular organisation (such as
229UCD).
230As with the division, this naming and grouping is a purely internal matter,
231and is really only visible when configuring and compiling the agent.
232
233So much for the MIB file. The next part considers the C header file.
234
Dave Shield880692e2000-01-31 11:41:08 +00002353. The C code header file
236=========================
Wes Hardakerf4029451999-03-10 23:07:05 +0000237
238If the MIB file is the definition of the module for external network
239management applications (where applications includes network management
240personnel!), then the header file has traditionally served effectively the
241same purpose for the agent itself.
242Recent changes to the recommended code structure has resulted in the header
Dave Shield880692e2000-01-31 11:41:08 +0000243file becoming increasingly simpler. It now simply contains definitions of the
Wes Hardakerf4029451999-03-10 23:07:05 +0000244publically visible routines, and can be generated completely by mib2c.
245
246Function prototypes
Dave Shield880692e2000-01-31 11:41:08 +0000247-------------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000248
249For those interested in the details of this file (for example, if coding a
250module by hand), then the details of these definitions are as follows. Every
251header file will have the following two function prototype definitions
252
Dave Shield880692e2000-01-31 11:41:08 +0000253 extern void init_example (void);
254 extern FindVarMethod var_example;
Wes Hardakerf4029451999-03-10 23:07:05 +0000255
256If the module includes any tables, or other collections of variables that
257are implemented in separate routines, then this second definition will be
258repeated for each of these.
259In addition, if any of the variables can be SET (and it is intended to
260implement them as such), there will be a function prototype definitions for
261each of these, of the form:
262
Dave Shield880692e2000-01-31 11:41:08 +0000263 extern WriteMethod write_varName;
Wes Hardakerf4029451999-03-10 23:07:05 +0000264
Dave Shield880692e2000-01-31 11:41:08 +0000265These prototypes are in fact typedef'ed in <agent/snmp_vars.h>.
Wes Hardakerf4029451999-03-10 23:07:05 +0000266
Dave Shield658d54c2004-11-17 14:49:38 +0000267Module dependencies
Dave Shield880692e2000-01-31 11:41:08 +0000268-------------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000269
270This header file is also used to inform the compilation system of any
271dependancies between this module and any others. There is one utility module
272which is required by almost every module, and this is included using the
273directive
274
Dave Shield658d54c2004-11-17 14:49:38 +0000275 config_require( util_funcs )
Wes Hardakerf4029451999-03-10 23:07:05 +0000276
277(which is produced automatically by mib2c). This same syntax can be used to
278trigger the inclusion of other related modules. An example of this can be
Dave Shield880692e2000-01-31 11:41:08 +0000279seen in mibII/route_write.h which relies on the mibII/ip module, thus:
Wes Hardakerf4029451999-03-10 23:07:05 +0000280
Dave Shield880692e2000-01-31 11:41:08 +0000281 config_require( mibII/ip )
282
283One use of this directive is to define a module group, by supplying a header
284file consisting exclusively of such config_require directives. It can then
285be included or excluded from the agent very simply. Examples of this can be
Wes Hardakerf4029451999-03-10 23:07:05 +0000286seen in mibgroup/mibII.h or mibgroup/host.h, which list the consituent
Dave Shield880692e2000-01-31 11:41:08 +0000287sub-modules of the MIB-II and Host Resources MIBs respectively.
288
289MIB file information
290--------------------
291
292Most of the information in this file is (understandably) aimed at the network
293management agent itself. However, there is one common header file directive
294that is actually intended to affect the utility commands that are included
295within the full distribution:
296
297 config_add_mib( HOST-RESOURCES-MIB )
298
299 This is used to add the MIB file being implemented to the default list of
300MIBs loaded by such commands. This means that querying the agent will return
301informative names and values, rather than the raw numeric forms that SNMP
302actually works with. Of course, it is always possible for the utilities
303to specify that this MIB should be loaded anyway. But specifying this file
304within the module header file is a useful hint that a particular MIB should
305be loaded, without needing to ask for it explicitly.
306 Note that this will only affect the binaries compiled as part of the same
307configuration run. It will have no effect on pre-installed binaries, or
308those compiled following a different configuration specification.
309
310Magic Numbers
311-------------
312
313The other common element within the header file defines a set of "magic
314numbers" - one for each object within the implementation module. In fact,
315this can equally well appear within the main code file, as part of the
316variable structure (which will be described in the next part).
317 This is the technique used by mib2c, but most handcrafted modules have
318tended to define these as part of the header file, probably for clarity.
319
320 The only necessity is that the names and values are distinct (or more
321precisely, the values are distinct within a single variable handling routine).
322In practise, they tend to be defined using integers incrementing from 1,
323or as the same as the final sub-identifier of the corresponding MIB object
324(or indeed both, as these are frequently themselves successive integers).
325 This is not mandatory, and a counter-example can be seen in the
326example module, where two of the object form a sub-tree, and the corresponding
327magic numbers are based on the final *two* sub-identifiers (to ensure that
328the values are unique). But this construction is definitely unusual, and
329the majority of modules simply use successive integers.
Wes Hardakerf4029451999-03-10 23:07:05 +0000330
331Header file protection
Dave Shield880692e2000-01-31 11:41:08 +0000332----------------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000333
334Normally, the only other contents of the header file will be the
Dave Shield880692e2000-01-31 11:41:08 +0000335#ifndef/#define/#endif statements surrounding the whole file. This is used
336to ensure that the header file is only included once by any source code file
337(or more accurately, that there is no effect if it is inadvertantly included
338a second time).
Wes Hardakerf4029451999-03-10 23:07:05 +0000339Again, as with the rest of the header file, this is generated automatically
340by mib2c.
341
342Having finished all the preparatory work (or let mib2c deal with it), the
343next part starts to look at the code file that actually implements the
344module.
345
Dave Shield880692e2000-01-31 11:41:08 +00003464. Core structure of the implementation code
347============================================
Wes Hardakerf4029451999-03-10 23:07:05 +0000348
349The core work of implementing the module is done in the C code file. As
350indicated earlier, much of the detail of this will be dependent on the
351particular module being implemented, and this can only be described by the
352individual programmer concerned.
353However, there is a fairly clearly defined framework that the implementation
354will need to follow, though this varies slightly depending on the style of
355the module being implemented (in particular whether it forms a table or a
356series of individual values). The differences will be covered in the
357following pages, but we first need to consider the overall shape of the
358framework, and the elements that are common to all styles. These are
359essentially the compulsory routines, the common header definitions, and
360assorted initialisation code.
361As with the header file, most of this will be generated automatically by
362mib2c.
363
364Standard includes
Dave Shield880692e2000-01-31 11:41:08 +0000365-----------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000366
367Certain header files are either compulsory, or required so frequently that
368they should be included as a matter of course. These are as follows:
369
370 #include <config.h> // local SNMP configuration details
371 #include "mib_module_config.h" // list of which modules are supported
Wes Hardakerf4029451999-03-10 23:07:05 +0000372 #if HAVE_STDLIB_H
373 #include <stdlib.h>
374 #endif
Michael Slifcak41b8c8f1999-11-04 12:33:32 +0000375 #if HAVE_STRING_H
376 #include <string.h>
Dave Shield880692e2000-01-31 11:41:08 +0000377 #else
378 #include <strings.h>
Wes Hardakerf4029451999-03-10 23:07:05 +0000379 #endif
380
381 #include <sys/types.h>
382
383All of these will usually be the first files to be included.
384
385 #include "mibincl.h" // Standard set of SNMP includes
386 #include "util_funcs.h" // utility function declarations
387 #include "read_config.h" // if the module uses run-time
388 // configuration controls
389 #include "auto_nlist.h" // structures for a BSD-based
390 // kernel using nlist
Dave Shield880692e2000-01-31 11:41:08 +0000391 #include "system.h"
Wes Hardakerf4029451999-03-10 23:07:05 +0000392
393 #include "name.h" // the module-specific header
394
395These conventionally come at the end of the list of includes. In between
396will come all the standard system-provided header files required for the
Dave Shield880692e2000-01-31 11:41:08 +0000397library functions used in the file.
Wes Hardakerf4029451999-03-10 23:07:05 +0000398
399Module definition
Dave Shield880692e2000-01-31 11:41:08 +0000400-----------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000401
402Much of the code defining the contents of the MIB has traditionally been
403held in the header file. However, much of this has slowly migrated to the
404code file, and this is now the recommended location for it (as typified by
Dave Shield880692e2000-01-31 11:41:08 +0000405the output of mib2c).
406 The main element of this is a variable structure specifying the details of
407the objects implemented. This takes the form of an unconstrained array of
408type struct variableN (where N is the length of the longest suffix in the
409table). Thus
Wes Hardakerf4029451999-03-10 23:07:05 +0000410
Dave Shield880692e2000-01-31 11:41:08 +0000411 struct variable2 example_variables[] = {
412 <individual entries go here>
Wes Hardakerf4029451999-03-10 23:07:05 +0000413 };
414
Dave Shield880692e2000-01-31 11:41:08 +0000415Each entry corresponds to one object in the MIB tree (or one column in the
Wes Hardakerf4029451999-03-10 23:07:05 +0000416case of table entries), and these should be listed in increasing OID order.
417A single entry consists of six fields:
418
Dave Shield880692e2000-01-31 11:41:08 +0000419 * a magic number (the #defined integer constant described above)
420 * a type indicator (from the values listed in <snmplib/snmp_impl.h>)
Wes Hardakerf4029451999-03-10 23:07:05 +0000421 * an access indicator (essentially RWRITE or RONLY)
422 * the name of the routine used to handle this entry
423 * the length of the OID suffix used, and
424 * an array of integers specifying this suffix (more on this in a moment)
425
Dave Shield880692e2000-01-31 11:41:08 +0000426Thus a typical variable entry would look like:
Wes Hardakerf4029451999-03-10 23:07:05 +0000427
Dave Shield880692e2000-01-31 11:41:08 +0000428 { EXAMPLESTRING, ASN_OCTET_STR, RONLY, var_example, 1, {1}}
Wes Hardakerf4029451999-03-10 23:07:05 +0000429
Dave Shield880692e2000-01-31 11:41:08 +0000430If the magic numbers have not been defined in the header file, then they
431should be defined here, usually comming immediately before the corresponding
432variable entry. This is the technique used by mib2c.
433
434Note that in practise, only certain sizes of the structure variableN
Wes Hardakerf4029451999-03-10 23:07:05 +0000435are defined (listed in <agent/var_struct.h>), being sufficient to meet the
436common requirements. If your particular module needs a non-supported value,
437the easiest thing is simply to use the next largest value that is supported.
438
Dave Shield880692e2000-01-31 11:41:08 +0000439The module also needs to declare the location within the MIB tree where
440it should be registered. This is done using a declaration of the form
Wes Hardakerf4029451999-03-10 23:07:05 +0000441
Dave Shield880692e2000-01-31 11:41:08 +0000442 oid example_variables_oid[] = { 1,3,6,1,4,1,2021,254 }
Wes Hardakerf4029451999-03-10 23:07:05 +0000443
Dave Shield880692e2000-01-31 11:41:08 +0000444where the contents of the array give the object identifier of the root of
Wes Hardakerf4029451999-03-10 23:07:05 +0000445the module.
446
447Module initialisation
Dave Shield880692e2000-01-31 11:41:08 +0000448---------------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000449
450Many modules require some form of initialisation before they can start
451providing the necessary information. This is done by providing a routine
452called init_{name} (where {name} is the name of the module).
Dave Shield880692e2000-01-31 11:41:08 +0000453This routine is theoretically optional, but in practise is required to
454register this module with the main agent at the very least. This specifies
455the list of variables being implemented (from the variableN structure)
456and declare where these fit into the overall MIB tree.
Wes Hardakerf4029451999-03-10 23:07:05 +0000457
458This is done by using the REGISTER_MIB macro, as follows:
459
Dave Shield880692e2000-01-31 11:41:08 +0000460 REGISTER_MIB( "example", example_variables, variable2,
461 example_variables_oid );
Wes Hardakerf4029451999-03-10 23:07:05 +0000462
Dave Shield880692e2000-01-31 11:41:08 +0000463where "example" is used for identification purposed (and is usually the name
464being used for the module), example_variables is the structure defining the
465variables being implemented, variable2 is the type used for this structure,
466and example_variables_oid is the location of the root.
Wes Hardakerf4029451999-03-10 23:07:05 +0000467
468In fact, this macro is simply a wrapper round the routine register_mib(),
Dave Shield880692e2000-01-31 11:41:08 +0000469but the details of this can safely be ignored, unless more control over the
470registration is required.
Wes Hardakerf4029451999-03-10 23:07:05 +0000471
472One common requirement, particularly on older operating systems or for the
473more obscure areas of the system, is to be able to read data directly from
474kernel memory. The preparation for this is typically done here by one or
475more statements of the form
476
477 #ifdef {NAME}_SYMBOL
478 auto_nlist( {NAME}_SYMBOL, 0, 0);
479 #endif
480
481where {NAME}_SYMBOL is defined as part of the system-specific configuration,
482to be the name of the appropriate kernel variable or data structure. (The
483two 0 values are because the kernel information is simply being primed at
484this point - this call will be reused later when the actual values are
485required). Note that this is probably the first thing described so far which
486isn't provided by mib2c!
487
Dave Shield880692e2000-01-31 11:41:08 +0000488Other possibilities for initialisation may include registering config file
489directive handlers (which are documented in the read_config(5) man page), and
490registering the MIB module (either in whole or in part) in the sysOR table.
491The first of these is covered in the example module, and the second in many
492of the other modules within the main UCD distribution.
Wes Hardakerf4029451999-03-10 23:07:05 +0000493
494Variable handling
Dave Shield880692e2000-01-31 11:41:08 +0000495-----------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000496
497The other obligatory routine is that which actually handles a request for a
498particular variable instance. This is the routine that appeared in the
Dave Shield880692e2000-01-31 11:41:08 +0000499variableN structure, so while the name is not fixed, it should be the same
500as was used there.
Wes Hardakerf4029451999-03-10 23:07:05 +0000501This routine has six parameters, which will be described in turn.
502
503Four of these parameters are used for passing in information about the
504request, these being:
505
506 struct variable *vp;
507 // The entry in the variableN array from the
Dave Shield880692e2000-01-31 11:41:08 +0000508 // header file, for the object under consideration.
Wes Hardakerf4029451999-03-10 23:07:05 +0000509 // Note that the name field of this structure has been
510 // completed into a fully qualified OID, by prepending
511 // the prefix common to the whole array.
512 oid *name; // The OID from the request
513 int *length; // The length of this OID
514 int exact; // A flag to indicate whether this is an exact
515 // request (GET/SET) or an 'inexact' one (GETNEXT)
516
Dave Shield880692e2000-01-31 11:41:08 +0000517Four of the parameters are used to return information about the answer.
518The function also returns a pointer to the actual data for the variable
519requested (or NULL if this data is not available for any reason).
520The other result parameters are:
Wes Hardakerf4029451999-03-10 23:07:05 +0000521
522 oid *name; // The OID being returned
523 int *length; // The length of this OID
524 int *var_len; // The length of the answer being returned
525 WriteMethod **write_method;
526 // A pointer to the SET function for this variable
527
528Note that two of the parameters (name and length) serve a dual purpose,
529being used for both input and output.
530
531The first thing that this routine needs to do is to validate the request, to
532ensure that it does indeed lie in the range implemented by this particular
533module. This is done in slightly different ways, depending on the style of
Dave Shield880692e2000-01-31 11:41:08 +0000534the module, so this will be discussed in more detail later.
535 At the same time, it is common to retrieve some of the information needed
536for answering the query.
Wes Hardakerf4029451999-03-10 23:07:05 +0000537
Dave Shield880692e2000-01-31 11:41:08 +0000538Then the routine uses the Magic Number field from the vp parameter to determine
539which of the possible variables being implemented is being requested. This is
Wes Hardakerf4029451999-03-10 23:07:05 +0000540done using a switch statement, which should have as many cases as there are
541entries in the variableN array (or more precisely, as many as specify this
542routine as their handler), plus an additional default case to handle an
543erroneous call.
544Each branch of the switch statement needs to ensure that the return
545parameters are filled in correctly, set up a (static) return variable with
546the correct data, and then return a pointer to this value. These can be done
547separately for each branch, or once at the start, being overridden in
548particular branches if necessary.
549
550In fact, the default validation routines make the assumption that the
Dave Shield880692e2000-01-31 11:41:08 +0000551variable is both read-only, and of integer type (which includes the COUNTER
552and GAUGE types among others), and set the return paramaters write_method and
553var_len appropriately. These settings can then be corrected for those cases
554when either or both of these assumptions are wrong. Examples of this can be
555seen in the example module.
556EXAMPLEINTEGER is writeable, so this branch sets the write_method parameter,
557and EXAMPLEOBJECTID is not an integer, so this branch sets the var_len
558parameter. In the case of EXAMPLESTRING, both assumptions are wrong, so this
559branch needs to set both these parameters explicitly.
Wes Hardakerf4029451999-03-10 23:07:05 +0000560
561Note that because the routine returns a pointer to a static result, a
562suitable variable must be declared somewhere for this. Two global variables
563are provided for this purpose - long_return (for integer results) and
564return_buf (for other types). This latter is a generic array (of type
565u_char) that can contain up to 256 bytes of data. Alternatively, static
566variables can be declared, either within the code file, or local to this
567particular variable routine. This last is the approach adopted by mib2c,
568which defines four such local variables, (long_ret, string, objid and c64).
569
570Mib2c requirements
Dave Shield880692e2000-01-31 11:41:08 +0000571------------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000572
573Most of the code described here is generated by mib2c. The main exceptions
574(which therefore need to be provided by the programmer) are
575
576 * Any initialisation, other than the basic registration
577 (including kernel data initialisation, config file handling, or sysOR
578 registration).
Dave Shield880692e2000-01-31 11:41:08 +0000579 * Retrieving the necessary data, and setting the appropriate return
580 value correctly.
Wes Hardakerf4029451999-03-10 23:07:05 +0000581 * The var_len (and possibly write_method) return parameters for variable
582 types that are not recognised by mib2c
Dave Shield880692e2000-01-31 11:41:08 +0000583 * The contents of any write routines (see later).
Wes Hardakerf4029451999-03-10 23:07:05 +0000584
585Everything else should be useable as generated.
586
Wes Hardakerf4029451999-03-10 23:07:05 +0000587This concludes the preliminary walk-through of the general structure of the
588C implementation. To fill in the details, we will need to consider the
589various styles of module separately. The next part will look at scalar (i.e.
590non-table based) modules.
591
Dave Shield880692e2000-01-31 11:41:08 +00005925. Non-table-based modules
593==========================
Wes Hardakerf4029451999-03-10 23:07:05 +0000594
595Having looked at the general structure of a module implementation, it's now
596time to look at this in more detail. We'll start with the simplest style of
597module - a collection of independent variables. This could easily be
598implemented as a series of completely separate modules - the main reason for
599combining them is to avoid the proliferation of multiple versions of very
600similar code.
601
602Recall that the variable handling routine needs to cover two distinct
603purposes - validation of the request, and provision of the answer. In this
604style of module, these are handled separately. Once again, mib2c does much
605of the donkey work, generating the whole of the request validation code (so
606the description of this section can be skipped if desired), and even
607providing a skeleton for returning the data. This latter still requires some
608input from the programmer, to actually return the correct results (rather
609than dummy values).
610
611Request Validation
Dave Shield880692e2000-01-31 11:41:08 +0000612------------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000613
614This is done using a standard utility function header_generic. The
615parameters for this are exactly the same as for the main routine, and are
616simply passed through directly. It returns an integer result, as a flag to
617indicate whether the validation succeeded or not.
618If the validation fails, then the main routine should return immediately,
619leaving the parameters untouched, and indicate the failure by returning a
620NULL value. Thus the initial code fragment of a scalar-variable style
621implementation will typically look like:
622
623 u_char *
624 var_system(vp, name, length, exact, var_len, write_method)
625 {
626 if (header_generic(vp, name, length, exact, var_len, write_method)
627 == MATCH_FAILED )
628 return NULL;
629
630 [ etc, etc, etc ]
631 }
632
633Although the utility function can be used as a "black box", it's worth
Dave Shield880692e2000-01-31 11:41:08 +0000634looking more closely at exactly what it does (since the table-handling
635modules will need to do something fairly similar). It has two (or possibly
636three) separate functions:
Wes Hardakerf4029451999-03-10 23:07:05 +0000637
638 * checking that the request is valid,
639 * setting up the OID for the result,
640 * and (optionally) setting up default values for the other return
641 parameters.
642
643In order to actually validate the request, the header routine first needs to
644construct the OID under consideration, in order to compare it with that
645originally asked for. The driving code has already combined the OID prefix
646(constant throughout the module) with the entry-specific suffix, before
647calling the main variable handler. This is available via the name field of
648the parameter vp. For a scalar variable, completing the OID is therefore
649simply a matter of appending the instance identifier 0 to this. The full OID
650is built up in a local oid array newname defined for this purpose.
651This gives the following code fragment:
652
653 int
654 header_generic(vp, name, length, exact, var_len, write_method)
655 {
Wes Hardaker84f4a451999-03-10 23:14:17 +0000656 oid newname[MAX_OID_LEN];
Wes Hardakerf4029451999-03-10 23:07:05 +0000657
658 memcpy((char *)newname, (char *)vp->name,
659 (int)vp->namelen * sizeof(oid));
660 newname[ vp->namelen ] = 0;
661
662 :
663 }
664
665Having formed the OID, this can then be compared against the variable
666specified in the original request, which is available as the name parameter.
667This comparison is done using the snmp_oid_compare function, which takes the
668two OIDs (together with their respective lengths), and returns -1, 0 or 1
669depending on whether the first OID precedes, matches or follows the second.
670
671In the case of an 'exact' match (i.e. a GET/SET/etc), then the request is
Dave Shield880692e2000-01-31 11:41:08 +0000672only valid if the two OIDs are identical (snmp_oid_compare returns 0). In
673the case of a GETNEXT (or GETBULK) request, it's valid if the OID being
Wes Hardakerf4029451999-03-10 23:07:05 +0000674considered comes after that of the original request (snmp_oid_compare
Dave Shield880692e2000-01-31 11:41:08 +0000675returns -1).
Wes Hardakerf4029451999-03-10 23:07:05 +0000676
677This gives the code fragment
678
679 result = snmp_oid_compare(name, *length, newname, (int)vp->namelen + 1);
680 // +1 because of the extra instance sub-identifier
681 if ((exact && (result != 0)) // GET match fails
682 || (!exact && (result >= 0))) // GETNEXT match fails
683 return(MATCH_FAILED);
684
685Note that in this case, we're only interested in the single variable
686indicated by the vp parameter. The fact that this module may well implement
687other variables as well is ignored. The 'lexically next' requirement of the
688GETNEXT request is handled by working through the variable entries in order
689until one matches. And yes, this is not the most efficient implementation
690possible!
691Note that in releases prior to 3.6, the snmp_oid_compare function was called
692simply compare.
693
694Finally, having determined that the request is valid, this routine must
695update the name and length parameters to return the OID being processed. It
Dave Shield880692e2000-01-31 11:41:08 +0000696also sets default values for the other two return parameters.
Wes Hardakerf4029451999-03-10 23:07:05 +0000697
698 memcpy( (char *)name,(char *)newname,
699 ((int)vp->namelen + 1) * sizeof(oid));
700 *length = vp->namelen + 1;
701 *write_method = 0; // Non-writeable
Dave Shield880692e2000-01-31 11:41:08 +0000702 *var_len = sizeof(long); // default to integer results
Wes Hardakerf4029451999-03-10 23:07:05 +0000703 return(MATCH_SUCCEEDED);
704
705These three code fragments combine to form the full header_generic code
706which can be seen in the file util_funcs.c
707
708Note: This validation used to be done using a separate function for each
709module (conventionally called header_{name}), and many modules may still be
710coded in this style. The code for these are to all intents and purposes
711identical to the header_generic routine described above.
712
713Data Retrieval
Dave Shield880692e2000-01-31 11:41:08 +0000714--------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000715
Dave Shield880692e2000-01-31 11:41:08 +0000716The other main job of the request handling routine is to retrieve any
717necessary data, and return the appropriate answer to the original request.
718This must be done even if mib2c is being used to generate the framework of
719the implementation. As has been indicated earlier, the different cases are
Wes Hardakerf4029451999-03-10 23:07:05 +0000720handled using a switch statement, with the Magic Number field of the vp
721parameter being used to distinguish between them.
722The data necessary for answering the request can be retrieved for each
723variable individually in the relevant case statement (as is the case with
724the system group), or using a common block of data before processing the
725switch (as is done for the ICMP group, among others).
726
727With many of the modules implemented so far, this data is read from a kernel
728structure. This can be done using the auto_nlist routine already mentioned,
729providing a variable in which to store the results and an indication of its
730size (see the !HAVE_SYS_TCPIPSTATS_H case of the ICMP group for an example).
731Alternatively, there may be ioctl calls on suitable devices, specific system
732calls, or special files that can be read to provide the necessary
733information.
734
Dave Shield880692e2000-01-31 11:41:08 +0000735If the available data provides the requested value immediately, then the
736individual branch becomes a simple assignment to the appropriate static
737return variable - either one of the global static variables (e.g. long_return)
738or the local equivalents (such as generated by mib2c).
739Otherwise, the requested value may need to be calculated by combining two or
Wes Hardakerf4029451999-03-10 23:07:05 +0000740more items of data (e.g. IPINHDRERRORS in mibII/ip.c) or by applying a
741mapping or other calculation involving available information (e.g.
Dave Shield880692e2000-01-31 11:41:08 +0000742IPFORWARDING from the same group).
Wes Hardakerf4029451999-03-10 23:07:05 +0000743
744In each of these cases, the routine should return a pointer to the result
745value, casting this to the pseudo-generic (u_char *)
746
Wes Hardakerf4029451999-03-10 23:07:05 +0000747So much for the scalar case. The next part looks at how to handle simple
748tables.
749
Dave Shield880692e2000-01-31 11:41:08 +00007506. Simple tables
751================
Wes Hardakerf4029451999-03-10 23:07:05 +0000752
753Having considered the simplest style of module implementation, we now turn
754our attention to the next style - a simple table. The tabular nature of
755these is immediately apparent from the MIB definition file, but the
Dave Shield880692e2000-01-31 11:41:08 +0000756qualifier "simple" deserves a word of explanation.
Wes Hardakerf4029451999-03-10 23:07:05 +0000757A simple table, in this context, has four characteristics:
758
759 1. It is indexed by a single integer value;
760 2. Such indices run from 1 to a determinable maximum;
761 3. All indices within this range are valid;
762 4. The data for a particular index can be retrieved directly
763 (e.g. by indexing into an underlying data structure).
764
765If any of the conditions are not met, then the table is not a pure simple
766one, and the techniques described here are not applicable. The next section
767of this guide will cover the more general case. (In fact, it may be possible
768to use the bulk of the techniques covered here, though special handling will
769be needed to cope with the invalid assumption or assumptions). Note that
770mib2c assumes that all tables are simple.
771
772As with the scalar case, the variable routine needs to provide two basic
773functions - request validation and data retrieval.
774
775Validation
Dave Shield880692e2000-01-31 11:41:08 +0000776----------
Wes Hardakerf4029451999-03-10 23:07:05 +0000777
Dave Shield880692e2000-01-31 11:41:08 +0000778This is provided by the shared utility routine header_simple_table. As with
Wes Hardakerf4029451999-03-10 23:07:05 +0000779the scalar header routine, this takes the same parameters as the main
780variable routine, with one addition - the maximum valid index. Mib2c
781generates a dummy token for this, which must be replaced by the appropriate
782value.
783As with the header routine, it also returns an indication of whether the
784request was valid, as well as setting up the return parameters with the
Dave Shield880692e2000-01-31 11:41:08 +0000785matching OID information, and defaults for var_len and write_method.
Wes Hardakerf4029451999-03-10 23:07:05 +0000786Note that in releases prior to 3.6, this job was performed by the routine
787checkmib. However, the return values of this were the reverse of those for
Dave Shield880692e2000-01-31 11:41:08 +0000788generic_header and header_simple_table. A version of checkmib is still
789available for compatability purposes, but you are encouraged to use
790header_simple_table instead.
Wes Hardakerf4029451999-03-10 23:07:05 +0000791
792The basic code fragment (see ucd-snmp/disk.c) is therefore of the form:
793
794 unsigned char *
795 var_extensible_disk(vp, name, length, exact, var_len, write_method)
796 {
797 if (header_simple_table(vp,name,length,exact,var_len,write_method,numdisks)
798 == MATCH_FAILED)
799 return(NULL);
800
801 [ etc, etc, etc ]
802
803 }
804
805Note that the maximum index value parameter does not have to be a
806permanently fixed constant. It specifies the maximum valid index at the time
807the request is processed, and a subsequent request may have a different
808maximum.
809An example of this can be seen in mibII/sysORTable.c where the table is held
810purely internally to the agent code, including its size (and hence the
811maximum valid index). This maximum could also be retrieved via a system
812call, or via a kernel data variable.
813
814Data Retrieval
Dave Shield880692e2000-01-31 11:41:08 +0000815--------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000816
817As with the scalar case, the other required function is to retrieve the data
818requested. However, given the definition of a simple table this is simply a
819matter of using the single, integer index sub-identifier to index into an
820existing data structure. This index will always be the last index of the OID
821returned by header_simple_table, so can be obtained as name[*length-1].
822A good example of this type of table can be seen in ucd-snmp/disk.c
823
824With some modules, this underlying table may be relatively large, or only
825accessible via a slow or cumbersome interface. The implementation described
826so far may prove unacceptably slow, particularly when walking a MIB tree
827requires the table to be loaded afresh for each variable requested.
828
829In these circumstances, a useful technique is to cache the table when it is
830first read in, and use that cache for subsequent requests. This can be done
831by having a separate routine to read in the table. This uses two static
832variables, one a structure or array for the data itself, and the other an
833additional timestamp to indicate when the table was last loaded. When a call
834is made to this routine to "read" the table, it can first check whether the
835cached table is "new enough". If so, it can return immediately, and the
836system will use the cached data.
837Only if the cached version is sufficiently old that it's probably out of
838date, is it necessary to retrieve the current data, updating the cached
839version and the timestamp value.
840This is particularly useful if the data itself is relatively static, such as
841a list of mounted filesystems. There is an example of this technique in the
842Host Resources implementation.
843
844As with the scalar case, mib2c simply provides placeholder dummy return
Dave Shield880692e2000-01-31 11:41:08 +0000845values. It's up to the programmer to fill in the details.
Wes Hardakerf4029451999-03-10 23:07:05 +0000846
847The next part concludes the examination of the detailed implementation by
848looking at more general tables.
849
Dave Shield880692e2000-01-31 11:41:08 +00008507. General Tables
851=================
Wes Hardakerf4029451999-03-10 23:07:05 +0000852
853Some table structures are not suitable for the simple table approach, due to
854the failure of one or more of the assumptions listed earlier. Perhaps they
855are indexed by something other than a single integer (such as a 4-octet IP
856address), or the maximum index is not easily determinable (such as the
857interfaces table), or not all indices are valid (running software), or the
Dave Shield880692e2000-01-31 11:41:08 +0000858necessary data is not directly accessible (interfaces again).
Wes Hardakerf4029451999-03-10 23:07:05 +0000859In such circumstances, a more general approach is needed. In contrast with
860the two styles already covered, this style of module will commonly combine
861the two functions of request validation and data retrieval. Note that mib2c
862will assume the simple table case, and this will need to be corrected.
863
864General table algorithm
Dave Shield880692e2000-01-31 11:41:08 +0000865-----------------------
Wes Hardakerf4029451999-03-10 23:07:05 +0000866
867The basic algorithm is as follows:
868
869 Perform any necessary initialization, then walk through the
870 underlying instances, retrieving the data for each one, until the
871 desired instance is found. If no valid entry is found, return
872 failure.
873
874For an exact match (GET and similar), identifying the desired instance is
875trivial - construct the OID (from the 'vp' variable parameter and the index
876value or values), and see whether it matches the requested OID.
877For GETNEXT, the situation is not quite so simple. Depending on the
878underlying representation of the data, the entries may be returned in the
879same order as they should appear in the table (i.e. lexically increasing by
880index). However, this is not guaranteed, and the natural way of retrieving
881the data may be in some "random" order. In this case, then the whole table
882needs to be traversed for each request. in order to determine the
883appropriate successor.
884This random order is the worst case, and dictates the structure of the code
885used in most currently implemented tables. The ordered case can be regarded
886as a simplification of this more general one.
887
888The algorithm outlined above can now be expanded into the following
889pseudo-code:
890
891 Init_{Name}_Entry(); // Perform any necessary initialisation
892
Dave Shield880692e2000-01-31 11:41:08 +0000893 while (( index = Get_Next_{Name}_Entry() ) != EndMarker ) {
Wes Hardakerf4029451999-03-10 23:07:05 +0000894 // This steps through the underlying table,
Dave Shield880692e2000-01-31 11:41:08 +0000895 // returning the current index,
896 // or some suitable end-marker when all
Wes Hardakerf4029451999-03-10 23:07:05 +0000897 // the entries have been examined.
898 // Note that this routine should also return the
Dave Shield880692e2000-01-31 11:41:08 +0000899 // data for this entry, either via a parameter
900 // or using some external location.
Wes Hardakerf4029451999-03-10 23:07:05 +0000901
902 construct OID from vp->name and index
903 compare new OID and request
904 if valid {
905 save current data
906 if finished // exact match, or ordered table
907 break; // so don't look at any more entries
908
909 }
910
911 // Otherwise, we need to loop round, and examine
912 // the next entry in the table. Either because
913 // the entry wasn't valid for this request,
914 // or the entry was a possible "next" candidate,
915 // but we don't know that there isn't there's a
916 // better one later in the table.
917 }
918
919 if no saved data // Nothing matched
920 return failure
921
922 // Otherwise, go on to the switch handling
923 // we've already covered in the earlier styles.
924
925This is now very close to the actual code used in many current
926implementations (such as the the routine header_ifEntry in
927mibII/interfaces.c). Notice that the pseudo-code fragment if valid expands
928in practise to
929
930 if ((exact && (result == 0)) ||
931 // GET request, and identical OIDs
932 (!exact && (result < 0)) )
933 // GETNEXT, and candidate OID is later
934 // than requested OID.
935
936This is a very common expression, that can be seen in most of the table
937implementations.
938
939Notice also that the interfaces table returns immediately the first valid
940entry is found, even for GETNEXT requests. This is because entries are
941returned in lexical order, so the first succeeding entry will be the one
942that's required.
943(As an aside, this also means that the underlying data can be saved
944implicitly within the 'next entry' routine - not very clean, but it saves
945some unnecessary copying).
946
947The more general case can be seen in the TCP and UDP tables (see mibII/tcp.c
948and mibII/udp.c). Here, the if valid fragment expands to:
949
950 if ( exact && (result == 0)) {
951 // save results
952 break;
953 }
954 else if (!exact && (result < 0)) {
955 if ( .... ) { // no saved OID, or this OID
956 // precedes the saved OID
957 // save this OID into 'lowest'
958 // save the results into Lowinpcb
959 // don't break, since we still need to look
960 // at the rest of the table
961 }
962 }
963
964The GET match handling is just as we've already seen - is this the requested
Dave Shield880692e2000-01-31 11:41:08 +0000965OID or not. If so, save the results and move on to the switch statement.
966 The GETNEXT case is more complicated. As well as considering whether this
967is a possible match (using the same test we've already seen), we also have to
968check whether this is a better match than anything we've already seen. This
969is done by comparing the current candidate (newname) with the best match found
970so far (lowest).
971 Only if this extra comparison shows that the new OID is earlier than the
Wes Hardakerf4029451999-03-10 23:07:05 +0000972saved one, do we need to save both the new OID, and any associated data
973(such as the inpcb block, and state flag). But having found one better
974match, we don't know that there isn't an even better one later on. So we
975can't break out of the enclosing loop - we need to keep going and examine
976all the remaining entries of the table.
977
978These two cases (the TCP and UDP tables) also show a more general style of
979indexing. Rather than simply appending a single index value to the OID
980prefix, these routines have to add the local four-octet IP address plus port
981(and the same for the remote end in the case of the TCP table). This is the
982purpose of the op and cp section of code that precedes the comparison.
983
984These two are probably among the most complex cases you are likely to
985encounter. If you can follow the code here, then you've probably cracked the
986problem of understanding how the agent works.
987
988Finally, the next part discusses how to implement a writable (or SETable)
989object in a MIB module.
990
Dave Shield880692e2000-01-31 11:41:08 +00009918. How to implement a SETable object
992====================================
Wes Hardakerf4029451999-03-10 23:07:05 +0000993
994Finally, the only remaining area to cover is that of setting data - the
995handling of SNMPSET. Particular care should be taken here for two reasons.
996
Dave Shield880692e2000-01-31 11:41:08 +0000997Firstly, any errors in the earlier sections can have limited effect. The
Wes Hardakerf4029451999-03-10 23:07:05 +0000998worst that is likely to happen is that the agent will either return invalid
999information, or possibly crash. Either way, this is unlikely to affect the
1000operation of the workstation as a whole. If there are problems in the
1001writing routine, the results could be catastrophic (particularly if writing
1002data directly into kernel memory).
1003
1004Secondly, this is the least well understood area of the agent, at least by
Dave Shield880692e2000-01-31 11:41:08 +00001005the author. There are relatively few variables that are defined as READ-WRITE
1006in the relevant MIBs, and even fewer that have actually been implemented as
1007such. I'm therefore describing this from a combination of my understanding
1008of how SETs ought to work, personal experience of very simple SET handling
1009and what's actually been done by others (which do not necessarily coincide).
Wes Hardakerf4029451999-03-10 23:07:05 +00001010
1011There are also subtle differences between the setting of simple scalar
1012variables (or individual entries within a table), and the creation of a new
1013row within a table. This will therefore be considered separately.
1014
1015With these caveats, and a healthy dose of caution, let us proceed. Note that
1016the UCD-SNMP development team can accept no responsibility for any damage or
1017loss resulting from either following or ignoring the information presented
1018here. You coded it - you fix it!
1019
1020Write routine
Dave Shield880692e2000-01-31 11:41:08 +00001021-------------
Wes Hardakerf4029451999-03-10 23:07:05 +00001022
1023The heart of SET handling is the write_method parameter from the variable
1024handling routine. This is a pointer to the relevant routine for setting the
1025variable in question. Mib2c will generate one such routine for each setable
1026variable. This routine should be declared using the template
1027
1028 int
1029 write_variable(
1030 int action,
1031 u_char *var_val,
1032 u_char var_val_type,
1033 int var_val_len,
1034 u_char *statP,
1035 oid *name,
1036 int name_len );
1037
1038Most of these parameters are fairly self explanatory:
1039The last two hold the OID to be set, just as was passed to the main variable
1040routine.
1041
1042The second, third and fourth parameters provide information about the new
1043desired value, both the type, value and length. This is very similar to the
Dave Shield880692e2000-01-31 11:41:08 +00001044way that results are returned from the main variable routine.
Wes Hardakerf4029451999-03-10 23:07:05 +00001045
1046The return value of the routine is simply an indication of whether the
1047current stage of the SET was successful or not. We'll come back to this in a
1048minute. Note that it is the responsibility of this routine to check that the
1049OID and value provided are appropriate for the variable being implemented.
1050This includes (but is not limited to) checking:
1051
1052 * the OID is recognised as one this routine can handle
1053 (this should be true if the routine only handles the one variable, and
1054 there are no errors in the main variable routine or driving code, but
1055 it does no harm to check).
1056 * the value requested is the correct type expected for this OID
1057 * the value requested is appropriate for this OID
1058 (within particular ranges, suitable length, etc, etc)
1059
1060There are two parameters remaining to be considered.
1061
Dave Shield880692e2000-01-31 11:41:08 +00001062The fifth parameter, statP, is the value that would be returned from a GET
1063request on this particular variable. It could be used to check that the
1064requested new value is consistent with the current state, but its main use
1065is to denote that a new table row is being created.
1066In most cases (particularly when dealing with scalar values or single elements
1067of tables), you can normally simply ignore this parameter.
Wes Hardakerf4029451999-03-10 23:07:05 +00001068
1069Actions
Dave Shield880692e2000-01-31 11:41:08 +00001070-------
Wes Hardakerf4029451999-03-10 23:07:05 +00001071
Dave Shield880692e2000-01-31 11:41:08 +00001072The final parameter to consider is the first one - action. To understand
Wes Hardakerf4029451999-03-10 23:07:05 +00001073this, it's necessary to know a bit about how SETs are implemented.
1074The design of SNMP calls for all variables in a SET request to be done "as
1075if simultaneously" - i.e. they should all succeed or all fail. However, in
1076practise, the variables are handled in succession. Thus, if one fails, it
1077must be possible to "undo" any changes made to the other variables in the
1078request.
1079This is a well understood requirement in the database world, and is usually
1080implemented using a "multi-stage commit". This is certainly the mechanism
1081expected within the SNMP community (and has been made explicit in the work
1082of the AgentX extensibility group). In other words, the routine to handle
1083setting a variable will be called more than once, and the routine must be
1084able to perform the appropriate actions depending on how far through the
1085process we currently are. This is determined by the value of the action
1086parameter.
1087
1088This is implemented using three basic phases:
1089
Dave Shield880692e2000-01-31 11:41:08 +00001090RESERVE is used to check the syntax of all the variables provided, that the
1091values being set are sensible and consistent, and to allocate any resources
1092required for performing the SET. After this stage, the expectation is that
1093the set ought to succeed, though this is not guaranteed.
Wes Hardakerf4029451999-03-10 23:07:05 +00001094(In fact, with the UCD agent, this is done in two passes - RESERVE1, and
1095RESERVE2, to allow for dependancies between variables).
1096
1097If any of these calls fail (in either pass) the write routines are called
1098again with the FREE action, to release any resources that have been
1099allocated. The agent will then return a failure response to the requesting
1100application.
1101
1102Assuming that the RESERVE phase was successful, the next stage is indicated
1103by the action value ACTION. This is used to actually implement the set
1104operation. However, this must either be done into temporary (persistent)
1105storage, or the previous value stored similarly, in case any of the
1106subsequent ACTION calls fail.
Dave Shield880692e2000-01-31 11:41:08 +00001107 This can be seen in the example module, where both write routines have
1108static 'old' variables, to hold the previous value of the relevant object.
Wes Hardakerf4029451999-03-10 23:07:05 +00001109
Dave Shield880692e2000-01-31 11:41:08 +00001110If the ACTION phase does fail (for example due to an apparently valid, but
Wes Hardakerf4029451999-03-10 23:07:05 +00001111unacceptable value, or an unforeseen problem), then the list of write
1112routines are called again, with the UNDO action. This requires the routine
1113to reset the value that was changed to its previous value (assuming it was
1114actually changed), and then to release any resources that had been
1115allocated. As with the FREE phase, the agent will then return an indication
1116of the error to the requesting application.
1117
1118Only once the ACTION phase has completed successfully, can the final COMMIT
1119phase be run. This is used to complete any writes that were done into
1120temporary storage, and then release any allocated resources. Note that all
1121the code in this phase should be "safe" code that cannot possibly fail (cue
1122hysterical laughter). The whole intent of the ACTION/COMMIT division is that
1123all of the fallible code should be done in the ACTION phase, so that it can
1124be backed out if necessary.
1125
1126Table row creation
Dave Shield880692e2000-01-31 11:41:08 +00001127------------------
Wes Hardakerf4029451999-03-10 23:07:05 +00001128
Dave Shield880692e2000-01-31 11:41:08 +00001129What about creating new rows in a table, I hear you ask. Good Question.
1130This case can often be detected by the fact that a GET request would have
1131failed, and hence the fifth parameter, statP, will be null. This contrasts
1132with changing the values of an element of an existing row, when the statP
1133parameter would hold the previous value.
Wes Hardakerf4029451999-03-10 23:07:05 +00001134
Dave Shield880692e2000-01-31 11:41:08 +00001135The details of precisely how to create a new row will clearly depend on the
1136underlying format of the table. However, one implementation strategy would
1137be as follows:
Wes Hardakerf4029451999-03-10 23:07:05 +00001138
Dave Shield880692e2000-01-31 11:41:08 +00001139 * The first column object to be SET would return a null value from the
1140 var_name routine. This null statP parameter would be the signal
1141 to create a new temporary instance of the underlying data structure,
1142 filled with dummy values.
1143 * Subsequent column objects would return pointers to the appropriate
1144 field of this new data structure from the var_name routine,
1145 which would then be filled in by the write routine.
1146 * Once all the necessary fields had been SET, the completed temporary
1147 instance could be moved into the "standard" structure (or copied,
1148 or otherwise used to set things up appropriately).
1149
1150However, this is purely a theoretical strategy, and has not been tried
1151by the author. No guarantees are given as to whether this would actually
1152work. There are also questions regarding how to handle incomplete
1153or overlapping SET requests.
1154Anyone who has experience of doing this, please get in touch!
1155
Wes Hardakerf4029451999-03-10 23:07:05 +00001156 ------------------------------------------------------------------------
1157And that's it. Congratulations for getting this far. If you understand
1158everything that's been said, then you now know as much as the rest of us
1159about the inner workings of the UCD-SNMP agent. (Well, very nearly).
1160All that remains is to try putting this into practise. Good luck!
1161
1162And if you've found this helpful, gifts of money, chocolate, alcohol, and
1163above all feedback, would be most appreciated :-)
1164
1165 ------------------------------------------------------------------------
Dave Shield880692e2000-01-31 11:41:08 +00001166Copyright 1999, 2000 - D.T.Shield.
1167Not to be distributed without the explicit permission of the author.