Recall that the variable handling routine needs to cover two distinct
purposes - validation of the request, and provision of the answer. In
this style of module, these are handled separately.
Once again, mib2c
does much of the donkey work, generating
the whole of the request validation code (so the description of this section
can be skipped if desired), and even providing a skeleton
for returning the data. This latter still requires some input from the
programmer, to actually return the correct results (rather than dummy values).
header_generic
.
The parameters for this are exactly the same as
for the main routine, and are simply passed through directly.
It returns an integer result, as a flag to indicate
whether the validation succeeded or not.
u_char * var_system(vp, name, length, exact, var_len, write_method) { if (header_generic(vp, name, length, exact, var_len, write_method) == MATCH_FAILED ) return NULL; [ etc, etc, etc ] }Although the utility function can be used as a "black box", it's worth looking more closely at exactly what it does (since the table-handling modules will need to do something fairly similar). It has two (or possibly three) separate functions:
name
field of the parameter vp
.
For a scalar variable, completing the OID is therefore
simply a matter of appending the instance identifier 0 to this.
The full OID is
built up in a local oid array newname
defined for this purpose.
int header_generic(vp, name, length, exact, var_len, write_method) { oid newname[MAX_NAME_LEN]; memcpy((char *)newname, (char *)vp->name, (int)vp->namelen * sizeof(oid)); newname[ vp->namelen ] = 0; : }
Having formed the OID, this can then be compared against the variable
specified in the original request, which is available as the name
parameter.
This comparison is done using the snmp_oid_compare
function, which takes the
two OIDs (together with their respective lengths), and returns -1, 0 or 1
depending on whether the first OID precedes, matches or follows the second.
In the case of an 'exact' match (i.e. a GET/SET/etc), then the request is only
valid if the two OIDs are identical (snmp_oid_compare
returns 0).
In the case of a
GETNEXT (or GETBULK) request, it's valid if the OID being considered comes
after that of the original request (snmp_oid_compare
returns -1).
This gives the code fragment
result = snmp_oid_compare(name, *length, newname, (int)vp->namelen + 1); // +1 because of the extra instance sub-identifier if ((exact && (result != 0)) // GET match fails || (!exact && (result >= 0))) // GETNEXT match fails return(MATCH_FAILED);Note that in this case, we're only interested in the single variable indicated by the
vp
parameter. The fact that this module may well
implement other variables as well is ignored. The 'lexically next'
requirement of the GETNEXT request is handled by working through the
variable entries in order until one matches. And yes, this is not the
most efficient implementation possible!
snmp_oid_compare
function was called simply compare
.
Finally, having determined that the request is valid, this routine must
update the name
and length
parameters to return
the OID being processed.
It also sets default values for the other two return parameters.
memcpy( (char *)name,(char *)newname, ((int)vp->namelen + 1) * sizeof(oid)); *length = vp->namelen + 1; *write_method = 0; // Non-writeable *var_len = sizeof(long); // default to integer results return(MATCH_SUCCEEDED);These three code fragments combine to form the full
header_generic
code which can be seen in the file util_funcs.c
Note: This validation used to be done using a separate function for
each module (conventionally called header_
{name}),
and many modules may still be coded in this style. The code for these are
to all intents and purposes identical to the header_generic
routine described above.
mib2c
is being used to generate
the framework of the implementation.
As has been indicated earlier, the different cases are handled using
a switch
statement, with the Magic Number field of the vp
parameter
being used to distinguish between them.
With many of the modules implemented so far, this data is read
from a kernel structure. This can be done using the auto_nlist
routine
already mentioned, providing a variable in which to store the results
and an indication of its size (see the !HAVE_SYS_TCPIPSTATS_H
case of
the ICMP group for an example).
Alternatively, there may be ioctl
calls on suitable
devices, specific system calls, or special files that can be read to
provide the necessary information.
If the available data provides the requested value immediately, then
the individual branch becomes a simple assignment to the appropriate
static return variable - either one of the global static variables
(e.g long_return
) or the local equivalents (such as generated bymib2c
).
Otherwise, the requested value may need to be calculated by combining two
or more items of data (e.g. IPINHDRERRORS
in mibII/ip.c
) or by applying
a mapping or other calculation involving available information (e.g. IPFORWARDING
from the same group).
In each of these cases, the routine should return a pointer to the
result value, casting this to the pseudo-generic (u_char *)
Last modified: Wednesday, 01-Aug-2018 04:41:28 UTC
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.