AGENT.txt
上传用户:wxp200602
上传日期:2007-10-30
资源大小:4028k
文件大小:55k
源码类别:

SNMP编程

开发平台:

Unix_Linux

  1. Note, this is based on the text from a web page, which can be found in 
  2. the documentation section of the http://www.net-snmp.org web page.
  3. Extending the UCD-SNMP agent
  4. ============================
  5. This document describes the procedure for writing code to extend
  6. the functionality of the v4 UCD-SNMP network management agent.
  7. Modules written using this procedure should also work with the v5
  8. Net-SNMP agent, though such modules would not take advantage of the
  9. new handler-based helper mechanism.  See the on-line documentation
  10. for more information and examples of the newer approach.
  11. We would be very interested in comment and feedback about how useful
  12. (or otherwise) you find this description, and ways in which it could
  13. be improved.
  14. The information is designed to be read in order - the structure being:
  15.   1. Overview & Introduction
  16.   2. MIB files, and how they relate to the agent implementation
  17.   3. Header files
  18.   4. The basic structure of module implementation code
  19.   5. The details of non-table based implementations
  20.   6. The details of simple table based implementations
  21.   7. The details of more general table based implementations
  22.   8. How to implement SET-able variables
  23. While the document is intended to be generally self-contained,
  24. it does occasionally refer to code files shipped with the main UCD
  25. distribution (in particular the example module), and it may prove
  26. useful to have these files available for reference.
  27. 1. How to write a Mib module
  28. ============================
  29. Introduction
  30. ------------
  31. The design of the UCD SNMP agent has always been shaped by the desire to be
  32. able to extend its functionality by adding new modules. One of the earliest
  33. developments from the underlying CMU code base was the ability to call
  34. external scripts, and this is probably the simplest method of extending the
  35. agent.
  36. However, there are circumstances where such an approach is felt to be
  37. inappropriate - perhaps from considerations of speed, access to the
  38. necessary data, reliability or elegance. In such cases, the obvious solution
  39. is to provide C code that can be compiled into the agent itself to implement
  40. the desired module. Many of the more recent developments in the code
  41. structure have been intended to ease this process. In particular, one of the
  42. more recent additions to the suite is the tool mib2c. This is designed to
  43. take a portion of the MIB tree (as defined by a MIB file) and generate the
  44. code skeleton necessary to implement this. This document will cover the use
  45. mib2c, as well as describing the requirements and functionality of the code
  46. in more detail.
  47. In order to implement a new MIB module, three files are necessary, and these
  48. will be considered in turn. Note that, by the very nature of the task, this
  49. document cannot cover the details of precisely how to obtain the necessary
  50. information from the operating system or application. Instead, it describes
  51. the code framework that is needed, freeing the implementer from needing to
  52. understand the detailed internals of the agent, and allowing them to
  53. concentrate on the particular problem in hand.
  54. It may prove useful to examine some of the existing module implementations
  55. and examples in the light of this description, and suitable examples will be
  56. referred to at the appropriate points. However, it should be remembered that
  57. the UCD agent seeks to support a wide variety of systems, often with
  58. dramatically differing implementations and interfaces, and this is reflected
  59. in the complexity of the code. Also, the agent has developed gradually over
  60. the years, and there is often some measure of duplication or redundancy as a
  61. result.
  62. As the FAQ states, the official slogan of the UCD-SNMP developers is
  63.      The current implementation is non-obvious and may need to be
  64.      improved.
  65. This document describes the ideal, straightforward cases - real life is
  66. rarely so simple, and the example modules may prove easier to follow at a
  67. first reading.
  68. It is also advisable to have a compiled and installed implementation
  69. available before starting to extend the agent. This will make debugging and
  70. testing the agent much easier.
  71. A note regarding terminology - the word "module" is widely used throughout
  72. this document, with a number of different meanings.
  73.    * support for a new MIB,
  74.      i.e. the whole of the functionality that is required. This is usually
  75.      termed a MIB module;
  76.    * a self-contained subset of this, implemented as a single unit.
  77.      This is usually termed an implementation module (or simply "a module");
  78.    * the combination of such subsets, usually termed a module group.
  79. Note that the first and third of these are often synonymous - the
  80. difference being that a MIB module refers to the view from outside the
  81. agent, regarding this as a seamless whole and hiding the internal
  82. implementation. A "module group" is used where the internal structure is of
  83. more relevance, and recognises the fact that the functionality may be
  84. provided by a number of co-operating implementation modules.
  85. Anyway, enough waffle - on with the details: The three files needed are
  86.    * a MIB definition file;
  87.    * a C header file;
  88.    * a C implementation file.
  89. The next part looks at the MIB definition file, and how this impacts on the
  90. agent implementation.
  91. 2. The MIB File
  92. ===============
  93. The first file needed is the MIB file that defines the MIB module to be
  94. implemented.
  95. Strictly speaking, this is not absolutely necessary, as the agent itself
  96. does not make any direct use of the MIB definitions. However, it is
  97. advisable to start with this for three reasons:
  98.    * It provides an initial specification for what is to be implemented.
  99.      Code development is always easier if you know what you are meant to be
  100.      writing!
  101.    * If the new MIB file is read in with the other MIB files,
  102.      this lets the applications provided with the suite be used to test the
  103.      new agent, and report (hopefully meaningful) symbolic OIDs and values,
  104.      rather than the bare numeric forms.
  105.      (N.B: Remember to tell the application to load the new MIB. See the
  106.      relevant question in the FAQ)
  107.    * The tool mib2c uses this description to produce the two code files.
  108.      This is by far the easiest way to develop a new module.
  109.      (Note that the v5 version of mib2c is generally similar, but does
  110.      not correspond exactly to the v4 version described here)
  111. If the intention is to implement a 'standard' MIB module, or a
  112. vendor-specific one, then the construction of this file will have already
  113. been done for you. If the intention is to provide a totally new, private
  114. module, then you will need to write this yourself, in addition to the agent
  115. code files.
  116. A description of MIB file format and syntax is beyond the scope of this
  117. document, and most books on SNMP management should provide some information
  118. on this subject. One book which concentrates on this is
  119.      Understanding SNMP MIBS
  120.      (Perkins & McGinnis, Prentice Hall, ISBN 0-13-437708-7).
  121. This blatant plug is wholly unrelated to the fact that David Perkins is an
  122. active member of the development group, and is regarded as our resident
  123. "protocol guru and policeman". (In fact, this book concentrates on MIB
  124. files in rather more detail than is appropriate in more general SNMP works).
  125. Information on other books covering SNMP and Network Management more generally
  126. is available on the SimpleWeb site (among other places).
  127. See the FAQ for more details.
  128. Assigned OID numbers
  129. --------------------
  130. One word of advice - even if you are developing a totally private MIB
  131. module, you will still need to position this somewhere within the overall
  132. MIB tree. Please do NOT simply choose a location "at random". Any such is
  133. likely to have either been assigned to some other organisation, or may be so
  134. assigned some time in the future. However much you may regard your project
  135. as a totally internal affair, such projects have a tendency to exceed their
  136. expected scope, both in terms of lifetime and distribution (not to mention
  137. the potential OID clash if you subsequently need to use elements from the
  138. legitimate owner's tree).
  139. It is simple and cheap (i.e. free!) to obtain your own official segment of
  140. the MIB tree (see http://www.iana.org for an application form), and having
  141. done so, you then have complete global authority over it. If you have
  142. problems with this, it's worth contacting the development team (email:
  143. net-snmp-coders@lists.sourceforge.net) for advice. Please do think to the
  144. future, and be a good Net citizen by using a legitimately assigned OID as
  145. the root of your new MIB.
  146. MIB division
  147. ------------
  148. The next point to consider, whether writing by hand or using mib2c,
  149. implementing an existing MIB, or writing a new one, is whether and how to
  150. divide up the MIB tree. This is a purely internal implementation decision,
  151. and will not be visible to management applications querying the agent. A
  152. sensible choice of partitioning will result in a simpler, clearer
  153. implementation, which should ease both the initial development and
  154. subsequent maintenance of the module.
  155. Unfortunately, this choice is one of the module-specific decisions, so must
  156. be made on a case-by-case basis. For a simple, self-contained module, it may
  157. well be reasonable to implement the module as a single block (examples
  158. include the SNMP statistics subtree RFC 1907 or the TCP subtree RFC 2011).
  159. More complex and diverse modules (such as the Host Resources MIB - RFC 1514)
  160. are more naturally considered as a number of individual sub-modules.
  161. Some guidelines to bear in mind when deciding on this division:
  162.    * A MIB sub-tree consisting purely of scalar objects with a common
  163.      OID prefix would normally be handled in a single implementation module;
  164.    * Separate scalar subtrees would normally be in different implementation
  165.      modules;
  166.    * A table can either be handled within the same implementation module
  167.      as related scalar objects in the same subtree, or in a separate
  168.      implementation module;
  169.    * Variables that rely on the same underlying data structure to retrieve
  170.      their values, should probably be in the same implementation module (and
  171.      conversely, (though less so) those that don't, shouldn't).
  172. As an initial rule of thumb, a good initial division is likely to be
  173. obtained by treating each table and each scalar sub-tree separately. This
  174. can be seen in the current agent, where most of the MIB-II modules (RFC
  175. 1213) are implemented in separate files (see the files under mibgroup/mibII).
  176. Note that many of these combine scalar and table handling in the same file,
  177. though they are implemented using separate routines.
  178.   This is also the approach used by mib2c, which constructs a single pair of
  179. code files, but uses a separate routine for each table (and another for all
  180. the scalar variables).
  181.   Ultimately, the final consideration (concerning the underlying data) is
  182. the most important, and should guide the basic division. For example, the
  183. Host Resources Running Software and Running Software Performance modules,
  184. while separate in the MIB tree, use the same underlying kernel data and so
  185. are implemented together.
  186. MIB name
  187. --------
  188. The final requirement at this stage is to choose a name for each
  189. implementation module. This should be reasonably short, meaningful, unique
  190. and unlikely to clash with other (existing or future) modules. Mib2c uses
  191. the label of the root node of the MIB sub-tree as this name, and this is a
  192. reasonable choice in most cases.
  193. Recent changes to the agent code organisation have introduced the idea of
  194. module groups of related implementation modules. This is used, for example,
  195. to identify the constituent modules of a 'split' MIB (such as the Host
  196. Resources MIB), or those relating to a particular organisation (such as
  197. UCD).
  198. As with the division, this naming and grouping is a purely internal matter,
  199. and is really only visible when configuring and compiling the agent.
  200. So much for the MIB file. The next part considers the C header file.
  201. 3. The C code header file
  202. =========================
  203. If the MIB file is the definition of the module for external network
  204. management applications (where applications includes network management
  205. personnel!), then the header file has traditionally served effectively the
  206. same purpose for the agent itself.
  207. Recent changes to the recommended code structure has resulted in the header
  208. file becoming increasingly simpler. It now simply contains definitions of the
  209. publically visible routines, and can be generated completely by mib2c.
  210. Function prototypes
  211. -------------------
  212. For those interested in the details of this file (for example, if coding a
  213. module by hand), then the details of these definitions are as follows. Every
  214. header file will have the following two function prototype definitions
  215.         extern void          init_example (void);
  216.         extern FindVarMethod var_example;
  217. If the module includes any tables, or other collections of variables that
  218. are implemented in separate routines, then this second definition will be
  219. repeated for each of these.
  220. In addition, if any of the variables can be SET (and it is intended to
  221. implement them as such), there will be a function prototype definitions for
  222. each of these, of the form:
  223.         extern WriteMethod write_varName;
  224. These prototypes are in fact typedef'ed in <agent/snmp_vars.h>.
  225. Module dependencies
  226. -------------------
  227. This header file is also used to inform the compilation system of any
  228. dependancies between this module and any others. There is one utility module
  229. which is required by almost every module, and this is included using the
  230. directive
  231.         config_require( util_funcs )
  232. (which is produced automatically by mib2c). This same syntax can be used to
  233. trigger the inclusion of other related modules. An example of this can be
  234. seen in mibII/route_write.h which relies on the mibII/ip module, thus:
  235.         config_require( mibII/ip )
  236. One use of this directive is to define a module group, by supplying a header
  237. file consisting exclusively of such config_require directives.  It can then
  238. be included or excluded from the agent very simply. Examples of this can be
  239. seen in mibgroup/mibII.h or mibgroup/host.h, which list the consituent
  240. sub-modules of the MIB-II and Host Resources MIBs respectively.
  241. MIB file information
  242. --------------------
  243. Most of the information in this file is (understandably) aimed at the network
  244. management agent itself.  However, there is one common header file directive
  245. that is actually intended to affect the utility commands that are included
  246. within the full distribution:
  247. config_add_mib( HOST-RESOURCES-MIB )
  248.   This is used to add the MIB file being implemented to the default list of
  249. MIBs loaded by such commands.  This means that querying the agent will return
  250. informative names and values, rather than the raw numeric forms that SNMP
  251. actually works with.  Of course, it is always possible for the utilities
  252. to specify that this MIB should be loaded anyway.  But specifying this file
  253. within the module header file is a useful hint that a particular MIB should
  254. be loaded, without needing to ask for it explicitly.
  255.   Note that this will only affect the binaries compiled as part of the same
  256. configuration run.  It will have no effect on pre-installed binaries, or
  257. those compiled following a different configuration specification.
  258. Magic Numbers
  259. -------------
  260. The other common element within the header file defines a set of "magic
  261. numbers" - one for each object within the implementation module. In fact,
  262. this can equally well appear within the main code file, as part of the
  263. variable structure (which will be described in the next part).
  264.   This is the technique used by mib2c, but most handcrafted modules have
  265. tended to define these as part of the header file, probably for clarity.
  266.   The only necessity is that the names and values are distinct (or more
  267. precisely, the values are distinct within a single variable handling routine).
  268. In practise, they tend to be defined using integers incrementing from 1,
  269. or as the same as the final sub-identifier of the corresponding MIB object
  270. (or indeed both, as these are frequently themselves successive integers).
  271.   This is not mandatory, and a counter-example can be seen in the
  272. example module, where two of the object form a sub-tree, and the corresponding
  273. magic numbers are based on the final *two* sub-identifiers (to ensure that
  274. the values are unique).  But this construction is definitely unusual, and
  275. the majority of modules simply use successive integers.
  276. Header file protection
  277. ----------------------
  278. Normally, the only other contents of the header file will be the
  279. #ifndef/#define/#endif statements surrounding the whole file. This is used
  280. to ensure that the header file is only included once by any source code file
  281. (or more accurately, that there is no effect if it is inadvertantly included
  282. a second time).
  283. Again, as with the rest of the header file, this is generated automatically
  284. by mib2c.
  285. Having finished all the preparatory work (or let mib2c deal with it), the
  286. next part starts to look at the code file that actually implements the
  287. module.
  288. 4. Core structure of the implementation code
  289. ============================================
  290. The core work of implementing the module is done in the C code file. As
  291. indicated earlier, much of the detail of this will be dependent on the
  292. particular module being implemented, and this can only be described by the
  293. individual programmer concerned.
  294. However, there is a fairly clearly defined framework that the implementation
  295. will need to follow, though this varies slightly depending on the style of
  296. the module being implemented (in particular whether it forms a table or a
  297. series of individual values). The differences will be covered in the
  298. following pages, but we first need to consider the overall shape of the
  299. framework, and the elements that are common to all styles. These are
  300. essentially the compulsory routines, the common header definitions, and
  301. assorted initialisation code.
  302. As with the header file, most of this will be generated automatically by
  303. mib2c.
  304. Standard includes
  305. -----------------
  306. Certain header files are either compulsory, or required so frequently that
  307. they should be included as a matter of course. These are as follows:
  308.   #include <config.h>                   // local SNMP configuration details
  309.   #include "mib_module_config.h"        // list of which modules are supported
  310.   #if HAVE_STDLIB_H
  311.   #include <stdlib.h>
  312.   #endif
  313.   #if HAVE_STRING_H
  314.   #include <string.h>
  315.   #else
  316.   #include <strings.h>
  317.   #endif
  318.   #include <sys/types.h>
  319. All of these will usually be the first files to be included.
  320.   #include "mibincl.h"                  // Standard set of SNMP includes
  321.   #include "util_funcs.h"               // utility function declarations
  322.   #include "read_config.h"              // if the module uses run-time
  323.                                         //      configuration controls
  324.   #include "auto_nlist.h"               // structures for a BSD-based
  325.                                         //      kernel using nlist
  326.   #include "system.h"
  327.   #include "name.h"                     // the module-specific header
  328. These conventionally come at the end of the list of includes. In between
  329. will come all the standard system-provided header files required for the
  330. library functions used in the file.
  331. Module definition
  332. -----------------
  333. Much of the code defining the contents of the MIB has traditionally been
  334. held in the header file. However, much of this has slowly migrated to the
  335. code file, and this is now the recommended location for it (as typified by
  336. the output of mib2c).
  337.   The main element of this is a variable structure specifying the details of 
  338. the objects implemented.  This takes the form of an unconstrained array of
  339. type struct variableN (where N is the length of the longest suffix in the
  340. table). Thus
  341.                 struct variable2 example_variables[] = {
  342. <individual entries go here>
  343.                 };
  344. Each entry corresponds to one object in the MIB tree (or one column in the
  345. case of table entries), and these should be listed in increasing OID order.
  346. A single entry consists of six fields:
  347.    * a magic number (the #defined integer constant described above)
  348.    * a type indicator (from the values listed in <snmplib/snmp_impl.h>)
  349.    * an access indicator (essentially RWRITE or RONLY)
  350.    * the name of the routine used to handle this entry
  351.    * the length of the OID suffix used, and
  352.    * an array of integers specifying this suffix (more on this in a moment)
  353. Thus a typical variable entry would look like:
  354.         { EXAMPLESTRING, ASN_OCTET_STR, RONLY, var_example, 1, {1}}
  355. If the magic numbers have not been defined in the header file, then they
  356. should be defined here, usually comming immediately before the corresponding
  357. variable entry.  This is the technique used by mib2c.
  358. Note that in practise, only certain sizes of the structure variableN
  359. are defined (listed in <agent/var_struct.h>), being sufficient to meet the
  360. common requirements. If your particular module needs a non-supported value,
  361. the easiest thing is simply to use the next largest value that is supported.
  362. The module also needs to declare the location within the MIB tree where
  363. it should be registered. This is done using a declaration of the form
  364.         oid example_variables_oid[] = { 1,3,6,1,4,1,2021,254 }
  365. where the contents of the array give the object identifier of the root of
  366. the module.
  367. Module initialisation
  368. ---------------------
  369. Many modules require some form of initialisation before they can start
  370. providing the necessary information. This is done by providing a routine
  371. called init_{name} (where {name} is the name of the module).
  372. This routine is theoretically optional, but in practise is required to
  373. register this module with the main agent at the very least. This specifies
  374. the list of variables being implemented (from the variableN structure)
  375. and declare where these fit into the overall MIB tree.
  376. This is done by using the REGISTER_MIB macro, as follows:
  377.         REGISTER_MIB( "example",  example_variables, variable2,
  378.                         example_variables_oid );
  379. where "example" is used for identification purposed (and is usually the name
  380. being used for the module), example_variables is the structure defining the
  381. variables being implemented, variable2 is the type used for this structure,
  382. and example_variables_oid is the location of the root.
  383. In fact, this macro is simply a wrapper round the routine register_mib(),
  384. but the details of this can safely be ignored, unless more control over the
  385. registration is required.
  386. One common requirement, particularly on older operating systems or for the
  387. more obscure areas of the system, is to be able to read data directly from
  388. kernel memory. The preparation for this is typically done here by one or
  389. more statements of the form
  390.         #ifdef {NAME}_SYMBOL
  391.         auto_nlist( {NAME}_SYMBOL, 0, 0);
  392.         #endif
  393. where {NAME}_SYMBOL is defined as part of the system-specific configuration,
  394. to be the name of the appropriate kernel variable or data structure. (The
  395. two 0 values are because the kernel information is simply being primed at
  396. this point - this call will be reused later when the actual values are
  397. required). Note that this is probably the first thing described so far which
  398. isn't provided by mib2c!
  399. Other possibilities for initialisation may include registering config file
  400. directive handlers (which are documented in the read_config(5) man page), and
  401. registering the MIB module (either in whole or in part) in the sysOR table.
  402. The first of these is covered in the example module, and the second in many
  403. of the other modules within the main UCD distribution.
  404. Variable handling
  405. -----------------
  406. The other obligatory routine is that which actually handles a request for a
  407. particular variable instance. This is the routine that appeared in the
  408. variableN structure, so while the name is not fixed, it should be the same
  409. as was used there.
  410. This routine has six parameters, which will be described in turn.
  411. Four of these parameters are used for passing in information about the
  412. request, these being:
  413.         struct variable *vp;
  414.                 // The entry in the variableN array from the
  415.                 //   header file, for the object under consideration.
  416.                 // Note that the name field of this structure has been
  417.                 //   completed into a fully qualified OID, by prepending
  418.                 //   the prefix common to the whole array.
  419.         oid *name;      // The OID from the request
  420.         int *length;    // The length of this OID
  421.         int exact;      // A flag to indicate whether this is an exact
  422.                         // request (GET/SET) or an 'inexact' one (GETNEXT)
  423. Four of the parameters are used to return information about the answer.
  424. The function also returns a pointer to the actual data for the variable
  425. requested (or NULL if this data is not available for any reason).
  426. The other result parameters are:
  427.         oid *name;      // The OID being returned
  428.         int *length;    // The length of this OID
  429.         int *var_len;   // The length of the answer being returned
  430.         WriteMethod **write_method;
  431.                         // A pointer to the SET function for this variable
  432. Note that two of the parameters (name and length) serve a dual purpose,
  433. being used for both input and output.
  434. The first thing that this routine needs to do is to validate the request, to
  435. ensure that it does indeed lie in the range implemented by this particular
  436. module. This is done in slightly different ways, depending on the style of
  437. the module, so this will be discussed in more detail later.
  438.   At the same time, it is common to retrieve some of the information needed
  439. for answering the query.
  440. Then the routine uses the Magic Number field from the vp parameter to determine
  441. which of the possible variables being implemented is being requested. This is
  442. done using a switch statement, which should have as many cases as there are
  443. entries in the variableN array (or more precisely, as many as specify this
  444. routine as their handler), plus an additional default case to handle an
  445. erroneous call.
  446. Each branch of the switch statement needs to ensure that the return
  447. parameters are filled in correctly, set up a (static) return variable with
  448. the correct data, and then return a pointer to this value. These can be done
  449. separately for each branch, or once at the start, being overridden in
  450. particular branches if necessary.
  451. In fact, the default validation routines make the assumption that the
  452. variable is both read-only, and of integer type (which includes the COUNTER
  453. and GAUGE types among others), and set the return paramaters write_method and
  454. var_len appropriately. These settings can then be corrected for those cases
  455. when either or both of these assumptions are wrong. Examples of this can be
  456. seen in the example module.
  457. EXAMPLEINTEGER is writeable, so this branch sets the write_method parameter,
  458. and EXAMPLEOBJECTID is not an integer, so this branch sets the var_len
  459. parameter.  In the case of EXAMPLESTRING, both assumptions are wrong, so this
  460. branch needs to set both these parameters explicitly.
  461. Note that because the routine returns a pointer to a static result, a
  462. suitable variable must be declared somewhere for this. Two global variables
  463. are provided for this purpose - long_return (for integer results) and
  464. return_buf (for other types). This latter is a generic array (of type
  465. u_char) that can contain up to 256 bytes of data. Alternatively, static
  466. variables can be declared, either within the code file, or local to this
  467. particular variable routine. This last is the approach adopted by mib2c,
  468. which defines four such local variables, (long_ret, string, objid and c64).
  469. Mib2c requirements
  470. ------------------
  471. Most of the code described here is generated by mib2c. The main exceptions
  472. (which therefore need to be provided by the programmer) are
  473.    * Any initialisation, other than the basic registration
  474.      (including kernel data initialisation, config file handling, or sysOR
  475.      registration).
  476.    * Retrieving the necessary data, and setting the appropriate return
  477.      value correctly.
  478.    * The var_len (and possibly write_method) return parameters for variable
  479.      types that are not recognised by mib2c
  480.    * The contents of any write routines (see later).
  481. Everything else should be useable as generated.
  482. This concludes the preliminary walk-through of the general structure of the
  483. C implementation. To fill in the details, we will need to consider the
  484. various styles of module separately. The next part will look at scalar (i.e.
  485. non-table based) modules.
  486. 5. Non-table-based modules
  487. ==========================
  488. Having looked at the general structure of a module implementation, it's now
  489. time to look at this in more detail. We'll start with the simplest style of
  490. module - a collection of independent variables. This could easily be
  491. implemented as a series of completely separate modules - the main reason for
  492. combining them is to avoid the proliferation of multiple versions of very
  493. similar code.
  494. Recall that the variable handling routine needs to cover two distinct
  495. purposes - validation of the request, and provision of the answer. In this
  496. style of module, these are handled separately. Once again, mib2c does much
  497. of the donkey work, generating the whole of the request validation code (so
  498. the description of this section can be skipped if desired), and even
  499. providing a skeleton for returning the data. This latter still requires some
  500. input from the programmer, to actually return the correct results (rather
  501. than dummy values).
  502. Request Validation
  503. ------------------
  504. This is done using a standard utility function header_generic. The
  505. parameters for this are exactly the same as for the main routine, and are
  506. simply passed through directly. It returns an integer result, as a flag to
  507. indicate whether the validation succeeded or not.
  508. If the validation fails, then the main routine should return immediately,
  509. leaving the parameters untouched, and indicate the failure by returning a
  510. NULL value. Thus the initial code fragment of a scalar-variable style
  511. implementation will typically look like:
  512.     u_char  *
  513.     var_system(vp, name, length, exact, var_len, write_method)
  514.     {
  515.         if (header_generic(vp, name, length, exact, var_len, write_method)
  516.                                                 == MATCH_FAILED )
  517.             return NULL;
  518.         [ etc, etc, etc ]
  519.     }
  520. Although the utility function can be used as a "black box", it's worth
  521. looking more closely at exactly what it does (since the table-handling
  522. modules will need to do something fairly similar). It has two (or possibly
  523. three) separate functions:
  524.    * checking that the request is valid,
  525.    * setting up the OID for the result,
  526.    * and (optionally) setting up default values for the other return
  527.      parameters.
  528. In order to actually validate the request, the header routine first needs to
  529. construct the OID under consideration, in order to compare it with that
  530. originally asked for. The driving code has already combined the OID prefix
  531. (constant throughout the module) with the entry-specific suffix, before
  532. calling the main variable handler. This is available via the name field of
  533. the parameter vp. For a scalar variable, completing the OID is therefore
  534. simply a matter of appending the instance identifier 0 to this. The full OID
  535. is built up in a local oid array newname defined for this purpose.
  536. This gives the following code fragment:
  537.     int
  538.     header_generic(vp, name, length, exact, var_len, write_method)
  539.     {
  540.         oid newname[MAX_OID_LEN];
  541.         memcpy((char *)newname, (char *)vp->name,
  542.                         (int)vp->namelen * sizeof(oid));
  543.         newname[ vp->namelen ] = 0;
  544.                 :
  545.     }
  546. Having formed the OID, this can then be compared against the variable
  547. specified in the original request, which is available as the name parameter.
  548. This comparison is done using the snmp_oid_compare function, which takes the
  549. two OIDs (together with their respective lengths), and returns -1, 0 or 1
  550. depending on whether the first OID precedes, matches or follows the second.
  551. In the case of an 'exact' match (i.e. a GET/SET/etc), then the request is
  552. only valid if the two OIDs are identical (snmp_oid_compare returns 0). In
  553. the case of a GETNEXT (or GETBULK) request, it's valid if the OID being
  554. considered comes after that of the original request (snmp_oid_compare
  555. returns -1).
  556. This gives the code fragment
  557.         result = snmp_oid_compare(name, *length, newname, (int)vp->namelen + 1);
  558.                         // +1 because of the extra instance sub-identifier
  559.         if ((exact && (result != 0))            // GET match fails
  560.                 || (!exact && (result >= 0)))   // GETNEXT match fails
  561.             return(MATCH_FAILED);
  562. Note that in this case, we're only interested in the single variable
  563. indicated by the vp parameter. The fact that this module may well implement
  564. other variables as well is ignored. The 'lexically next' requirement of the
  565. GETNEXT request is handled by working through the variable entries in order
  566. until one matches. And yes, this is not the most efficient implementation
  567. possible!
  568. Note that in releases prior to 3.6, the snmp_oid_compare function was called
  569. simply compare.
  570. Finally, having determined that the request is valid, this routine must
  571. update the name and length parameters to return the OID being processed. It
  572. also sets default values for the other two return parameters.
  573.         memcpy( (char *)name,(char *)newname,
  574.                 ((int)vp->namelen + 1) * sizeof(oid));
  575.         *length = vp->namelen + 1;
  576.         *write_method = 0;              // Non-writeable
  577.         *var_len = sizeof(long);        // default to integer results
  578.         return(MATCH_SUCCEEDED);
  579. These three code fragments combine to form the full header_generic code
  580. which can be seen in the file util_funcs.c
  581. Note: This validation used to be done using a separate function for each
  582. module (conventionally called header_{name}), and many modules may still be
  583. coded in this style. The code for these are to all intents and purposes
  584. identical to the header_generic routine described above.
  585. Data Retrieval
  586. --------------
  587. The other main job of the request handling routine is to retrieve any
  588. necessary data, and return the appropriate answer to the original request.
  589. This must be done even if mib2c is being used to generate the framework of
  590. the implementation. As has been indicated earlier, the different cases are
  591. handled using a switch statement, with the Magic Number field of the vp
  592. parameter being used to distinguish between them.
  593. The data necessary for answering the request can be retrieved for each
  594. variable individually in the relevant case statement (as is the case with
  595. the system group), or using a common block of data before processing the
  596. switch (as is done for the ICMP group, among others).
  597. With many of the modules implemented so far, this data is read from a kernel
  598. structure. This can be done using the auto_nlist routine already mentioned,
  599. providing a variable in which to store the results and an indication of its
  600. size (see the !HAVE_SYS_TCPIPSTATS_H case of the ICMP group for an example).
  601. Alternatively, there may be ioctl calls on suitable devices, specific system
  602. calls, or special files that can be read to provide the necessary
  603. information.
  604. If the available data provides the requested value immediately, then the
  605. individual branch becomes a simple assignment to the appropriate static
  606. return variable - either one of the global static variables (e.g. long_return)
  607. or the local equivalents (such as generated by mib2c).
  608. Otherwise, the requested value may need to be calculated by combining two or
  609. more items of data (e.g. IPINHDRERRORS in mibII/ip.c) or by applying a
  610. mapping or other calculation involving available information (e.g.
  611. IPFORWARDING from the same group).
  612. In each of these cases, the routine should return a pointer to the result
  613. value, casting this to the pseudo-generic (u_char *)
  614. So much for the scalar case. The next part looks at how to handle simple
  615. tables.
  616. 6. Simple tables
  617. ================
  618. Having considered the simplest style of module implementation, we now turn
  619. our attention to the next style - a simple table. The tabular nature of
  620. these is immediately apparent from the MIB definition file, but the
  621. qualifier "simple" deserves a word of explanation.
  622. A simple table, in this context, has four characteristics:
  623.   1. It is indexed by a single integer value;
  624.   2. Such indices run from 1 to a determinable maximum;
  625.   3. All indices within this range are valid;
  626.   4. The data for a particular index can be retrieved directly
  627.      (e.g. by indexing into an underlying data structure).
  628. If any of the conditions are not met, then the table is not a pure simple
  629. one, and the techniques described here are not applicable. The next section
  630. of this guide will cover the more general case. (In fact, it may be possible
  631. to use the bulk of the techniques covered here, though special handling will
  632. be needed to cope with the invalid assumption or assumptions). Note that
  633. mib2c assumes that all tables are simple.
  634. As with the scalar case, the variable routine needs to provide two basic
  635. functions - request validation and data retrieval.
  636. Validation
  637. ----------
  638. This is provided by the shared utility routine header_simple_table. As with
  639. the scalar header routine, this takes the same parameters as the main
  640. variable routine, with one addition - the maximum valid index. Mib2c
  641. generates a dummy token for this, which must be replaced by the appropriate
  642. value.
  643. As with the header routine, it also returns an indication of whether the
  644. request was valid, as well as setting up the return parameters with the
  645. matching OID information, and defaults for var_len and write_method.
  646. Note that in releases prior to 3.6, this job was performed by the routine
  647. checkmib. However, the return values of this were the reverse of those for
  648. generic_header and header_simple_table. A version of checkmib is still
  649. available for compatability purposes, but you are encouraged to use
  650. header_simple_table instead.
  651. The basic code fragment (see ucd-snmp/disk.c) is therefore of the form:
  652.         unsigned char *
  653.         var_extensible_disk(vp, name, length, exact, var_len, write_method)
  654.         {
  655.             if (header_simple_table(vp,name,length,exact,var_len,write_method,numdisks)
  656.                                         == MATCH_FAILED)
  657.                 return(NULL);
  658.             [ etc, etc, etc ]
  659.         }
  660. Note that the maximum index value parameter does not have to be a
  661. permanently fixed constant. It specifies the maximum valid index at the time
  662. the request is processed, and a subsequent request may have a different
  663. maximum.
  664. An example of this can be seen in mibII/sysORTable.c where the table is held
  665. purely internally to the agent code, including its size (and hence the
  666. maximum valid index). This maximum could also be retrieved via a system
  667. call, or via a kernel data variable.
  668. Data Retrieval
  669. --------------
  670. As with the scalar case, the other required function is to retrieve the data
  671. requested. However, given the definition of a simple table this is simply a
  672. matter of using the single, integer index sub-identifier to index into an
  673. existing data structure. This index will always be the last index of the OID
  674. returned by header_simple_table, so can be obtained as name[*length-1].
  675. A good example of this type of table can be seen in ucd-snmp/disk.c
  676. With some modules, this underlying table may be relatively large, or only
  677. accessible via a slow or cumbersome interface. The implementation described
  678. so far may prove unacceptably slow, particularly when walking a MIB tree
  679. requires the table to be loaded afresh for each variable requested.
  680. In these circumstances, a useful technique is to cache the table when it is
  681. first read in, and use that cache for subsequent requests. This can be done
  682. by having a separate routine to read in the table. This uses two static
  683. variables, one a structure or array for the data itself, and the other an
  684. additional timestamp to indicate when the table was last loaded. When a call
  685. is made to this routine to "read" the table, it can first check whether the
  686. cached table is "new enough". If so, it can return immediately, and the
  687. system will use the cached data.
  688. Only if the cached version is sufficiently old that it's probably out of
  689. date, is it necessary to retrieve the current data, updating the cached
  690. version and the timestamp value.
  691. This is particularly useful if the data itself is relatively static, such as
  692. a list of mounted filesystems. There is an example of this technique in the
  693. Host Resources implementation.
  694. As with the scalar case, mib2c simply provides placeholder dummy return
  695. values. It's up to the programmer to fill in the details.
  696. The next part concludes the examination of the detailed implementation by
  697. looking at more general tables.
  698. 7. General Tables
  699. =================
  700. Some table structures are not suitable for the simple table approach, due to
  701. the failure of one or more of the assumptions listed earlier. Perhaps they
  702. are indexed by something other than a single integer (such as a 4-octet IP
  703. address), or the maximum index is not easily determinable (such as the
  704. interfaces table), or not all indices are valid (running software), or the
  705. necessary data is not directly accessible (interfaces again).
  706. In such circumstances, a more general approach is needed. In contrast with
  707. the two styles already covered, this style of module will commonly combine
  708. the two functions of request validation and data retrieval. Note that mib2c
  709. will assume the simple table case, and this will need to be corrected.
  710. General table algorithm
  711. -----------------------
  712. The basic algorithm is as follows:
  713.      Perform any necessary initialization, then walk through the
  714.      underlying instances, retrieving the data for each one, until the
  715.      desired instance is found. If no valid entry is found, return
  716.      failure.
  717. For an exact match (GET and similar), identifying the desired instance is
  718. trivial - construct the OID (from the 'vp' variable parameter and the index
  719. value or values), and see whether it matches the requested OID.
  720. For GETNEXT, the situation is not quite so simple. Depending on the
  721. underlying representation of the data, the entries may be returned in the
  722. same order as they should appear in the table (i.e. lexically increasing by
  723. index). However, this is not guaranteed, and the natural way of retrieving
  724. the data may be in some "random" order. In this case, then the whole table
  725. needs to be traversed for each request. in order to determine the
  726. appropriate successor.
  727. This random order is the worst case, and dictates the structure of the code
  728. used in most currently implemented tables. The ordered case can be regarded
  729. as a simplification of this more general one.
  730. The algorithm outlined above can now be expanded into the following
  731. pseudo-code:
  732.         Init_{Name}_Entry();    // Perform any necessary initialisation
  733.         while (( index = Get_Next_{Name}_Entry() ) != EndMarker ) {
  734.                         // This steps through the underlying table,
  735.                         //   returning the current index,
  736.                         //   or some suitable end-marker when all
  737.                         //   the entries have been examined.
  738.                         // Note that this routine should also return the
  739.                         //   data for this entry, either via a parameter
  740.                         //   or using some external location.
  741.             construct OID from vp->name and index
  742.             compare new OID and request
  743.             if valid {
  744.                 save current data
  745.                 if finished     // exact match, or ordered table
  746.                     break;      //  so don't look at any more entries
  747.             }
  748.                 //  Otherwise, we need to loop round, and examine
  749.                 //    the next entry in the table.  Either because
  750.                 //    the entry wasn't valid for this request,
  751.                 //    or the entry was a possible "next" candidate,
  752.                 //    but we don't know that there isn't there's a
  753.                 //    better one later in the table.
  754.         }
  755.         if no saved data        // Nothing matched
  756.            return failure
  757.                 // Otherwise, go on to the switch handling
  758.                 //  we've already covered in the earlier styles.
  759. This is now very close to the actual code used in many current
  760. implementations (such as the the routine header_ifEntry in
  761. mibII/interfaces.c). Notice that the pseudo-code fragment if valid expands
  762. in practise to
  763.         if ((exact && (result == 0))  ||
  764.                         // GET request, and identical OIDs
  765.             (!exact && (result < 0)) )
  766.                         // GETNEXT, and candidate OID is later
  767.                         //  than requested OID.
  768. This is a very common expression, that can be seen in most of the table
  769. implementations.
  770. Notice also that the interfaces table returns immediately the first valid
  771. entry is found, even for GETNEXT requests. This is because entries are
  772. returned in lexical order, so the first succeeding entry will be the one
  773. that's required.
  774. (As an aside, this also means that the underlying data can be saved
  775. implicitly within the 'next entry' routine - not very clean, but it saves
  776. some unnecessary copying).
  777. The more general case can be seen in the TCP and UDP tables (see mibII/tcp.c
  778. and mibII/udp.c). Here, the if valid fragment expands to:
  779.         if ( exact && (result == 0)) {
  780.                 // save results
  781.                 break;
  782.         }
  783.         else if (!exact && (result < 0)) {
  784.             if ( .... ) {       // no saved OID, or this OID
  785.                                 //  precedes the saved OID
  786.                 // save this OID into 'lowest'
  787.                 // save the results into Lowinpcb
  788.                 // don't break, since we still need to look
  789.                 //      at the rest of the table
  790.             }
  791.         }
  792. The GET match handling is just as we've already seen - is this the requested
  793. OID or not. If so, save the results and move on to the switch statement.
  794.   The GETNEXT case is more complicated. As well as considering whether this
  795. is a possible match (using the same test we've already seen), we also have to
  796. check whether this is a better match than anything we've already seen. This
  797. is done by comparing the current candidate (newname) with the best match found
  798. so far (lowest).
  799.   Only if this extra comparison shows that the new OID is earlier than the
  800. saved one, do we need to save both the new OID, and any associated data
  801. (such as the inpcb block, and state flag). But having found one better
  802. match, we don't know that there isn't an even better one later on. So we
  803. can't break out of the enclosing loop - we need to keep going and examine
  804. all the remaining entries of the table.
  805. These two cases (the TCP and UDP tables) also show a more general style of
  806. indexing. Rather than simply appending a single index value to the OID
  807. prefix, these routines have to add the local four-octet IP address plus port
  808. (and the same for the remote end in the case of the TCP table). This is the
  809. purpose of the op and cp section of code that precedes the comparison.
  810. These two are probably among the most complex cases you are likely to
  811. encounter. If you can follow the code here, then you've probably cracked the
  812. problem of understanding how the agent works.
  813. Finally, the next part discusses how to implement a writable (or SETable)
  814. object in a MIB module.
  815. 8. How to implement a SETable object
  816. ====================================
  817. Finally, the only remaining area to cover is that of setting data - the
  818. handling of SNMPSET. Particular care should be taken here for two reasons.
  819. Firstly, any errors in the earlier sections can have limited effect. The
  820. worst that is likely to happen is that the agent will either return invalid
  821. information, or possibly crash. Either way, this is unlikely to affect the
  822. operation of the workstation as a whole. If there are problems in the
  823. writing routine, the results could be catastrophic (particularly if writing
  824. data directly into kernel memory).
  825. Secondly, this is the least well understood area of the agent, at least by
  826. the author. There are relatively few variables that are defined as READ-WRITE
  827. in the relevant MIBs, and even fewer that have actually been implemented as
  828. such. I'm therefore describing this from a combination of my understanding
  829. of how SETs ought to work, personal experience of very simple SET handling
  830. and what's actually been done by others (which do not necessarily coincide).
  831. There are also subtle differences between the setting of simple scalar
  832. variables (or individual entries within a table), and the creation of a new
  833. row within a table. This will therefore be considered separately.
  834. With these caveats, and a healthy dose of caution, let us proceed. Note that
  835. the UCD-SNMP development team can accept no responsibility for any damage or
  836. loss resulting from either following or ignoring the information presented
  837. here. You coded it - you fix it!
  838. Write routine
  839. -------------
  840. The heart of SET handling is the write_method parameter from the variable
  841. handling routine. This is a pointer to the relevant routine for setting the
  842. variable in question. Mib2c will generate one such routine for each setable
  843. variable. This routine should be declared using the template
  844.         int
  845.         write_variable(
  846.            int      action,
  847.            u_char   *var_val,
  848.            u_char   var_val_type,
  849.            int      var_val_len,
  850.            u_char   *statP,
  851.            oid      *name,
  852.            int      name_len );
  853. Most of these parameters are fairly self explanatory:
  854. The last two hold the OID to be set, just as was passed to the main variable
  855. routine.
  856. The second, third and fourth parameters provide information about the new
  857. desired value, both the type, value and length. This is very similar to the
  858. way that results are returned from the main variable routine.
  859. The return value of the routine is simply an indication of whether the
  860. current stage of the SET was successful or not. We'll come back to this in a
  861. minute. Note that it is the responsibility of this routine to check that the
  862. OID and value provided are appropriate for the variable being implemented.
  863. This includes (but is not limited to) checking:
  864.    * the OID is recognised as one this routine can handle
  865.      (this should be true if the routine only handles the one variable, and
  866.      there are no errors in the main variable routine or driving code, but
  867.      it does no harm to check).
  868.    * the value requested is the correct type expected for this OID
  869.    * the value requested is appropriate for this OID
  870.      (within particular ranges, suitable length, etc, etc)
  871. There are two parameters remaining to be considered.
  872. The fifth parameter, statP, is the value that would be returned from a GET
  873. request on this particular variable. It could be used to check that the
  874. requested new value is consistent with the current state, but its main use
  875. is to denote that a new table row is being created.
  876. In most cases (particularly when dealing with scalar values or single elements
  877. of tables), you can normally simply ignore this parameter.
  878. Actions
  879. -------
  880. The final parameter to consider is the first one - action. To understand
  881. this, it's necessary to know a bit about how SETs are implemented.
  882. The design of SNMP calls for all variables in a SET request to be done "as
  883. if simultaneously" - i.e. they should all succeed or all fail. However, in
  884. practise, the variables are handled in succession. Thus, if one fails, it
  885. must be possible to "undo" any changes made to the other variables in the
  886. request.
  887. This is a well understood requirement in the database world, and is usually
  888. implemented using a "multi-stage commit". This is certainly the mechanism
  889. expected within the SNMP community (and has been made explicit in the work
  890. of the AgentX extensibility group). In other words, the routine to handle
  891. setting a variable will be called more than once, and the routine must be
  892. able to perform the appropriate actions depending on how far through the
  893. process we currently are. This is determined by the value of the action
  894. parameter.
  895. This is implemented using three basic phases:
  896. RESERVE is used to check the syntax of all the variables provided, that the
  897. values being set are sensible and consistent, and to allocate any resources
  898. required for performing the SET. After this stage, the expectation is that
  899. the set ought to succeed, though this is not guaranteed.
  900. (In fact, with the UCD agent, this is done in two passes - RESERVE1, and
  901. RESERVE2, to allow for dependancies between variables).
  902. If any of these calls fail (in either pass) the write routines are called
  903. again with the FREE action, to release any resources that have been
  904. allocated. The agent will then return a failure response to the requesting
  905. application.
  906. Assuming that the RESERVE phase was successful, the next stage is indicated
  907. by the action value ACTION. This is used to actually implement the set
  908. operation. However, this must either be done into temporary (persistent)
  909. storage, or the previous value stored similarly, in case any of the
  910. subsequent ACTION calls fail.
  911.  This can be seen in the example module, where both write routines have
  912. static 'old' variables, to hold the previous value of the relevant object.
  913. If the ACTION phase does fail (for example due to an apparently valid, but
  914. unacceptable value, or an unforeseen problem), then the list of write
  915. routines are called again, with the UNDO action. This requires the routine
  916. to reset the value that was changed to its previous value (assuming it was
  917. actually changed), and then to release any resources that had been
  918. allocated. As with the FREE phase, the agent will then return an indication
  919. of the error to the requesting application.
  920. Only once the ACTION phase has completed successfully, can the final COMMIT
  921. phase be run. This is used to complete any writes that were done into
  922. temporary storage, and then release any allocated resources. Note that all
  923. the code in this phase should be "safe" code that cannot possibly fail (cue
  924. hysterical laughter). The whole intent of the ACTION/COMMIT division is that
  925. all of the fallible code should be done in the ACTION phase, so that it can
  926. be backed out if necessary.
  927. Table row creation
  928. ------------------
  929. What about creating new rows in a table, I hear you ask. Good Question.
  930. This case can often be detected by the fact that a GET request would have
  931. failed, and hence the fifth parameter, statP, will be null.  This contrasts
  932. with changing the values of an element of an existing row, when the statP
  933. parameter would hold the previous value.
  934. The details of precisely how to create a new row will clearly depend on the
  935. underlying format of the table.  However, one implementation strategy would
  936. be as follows:
  937.   *  The first column object to be SET would return a null value from the
  938. var_name routine.  This null statP parameter would be the signal
  939. to create a new temporary instance of the underlying data structure,
  940. filled with dummy values.
  941.   *  Subsequent column objects would return pointers to the appropriate
  942. field of this new data structure from the var_name routine,
  943. which would then be filled in by the write routine.
  944.   *  Once all the necessary fields had been SET, the completed temporary
  945. instance could be moved into the "standard" structure (or copied,
  946. or otherwise used to set things up appropriately).
  947. However, this is purely a theoretical strategy, and has not been tried
  948. by the author.  No guarantees are given as to whether this would actually
  949. work.  There are also questions regarding how to handle incomplete
  950. or overlapping SET requests.
  951. Anyone who has experience of doing this, please get in touch!
  952.   ------------------------------------------------------------------------
  953. And that's it. Congratulations for getting this far. If you understand
  954. everything that's been said, then you now know as much as the rest of us
  955. about the inner workings of the UCD-SNMP agent. (Well, very nearly).
  956. All that remains is to try putting this into practise. Good luck!
  957. And if you've found this helpful, gifts of money, chocolate, alcohol, and
  958. above all feedback, would be most appreciated :-)
  959.   ------------------------------------------------------------------------
  960. Copyright 1999, 2000 - D.T.Shield.
  961. Not to be distributed without the explicit permission of the author.