Subject: suggested changes and additions to draft-ietf-snmpconf-pm-08.txt

From: Steve Moulton <[email-masked]>
Date: Fri, 02 Nov 2001 18:27:23 -0500

In implementing and writing sample scripts for the Policy-based
Management MIB, we found a couple of usability shortcomings that
resulted in us writing more complex scripts than are really
necessary.  We discussed the ideas with Steve Waldbusser,
and came up with what follows.

In practice, one will often create a variable binding list
that is reused in several operations.  Two such cases are

.  an operation is repeated with several elements, perhaps either
   by modifying a single variable binding to address each element, or
   by modifying the non local arguments to specify several targets.

.  When a operation fails, one often needs the original PDU to
   retry the operation.  As it stands now, the type field of the
   offending variable binding is altered to be the error status
   for the whole PDU.  Thus, one needs to scan the entire variable
   binding list to determine the offending variable binding.  One must
   then scan the PDU and alter the offending variable binding, or
   discard and recreate the PDU.  This leads to cumbersome extra
   script code.  Furthermore, there are situations defined 
   in SNMP where the error status may be non-zero,
   but the error index set to zero as a result of an SNMP operation.
   The action to be taken by the policy language implementation 
   (which variable bindings to alter, and how) when this happens 
   are not well defined.

Finally, the document overlooked getBulk.  This oversight needs
to be corrected.

To fix these shortcomings, we can modify the accessor functions slightly:

.  In snmpSend, replace the PDU and numVarbinds arguments with 
   reqPdu, reqNumVarbinds, respPdu and respNumVarBinds arguments
   so that the request variable binding is not altered by the call,
   unless it is specified as both the request and response
   argument.

.  add a readError(PDU, &errorStatus, &errorIndex, &hasException)
   function to allow easy determination of the status of the
   response PDU.  This removes the necessity to artificially alter 
   a variable binding and thus the need to scan the variable binding
   list to determine what error occurred.

.  add a writeBulkParameters(PDU, nonRepeaters, maxRepetitions)
   function.  This additional function allows us to do getBulk.

A nice side effect is that the SNMP error constants can
be mapped onto their natural values, rather than the artificial
values made necessary by altering the type fields in the
variable bindings to an unambiguous value (made necessary by 
the ASN.1 types and the SNMP error codes colliding).

Changes to the Policy MIB document might look like this:

9.1.4.  General SNMP Functions

It is desirable for a general SNMP interface have the ability
to perform SNMP operations on multiple variables at once and
for it to allow multiple varbind lists to exist at once. The
newPdu, readVar and writeVar functions exist in order to
provide these facilities in a language without pointers,
arrays and memory allocators.

newPDU is called to allocate a PDU and return an integer
handle to it. Since PDUs are automatically freed when the
script exits and because they can be reused during execution,
there is no freePDU().

readVar and writeVar access a variable length varbindlist for
a PDU. The PDU handle and the index of the variable within
that PDU are specified in every readVar and writeVar
operation. Once a PDU has been fully specified by one or more
calls to writeVar, it is passed to snmpSend (by referencing
the PDU handle) and the number of varbinds to be included in
the operation. When a response is returned, the contents of
the response are returned in another PDU and may be read by
one or more calls to readVar. Error information may be read
from the PDU with the readError function. Because GetBulk PDUs
send additional information in the SNMP header, the
writeBulkParameters function is provided to configure these
parameters.

Varbinds in this data store are created automatically whenever
they are written by any writeVar, readVar, or snmpSend
operation. It is an RTE to read a varbind that has not been
previously written.

For example:
  var pdu = newPDU();
  var nVars = 0, oid, type, value;

  writeVar(pdu, nVars++, "sysDescr.0", ...);
  writeVar(pdu, nVars++, "sysOID.0", ...);
  writeVar(pdu, nVars++, "ifNumber.0", ...);
  if (snmpSend(pdu, nVars, Get, pdu, nVars, ...))
      return;
  readVar(pdu, 0, oid, type, value);
  readVar(pdu, 1, ...)
  readVar(pdu, 2, ...)
  ...

or,
  var pdu = newPDU();
  var nVars = 0, oid1, oid2;

  writeVar(pdu, nVars++, "ifIndex", ...);
  writeVar(pdu, nVars++, "ifType", ...);
  while(!done){
    if (snmpSend(pdu, nVars, Getnext, pdu, nVars, ...))
        continue;
    readVar(pdu, 0, oid1, ...);
    readVar(pdu, 1, oid2, ...);
    /* leave OIDs alone, now PDU #0 is set up for next step
       in table walk. */
    if (oidncmp(oid1, "ifIndex", oidlen("ifIndex")))
      done = 0;
    ...
  }

Note that in the preceding examples, descriptors such as
ifType and sysDescr are used in object identifiers solely as a
notational convenience and the actual code downloaded to the
policy MIB agent must use a dotted decimal notation only, as
there may be no MIB compiler (or MIB) available on the policy
MIB agent.

To be conformant to this specification, implementations must
allow each policy script invocation to allocate at least 5
PDUs with at least 64 varbinds per list. It is suggested that
implementations limit the total number of PDUs per invocation
to protect other script invocations from a malfunctioning
script (e.g. a script that calls newPDU() in a loop).

9.1.4.1.  newPDU()

    integer newPDU()

        newPDU will allocate a new PDU and return a handle to the
        PDU. If no PDU could be allocated, -1 will be returned.

9.1.4.2.  writeVar()

    writeVar(integer pdu, integer varBindIndex,
             string oid, var value, integer type)

        writeVar will store 'oid', 'value' and 'type' in
        the specified varbind.

        'pdu' is the handle to a PDU allocated by newPDU().

        'varBindIndex' is a non-negative integer that identifies the
        varbind within the specified PDU modified by this call. The
        first varbind is number 0.

        'oid' is a string containing an ASCII dotted-decimal
        representation of an object identifier
        (e.g. "1.3.6.1.2.1.1.1.0").

        'value' is the value to be stored, of a type appropriate to the
        'type' parameter.

        'type' will be the type of the value parameter and will be set
        to one of the values for DataType Constants.

        It is an RTE if any of the parameters don't conform to the
        rules above.

9.1.4.3.  readVar()

    readVar(integer pdu, integer varBindIndex,
            string &oid, var &value, integer &type)

        readVar will retrieve the oid, the value and it's type
        from the specified varbind.

        'pdu' is the handle to a PDU allocated by newPDU().

        'varBindIndex' is a non-negative integer that identifies the
        varbind within the specified PDU read by this call. The
        first varbind is number 0.

        The object identifier value of the referenced varbind will be
        copied into the 'oid' parameter, formatted in an ASCII
        dotted-decimal representation (e.g. "1.3.6.1.2.1.1.1.0").

        'value' is the value retrieved, of a type appropriate to the
        'type' parameter.

        'type' is the type of the value parameter and will be set to
        one of the values for DataType Constants.

        If 'pdu' doesn't reference a valid PDU or 'varBindIndex'
        doesn't reference a valid varbind, the function returns
        without modifying 'oid', 'value' or 'type'.

9.1.4.4.  snmpSend()

    integer snmpSend(integer reqPDU, integer reqNumVarbinds,
                     integer opcode,
                     integer &respPDU, integer &respNumVarbinds,
                     [, string context , NonLocalArgs] )

        snmpSend will perform an SNMP operation by sending 'reqPDU'
        and returning the results in 'respPDU'. Both 'reqPDU' and
        'respPDU' must previously have been allocated with
        newPDU. 'reqPDU' and 'respPDU' may both contain the same PDU
        handle, in which case the 'reqPDU' is sent and then replaced
        with the contents of the received PDU. If the opcode specifies
        a Trap or V2trap, 'respPDU' will not be modified.

        'reqNumVarbinds' is a integer greater than zero that specified
        which varbinds in the PDU will be used in this
        operation. The first 'reqNumVarbinds' in the PDU are
        used. 'respNumVarbinds' will be modified to contain the number
        of varbinds received in the response PDU which in the case of
        GetBulk or an error may be substantially different than
        reqNumVarbinds.

        'opcode' is the type of SNMP operation to perform and must be
        one of the values for SNMP Operation Constants listed
        elsewhere in this document.

        Note that no actual SNMP PDU needs to be generated and parsed
        when the policy MIB agent resides on the same system as the
        managed elements. If no PDU is generated, the agent must
        correctly simulate the behavior of the SNMP Response PDU,
        particularly in case of an error.

        The optional 'context' argument contains the SNMP context to
        operate on. If 'context' is not present, the context of
        "this element" will be used. If 'context' is the zero
        length string, the default context is used.

        The optional 'NonLocalArgs' provide addressing and security
        information to perform an SNMP operation on a different system
        than "this element".

        This function returns zero unless an error occurs in which
        case it returns the proper SNMP Error Constant. If an error
        occurred, respPDU will contain the response PDU as received
        from the agent unless no response PDU was received in which
        case respNumVarbinds will be 0. In any event, readError may be
        called on the pdu to determine error information for the
        transaction.

        If a SNMP Version 1 trap is requested (the opcode is Trap(4)),
        then SNMP Version 2 trap parameters are supplied and
        converted according to the rules of [RFC2576] section 3.2.
        The first variable binding must be sysUpTime.0, and the second
        must be snmpTrapOID.0 [RFC1905, section 4.2.6].  Subsequent
        variable bindings are copied to the SNMP Version 1 trap PDU in
        the usual fashion.

9.1.4.5.  readError()

    readError(integer pdu, integer numVarbinds, integer &errorStatus,
              integer &errorIndex, integer &hasException)

    Returns the error information in a PDU.

    'errorStatus' contains the error-status field from the response
    PDU or a local error constant if the error was generated
    locally. If no error was experienced or no PDU was ever copied
    into this PDU, this value will be 0.

    'errorIndex' contains the error-index field from the response
    PDU. If no PDU was ever copied into this PDU, this value will
    be 0.

    'hasException' will be 1 if any of the first 'numVarbinds'
    varbinds in the PDU contain an exception (Nosuchobject,
    Nosuchinstance, Endofmibview), otherwise it will be 0.

9.1.4.6.  writeBulkParameters()

    writeBulkParameters(integer pdu, integer nonRepeaters,
                        integer maxRepetitions)

    Modifies the parameters in a PDU in anticipation of sending a
    GetBulk operation. 'nonRepeaters' will be copied into the PDU's
    non-repeaters field and 'maxRepetitions' will be copied into the
    max-repetitions field.

9.2.  Constants

The following constants are defined for use with all SNMP
Accessor Functions. Policy code will be executed in an
environment where the following constants are declared. (Note
that the constant declarations below will not be visible in
the policyCondition or policyAction code.)

While these declarations are expressed here as C 'const's, the
'const' construct itself is not available to be used inside of
policy code.

  // Datatype Constants

  const int Integer       = 2;
  const int Integer32     = 2;
  const int String        = 4;
  const int Bits          = 4;
  const int Null          = 5;
  const int Oid           = 6;
  const int Ipaddress     = 64;
  const int Counter32     = 65;
  const int Gauge32       = 66;
  const int Unsigned32    = 66;
  const int Timeticks     = 67;
  const int Opaque        = 68;
  const int Counter64     = 70;

  // SNMP Exceptions
  const int Nosuchobject         = 128;
  const int Nosuchinstance       = 129;
  const int Endofmibview         = 130;

  // SNMP Error Constants

  const int Noerror              = 0;
  const int Toobig               = 1;
  const int Nosuchname           = 2;
  const int Badvalue             = 3;
  const int Readonly             = 4;
  const int Generr               = 5;
  const int Noaccess             = 6;
  const int Wrongtype            = 7;
  const int Wronglength          = 8;
  const int Wrongencoding        = 9;
  const int Wrongvalue           = 10;
  const int Nocreation           = 11;
  const int Inconsistentvalue    = 12;
  const int Resourceunavailable  = 13;
  const int Commitfailed         = 14;
  const int Undofailed           = 15;
  const int Authorizationerror   = 16;
  const int Notwritable          = 17;
  const int InconsistentName     = 18;

  // "Local" Errors
  // These are also possible choices for errorStatus returns

  // For example: unknown PDU, maxVarbinds is bigger than number written
  // with writeVar, unknown opcode, etc.
  const int Badparameter         = 1000;

  // Request would have created a PDU larger than local limitations
  const int Toolong              = 1001;

  // A response to the request was received but errors were encountered
  // when parsing it.
  const int Parseerror           = 1002;

  // Local system has complained of an authentication failure
  const int Authfailure          = 1003;

  // No valid response was received in a timely fashion
  const int Timeout              = 1004;

  // General local failure including lack of resources
  const int GeneralFailure       = 1005;

  // SNMP Operation Constants

  const int Get                  = 0;
  const int Getnext              = 1;
  const int Set                  = 3;
  const int Trap                 = 4;
  const int Getbulk              = 5;
  const int Inform               = 6;
  const int V2trap               = 7;

  // Constants for SnmpMessageProcessingModel and SnmpSecurityModel

  const int SNMPv1              = 0;
  const int SNMPv2c             = 1;
  const int SNMPv3              = 3;

  // SnmpSecurityLevel Constants

  const int noAuthNoPriv        = 1;
  const int authNoPriv          = 2;
  const int authPriv            = 3;

---
Steve Moulton        SNMP Research, Inc            voice: +1 865 573 1434
Sr Software Engineer 3001 Kimberlin Heights Rd.    fax: +1 865 573 9197
[email-masked]     Knoxville, TN 37920-9716 USA  http://www.snmp.com