Network Working Group                                         A. Romanov
Internet-Draft                                                       QQI
Expires: October 1, 2002                                   April 2, 2002


                  Developing High Quality SNMP Agents
                      draft-aromanov-snmp-hiqa-01

Status of this Memo

   This document is an Internet-Draft and is in full conformance with
   all provisions of Section 10 of RFC2026.

   Internet-Drafts are working documents of the Internet Engineering
   Task Force (IETF), its areas, and its working groups.  Note that
   other groups may also distribute working documents as Internet-
   Drafts.

   Internet-Drafts are draft documents valid for a maximum of six months
   and may be updated, replaced, or obsoleted by other documents at any
   time.  It is inappropriate to use Internet-Drafts as reference
   material or to cite them other than as "work in progress."

   The list of current Internet-Drafts can be accessed at http://
   www.ietf.org/ietf/1id-abstracts.txt.

   The list of Internet-Draft Shadow Directories can be accessed at
   http://www.ietf.org/shadow.html.

   This Internet-Draft will expire on October 1, 2002.

Copyright Notice

   Copyright (C) The Internet Society (2002).  All Rights Reserved.

Abstract

   SNMP is a ubiquitous protocol.  Most of the software developers
   working in the embedded space are involved into developing MIB
   handlers and SNMP agents one way or another.  At the same time most
   of these developers are not directly involved into SNMP standard
   itself, and there is a number of often overlooked fine points there.
   This document will provide a list of steps and rules to avoid popular
   problems in order to develop a high quality SNMP agent.








Romanov                  Expires October 1, 2002                [Page 1]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


Table of Contents

   1.  Overview . . . . . . . . . . . . . . . . . . . . . . . . . . .  3
   2.  Conventions Used in This Document  . . . . . . . . . . . . . .  3
   3.  Index Processing Issues  . . . . . . . . . . . . . . . . . . .  3
   3.1 Index Processing for Get and Set Requests  . . . . . . . . . .  3
   3.2 Index Processing for GetNex and GetBulk Requests . . . . . . .  4
   4.  Issues Related to the Set-Request Processing . . . . . . . . .  5
   4.1 Consistency Checking . . . . . . . . . . . . . . . . . . . . .  5
   4.2 Miscellaneous Set Request Issues . . . . . . . . . . . . . . .  7
   5.  Agent Design Issues  . . . . . . . . . . . . . . . . . . . . .  7
   6.  Intellectual Propery . . . . . . . . . . . . . . . . . . . . .  8
   7.  Security Considerations  . . . . . . . . . . . . . . . . . . .  9
       References . . . . . . . . . . . . . . . . . . . . . . . . . .  9
       Author's Address . . . . . . . . . . . . . . . . . . . . . . . 10
   A.  GetNext and GetBulk Request Index Processing Examples  . . . . 10
   A.1 Processing Integer Index . . . . . . . . . . . . . . . . . . . 10
   A.2 Processing IP Address Index  . . . . . . . . . . . . . . . . . 12
   A.3 Processing Non-implied String Index  . . . . . . . . . . . . . 14
   A.4 Putting It All Together  . . . . . . . . . . . . . . . . . . . 17
   B.  Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 19
       Full Copyright Statement . . . . . . . . . . . . . . . . . . . 20





























Romanov                  Expires October 1, 2002                [Page 2]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


1. Overview

   The goal of this memo is to facilitate development of SNMP agents in
   the context of SNMP agent frameworks.  Modern SNMP agent frameworks
   are mature and they provide a good base to build a high-quality
   agent.  These frameworks relieve an application developer from the
   bulk of the work related to the protocol transaction handling.
   However, there are still issues that have to be taken care of by an
   application developer.  Unfortunately, there is a wide spread
   misunderstanding of some of the fine issues in this area.  Moreover,
   there are new companies entering SNMP framework business, companies
   who develop their own frameworks and numerous companies that do deep
   modifications of existing frameworks.  The author has an opportunity
   to observe it while working as a consultant for many years: even the
   most experienced developers usually miss/disregard one or more of the
   issues addressed in this memo.

2. Conventions Used in This Document

   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
   "SHOULD", "SHOULD NOT", "RECOMMENDED",  "MAY", and "OPTIONAL" in this
   document are to be interpreted as described in [RFC2119].

3. Index Processing Issues

   An SNMP instance is identified by a string of OIDs representing the
   object name appended with a string of OIDs representing the index of
   the instance (we will call it an `index string').  In most cases it
   is left to the MIB developer to find an variable instance matching to
   an index string.

   Often it is done in a simple staighforward way: for every row in the
   table agent converts values of index variables into OIDs and then
   picks up the best matching entry, if any.  The great advantage of
   this method is that it allows to avoid most if not all of the index
   processing pitfalls discussesd below.  However, there are performance
   and functionality trade-offs associated with this method.  So, most
   implementors choose another method, where index string is first
   converted into a set of index variables and then these values are
   used as a search criteria in the instance database.  The rest of this
   section is devoted to a discussion of issues associated with the
   latter method.

3.1 Index Processing for Get and Set Requests

   Fortunately, there are very few problems arisen in this area.  The
   first thing to do is to check the length of the index string and if
   it is inconsistent with the required length, `noSuchInstance'



Romanov                  Expires October 1, 2002                [Page 3]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


   (`noCreation' if it is Set request) MUST be a result of the
   operation.  Also, when the indexing by non-implied string (or non-
   implied object identifier) is used, the length of the string encoded
   as the first OID of the index string.  This value MUST be checked
   against overall length of the index string too.

   Second, we have to remember that the range of an OID component (0-
   4294967295) may be wider than the range of the variable it is being
   mapped into.  For example, the range of integers used for index
   components is 0-2147483647, while the range of IP address components
   and the range of the string components is 0-255.  So, OID 4123456789
   indicates a non-existent integer indexed instance, index string
   1.2.345.4 indicates a non-existent instance indexed by an IP address
   or IP mask and index string 4.65.66.670.68 indicates a non-existent
   instance indexed by a non-implied string.  In all cases of incorrect
   range `noSuchInstance' (`noCreation') MUST be the result of the
   operation.  Second, we have to remember that the range of OID (0-
   4294967295) is wider than the range of the variable it is being
   mapped into.  For example, the range of integers used for index is 0-
   2147483647, the range of IP address components and the range of the
   string components is 0-255.  So, OID 4123456789 indicates non-
   existent integer indexed instance, index string 1.2.345.4 indicates
   non- existent instance indexed by IP address or IP mask and index
   string 4.65.66.670.68 indicates non-existent instance indexed by non-
   implied string.  In all cases of incorrect range `noSuchInstance'
   (`noCreation') MUST be a result of the operation.

   So, index processing in the case is simple: check the length of the
   index string, check the range of every OID, and in case of any
   problems return `noSuchInstance' (`noCreation').

3.2 Index Processing for GetNex and GetBulk Requests

   A properly implemented SNMP agent does not assume that an NMS will
   necessarily provide an index string in a GetNextRequest-PDU or
   GetBulkRequest-PDU that specifies an actual or potential object
   instance.  For example, example, in order to find the first remote
   host connected to particular local TCP port an NMS application might
   submit a GetNextRequest-PDU with a partial index containing only the
   local address and local port.  Let us first spell out the general
   principles and then we will show how to apply them to the particular
   cases of various index specifications.

   It is worthwhile to note here that all known MIBs do have no more
   than one variable length index component and it is always the last
   one in the index string.  So, in all practical cases the starting
   offset of every index component is fixed within an index string.




Romanov                  Expires October 1, 2002                [Page 4]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


   1.  If index string is longer than a properly formed one, it MUST be
       truncated.  For example, if MIB variable is indexed by an IP
       address, then the first instance after 1.2.3.4 and the first
       instance after 1.2.3.4.5.6.7.8 are the same instance.

   2.  If index string is shorter than the length of a properly formed
       one, (a) it MUST be padded with zeros and then (b) it MUST be
       checked whether there is an instance exactly matching padded
       index string.  For example, if the MIB variable is indexed by an
       IP address, then the first instance after 1.2.3 is 1.2.3.0 if
       such instance exists.  Skipping step (b) is a very popular bug.

   3.  If supplied index string contains out of range OID, then (a) if
       this OID is the first one in the index string search has to be
       advanced to the next object in the MIB view, otherwise (b)
       previous OID has to be incremented  (if incrementing makes it out
       of range OID, then move to the previous OID and repeat steps (a)
       and (b)), (c) index string MUST be truncated starting from the
       OID, and then the operations of the step (2) above MUST be
       performed.  Naturally, OID range checking MUST start at the end
       of index string and progress towards its beginning.  Appendix A,
       contains index processing examples for the most popular cases.


4. Issues Related to the Set-Request Processing

4.1 Consistency Checking

   Unfortunately, there is a lot of confusion in the developer community
   with regard to the practical requirements of the depth and
   sophistication of consistency checking.  Some developers assume that
   the standard requires that an agent should be able to verify
   consistency of every combination of variables that would fit into
   biggest SetRequest-PDU.  Naturally, they feel that this is an
   absolutely unrealistic requirement and they resort to completely
   ignoring it.  Others simply do best effort consistency checking with
   the actual meaning of the `best effort' that varies wildly from
   product to product and even from MIB to MIB within the same product.
   Some companies build their own agent frameworks that impose severe
   restrictions on the ability of an agent to do effective consistency
   checking and some companies build agent frameworks that waste a lot
   of resources providing capabilities far beyond immediate necessity.
   And practically in all cases agent fails to complain if it receives a
   SetRequest-PDU that is more complicated than it is designed to
   process.

   Actually, the standard simply requires that (a) agent has to check
   consistency of every variable in the PDU vs.  the current managed



Romanov                  Expires October 1, 2002                [Page 5]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


   device status and other variables in the PDU, (b) if the proposed
   value is consistent and there is no `other' reasons preventing
   successful completion (e.g., if the PDU has too many variables for a
   particular agent implementation to analyze) then the set operation
   must continue, otherwise (c) `inconsistentValue' should be returned
   [RFC-PROTO].

   So, what are the actual requirements on consistency checking
   abilities imposed by the standard?  It is left to the developer,
   i.e., as in many other cases, the standard relies on the market place
   instead of specifying precise level.  For example if a developer aims
   too low, there will be problems with managing a device in the field
   and hence a considerable market place pressure to rectify the
   situation;  and if a developer aims too high, it will negatively
   affect time to market and development costs.

   Does the standards allow to implement an SNMP agent accepting only
   one variable per SetRequest-PDU?  It is not prohibited by protocol
   operations [RFC-PROTO];  however, all SNMP agents have to implement
   the SNMPv2-MIB [RFC-MIB].  This MIB contains a TestAndIncr [RFC2579]
   variable snmpSetSerialNo.  TestAndIncr objects (often called
   spinlocks) are intended to control access to other objects, so they
   have to be present in the PDU together with the variables that they
   control access to.  So, it will impossible to fully implement even
   minimally required set of MIBs with the agent accepting only one
   variable per SetRequest-PDU.

   So, let us spell out the requirement for the "minimal" implementation
   of an SNMP agent: (a) an agent MUST be able to properly check
   consistency of the following combination of variables (regardless of
   order in the PDU): (1) snmpSetSerialNo, (2) any variable, (3) any
   combination of spin-lock variables associated with the above
   variable, if any; (b) agent MUST return `inconsistentValue' if the
   complexity of SetRequest-PDU exceeds agents ability to perform
   consistency checking.  Again, skipping requirement (b) is a very
   popular bug.  Naturally, this implementation should use
   `createAndWait' method of row creation.

   Does implementing such a minimal agent make much sense?  In many
   cases it is a perfectly valid implementation, but at the same time,
   it is very limiting for many practical cases.  So, the "reasonable"
   implementation of an SNMP agent SHOULD support row creation with
   `createAndGo' and it SHOULD provide consistency checking extended at
   least to the variables belonging to a single row in the conceptual
   table.  This "reasonable" implementation provides substantial
   benefits, with minimal additional efforts.

   Naturally, nothing prevents a developer to go way beyond this



Romanov                  Expires October 1, 2002                [Page 6]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


   "reasonable" implementation level.  Let us call such implementations
   "advanced".  Also, it is perfectly legal to mix various levels of
   implementations within the same agent.  Developers who develop or
   customize SNMP agent frameworks have to be very careful to select an
   appropriate maximum implementation level to be supported by the
   framework.  For example, if a framework supports only a minimal
   implementation, it will be hardly possible to implement legacy MIBs
   with tables without RowStatus component.

   Also, there is an often-overlooked issue mostly related to the
   consistency checking in "advanced" implementations.  There are always
   a number of managed systems parameters where consistency checking,
   resource allocation and/or undo operations are practically impossible
   to accomplish with 100% level of reliability.  Good thing is that as
   a rule these operations are inherently atomic and the failure do not
   change the management system state.  Consistency checks for these
   cases SHOULD NOT allow these variables to be mixed with any other non
   spin-lock variable, so the dangerous operation would rely on inherent
   atomicity instead of checking.

4.2 Miscellaneous Set Request Issues

   The intended use of `createAndWait' and `notInService' RowStatus
   values is to create and manipulate very long rows.  Otherwise, they
   do not provide any additional value so reasonable and advanced
   implementations of an SNMP agent MAY choose not to support these
   values for MIBs with rows of normal length.  Naturally, a minimal
   implementation MUST support these values.

   Since the `commitFailed' error code does not convey any meaningful
   information to NMS, an SNMP agent MAY substitute some meaningful
   error code (e.g.  `resourceNotAvailable') for such cases.  Naturally,
   an SNMP agent SHOULD NOT ever find itself in the situation where it
   will return `undoFailed'.

5. Agent Design Issues

   There are a number of design issues to be considered.  It may require
   a separate memo to discuss each of them in detail.  So, this memo
   will be limited to a brief listing of often overlooked design issues.

   1.  The spectrum and frequency of requests issued by NMSs are
       unpredictable and there is always a pretty real possibility of
       NMS bugs, which can result in the excessive load on the SNMP
       agent.  So, it is essential to run SNMP agents as a low priority
       thread or to take other steps to prevent SNMP agent activities
       from affecting managed system performance.  This is also a
       security issue, described below.



Romanov                  Expires October 1, 2002                [Page 7]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


   2.  There is a popular design that links rows in the GetNext order
       and also puts them into a hash table to provide fast access to
       the current row.  It works perfectly well for Get and Set
       operations and it also works fine the bulk of GetNext cases, when
       the search string exactly matches an existing row.  However, an
       NMS is under no obligation to provide index of an existing
       instance as an index string, so in some cases a long linear
       search is unavoidable.  So it is important to take some
       precautions to guarantee that long linear searches will not
       impact managed system performance (e.g., along the lines of item
       (1) above).

   3.  On the systems with memory protection, it is advisable to map
       tables into read-only shared memory, because user space-kernel
       space transitions are very expensive.  Again along the lines of
       the item (1) above kernel transactions should be limited only to
       the area where it is absolutely essential: namely Set requests.

   4.  Often, it is desirable to provide a common backend for various
       management interfaces (SNMP, WEB, CORBA, CLI, etc.).  It is
       surprisingly popular to select an SNMP agent as such a backend.
       The author strongly advises against this design, unless the
       managed device is a truly trivial one.  In author's experience it
       never brought to anybody anything but trouble.


6. Intellectual Propery

   The IETF takes no position regarding the validity or scope of any
   intellectual property or other rights that might be claimed to
   pertain to the implementation or use of the technology described in
   this document or the extent to which any license under such rights
   might or might not be available; neither does it represent that it
   has made any effort to identify any such rights.  Information on the
   IETF's procedures with respect to rights in standards-track and
   standards-related documentation can be found in [RFC2028].  Copies of
   claims of rights made available for publication and any assurances of
   licenses to be made available, or the result of an attempt made to
   obtain a general license or permission for the use of such
   proprietary rights by implementors or users of this specification can
   be obtained from the IETF Secretariat.

   The IETF invites any interested party to bring to its attention any
   copyrights, patents or patent applications, or other proprietary
   rights which may cover technology that may be required to practice
   this standard.  Please address the information to the IETF Executive
   Director.




Romanov                  Expires October 1, 2002                [Page 8]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


7. Security Considerations

   SNMPv3 security specifically does not protect against denial of
   service attacks [SNMP-USM], so SNMPv3 entities are relatively
   vulnerable to these attacks: in most configurations NMSs make a
   substantial use of insecure communications to convey essential
   information, agent allows pretty significant replay window, which
   could be exploited to overload the managed system with requests.
   Using complex instance level granularity access greatly aggravates
   the situation.

   Also, SNMPv3 agent security configuration is a complex matter, even
   minor imperfection in the agent's security configuration may expose
   the managed system to the inappropriate level of the risk.

   So, it is RECOMMENDED to have a built-in possibility to start an
   agent in  `high-security mode' where it will drop all insecure
   communications delivered to it and it will never emit an insecure
   communication on its own, regardless of its configuration parameters.

   Also, it is RECOMMENDED to strictly follow design recommendation (1)
   in the previous section in order to eliminate vulnerabilities
   associated with the denial of service attacks exploiting replay
   windows.  For the same purpose it is RECOMMENDED that an agent start
   any Set request with processing of the snmpSetSerialNo if it is
   present in the PDU.  Although not related to the agent side, it is
   important to rememeber that every NMS issuing a Set request without
   snmpSetSerialNo exposes an agent to a possible denial of service
   attack.

References

   [RFC2119]    Bradner, S., "Key words for use in RFCs to Indicate
                Requirement Levels", BCP 14, RFC 2119, March 1997.

   [RFC-PROTO]  Rose, M., Case, J., Waldbusser, S., McCloghrie, K. and
                R. Presuhn, "Version 2 of the Protocol Operations for
                the Simple Network Management  Protocol", draft-ietf-
                snmpv3-update-proto-08 (work in progress), October 2001.

   [RFC-MIB]    Rose, M., Case, J., Waldbusser, S., McCloghrie, K. and
                R. Presuhn, "Management Information Base for the Simple
                Network Management Protocol", draft-ietf-snmpv3-update-
                mib-07 (work in progress), October 2001.

   [RFC2579]    McCloghrie, K., Perkins, D., Schoenwaelder, J., Case,
                J., McCloghrie, K., Rose, M. and S. Waldbusser, "Textual
                Conventions for SMIv2", STD 58, RFC 2579, April 1999.



Romanov                  Expires October 1, 2002                [Page 9]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


   [RFC2028]    Hovey, R. and S. Bradner, "The Organizations Involved in
                the IETF Standards Process", BCP 11, RFC 2028, October
                1996.

   [SNMP-USM]   Wijnen, B. and U. Blumenthal, "User-based Security Model
                (USM) for version 3 of the Simple Network  Management
                Protocol (SNMPv3)", draft-ietf-snmpv3-usm-v2-rfc2574bis-
                01 (work in progress), November 2001.

   [RFC2012]    McCloghrie, K., "SNMPv2 Management Information Base for
                the Transmission Control Protocol using SMIv2", RFC
                2012, November 1996.


Author's Address

   Aleksey Romanov
   Quality Quorum, Inc.

   EMail: qqi@world.std.com

Appendix A. GetNext and GetBulk Request Index Processing Examples

A.1 Processing Integer Index

   Below is a function that converts a part of an index string into
   integer.  This function converts the OID component located at offset
   `off' in a fully formed index string supplied by the NMS, which is
   represented by `indexString' and `indexStringLength'.  Note that
   `off' could be greater or equal than `indexStringLength'.  If the
   fully formed index string does not end with the integer in question
   (i.e., contains other index components beyond it), it is quite
   possible that processing of the next index component will require
   that the current OID be incremented;  in that case `inBump' will be
   set to a non-zero value.  The maximum acceptable value is passed as
   `maxIntVal'.  The converted integer will be placed into `intVal'.  If
   it is necessary to probe for an exactly matching instance before the
   converted value can be used, `checkExact' will be set to a non-zero
   value.  This function returns a non-zero value if the previous OID
   (i.e., the prefix of the index being converted) has to be
   incremented.


      int
      nextprocOid2Int(const uint32 *indexString,
                      int indexStringLength,
                      int off, int inBump,
                      int32 maxIntVal, int32 *intVal,



Romanov                  Expires October 1, 2002               [Page 10]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


                      int *checkExact)
      {
        uint32 oidVal;

        assert(indexString != NULL ||
               indexStringLength == 0);
        assert(indexStringLength >= 0);
        assert(off >= 0);
        assert(indexStringLength > (off+1) || !inBump);
        assert(maxIntVal >= 0);
        assert(intVal != NULL);
        assert(checkExact != NULL);

        if(off >= indexStringLength)
          {
            /* Index string is short */
            assert(inBump == 0);
            *intVal = 0;
            *checkExact = 1;
            return 0;
          }

        oidVal = indexString[off];
        if(oidVal > (uint32)maxIntVal ||
           (inBump && oidVal == (uint32)maxIntVal))
          {
            /* OID is out of range */
            *intVal = 0;
            *checkExact = 1;

            return 1;
          }

        if(inBump)
          {
            *intVal = oidVal + 1;
          }
        else
          {
            *intVal = oidVal;
          }

        *checkExact = 0;
        return 0;
      }






Romanov                  Expires October 1, 2002               [Page 11]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


A.2 Processing IP Address Index

   Below is a function that converts a part of an index string into IP
   address.  This function converts OID components starting at offset
   `off' in a fully formed index string supplied by the NMS, which is
   represented by `indexString' and `indexStringLength'.  Note that
   `off' could be greater or equal than 'indexStringLength'.  If the
   fully formed index string does not end with the IP address in
   question (i.e., contains other index components beyond it), it is
   quite possible that processing of the next index component will
   require that the current OID component string be incremented;  in
   that case `inBump' will be non-zero.  The converted IP address (in
   host order) will be placed into `addrVal'.  If it is necessary to
   probe for an exactly matching instance before the converted value can
   be used, `checkExact' will be set to a non-zero value.  This function
   returns a non-zero value if the previous OID (i.e., the prefix of the
   index being converted) has to be incremented.


      int
      nextprocOid2IpAddr(const uint32 *indexString,
                         int indexStringLength,
                         int off, int inBump,
                         uint32 *addrVal,
                         int *checkExact)
      {
        const uint32 *oid, *first, *last;
        uint32 tmp;
        int exact;

        assert(indexString != NULL ||
               indexStringLength == 0);
        assert(indexStringLength >= 0);
        assert(off >= 0);
        assert(indexStringLength > (off + 5) || !inBump);
        assert(addrVal != NULL);
        assert(checkExact != NULL);

        if(off >= indexStringLength)
          {
            /* Index string is short */
            assert(inBump == 0);
            *addrVal = 0;
            *checkExact = 1;
            return 0;
          }

        first = &indexString[off];



Romanov                  Expires October 1, 2002               [Page 12]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


        exact = 0;

        if(indexStringLength >= (off + 4))
          {
            /* We have full address specified */
            last = &indexString[off+3];
          }
        else
          {
            last = &indexString[indexStringLength-1];
            exact = 1;
          }

        tmp = 0;

        for(oid=last; oid>=first; oid--)
          {
             if(*oid > 255 || (inBump && *oid == 255))
              {
                if(oid == first)
                  {
                    *addrVal = 0;
                    *checkExact = 1;
                    return 1;
                  }

                tmp = 0;
                exact = 1;
                inBump = 1;

                continue;
              }

            if(inBump)
              {
                 tmp += (((*oid) + 1) <<
                         8*(3 - (oid - first)));
                 inBump = 0;
              }
            else
              {
                 tmp += ((*oid) <<
                         8*(3 - (oid - first)));
              }
          }

        assert(!inBump);




Romanov                  Expires October 1, 2002               [Page 13]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


        *addrVal = tmp;
        *checkExact = exact;

        return 0;
      }


A.3 Processing Non-implied String Index

   First it may worth reminding that the first element of this index is
   the length of the string, so "bb" would go before "aaa", which may be
   counterintuitive for developers accustomed to normal C-string
   ordering.

   Below is a function that converts a part of an index string into
   array of unsigned characters.  This function converts OID components
   located at offset `off' in a fully formed index string supplied by
   the NMS, which is represented by `indexString' and
   `indexStringLength'.  Note that `off' could be greater or equal than
   `indexStringLength'.  In all practical cases this element will be the
   last one in the index string, so we are not going to be too generic
   and this function does not include `inBump' parameter.  The converted
   string will be placed into `stringVal', the length of available
   buffer is passed as `maxStringLength', and the length of processed
   string is placed into `stringLength'.  If it is necessary to probe
   for an exactly matching instance before the converted value can be
   used, `checkExact' will be set to a non-zero value.  This function
   returns a non-zero value if the previous OID (i.e., the prefix of the
   index being converted) has to be incremented.


      int
      nextprocOid2Str(const uint32 *indexString,
                      int indexStringLength,
                      int off, int maxStringLength,
                      int *stringLength,
                      uint8* stringVal,
                      int *checkExact)
      {
        const uint32 *oid, *first, *last;
        int len, bump;
        uint8 *s, *resetStart;

        assert(indexString != NULL ||
               indexStringLength == 0);
        assert(indexStringLength >= 0);
        assert(off >= 0);
        assert(maxStringLength > 0);



Romanov                  Expires October 1, 2002               [Page 14]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


        assert(stringLength != NULL);
        assert(stringVal != NULL);
        assert(checkExact != NULL);

        if(off >= indexStringLength)
          {
            /* Index string is short */
            *stringLength = 0;
            *checkExact = 1;
            return 0;
          }

        if(maxStringLength > 128)
          {
            /* There is no point to deal with
               strings longer than the whole name
               length limit imposed by protocol */
            maxStringLength = 128;
          }

        if(indexString[off] > (uint32)maxStringLength)
          {
            *stringLength = 0;
            *checkExact = 1;
            return 1;
          }

        len = (int)indexString[off];

        if(len == 0)
          {
            /* Empty string */
            *stringLength = 0;
            *checkExact = 0;
            return 0;
          }

        off++;

        if(off >= indexStringLength)
          {
            /* Only length is present */
            memset(stringVal, 0, len);
            *stringLength = len;
            *checkExact = 1;
            return 0;
          }




Romanov                  Expires October 1, 2002               [Page 15]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


        first = &indexString[off];

        if(indexStringLength >= (off + len))
          {
            /* We have full string provided */
            last = &indexString[off+len-1];
            resetStart = NULL;
          }
        else
          {
            last = &indexString[indexStringLength-1];
            resetStart = stringVal + (indexStringLength
                                      - off);
          }

        bump = 0;

        for(oid=last,s=stringVal+(last - first);
            oid>=first; oid--,s--)
          {
            assert((s - stringVal) == (oid - first));

            if(*oid > 255 || (bump && *oid == 255))
              {
                resetStart = s;
                bump = 1;

                continue;
              }

            if(bump)
              {
                 *s = (uint8) ((*oid) + 1);
                 bump = 0;
              }
            else
              {
                 *s += (uint8)(*oid);
              }
          }

        if(bump)
          {
            if(len == maxStringLength)
              {
                *stringLength = 0;
                *checkExact = 1;
                return 1;



Romanov                  Expires October 1, 2002               [Page 16]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


              }

            len++;

            memset(stringVal, 0, len);

            *stringLength = len;
            *checkExact = 1;
            return 0;
          }

        *stringLength = len;
        if(resetStart != NULL)
          {
             assert((resetStart - stringVal) < len);
             memset(resetStart, 0,
                    (len - (resetStart - stringVal)));
             *checkExact = 1;
          }
        else
          {
             *checkExact = 0;
          }

        return 0;
      }


A.4 Putting It All Together

   Let us consider an example of tcpConnTable, it is indexed by
   tcpConnLocalAddress, tcpConnLocalPort, tcpConnRemAddress and
   tcpConnRemPort where the corresponding index string offsets are 0, 4,
   6, and 10 [RFC2012]


      int
      nextTcpTableEntry(const uint32 *indexString,
                        int indexStringLength,
                        struct tcpTableEntry *e)
      {
        int ret, bump, exact, curExact;
        int32 localPort, remotePort;
        uint32 localAddr, remoteAddr;

        exact = 0;

        bump = nextprocOid2Int(indexString,



Romanov                  Expires October 1, 2002               [Page 17]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


                               indexStringLength, 10,
                               0, 0xffff, &remotePort,
                               &curExact);

        if(curExact)
          {
            exact = 1;
          }

        bump = nextprocOid2IpAddr(indexString,
                                  indexStringLength, 6,
                                  bump, &remoteAddr,
                                  &curExact);
        if(curExact)
          {
            exact = 1;
            remotePort = 0;
          }

        bump = nextprocOid2Int(indexString,
                               indexStringLength, 4,
                               bump, 0xffff,
                               &localPort, &curExact);
        if(curExact)
          {
            exact = 1;
            remotePort = 0;
            remoteAddr = 0;
          }

        bump = nextprocOid2IpAddr(indexString,
                                  indexStringLength, 0,
                                  bump, &localAddr,
                                  &curExact);
        if(bump)
          {
            return NOTFOUND;
          }

        if(curExact)
          {
            exact = 1;
            remotePort = 0;
            remoteAddr = 0;
            localPort  = 0;
          }

        ret = NOTFOUND;



Romanov                  Expires October 1, 2002               [Page 18]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


        if(exact)
          {
            ret = retrieveTcpConnection(localAddr,
                                       localPort,
                                       remoteAddr,
                                       remotePort, e);
          }

      if(ret == NOTFOUND)
          {
            ret = retrieveNextTcpConnection(localAddr,
                                            localPort,
                                            remoteAddr,
                                            remotePort,
                                            e);
          }

        return ret;
      }


Appendix B. Acknowledgements

   Author is grateful to C.M.Heard for toughtful review and many
   improvements of the original draft.


























Romanov                  Expires October 1, 2002               [Page 19]

Internet-Draft     Developing High Quality SNMP Agents        April 2002


Full Copyright Statement

   Copyright (C) The Internet Society (2002).  All Rights Reserved.

   This document and translations of it may be copied and furnished to
   others, and derivative works that comment on or otherwise explain it
   or assist in its implementation may be prepared, copied, published
   and distributed, in whole or in part, without restriction of any
   kind, provided that the above copyright notice and this paragraph are
   included on all such copies and derivative works.  However, this
   document itself may not be modified in any way, such as by removing
   the copyright notice or references to the Internet Society or other
   Internet organizations, except as needed for the purpose of
   developing Internet standards in which case the procedures for
   copyrights defined in the Internet Standards process must be
   followed, or as required to translate it into languages other than
   English.

   The limited permissions granted above are perpetual and will not be
   revoked by the Internet Society or its successors or assigns.

   This document and the information contained herein is provided on an
   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Acknowledgement

   Funding for the RFC Editor function is currently provided by the
   Internet Society.



















Romanov                  Expires October 1, 2002               [Page 20]