Blame AGENT.txt

Packit fcad23
Note, this is based on the text from a web page, which can be found in 
Packit fcad23
the documentation section of the http://www.net-snmp.org web page.
Packit fcad23
Packit fcad23
Extending the UCD-SNMP agent
Packit fcad23
============================
Packit fcad23
Packit fcad23
This document describes the procedure for writing code to extend
Packit fcad23
the functionality of the v4 UCD-SNMP network management agent.
Packit fcad23
Modules written using this procedure should also work with the v5
Packit fcad23
Net-SNMP agent, though such modules would not take advantage of the
Packit fcad23
new handler-based helper mechanism.  See the on-line documentation
Packit fcad23
for more information and examples of the newer approach.
Packit fcad23
We would be very interested in comment and feedback about how useful
Packit fcad23
(or otherwise) you find this description, and ways in which it could
Packit fcad23
be improved.
Packit fcad23
Packit fcad23
The information is designed to be read in order - the structure being:
Packit fcad23
Packit fcad23
  1. Overview & Introduction
Packit fcad23
  2. MIB files, and how they relate to the agent implementation
Packit fcad23
  3. Header files
Packit fcad23
  4. The basic structure of module implementation code
Packit fcad23
  5. The details of non-table based implementations
Packit fcad23
  6. The details of simple table based implementations
Packit fcad23
  7. The details of more general table based implementations
Packit fcad23
  8. How to implement SET-able variables
Packit fcad23
Packit fcad23
While the document is intended to be generally self-contained,
Packit fcad23
it does occasionally refer to code files shipped with the main UCD
Packit fcad23
distribution (in particular the example module), and it may prove
Packit fcad23
useful to have these files available for reference.
Packit fcad23
Packit fcad23
1. How to write a Mib module
Packit fcad23
============================
Packit fcad23
Packit fcad23
Introduction
Packit fcad23
------------
Packit fcad23
Packit fcad23
The design of the UCD SNMP agent has always been shaped by the desire to be
Packit fcad23
able to extend its functionality by adding new modules. One of the earliest
Packit fcad23
developments from the underlying CMU code base was the ability to call
Packit fcad23
external scripts, and this is probably the simplest method of extending the
Packit fcad23
agent.
Packit fcad23
However, there are circumstances where such an approach is felt to be
Packit fcad23
inappropriate - perhaps from considerations of speed, access to the
Packit fcad23
necessary data, reliability or elegance. In such cases, the obvious solution
Packit fcad23
is to provide C code that can be compiled into the agent itself to implement
Packit fcad23
the desired module. Many of the more recent developments in the code
Packit fcad23
structure have been intended to ease this process. In particular, one of the
Packit fcad23
more recent additions to the suite is the tool mib2c. This is designed to
Packit fcad23
take a portion of the MIB tree (as defined by a MIB file) and generate the
Packit fcad23
code skeleton necessary to implement this. This document will cover the use
Packit fcad23
mib2c, as well as describing the requirements and functionality of the code
Packit fcad23
in more detail.
Packit fcad23
Packit fcad23
In order to implement a new MIB module, three files are necessary, and these
Packit fcad23
will be considered in turn. Note that, by the very nature of the task, this
Packit fcad23
document cannot cover the details of precisely how to obtain the necessary
Packit fcad23
information from the operating system or application. Instead, it describes
Packit fcad23
the code framework that is needed, freeing the implementer from needing to
Packit fcad23
understand the detailed internals of the agent, and allowing them to
Packit fcad23
concentrate on the particular problem in hand.
Packit fcad23
Packit fcad23
It may prove useful to examine some of the existing module implementations
Packit fcad23
and examples in the light of this description, and suitable examples will be
Packit fcad23
referred to at the appropriate points. However, it should be remembered that
Packit fcad23
the UCD agent seeks to support a wide variety of systems, often with
Packit fcad23
dramatically differing implementations and interfaces, and this is reflected
Packit fcad23
in the complexity of the code. Also, the agent has developed gradually over
Packit fcad23
the years, and there is often some measure of duplication or redundancy as a
Packit fcad23
result.
Packit fcad23
As the FAQ states, the official slogan of the UCD-SNMP developers is
Packit fcad23
Packit fcad23
     The current implementation is non-obvious and may need to be
Packit fcad23
     improved.
Packit fcad23
Packit fcad23
This document describes the ideal, straightforward cases - real life is
Packit fcad23
rarely so simple, and the example modules may prove easier to follow at a
Packit fcad23
first reading.
Packit fcad23
It is also advisable to have a compiled and installed implementation
Packit fcad23
available before starting to extend the agent. This will make debugging and
Packit fcad23
testing the agent much easier.
Packit fcad23
Packit fcad23
A note regarding terminology - the word "module" is widely used throughout
Packit fcad23
this document, with a number of different meanings.
Packit fcad23
Packit fcad23
   * support for a new MIB,
Packit fcad23
     i.e. the whole of the functionality that is required. This is usually
Packit fcad23
     termed a MIB module;
Packit fcad23
   * a self-contained subset of this, implemented as a single unit.
Packit fcad23
     This is usually termed an implementation module (or simply "a module");
Packit fcad23
   * the combination of such subsets, usually termed a module group.
Packit fcad23
Packit fcad23
Note that the first and third of these are often synonymous - the
Packit fcad23
difference being that a MIB module refers to the view from outside the
Packit fcad23
agent, regarding this as a seamless whole and hiding the internal
Packit fcad23
implementation. A "module group" is used where the internal structure is of
Packit fcad23
more relevance, and recognises the fact that the functionality may be
Packit fcad23
provided by a number of co-operating implementation modules.
Packit fcad23
Packit fcad23
Anyway, enough waffle - on with the details: The three files needed are
Packit fcad23
Packit fcad23
   * a MIB definition file;
Packit fcad23
   * a C header file;
Packit fcad23
   * a C implementation file.
Packit fcad23
Packit fcad23
The next part looks at the MIB definition file, and how this impacts on the
Packit fcad23
agent implementation.
Packit fcad23
Packit fcad23
2. The MIB File
Packit fcad23
===============
Packit fcad23
Packit fcad23
The first file needed is the MIB file that defines the MIB module to be
Packit fcad23
implemented.
Packit fcad23
Strictly speaking, this is not absolutely necessary, as the agent itself
Packit fcad23
does not make any direct use of the MIB definitions. However, it is
Packit fcad23
advisable to start with this for three reasons:
Packit fcad23
Packit fcad23
   * It provides an initial specification for what is to be implemented.
Packit fcad23
     Code development is always easier if you know what you are meant to be
Packit fcad23
     writing!
Packit fcad23
   * If the new MIB file is read in with the other MIB files,
Packit fcad23
     this lets the applications provided with the suite be used to test the
Packit fcad23
     new agent, and report (hopefully meaningful) symbolic OIDs and values,
Packit fcad23
     rather than the bare numeric forms.
Packit fcad23
     (N.B: Remember to tell the application to load the new MIB. See the
Packit fcad23
     relevant question in the FAQ)
Packit fcad23
   * The tool mib2c uses this description to produce the two code files.
Packit fcad23
     This is by far the easiest way to develop a new module.
Packit fcad23
     (Note that the v5 version of mib2c is generally similar, but does
Packit fcad23
     not correspond exactly to the v4 version described here)
Packit fcad23
Packit fcad23
If the intention is to implement a 'standard' MIB module, or a
Packit fcad23
vendor-specific one, then the construction of this file will have already
Packit fcad23
been done for you. If the intention is to provide a totally new, private
Packit fcad23
module, then you will need to write this yourself, in addition to the agent
Packit fcad23
code files.
Packit fcad23
A description of MIB file format and syntax is beyond the scope of this
Packit fcad23
document, and most books on SNMP management should provide some information
Packit fcad23
on this subject. One book which concentrates on this is
Packit fcad23
Packit fcad23
     Understanding SNMP MIBS
Packit fcad23
     (Perkins & McGinnis, Prentice Hall, ISBN 0-13-437708-7).
Packit fcad23
Packit fcad23
This blatant plug is wholly unrelated to the fact that David Perkins is an
Packit fcad23
active member of the development group, and is regarded as our resident
Packit fcad23
"protocol guru and policeman". (In fact, this book concentrates on MIB
Packit fcad23
files in rather more detail than is appropriate in more general SNMP works).
Packit fcad23
Information on other books covering SNMP and Network Management more generally
Packit fcad23
is available on the SimpleWeb site (among other places).
Packit fcad23
See the FAQ for more details.
Packit fcad23
Packit fcad23
Assigned OID numbers
Packit fcad23
--------------------
Packit fcad23
Packit fcad23
One word of advice - even if you are developing a totally private MIB
Packit fcad23
module, you will still need to position this somewhere within the overall
Packit fcad23
MIB tree. Please do NOT simply choose a location "at random". Any such is
Packit fcad23
likely to have either been assigned to some other organisation, or may be so
Packit fcad23
assigned some time in the future. However much you may regard your project
Packit fcad23
as a totally internal affair, such projects have a tendency to exceed their
Packit fcad23
expected scope, both in terms of lifetime and distribution (not to mention
Packit fcad23
the potential OID clash if you subsequently need to use elements from the
Packit fcad23
legitimate owner's tree).
Packit fcad23
It is simple and cheap (i.e. free!) to obtain your own official segment of
Packit fcad23
the MIB tree (see http://www.iana.org for an application form), and having
Packit fcad23
done so, you then have complete global authority over it. If you have
Packit fcad23
problems with this, it's worth contacting the development team (email:
Packit fcad23
net-snmp-coders@lists.sourceforge.net) for advice. Please do think to the
Packit fcad23
future, and be a good Net citizen by using a legitimately assigned OID as
Packit fcad23
the root of your new MIB.
Packit fcad23
Packit fcad23
MIB division
Packit fcad23
------------
Packit fcad23
Packit fcad23
The next point to consider, whether writing by hand or using mib2c,
Packit fcad23
implementing an existing MIB, or writing a new one, is whether and how to
Packit fcad23
divide up the MIB tree. This is a purely internal implementation decision,
Packit fcad23
and will not be visible to management applications querying the agent. A
Packit fcad23
sensible choice of partitioning will result in a simpler, clearer
Packit fcad23
implementation, which should ease both the initial development and
Packit fcad23
subsequent maintenance of the module.
Packit fcad23
Unfortunately, this choice is one of the module-specific decisions, so must
Packit fcad23
be made on a case-by-case basis. For a simple, self-contained module, it may
Packit fcad23
well be reasonable to implement the module as a single block (examples
Packit fcad23
include the SNMP statistics subtree RFC 1907 or the TCP subtree RFC 2011).
Packit fcad23
More complex and diverse modules (such as the Host Resources MIB - RFC 1514)
Packit fcad23
are more naturally considered as a number of individual sub-modules.
Packit fcad23
Some guidelines to bear in mind when deciding on this division:
Packit fcad23
Packit fcad23
   * A MIB sub-tree consisting purely of scalar objects with a common
Packit fcad23
     OID prefix would normally be handled in a single implementation module;
Packit fcad23
   * Separate scalar subtrees would normally be in different implementation
Packit fcad23
     modules;
Packit fcad23
   * A table can either be handled within the same implementation module
Packit fcad23
     as related scalar objects in the same subtree, or in a separate
Packit fcad23
     implementation module;
Packit fcad23
   * Variables that rely on the same underlying data structure to retrieve
Packit fcad23
     their values, should probably be in the same implementation module (and
Packit fcad23
     conversely, (though less so) those that don't, shouldn't).
Packit fcad23
Packit fcad23
As an initial rule of thumb, a good initial division is likely to be
Packit fcad23
obtained by treating each table and each scalar sub-tree separately. This
Packit fcad23
can be seen in the current agent, where most of the MIB-II modules (RFC
Packit fcad23
1213) are implemented in separate files (see the files under mibgroup/mibII).
Packit fcad23
Note that many of these combine scalar and table handling in the same file,
Packit fcad23
though they are implemented using separate routines.
Packit fcad23
  This is also the approach used by mib2c, which constructs a single pair of
Packit fcad23
code files, but uses a separate routine for each table (and another for all
Packit fcad23
the scalar variables).
Packit fcad23
  Ultimately, the final consideration (concerning the underlying data) is
Packit fcad23
the most important, and should guide the basic division. For example, the
Packit fcad23
Host Resources Running Software and Running Software Performance modules,
Packit fcad23
while separate in the MIB tree, use the same underlying kernel data and so
Packit fcad23
are implemented together.
Packit fcad23
Packit fcad23
MIB name
Packit fcad23
--------
Packit fcad23
Packit fcad23
The final requirement at this stage is to choose a name for each
Packit fcad23
implementation module. This should be reasonably short, meaningful, unique
Packit fcad23
and unlikely to clash with other (existing or future) modules. Mib2c uses
Packit fcad23
the label of the root node of the MIB sub-tree as this name, and this is a
Packit fcad23
reasonable choice in most cases.
Packit fcad23
Recent changes to the agent code organisation have introduced the idea of
Packit fcad23
module groups of related implementation modules. This is used, for example,
Packit fcad23
to identify the constituent modules of a 'split' MIB (such as the Host
Packit fcad23
Resources MIB), or those relating to a particular organisation (such as
Packit fcad23
UCD).
Packit fcad23
As with the division, this naming and grouping is a purely internal matter,
Packit fcad23
and is really only visible when configuring and compiling the agent.
Packit fcad23
Packit fcad23
So much for the MIB file. The next part considers the C header file.
Packit fcad23
Packit fcad23
3. The C code header file
Packit fcad23
=========================
Packit fcad23
Packit fcad23
If the MIB file is the definition of the module for external network
Packit fcad23
management applications (where applications includes network management
Packit fcad23
personnel!), then the header file has traditionally served effectively the
Packit fcad23
same purpose for the agent itself.
Packit fcad23
Recent changes to the recommended code structure has resulted in the header
Packit fcad23
file becoming increasingly simpler. It now simply contains definitions of the
Packit fcad23
publically visible routines, and can be generated completely by mib2c.
Packit fcad23
Packit fcad23
Function prototypes
Packit fcad23
-------------------
Packit fcad23
Packit fcad23
For those interested in the details of this file (for example, if coding a
Packit fcad23
module by hand), then the details of these definitions are as follows. Every
Packit fcad23
header file will have the following two function prototype definitions
Packit fcad23
Packit fcad23
        extern void          init_example (void);
Packit fcad23
        extern FindVarMethod var_example;
Packit fcad23
Packit fcad23
If the module includes any tables, or other collections of variables that
Packit fcad23
are implemented in separate routines, then this second definition will be
Packit fcad23
repeated for each of these.
Packit fcad23
In addition, if any of the variables can be SET (and it is intended to
Packit fcad23
implement them as such), there will be a function prototype definitions for
Packit fcad23
each of these, of the form:
Packit fcad23
Packit fcad23
        extern WriteMethod write_varName;
Packit fcad23
Packit fcad23
These prototypes are in fact typedef'ed in <agent/snmp_vars.h>.
Packit fcad23
Packit fcad23
Module dependencies
Packit fcad23
-------------------
Packit fcad23
Packit fcad23
This header file is also used to inform the compilation system of any
Packit fcad23
dependancies between this module and any others. There is one utility module
Packit fcad23
which is required by almost every module, and this is included using the
Packit fcad23
directive
Packit fcad23
Packit fcad23
        config_require( util_funcs )
Packit fcad23
Packit fcad23
(which is produced automatically by mib2c). This same syntax can be used to
Packit fcad23
trigger the inclusion of other related modules. An example of this can be
Packit fcad23
seen in mibII/route_write.h which relies on the mibII/ip module, thus:
Packit fcad23
Packit fcad23
        config_require( mibII/ip )
Packit fcad23
Packit fcad23
One use of this directive is to define a module group, by supplying a header
Packit fcad23
file consisting exclusively of such config_require directives.  It can then
Packit fcad23
be included or excluded from the agent very simply. Examples of this can be
Packit fcad23
seen in mibgroup/mibII.h or mibgroup/host.h, which list the consituent
Packit fcad23
sub-modules of the MIB-II and Host Resources MIBs respectively.
Packit fcad23
Packit fcad23
MIB file information
Packit fcad23
--------------------
Packit fcad23
Packit fcad23
Most of the information in this file is (understandably) aimed at the network
Packit fcad23
management agent itself.  However, there is one common header file directive
Packit fcad23
that is actually intended to affect the utility commands that are included
Packit fcad23
within the full distribution:
Packit fcad23
Packit fcad23
	config_add_mib( HOST-RESOURCES-MIB )
Packit fcad23
Packit fcad23
  This is used to add the MIB file being implemented to the default list of
Packit fcad23
MIBs loaded by such commands.  This means that querying the agent will return
Packit fcad23
informative names and values, rather than the raw numeric forms that SNMP
Packit fcad23
actually works with.  Of course, it is always possible for the utilities
Packit fcad23
to specify that this MIB should be loaded anyway.  But specifying this file
Packit fcad23
within the module header file is a useful hint that a particular MIB should
Packit fcad23
be loaded, without needing to ask for it explicitly.
Packit fcad23
  Note that this will only affect the binaries compiled as part of the same
Packit fcad23
configuration run.  It will have no effect on pre-installed binaries, or
Packit fcad23
those compiled following a different configuration specification.
Packit fcad23
Packit fcad23
Magic Numbers
Packit fcad23
-------------
Packit fcad23
Packit fcad23
The other common element within the header file defines a set of "magic
Packit fcad23
numbers" - one for each object within the implementation module. In fact,
Packit fcad23
this can equally well appear within the main code file, as part of the
Packit fcad23
variable structure (which will be described in the next part).
Packit fcad23
  This is the technique used by mib2c, but most handcrafted modules have
Packit fcad23
tended to define these as part of the header file, probably for clarity.
Packit fcad23
Packit fcad23
  The only necessity is that the names and values are distinct (or more
Packit fcad23
precisely, the values are distinct within a single variable handling routine).
Packit fcad23
In practise, they tend to be defined using integers incrementing from 1,
Packit fcad23
or as the same as the final sub-identifier of the corresponding MIB object
Packit fcad23
(or indeed both, as these are frequently themselves successive integers).
Packit fcad23
  This is not mandatory, and a counter-example can be seen in the
Packit fcad23
example module, where two of the object form a sub-tree, and the corresponding
Packit fcad23
magic numbers are based on the final *two* sub-identifiers (to ensure that
Packit fcad23
the values are unique).  But this construction is definitely unusual, and
Packit fcad23
the majority of modules simply use successive integers.
Packit fcad23
Packit fcad23
Header file protection
Packit fcad23
----------------------
Packit fcad23
Packit fcad23
Normally, the only other contents of the header file will be the
Packit fcad23
#ifndef/#define/#endif statements surrounding the whole file. This is used
Packit fcad23
to ensure that the header file is only included once by any source code file
Packit fcad23
(or more accurately, that there is no effect if it is inadvertantly included
Packit fcad23
a second time).
Packit fcad23
Again, as with the rest of the header file, this is generated automatically
Packit fcad23
by mib2c.
Packit fcad23
Packit fcad23
Having finished all the preparatory work (or let mib2c deal with it), the
Packit fcad23
next part starts to look at the code file that actually implements the
Packit fcad23
module.
Packit fcad23
Packit fcad23
4. Core structure of the implementation code
Packit fcad23
============================================
Packit fcad23
Packit fcad23
The core work of implementing the module is done in the C code file. As
Packit fcad23
indicated earlier, much of the detail of this will be dependent on the
Packit fcad23
particular module being implemented, and this can only be described by the
Packit fcad23
individual programmer concerned.
Packit fcad23
However, there is a fairly clearly defined framework that the implementation
Packit fcad23
will need to follow, though this varies slightly depending on the style of
Packit fcad23
the module being implemented (in particular whether it forms a table or a
Packit fcad23
series of individual values). The differences will be covered in the
Packit fcad23
following pages, but we first need to consider the overall shape of the
Packit fcad23
framework, and the elements that are common to all styles. These are
Packit fcad23
essentially the compulsory routines, the common header definitions, and
Packit fcad23
assorted initialisation code.
Packit fcad23
As with the header file, most of this will be generated automatically by
Packit fcad23
mib2c.
Packit fcad23
Packit fcad23
Standard includes
Packit fcad23
-----------------
Packit fcad23
Packit fcad23
Certain header files are either compulsory, or required so frequently that
Packit fcad23
they should be included as a matter of course. These are as follows:
Packit fcad23
Packit fcad23
  #include <config.h>                   // local SNMP configuration details
Packit fcad23
  #include "mib_module_config.h"        // list of which modules are supported
Packit fcad23
  #if HAVE_STDLIB_H
Packit fcad23
  #include <stdlib.h>
Packit fcad23
  #endif
Packit fcad23
  #if HAVE_STRING_H
Packit fcad23
  #include <string.h>
Packit fcad23
  #else
Packit fcad23
  #include <strings.h>
Packit fcad23
  #endif
Packit fcad23
Packit fcad23
  #include <sys/types.h>
Packit fcad23
Packit fcad23
All of these will usually be the first files to be included.
Packit fcad23
Packit fcad23
  #include "mibincl.h"                  // Standard set of SNMP includes
Packit fcad23
  #include "util_funcs.h"               // utility function declarations
Packit fcad23
  #include "read_config.h"              // if the module uses run-time
Packit fcad23
                                        //      configuration controls
Packit fcad23
  #include "auto_nlist.h"               // structures for a BSD-based
Packit fcad23
                                        //      kernel using nlist
Packit fcad23
  #include "system.h"
Packit fcad23
Packit fcad23
  #include "name.h"                     // the module-specific header
Packit fcad23
Packit fcad23
These conventionally come at the end of the list of includes. In between
Packit fcad23
will come all the standard system-provided header files required for the
Packit fcad23
library functions used in the file.
Packit fcad23
Packit fcad23
Module definition
Packit fcad23
-----------------
Packit fcad23
Packit fcad23
Much of the code defining the contents of the MIB has traditionally been
Packit fcad23
held in the header file. However, much of this has slowly migrated to the
Packit fcad23
code file, and this is now the recommended location for it (as typified by
Packit fcad23
the output of mib2c).
Packit fcad23
  The main element of this is a variable structure specifying the details of 
Packit fcad23
the objects implemented.  This takes the form of an unconstrained array of
Packit fcad23
type struct variableN (where N is the length of the longest suffix in the
Packit fcad23
table). Thus
Packit fcad23
Packit fcad23
                struct variable2 example_variables[] = {
Packit fcad23
			<individual entries go here>
Packit fcad23
                };
Packit fcad23
Packit fcad23
Each entry corresponds to one object in the MIB tree (or one column in the
Packit fcad23
case of table entries), and these should be listed in increasing OID order.
Packit fcad23
A single entry consists of six fields:
Packit fcad23
Packit fcad23
   * a magic number (the #defined integer constant described above)
Packit fcad23
   * a type indicator (from the values listed in <snmplib/snmp_impl.h>)
Packit fcad23
   * an access indicator (essentially NETSNMP_OLDAPI_RWRITE or
Packit fcad23
     NETSNMP_OLDAPI_RONLY)
Packit fcad23
   * the name of the routine used to handle this entry
Packit fcad23
   * the length of the OID suffix used, and
Packit fcad23
   * an array of integers specifying this suffix (more on this in a moment)
Packit fcad23
Packit fcad23
Thus a typical variable entry would look like:
Packit fcad23
Packit fcad23
        { EXAMPLESTRING, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
Packit fcad23
          var_example, 1, {1}}
Packit fcad23
Packit fcad23
If the magic numbers have not been defined in the header file, then they
Packit fcad23
should be defined here, usually comming immediately before the corresponding
Packit fcad23
variable entry.  This is the technique used by mib2c.
Packit fcad23
Packit fcad23
Note that in practise, only certain sizes of the structure variableN
Packit fcad23
are defined (listed in <agent/var_struct.h>), being sufficient to meet the
Packit fcad23
common requirements. If your particular module needs a non-supported value,
Packit fcad23
the easiest thing is simply to use the next largest value that is supported.
Packit fcad23
Packit fcad23
The module also needs to declare the location within the MIB tree where
Packit fcad23
it should be registered. This is done using a declaration of the form
Packit fcad23
Packit fcad23
        oid example_variables_oid[] = { 1,3,6,1,4,1,2021,254 }
Packit fcad23
Packit fcad23
where the contents of the array give the object identifier of the root of
Packit fcad23
the module.
Packit fcad23
Packit fcad23
Module initialisation
Packit fcad23
---------------------
Packit fcad23
Packit fcad23
Many modules require some form of initialisation before they can start
Packit fcad23
providing the necessary information. This is done by providing a routine
Packit fcad23
called init_{name} (where {name} is the name of the module).
Packit fcad23
This routine is theoretically optional, but in practise is required to
Packit fcad23
register this module with the main agent at the very least. This specifies
Packit fcad23
the list of variables being implemented (from the variableN structure)
Packit fcad23
and declare where these fit into the overall MIB tree.
Packit fcad23
Packit fcad23
This is done by using the REGISTER_MIB macro, as follows:
Packit fcad23
Packit fcad23
        REGISTER_MIB( "example",  example_variables, variable2,
Packit fcad23
                        example_variables_oid );
Packit fcad23
Packit fcad23
where "example" is used for identification purposed (and is usually the name
Packit fcad23
being used for the module), example_variables is the structure defining the
Packit fcad23
variables being implemented, variable2 is the type used for this structure,
Packit fcad23
and example_variables_oid is the location of the root.
Packit fcad23
Packit fcad23
In fact, this macro is simply a wrapper round the routine register_mib(),
Packit fcad23
but the details of this can safely be ignored, unless more control over the
Packit fcad23
registration is required.
Packit fcad23
Packit fcad23
One common requirement, particularly on older operating systems or for the
Packit fcad23
more obscure areas of the system, is to be able to read data directly from
Packit fcad23
kernel memory. The preparation for this is typically done here by one or
Packit fcad23
more statements of the form
Packit fcad23
Packit fcad23
        #ifdef {NAME}_SYMBOL
Packit fcad23
        auto_nlist( {NAME}_SYMBOL, 0, 0);
Packit fcad23
        #endif
Packit fcad23
Packit fcad23
where {NAME}_SYMBOL is defined as part of the system-specific configuration,
Packit fcad23
to be the name of the appropriate kernel variable or data structure. (The
Packit fcad23
two 0 values are because the kernel information is simply being primed at
Packit fcad23
this point - this call will be reused later when the actual values are
Packit fcad23
required). Note that this is probably the first thing described so far which
Packit fcad23
isn't provided by mib2c!
Packit fcad23
Packit fcad23
Other possibilities for initialisation may include registering config file
Packit fcad23
directive handlers (which are documented in the read_config(5) man page), and
Packit fcad23
registering the MIB module (either in whole or in part) in the sysOR table.
Packit fcad23
The first of these is covered in the example module, and the second in many
Packit fcad23
of the other modules within the main UCD distribution.
Packit fcad23
Packit fcad23
Variable handling
Packit fcad23
-----------------
Packit fcad23
Packit fcad23
The other obligatory routine is that which actually handles a request for a
Packit fcad23
particular variable instance. This is the routine that appeared in the
Packit fcad23
variableN structure, so while the name is not fixed, it should be the same
Packit fcad23
as was used there.
Packit fcad23
This routine has six parameters, which will be described in turn.
Packit fcad23
Packit fcad23
Four of these parameters are used for passing in information about the
Packit fcad23
request, these being:
Packit fcad23
Packit fcad23
        struct variable *vp;
Packit fcad23
                // The entry in the variableN array from the
Packit fcad23
                //   header file, for the object under consideration.
Packit fcad23
                // Note that the name field of this structure has been
Packit fcad23
                //   completed into a fully qualified OID, by prepending
Packit fcad23
                //   the prefix common to the whole array.
Packit fcad23
        oid *name;      // The OID from the request
Packit fcad23
        int *length;    // The length of this OID
Packit fcad23
        int exact;      // A flag to indicate whether this is an exact
Packit fcad23
                        // request (GET/SET) or an 'inexact' one (GETNEXT)
Packit fcad23
Packit fcad23
Four of the parameters are used to return information about the answer.
Packit fcad23
The function also returns a pointer to the actual data for the variable
Packit fcad23
requested (or NULL if this data is not available for any reason).
Packit fcad23
The other result parameters are:
Packit fcad23
Packit fcad23
        oid *name;      // The OID being returned
Packit fcad23
        int *length;    // The length of this OID
Packit fcad23
        int *var_len;   // The length of the answer being returned
Packit fcad23
        WriteMethod **write_method;
Packit fcad23
                        // A pointer to the SET function for this variable
Packit fcad23
Packit fcad23
Note that two of the parameters (name and length) serve a dual purpose,
Packit fcad23
being used for both input and output.
Packit fcad23
Packit fcad23
The first thing that this routine needs to do is to validate the request, to
Packit fcad23
ensure that it does indeed lie in the range implemented by this particular
Packit fcad23
module. This is done in slightly different ways, depending on the style of
Packit fcad23
the module, so this will be discussed in more detail later.
Packit fcad23
  At the same time, it is common to retrieve some of the information needed
Packit fcad23
for answering the query.
Packit fcad23
Packit fcad23
Then the routine uses the Magic Number field from the vp parameter to determine
Packit fcad23
which of the possible variables being implemented is being requested. This is
Packit fcad23
done using a switch statement, which should have as many cases as there are
Packit fcad23
entries in the variableN array (or more precisely, as many as specify this
Packit fcad23
routine as their handler), plus an additional default case to handle an
Packit fcad23
erroneous call.
Packit fcad23
Each branch of the switch statement needs to ensure that the return
Packit fcad23
parameters are filled in correctly, set up a (static) return variable with
Packit fcad23
the correct data, and then return a pointer to this value. These can be done
Packit fcad23
separately for each branch, or once at the start, being overridden in
Packit fcad23
particular branches if necessary.
Packit fcad23
Packit fcad23
In fact, the default validation routines make the assumption that the
Packit fcad23
variable is both read-only, and of integer type (which includes the COUNTER
Packit fcad23
and GAUGE types among others), and set the return paramaters write_method and
Packit fcad23
var_len appropriately. These settings can then be corrected for those cases
Packit fcad23
when either or both of these assumptions are wrong. Examples of this can be
Packit fcad23
seen in the example module.
Packit fcad23
EXAMPLEINTEGER is writeable, so this branch sets the write_method parameter,
Packit fcad23
and EXAMPLEOBJECTID is not an integer, so this branch sets the var_len
Packit fcad23
parameter.  In the case of EXAMPLESTRING, both assumptions are wrong, so this
Packit fcad23
branch needs to set both these parameters explicitly.
Packit fcad23
Packit fcad23
Note that because the routine returns a pointer to a static result, a
Packit fcad23
suitable variable must be declared somewhere for this. Two global variables
Packit fcad23
are provided for this purpose - long_return (for integer results) and
Packit fcad23
return_buf (for other types). This latter is a generic array (of type
Packit fcad23
u_char) that can contain up to 256 bytes of data. Alternatively, static
Packit fcad23
variables can be declared, either within the code file, or local to this
Packit fcad23
particular variable routine. This last is the approach adopted by mib2c,
Packit fcad23
which defines four such local variables, (long_ret, string, objid and c64).
Packit fcad23
Packit fcad23
Mib2c requirements
Packit fcad23
------------------
Packit fcad23
Packit fcad23
Most of the code described here is generated by mib2c. The main exceptions
Packit fcad23
(which therefore need to be provided by the programmer) are
Packit fcad23
Packit fcad23
   * Any initialisation, other than the basic registration
Packit fcad23
     (including kernel data initialisation, config file handling, or sysOR
Packit fcad23
     registration).
Packit fcad23
   * Retrieving the necessary data, and setting the appropriate return
Packit fcad23
     value correctly.
Packit fcad23
   * The var_len (and possibly write_method) return parameters for variable
Packit fcad23
     types that are not recognised by mib2c
Packit fcad23
   * The contents of any write routines (see later).
Packit fcad23
Packit fcad23
Everything else should be useable as generated.
Packit fcad23
Packit fcad23
This concludes the preliminary walk-through of the general structure of the
Packit fcad23
C implementation. To fill in the details, we will need to consider the
Packit fcad23
various styles of module separately. The next part will look at scalar (i.e.
Packit fcad23
non-table based) modules.
Packit fcad23
Packit fcad23
5. Non-table-based modules
Packit fcad23
==========================
Packit fcad23
Packit fcad23
Having looked at the general structure of a module implementation, it's now
Packit fcad23
time to look at this in more detail. We'll start with the simplest style of
Packit fcad23
module - a collection of independent variables. This could easily be
Packit fcad23
implemented as a series of completely separate modules - the main reason for
Packit fcad23
combining them is to avoid the proliferation of multiple versions of very
Packit fcad23
similar code.
Packit fcad23
Packit fcad23
Recall that the variable handling routine needs to cover two distinct
Packit fcad23
purposes - validation of the request, and provision of the answer. In this
Packit fcad23
style of module, these are handled separately. Once again, mib2c does much
Packit fcad23
of the donkey work, generating the whole of the request validation code (so
Packit fcad23
the description of this section can be skipped if desired), and even
Packit fcad23
providing a skeleton for returning the data. This latter still requires some
Packit fcad23
input from the programmer, to actually return the correct results (rather
Packit fcad23
than dummy values).
Packit fcad23
Packit fcad23
Request Validation
Packit fcad23
------------------
Packit fcad23
Packit fcad23
This is done using a standard utility function header_generic. The
Packit fcad23
parameters for this are exactly the same as for the main routine, and are
Packit fcad23
simply passed through directly. It returns an integer result, as a flag to
Packit fcad23
indicate whether the validation succeeded or not.
Packit fcad23
If the validation fails, then the main routine should return immediately,
Packit fcad23
leaving the parameters untouched, and indicate the failure by returning a
Packit fcad23
NULL value. Thus the initial code fragment of a scalar-variable style
Packit fcad23
implementation will typically look like:
Packit fcad23
Packit fcad23
    u_char  *
Packit fcad23
    var_system(vp, name, length, exact, var_len, write_method)
Packit fcad23
    {
Packit fcad23
        if (header_generic(vp, name, length, exact, var_len, write_method)
Packit fcad23
                                                == MATCH_FAILED )
Packit fcad23
            return NULL;
Packit fcad23
Packit fcad23
        [ etc, etc, etc ]
Packit fcad23
    }
Packit fcad23
Packit fcad23
Although the utility function can be used as a "black box", it's worth
Packit fcad23
looking more closely at exactly what it does (since the table-handling
Packit fcad23
modules will need to do something fairly similar). It has two (or possibly
Packit fcad23
three) separate functions:
Packit fcad23
Packit fcad23
   * checking that the request is valid,
Packit fcad23
   * setting up the OID for the result,
Packit fcad23
   * and (optionally) setting up default values for the other return
Packit fcad23
     parameters.
Packit fcad23
Packit fcad23
In order to actually validate the request, the header routine first needs to
Packit fcad23
construct the OID under consideration, in order to compare it with that
Packit fcad23
originally asked for. The driving code has already combined the OID prefix
Packit fcad23
(constant throughout the module) with the entry-specific suffix, before
Packit fcad23
calling the main variable handler. This is available via the name field of
Packit fcad23
the parameter vp. For a scalar variable, completing the OID is therefore
Packit fcad23
simply a matter of appending the instance identifier 0 to this. The full OID
Packit fcad23
is built up in a local oid array newname defined for this purpose.
Packit fcad23
This gives the following code fragment:
Packit fcad23
Packit fcad23
    int
Packit fcad23
    header_generic(vp, name, length, exact, var_len, write_method)
Packit fcad23
    {
Packit fcad23
        oid newname[MAX_OID_LEN];
Packit fcad23
Packit fcad23
        memcpy((char *)newname, (char *)vp->name,
Packit fcad23
                        (int)vp->namelen * sizeof(oid));
Packit fcad23
        newname[ vp->namelen ] = 0;
Packit fcad23
Packit fcad23
                :
Packit fcad23
    }
Packit fcad23
Packit fcad23
Having formed the OID, this can then be compared against the variable
Packit fcad23
specified in the original request, which is available as the name parameter.
Packit fcad23
This comparison is done using the snmp_oid_compare function, which takes the
Packit fcad23
two OIDs (together with their respective lengths), and returns -1, 0 or 1
Packit fcad23
depending on whether the first OID precedes, matches or follows the second.
Packit fcad23
Packit fcad23
In the case of an 'exact' match (i.e. a GET/SET/etc), then the request is
Packit fcad23
only valid if the two OIDs are identical (snmp_oid_compare returns 0). In
Packit fcad23
the case of a GETNEXT (or GETBULK) request, it's valid if the OID being
Packit fcad23
considered comes after that of the original request (snmp_oid_compare
Packit fcad23
returns -1).
Packit fcad23
Packit fcad23
This gives the code fragment
Packit fcad23
Packit fcad23
        result = snmp_oid_compare(name, *length, newname, (int)vp->namelen + 1);
Packit fcad23
                        // +1 because of the extra instance sub-identifier
Packit fcad23
        if ((exact && (result != 0))            // GET match fails
Packit fcad23
                || (!exact && (result >= 0)))   // GETNEXT match fails
Packit fcad23
            return(MATCH_FAILED);
Packit fcad23
Packit fcad23
Note that in this case, we're only interested in the single variable
Packit fcad23
indicated by the vp parameter. The fact that this module may well implement
Packit fcad23
other variables as well is ignored. The 'lexically next' requirement of the
Packit fcad23
GETNEXT request is handled by working through the variable entries in order
Packit fcad23
until one matches. And yes, this is not the most efficient implementation
Packit fcad23
possible!
Packit fcad23
Note that in releases prior to 3.6, the snmp_oid_compare function was called
Packit fcad23
simply compare.
Packit fcad23
Packit fcad23
Finally, having determined that the request is valid, this routine must
Packit fcad23
update the name and length parameters to return the OID being processed. It
Packit fcad23
also sets default values for the other two return parameters.
Packit fcad23
Packit fcad23
        memcpy( (char *)name,(char *)newname,
Packit fcad23
                ((int)vp->namelen + 1) * sizeof(oid));
Packit fcad23
        *length = vp->namelen + 1;
Packit fcad23
        *write_method = 0;              // Non-writeable
Packit fcad23
        *var_len = sizeof(long);        // default to integer results
Packit fcad23
        return(MATCH_SUCCEEDED);
Packit fcad23
Packit fcad23
These three code fragments combine to form the full header_generic code
Packit fcad23
which can be seen in the file util_funcs.c
Packit fcad23
Packit fcad23
Note: This validation used to be done using a separate function for each
Packit fcad23
module (conventionally called header_{name}), and many modules may still be
Packit fcad23
coded in this style. The code for these are to all intents and purposes
Packit fcad23
identical to the header_generic routine described above.
Packit fcad23
Packit fcad23
Data Retrieval
Packit fcad23
--------------
Packit fcad23
Packit fcad23
The other main job of the request handling routine is to retrieve any
Packit fcad23
necessary data, and return the appropriate answer to the original request.
Packit fcad23
This must be done even if mib2c is being used to generate the framework of
Packit fcad23
the implementation. As has been indicated earlier, the different cases are
Packit fcad23
handled using a switch statement, with the Magic Number field of the vp
Packit fcad23
parameter being used to distinguish between them.
Packit fcad23
The data necessary for answering the request can be retrieved for each
Packit fcad23
variable individually in the relevant case statement (as is the case with
Packit fcad23
the system group), or using a common block of data before processing the
Packit fcad23
switch (as is done for the ICMP group, among others).
Packit fcad23
Packit fcad23
With many of the modules implemented so far, this data is read from a kernel
Packit fcad23
structure. This can be done using the auto_nlist routine already mentioned,
Packit fcad23
providing a variable in which to store the results and an indication of its
Packit fcad23
size (see the !HAVE_SYS_TCPIPSTATS_H case of the ICMP group for an example).
Packit fcad23
Alternatively, there may be ioctl calls on suitable devices, specific system
Packit fcad23
calls, or special files that can be read to provide the necessary
Packit fcad23
information.
Packit fcad23
Packit fcad23
If the available data provides the requested value immediately, then the
Packit fcad23
individual branch becomes a simple assignment to the appropriate static
Packit fcad23
return variable - either one of the global static variables (e.g. long_return)
Packit fcad23
or the local equivalents (such as generated by mib2c).
Packit fcad23
Otherwise, the requested value may need to be calculated by combining two or
Packit fcad23
more items of data (e.g. IPINHDRERRORS in mibII/ip.c) or by applying a
Packit fcad23
mapping or other calculation involving available information (e.g.
Packit fcad23
IPFORWARDING from the same group).
Packit fcad23
Packit fcad23
In each of these cases, the routine should return a pointer to the result
Packit fcad23
value, casting this to the pseudo-generic (u_char *)
Packit fcad23
Packit fcad23
So much for the scalar case. The next part looks at how to handle simple
Packit fcad23
tables.
Packit fcad23
Packit fcad23
6. Simple tables
Packit fcad23
================
Packit fcad23
Packit fcad23
Having considered the simplest style of module implementation, we now turn
Packit fcad23
our attention to the next style - a simple table. The tabular nature of
Packit fcad23
these is immediately apparent from the MIB definition file, but the
Packit fcad23
qualifier "simple" deserves a word of explanation.
Packit fcad23
A simple table, in this context, has four characteristics:
Packit fcad23
Packit fcad23
  1. It is indexed by a single integer value;
Packit fcad23
  2. Such indices run from 1 to a determinable maximum;
Packit fcad23
  3. All indices within this range are valid;
Packit fcad23
  4. The data for a particular index can be retrieved directly
Packit fcad23
     (e.g. by indexing into an underlying data structure).
Packit fcad23
Packit fcad23
If any of the conditions are not met, then the table is not a pure simple
Packit fcad23
one, and the techniques described here are not applicable. The next section
Packit fcad23
of this guide will cover the more general case. (In fact, it may be possible
Packit fcad23
to use the bulk of the techniques covered here, though special handling will
Packit fcad23
be needed to cope with the invalid assumption or assumptions). Note that
Packit fcad23
mib2c assumes that all tables are simple.
Packit fcad23
Packit fcad23
As with the scalar case, the variable routine needs to provide two basic
Packit fcad23
functions - request validation and data retrieval.
Packit fcad23
Packit fcad23
Validation
Packit fcad23
----------
Packit fcad23
Packit fcad23
This is provided by the shared utility routine header_simple_table. As with
Packit fcad23
the scalar header routine, this takes the same parameters as the main
Packit fcad23
variable routine, with one addition - the maximum valid index. Mib2c
Packit fcad23
generates a dummy token for this, which must be replaced by the appropriate
Packit fcad23
value.
Packit fcad23
As with the header routine, it also returns an indication of whether the
Packit fcad23
request was valid, as well as setting up the return parameters with the
Packit fcad23
matching OID information, and defaults for var_len and write_method.
Packit fcad23
Note that in releases prior to 3.6, this job was performed by the routine
Packit fcad23
checkmib. However, the return values of this were the reverse of those for
Packit fcad23
generic_header and header_simple_table. A version of checkmib is still
Packit fcad23
available for compatability purposes, but you are encouraged to use
Packit fcad23
header_simple_table instead.
Packit fcad23
Packit fcad23
The basic code fragment (see ucd-snmp/disk.c) is therefore of the form:
Packit fcad23
Packit fcad23
        unsigned char *
Packit fcad23
        var_extensible_disk(vp, name, length, exact, var_len, write_method)
Packit fcad23
        {
Packit fcad23
            if (header_simple_table(vp,name,length,exact,var_len,write_method,numdisks)
Packit fcad23
                                        == MATCH_FAILED)
Packit fcad23
                return(NULL);
Packit fcad23
Packit fcad23
            [ etc, etc, etc ]
Packit fcad23
Packit fcad23
        }
Packit fcad23
Packit fcad23
Note that the maximum index value parameter does not have to be a
Packit fcad23
permanently fixed constant. It specifies the maximum valid index at the time
Packit fcad23
the request is processed, and a subsequent request may have a different
Packit fcad23
maximum.
Packit fcad23
An example of this can be seen in mibII/sysORTable.c where the table is held
Packit fcad23
purely internally to the agent code, including its size (and hence the
Packit fcad23
maximum valid index). This maximum could also be retrieved via a system
Packit fcad23
call, or via a kernel data variable.
Packit fcad23
Packit fcad23
Data Retrieval
Packit fcad23
--------------
Packit fcad23
Packit fcad23
As with the scalar case, the other required function is to retrieve the data
Packit fcad23
requested. However, given the definition of a simple table this is simply a
Packit fcad23
matter of using the single, integer index sub-identifier to index into an
Packit fcad23
existing data structure. This index will always be the last index of the OID
Packit fcad23
returned by header_simple_table, so can be obtained as name[*length-1].
Packit fcad23
A good example of this type of table can be seen in ucd-snmp/disk.c
Packit fcad23
Packit fcad23
With some modules, this underlying table may be relatively large, or only
Packit fcad23
accessible via a slow or cumbersome interface. The implementation described
Packit fcad23
so far may prove unacceptably slow, particularly when walking a MIB tree
Packit fcad23
requires the table to be loaded afresh for each variable requested.
Packit fcad23
Packit fcad23
In these circumstances, a useful technique is to cache the table when it is
Packit fcad23
first read in, and use that cache for subsequent requests. This can be done
Packit fcad23
by having a separate routine to read in the table. This uses two static
Packit fcad23
variables, one a structure or array for the data itself, and the other an
Packit fcad23
additional timestamp to indicate when the table was last loaded. When a call
Packit fcad23
is made to this routine to "read" the table, it can first check whether the
Packit fcad23
cached table is "new enough". If so, it can return immediately, and the
Packit fcad23
system will use the cached data.
Packit fcad23
Only if the cached version is sufficiently old that it's probably out of
Packit fcad23
date, is it necessary to retrieve the current data, updating the cached
Packit fcad23
version and the timestamp value.
Packit fcad23
This is particularly useful if the data itself is relatively static, such as
Packit fcad23
a list of mounted filesystems. There is an example of this technique in the
Packit fcad23
Host Resources implementation.
Packit fcad23
Packit fcad23
As with the scalar case, mib2c simply provides placeholder dummy return
Packit fcad23
values. It's up to the programmer to fill in the details.
Packit fcad23
Packit fcad23
The next part concludes the examination of the detailed implementation by
Packit fcad23
looking at more general tables.
Packit fcad23
Packit fcad23
7. General Tables
Packit fcad23
=================
Packit fcad23
Packit fcad23
Some table structures are not suitable for the simple table approach, due to
Packit fcad23
the failure of one or more of the assumptions listed earlier. Perhaps they
Packit fcad23
are indexed by something other than a single integer (such as a 4-octet IP
Packit fcad23
address), or the maximum index is not easily determinable (such as the
Packit fcad23
interfaces table), or not all indices are valid (running software), or the
Packit fcad23
necessary data is not directly accessible (interfaces again).
Packit fcad23
In such circumstances, a more general approach is needed. In contrast with
Packit fcad23
the two styles already covered, this style of module will commonly combine
Packit fcad23
the two functions of request validation and data retrieval. Note that mib2c
Packit fcad23
will assume the simple table case, and this will need to be corrected.
Packit fcad23
Packit fcad23
General table algorithm
Packit fcad23
-----------------------
Packit fcad23
Packit fcad23
The basic algorithm is as follows:
Packit fcad23
Packit fcad23
     Perform any necessary initialization, then walk through the
Packit fcad23
     underlying instances, retrieving the data for each one, until the
Packit fcad23
     desired instance is found. If no valid entry is found, return
Packit fcad23
     failure.
Packit fcad23
Packit fcad23
For an exact match (GET and similar), identifying the desired instance is
Packit fcad23
trivial - construct the OID (from the 'vp' variable parameter and the index
Packit fcad23
value or values), and see whether it matches the requested OID.
Packit fcad23
For GETNEXT, the situation is not quite so simple. Depending on the
Packit fcad23
underlying representation of the data, the entries may be returned in the
Packit fcad23
same order as they should appear in the table (i.e. lexically increasing by
Packit fcad23
index). However, this is not guaranteed, and the natural way of retrieving
Packit fcad23
the data may be in some "random" order. In this case, then the whole table
Packit fcad23
needs to be traversed for each request. in order to determine the
Packit fcad23
appropriate successor.
Packit fcad23
This random order is the worst case, and dictates the structure of the code
Packit fcad23
used in most currently implemented tables. The ordered case can be regarded
Packit fcad23
as a simplification of this more general one.
Packit fcad23
Packit fcad23
The algorithm outlined above can now be expanded into the following
Packit fcad23
pseudo-code:
Packit fcad23
Packit fcad23
        Init_{Name}_Entry();    // Perform any necessary initialisation
Packit fcad23
Packit fcad23
        while (( index = Get_Next_{Name}_Entry() ) != EndMarker ) {
Packit fcad23
                        // This steps through the underlying table,
Packit fcad23
                        //   returning the current index,
Packit fcad23
                        //   or some suitable end-marker when all
Packit fcad23
                        //   the entries have been examined.
Packit fcad23
                        // Note that this routine should also return the
Packit fcad23
                        //   data for this entry, either via a parameter
Packit fcad23
                        //   or using some external location.
Packit fcad23
Packit fcad23
            construct OID from vp->name and index
Packit fcad23
            compare new OID and request
Packit fcad23
            if valid {
Packit fcad23
                save current data
Packit fcad23
                if finished     // exact match, or ordered table
Packit fcad23
                    break;      //  so don't look at any more entries
Packit fcad23
Packit fcad23
            }
Packit fcad23
Packit fcad23
                //  Otherwise, we need to loop round, and examine
Packit fcad23
                //    the next entry in the table.  Either because
Packit fcad23
                //    the entry wasn't valid for this request,
Packit fcad23
                //    or the entry was a possible "next" candidate,
Packit fcad23
                //    but we don't know that there isn't there's a
Packit fcad23
                //    better one later in the table.
Packit fcad23
        }
Packit fcad23
Packit fcad23
        if no saved data        // Nothing matched
Packit fcad23
           return failure
Packit fcad23
Packit fcad23
                // Otherwise, go on to the switch handling
Packit fcad23
                //  we've already covered in the earlier styles.
Packit fcad23
Packit fcad23
This is now very close to the actual code used in many current
Packit fcad23
implementations (such as the the routine header_ifEntry in
Packit fcad23
mibII/interfaces.c). Notice that the pseudo-code fragment if valid expands
Packit fcad23
in practise to
Packit fcad23
Packit fcad23
        if ((exact && (result == 0))  ||
Packit fcad23
                        // GET request, and identical OIDs
Packit fcad23
            (!exact && (result < 0)) )
Packit fcad23
                        // GETNEXT, and candidate OID is later
Packit fcad23
                        //  than requested OID.
Packit fcad23
Packit fcad23
This is a very common expression, that can be seen in most of the table
Packit fcad23
implementations.
Packit fcad23
Packit fcad23
Notice also that the interfaces table returns immediately the first valid
Packit fcad23
entry is found, even for GETNEXT requests. This is because entries are
Packit fcad23
returned in lexical order, so the first succeeding entry will be the one
Packit fcad23
that's required.
Packit fcad23
(As an aside, this also means that the underlying data can be saved
Packit fcad23
implicitly within the 'next entry' routine - not very clean, but it saves
Packit fcad23
some unnecessary copying).
Packit fcad23
Packit fcad23
The more general case can be seen in the TCP and UDP tables (see mibII/tcp.c
Packit fcad23
and mibII/udp.c). Here, the if valid fragment expands to:
Packit fcad23
Packit fcad23
        if ( exact && (result == 0)) {
Packit fcad23
                // save results
Packit fcad23
                break;
Packit fcad23
        }
Packit fcad23
        else if (!exact && (result < 0)) {
Packit fcad23
            if ( .... ) {       // no saved OID, or this OID
Packit fcad23
                                //  precedes the saved OID
Packit fcad23
                // save this OID into 'lowest'
Packit fcad23
                // save the results into Lowinpcb
Packit fcad23
                // don't break, since we still need to look
Packit fcad23
                //      at the rest of the table
Packit fcad23
            }
Packit fcad23
        }
Packit fcad23
Packit fcad23
The GET match handling is just as we've already seen - is this the requested
Packit fcad23
OID or not. If so, save the results and move on to the switch statement.
Packit fcad23
  The GETNEXT case is more complicated. As well as considering whether this
Packit fcad23
is a possible match (using the same test we've already seen), we also have to
Packit fcad23
check whether this is a better match than anything we've already seen. This
Packit fcad23
is done by comparing the current candidate (newname) with the best match found
Packit fcad23
so far (lowest).
Packit fcad23
  Only if this extra comparison shows that the new OID is earlier than the
Packit fcad23
saved one, do we need to save both the new OID, and any associated data
Packit fcad23
(such as the inpcb block, and state flag). But having found one better
Packit fcad23
match, we don't know that there isn't an even better one later on. So we
Packit fcad23
can't break out of the enclosing loop - we need to keep going and examine
Packit fcad23
all the remaining entries of the table.
Packit fcad23
Packit fcad23
These two cases (the TCP and UDP tables) also show a more general style of
Packit fcad23
indexing. Rather than simply appending a single index value to the OID
Packit fcad23
prefix, these routines have to add the local four-octet IP address plus port
Packit fcad23
(and the same for the remote end in the case of the TCP table). This is the
Packit fcad23
purpose of the op and cp section of code that precedes the comparison.
Packit fcad23
Packit fcad23
These two are probably among the most complex cases you are likely to
Packit fcad23
encounter. If you can follow the code here, then you've probably cracked the
Packit fcad23
problem of understanding how the agent works.
Packit fcad23
Packit fcad23
Finally, the next part discusses how to implement a writable (or SETable)
Packit fcad23
object in a MIB module.
Packit fcad23
Packit fcad23
8. How to implement a SETable object
Packit fcad23
====================================
Packit fcad23
Packit fcad23
Finally, the only remaining area to cover is that of setting data - the
Packit fcad23
handling of SNMPSET. Particular care should be taken here for two reasons.
Packit fcad23
Packit fcad23
Firstly, any errors in the earlier sections can have limited effect. The
Packit fcad23
worst that is likely to happen is that the agent will either return invalid
Packit fcad23
information, or possibly crash. Either way, this is unlikely to affect the
Packit fcad23
operation of the workstation as a whole. If there are problems in the
Packit fcad23
writing routine, the results could be catastrophic (particularly if writing
Packit fcad23
data directly into kernel memory).
Packit fcad23
Packit fcad23
Secondly, this is the least well understood area of the agent, at least by
Packit fcad23
the author. There are relatively few variables that are defined as READ-WRITE
Packit fcad23
in the relevant MIBs, and even fewer that have actually been implemented as
Packit fcad23
such. I'm therefore describing this from a combination of my understanding
Packit fcad23
of how SETs ought to work, personal experience of very simple SET handling
Packit fcad23
and what's actually been done by others (which do not necessarily coincide).
Packit fcad23
Packit fcad23
There are also subtle differences between the setting of simple scalar
Packit fcad23
variables (or individual entries within a table), and the creation of a new
Packit fcad23
row within a table. This will therefore be considered separately.
Packit fcad23
Packit fcad23
With these caveats, and a healthy dose of caution, let us proceed. Note that
Packit fcad23
the UCD-SNMP development team can accept no responsibility for any damage or
Packit fcad23
loss resulting from either following or ignoring the information presented
Packit fcad23
here. You coded it - you fix it!
Packit fcad23
Packit fcad23
Write routine
Packit fcad23
-------------
Packit fcad23
Packit fcad23
The heart of SET handling is the write_method parameter from the variable
Packit fcad23
handling routine. This is a pointer to the relevant routine for setting the
Packit fcad23
variable in question. Mib2c will generate one such routine for each setable
Packit fcad23
variable. This routine should be declared using the template
Packit fcad23
Packit fcad23
        int
Packit fcad23
        write_variable(
Packit fcad23
           int      action,
Packit fcad23
           u_char   *var_val,
Packit fcad23
           u_char   var_val_type,
Packit fcad23
           int      var_val_len,
Packit fcad23
           u_char   *statP,
Packit fcad23
           oid      *name,
Packit fcad23
           int      name_len );
Packit fcad23
Packit fcad23
Most of these parameters are fairly self explanatory:
Packit fcad23
The last two hold the OID to be set, just as was passed to the main variable
Packit fcad23
routine.
Packit fcad23
Packit fcad23
The second, third and fourth parameters provide information about the new
Packit fcad23
desired value, both the type, value and length. This is very similar to the
Packit fcad23
way that results are returned from the main variable routine.
Packit fcad23
Packit fcad23
The return value of the routine is simply an indication of whether the
Packit fcad23
current stage of the SET was successful or not. We'll come back to this in a
Packit fcad23
minute. Note that it is the responsibility of this routine to check that the
Packit fcad23
OID and value provided are appropriate for the variable being implemented.
Packit fcad23
This includes (but is not limited to) checking:
Packit fcad23
Packit fcad23
   * the OID is recognised as one this routine can handle
Packit fcad23
     (this should be true if the routine only handles the one variable, and
Packit fcad23
     there are no errors in the main variable routine or driving code, but
Packit fcad23
     it does no harm to check).
Packit fcad23
   * the value requested is the correct type expected for this OID
Packit fcad23
   * the value requested is appropriate for this OID
Packit fcad23
     (within particular ranges, suitable length, etc, etc)
Packit fcad23
Packit fcad23
There are two parameters remaining to be considered.
Packit fcad23
Packit fcad23
The fifth parameter, statP, is the value that would be returned from a GET
Packit fcad23
request on this particular variable. It could be used to check that the
Packit fcad23
requested new value is consistent with the current state, but its main use
Packit fcad23
is to denote that a new table row is being created.
Packit fcad23
In most cases (particularly when dealing with scalar values or single elements
Packit fcad23
of tables), you can normally simply ignore this parameter.
Packit fcad23
Packit fcad23
Actions
Packit fcad23
-------
Packit fcad23
Packit fcad23
The final parameter to consider is the first one - action. To understand
Packit fcad23
this, it's necessary to know a bit about how SETs are implemented.
Packit fcad23
The design of SNMP calls for all variables in a SET request to be done "as
Packit fcad23
if simultaneously" - i.e. they should all succeed or all fail. However, in
Packit fcad23
practise, the variables are handled in succession. Thus, if one fails, it
Packit fcad23
must be possible to "undo" any changes made to the other variables in the
Packit fcad23
request.
Packit fcad23
This is a well understood requirement in the database world, and is usually
Packit fcad23
implemented using a "multi-stage commit". This is certainly the mechanism
Packit fcad23
expected within the SNMP community (and has been made explicit in the work
Packit fcad23
of the AgentX extensibility group). In other words, the routine to handle
Packit fcad23
setting a variable will be called more than once, and the routine must be
Packit fcad23
able to perform the appropriate actions depending on how far through the
Packit fcad23
process we currently are. This is determined by the value of the action
Packit fcad23
parameter.
Packit fcad23
Packit fcad23
This is implemented using three basic phases:
Packit fcad23
Packit fcad23
RESERVE is used to check the syntax of all the variables provided, that the
Packit fcad23
values being set are sensible and consistent, and to allocate any resources
Packit fcad23
required for performing the SET. After this stage, the expectation is that
Packit fcad23
the set ought to succeed, though this is not guaranteed.
Packit fcad23
(In fact, with the UCD agent, this is done in two passes - RESERVE1, and
Packit fcad23
RESERVE2, to allow for dependancies between variables).
Packit fcad23
Packit fcad23
If any of these calls fail (in either pass) the write routines are called
Packit fcad23
again with the FREE action, to release any resources that have been
Packit fcad23
allocated. The agent will then return a failure response to the requesting
Packit fcad23
application.
Packit fcad23
Packit fcad23
Assuming that the RESERVE phase was successful, the next stage is indicated
Packit fcad23
by the action value ACTION. This is used to actually implement the set
Packit fcad23
operation. However, this must either be done into temporary (persistent)
Packit fcad23
storage, or the previous value stored similarly, in case any of the
Packit fcad23
subsequent ACTION calls fail.
Packit fcad23
 This can be seen in the example module, where both write routines have
Packit fcad23
static 'old' variables, to hold the previous value of the relevant object.
Packit fcad23
Packit fcad23
If the ACTION phase does fail (for example due to an apparently valid, but
Packit fcad23
unacceptable value, or an unforeseen problem), then the list of write
Packit fcad23
routines are called again, with the UNDO action. This requires the routine
Packit fcad23
to reset the value that was changed to its previous value (assuming it was
Packit fcad23
actually changed), and then to release any resources that had been
Packit fcad23
allocated. As with the FREE phase, the agent will then return an indication
Packit fcad23
of the error to the requesting application.
Packit fcad23
Packit fcad23
Only once the ACTION phase has completed successfully, can the final COMMIT
Packit fcad23
phase be run. This is used to complete any writes that were done into
Packit fcad23
temporary storage, and then release any allocated resources. Note that all
Packit fcad23
the code in this phase should be "safe" code that cannot possibly fail (cue
Packit fcad23
hysterical laughter). The whole intent of the ACTION/COMMIT division is that
Packit fcad23
all of the fallible code should be done in the ACTION phase, so that it can
Packit fcad23
be backed out if necessary.
Packit fcad23
Packit fcad23
Table row creation
Packit fcad23
------------------
Packit fcad23
Packit fcad23
What about creating new rows in a table, I hear you ask. Good Question.
Packit fcad23
This case can often be detected by the fact that a GET request would have
Packit fcad23
failed, and hence the fifth parameter, statP, will be null.  This contrasts
Packit fcad23
with changing the values of an element of an existing row, when the statP
Packit fcad23
parameter would hold the previous value.
Packit fcad23
Packit fcad23
The details of precisely how to create a new row will clearly depend on the
Packit fcad23
underlying format of the table.  However, one implementation strategy would
Packit fcad23
be as follows:
Packit fcad23
Packit fcad23
  *  The first column object to be SET would return a null value from the
Packit fcad23
	var_name routine.  This null statP parameter would be the signal
Packit fcad23
	to create a new temporary instance of the underlying data structure,
Packit fcad23
	filled with dummy values.
Packit fcad23
  *  Subsequent column objects would return pointers to the appropriate
Packit fcad23
	field of this new data structure from the var_name routine,
Packit fcad23
	which would then be filled in by the write routine.
Packit fcad23
  *  Once all the necessary fields had been SET, the completed temporary
Packit fcad23
	instance could be moved into the "standard" structure (or copied,
Packit fcad23
	or otherwise used to set things up appropriately).
Packit fcad23
Packit fcad23
However, this is purely a theoretical strategy, and has not been tried
Packit fcad23
by the author.  No guarantees are given as to whether this would actually
Packit fcad23
work.  There are also questions regarding how to handle incomplete
Packit fcad23
or overlapping SET requests.
Packit fcad23
Anyone who has experience of doing this, please get in touch!
Packit fcad23
Packit fcad23
  ------------------------------------------------------------------------
Packit fcad23
And that's it. Congratulations for getting this far. If you understand
Packit fcad23
everything that's been said, then you now know as much as the rest of us
Packit fcad23
about the inner workings of the UCD-SNMP agent. (Well, very nearly).
Packit fcad23
All that remains is to try putting this into practise. Good luck!
Packit fcad23
Packit fcad23
And if you've found this helpful, gifts of money, chocolate, alcohol, and
Packit fcad23
above all feedback, would be most appreciated :-)
Packit fcad23
Packit fcad23
  ------------------------------------------------------------------------
Packit fcad23
Copyright 1999, 2000 - D.T.Shield.
Packit fcad23
This file may be distributed as part of a source or binary packaging
Packit fcad23
of the Net-SNMP software suite.  It may not be distributed independently
Packit fcad23
without the explicit permission of the author.