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


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

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 April 29, 2003.

Copyright Notice

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

Abstract

   SNMP is a ubiquitous protocol.  Many software developers working in
   the embedded space develop or interface with MIB handlers and SNMP
   agents one way or another.  Often these developers are unfamiliar
   with SNMP standards and overlook a number of subtle points.  This
   document will provide a list of steps and rules to avoid common
   problems in order to develop a high quality SNMP agent.









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


Table of Contents

   1.  Overview . . . . . . . . . . . . . . . . . . . . . . . . . . .  3
   2.  Index Processing Issues  . . . . . . . . . . . . . . . . . . .  3
   2.1 Index Processing for Get and Set Requests  . . . . . . . . . .  3
   2.2 Index Processing for GetNext and GetBulk Requests  . . . . . .  4
   3.  Issues Related to Set-Request Processing . . . . . . . . . . .  5
   3.1 Consistency Checking . . . . . . . . . . . . . . . . . . . . .  5
   3.2 Miscellaneous Set Request Issues . . . . . . . . . . . . . . .  7
   4.  Agent Design Issues  . . . . . . . . . . . . . . . . . . . . .  7
   5.  Intellectual Property  . . . . . . . . . . . . . . . . . . . .  8
   6.  Security Considerations  . . . . . . . . . . . . . . . . . . .  8
       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  . . . . . . . . . . . . . . . . . . . 19
   B.  Acknowledgments  . . . . . . . . . . . . . . . . . . . . . . . 21
       Full Copyright Statement . . . . . . . . . . . . . . . . . . . 22






























Romanov                  Expires April 29, 2003                 [Page 2]

Internet-Draft     Developing High Quality SNMP Agents      October 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 widespread
   misunderstanding of some of the fine issues in this area.  Moreover,
   there are new companies entering SNMP framework business, companies
   that develop their own frameworks and numerous companies that do deep
   modifications of existing frameworks.  Often, even the most
   experienced developers miss or disregard one or more of the issues
   addressed in this memo.

2. Index Processing Issues

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

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

2.1 Index Processing for Get and Set Requests

   Fortunately, few problems arise 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' (`noCreation' if it is Set
   request) must be a result of the operation.  Also, when indexing by
   non-implied string (or non- implied object identifier), the length of
   the string is encoded as the first OID of the index string.  This
   value must be checked against overall length of the index string too.

   Second, the range of an OID component (0-4294967295) may be wider
   than the range of the variable it is being mapped into.  For example,



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


   the range of integers used for index components is 0-2147483647,
   while the range of IP address components and the range of 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.

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

2.2 Index Processing for GetNext and GetBulk Requests

   A properly implemented SNMP agent does not assume that a Network
   Management Station (NMS) will necessarily provide an index string in
   a GetNextRequest-PDU or GetBulkRequest-PDU that specifies an actual
   or potential object instance.  For example, when using the TCP MIB to
   find the first remote host connected to a particular local TCP port,
   an NMS application might submit a GetNextRequest-PDU with a partial
   index containing only the local address and local port.  It seems
   reasonable to start from general principles and then apply them to
   the particular cases of various index specifications.

   1.  If the 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 the index string is shorter than the length of a properly
       formed one,

       (a) pad the string with zeros and then

       (b) check whether an instance exists that exactly matched the
       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 the supplied index string contains an OID, which is greater
       than the limit imposed by underlying application variable, then

       (a) if this OID is the first one in the index string advance to
       the next object in the MIB view; otherwise

       (b) if previous OID is greater or equal to the limit move to the



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


       previous OID and start processing from the step (a); otherwise

       (c) increment the previous OID

       (d) index string must be truncated starting from the OID, and
       then the operations of the step (2) above must be performed.

       This 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.


3. Issues Related to Set-Request Processing

3.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 'best effort' varying markedly 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
   resources providing capabilities far beyond practical necessity.  In
   many cases an 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 check consistency of every variable in the PDU vs.  the
   current managed device status and other variables in the PDU;

   (b) if the proposed value is consistent and there are 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].

   The actual requirements on consistency checking abilities imposed by
   the standard are left to the developer, i.e., as in many other cases,
   the standard relies on the marketplace instead of specifying precise



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


   level.  For example if a developer aims too low, there will be
   problems with managing a device in the field and hence a considerable
   marketplace pressure to rectify the situation;  and if a developer
   aims too high, it will negatively affect time to market and
   development costs.

   In practical terms, the standards do not allow implementing an SNMP
   agent accepting only one variable per SetRequest-PDU.  It is not
   explicitly 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 spin-locks) are intended to control
   access to other objects, so they have to be present and processed in
   the PDU together with the variables that they control access to.

   To avoid further confusion, it seems reasonable to explicitly 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 their order in the
   PDU) at least: (1) snmpSetSerialNo, (2) any variable, and (3) any
   combination of spin-lock variables associated with the above
   variable, if any;

   (b) an agent must return  `inconsistentValue' if the complexity of
   the SetRequest-PDU exceeds the agent's ability to perform consistency
   checking: e.g.  if the PDU contains any other variable.  If an agent
   is not able to check consistency of a full row in the conceptual
   table it should use`createAndWait' method of row creation.

   A minimal implementation, though valid, is very limiting in many
   practical cases.  The market place is the ultimate judge, but in most
   practical cases the "reasonable" implementation of an SNMP agent will
   suffice.  Such an implementation 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
   additional benefits, with minimal efforts comparing to minimal
   implementation.  This is implementation is well supported by
   practically all available agent frameworks.

   Nothing prevents a developer from exceeding this reasonable
   implementation level.  Let us call such implementations "advanced".
   Also, it is perfectly legal to mix various levels of implementations
   within the same agent.  Organizations developing or customizing SNMP
   agent frameworks have to be very careful to select an appropriate
   maximum implementation level to be supported by the framework.  For



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


   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 system parameters where consistency checking,
   resource allocation and/or undo operations are practically impossible
   to accomplish with 100% level of reliability.  Fortunately, as a rule
   these operations are inherently atomic and the failure does 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 variables, so the dangerous operation would rely on
   inherent atomicity instead of checking.

3.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 'createAndWait'.

   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.  An SNMP
   agent should not ever find itself in the situation where it will
   return `undoFailed'.

4. 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.  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 the possibility of NMS bugs,
       which can result in excessive load on the SNMP agent.  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 major security issue,
       see below.

   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 for many GetNext cases, when



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


       the index 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 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.
       Experience shows that in reality it is a very poor choice of
       management system design, unless the managed device is a truly
       trivial one.


5. Intellectual Property

   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.

6. Security Considerations

   SNMPv3 security specifically does not protect against denial of
   service attacks [SNMP-USM], so SNMPv3 entities are relatively



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


   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.

   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
   remember that every NMS issuing a Set request without snmpSetSerialNo
   exposes an agent to a possible denial of service attack.

   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.

   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 will never emit an insecure communication on its
   own, regardless of its configuration parameters.

References

   [RFC2578]    McCloghrie, K., Perkins, D., Schoenwaelder, J., Case,
                J., McCloghrie, K., Rose, M. and S. Waldbusser,
                "Structure of Management Information Version 2 (SMIv2)",
                STD 58, RFC 2578, April 1999.

   [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.

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



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


                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 an
   integer.  This function converts the OID component located at offset
   `off' in a fully formed index string, an index string supplied by the
   NMS  is represented by `indexString' and `indexStringLength'.  Note
   that `off' could be greater that or equal to `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,
   		int *checkExact)
   {



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


     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 April 29, 2003                [Page 11]

Internet-Draft     Developing High Quality SNMP Agents      October 2002


A.2 Processing IP Address Index

   Below is a function that converts a part of an index string into an
   IP address.  This function converts OID components starting at offset
   `off' in a fully formed index string, an index string supplied by the
   NMS is represented by `indexString' and `indexStringLength'.  Note
   that `off' could be greater than or equal to '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 to increment the last OID representing the IP address;  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 April 29, 2003                [Page 12]

Internet-Draft     Developing High Quality SNMP Agents      October 2002


     exact = 0;

     if(indexStringLength >= (off + 4))
       {
         /* We have full address specified */
         last = &indexString[off+3];
       }
     else
       {
         assert(!inBump);
         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 April 29, 2003                [Page 13]

Internet-Draft     Developing High Quality SNMP Agents      October 2002


     *addrVal = tmp;
     *checkExact = exact;

     return 0;
   }


A.3 Processing Non-implied String Index

   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 lexicographic string ordering.

   If the non-implied string is not the last component of an index, a
   program has to perform an additional step, in order to determine
   presence and location of the next component in an index string.  The
   function below checks OID components located at offset `off' in a
   fully formed index string, an index string supplied by the NMS is
   represented by `indexString' and  `indexStringLength'.  Note that
   `off' could be greater than or equal to `indexStringLength'.  The
   limit on the string length is passed as `maxStringLength'.  If the
   next element is present in the index string this function will return
   a non-zero value and the its offset will be passed in `nextVarOff'.


   int
   nextprocOid2StrCheck(const uint32 *indexString,
   		     int indexStringLength,
   		     int off, int maxStringLength,
   		     int *nextVarOff)
   {
     assert(indexString != NULL ||
   	 indexStringLength == 0);
     assert(indexStringLength >= 0);
     assert(off >= 0);
     assert(maxStringLength > 0);
     assert(nextVarOff != NULL);

     if(off >= indexStringLength)
       {
         /* There is not enough oids even for the
   	 string not speaking about next value */
         return 0;
       }

     if(maxStringLength > 128)
       {
         /* There is no point to deal with



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


   	 strings longer than the whole name
   	 length limit imposed by protocol */
         maxStringLength = 128;
       }

     if(indexString[off] > maxStringLength)
       {
         /* We will have to bump anyway so
   	 the presence or absence of the next
   	 component is irrelevant */
         return 0;
       }

     /* Next component has to be checked */
     *nextVarOff = off + 1 + indexString[off];
     return 1;
   }

   Below is a function that converts a part of an index string into an
   array of unsigned characters.  This function converts OID components
   located at offset `off' in a fully formed index string, an index
   string supplied by the NMS is represented by `indexString' and
   `indexStringLength'.  Note that `off' could be greater than or equal
   to `indexStringLength'.  If the fully formed index string does not
   end with the string in question (i.e., contains other index
   components beyond it), it is quite possible that processing of the
   next index component will require to increment the last OID
   representing the string;  in that case `inBump' will be non-zero.
   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 inBump,
   		int maxStringLength,
   		int *stringLength,
   		uint8* stringVal,
   		int *checkExact)
   {
     const uint32 *oid, *first, *last;
     int len;



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


     uint8 *s, *resetStart;

     assert(indexString != NULL ||
   	 indexStringLength == 0);
     assert(indexStringLength >= 0);
     assert(off >= 0);
     assert(maxStringLength >= 0);
     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;
       }

     len = (int)indexString[off];

     if(inBump && len == 0)
       {
         if(maxStringLength == 0)
   	{
   	  *stringLength = 0;
   	  *checkExact = 1;
   	  return 1;
   	}

         *stringLength = 1;
         stringVal[0] = 0;
         *checkExact = 1;
         return 0;
       }

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



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


         *checkExact = 0;
         return 0;
       }

     if(len > (uint32)maxStringLength)
       {
         /* Length component indicates length which is too big */
         *stringLength = 0;
         *checkExact = 1;
         return 1;
       }

     off++;

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

     first = &indexString[off];

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

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

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



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


   	  inBump = 1;

   	  continue;
   	}

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

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

         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;
       }




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


     return 0;
   }


A.4 Putting It All Together

   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,
   			 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)



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


       {
         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;

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

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

     return ret;
   }







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


Appendix B. Acknowledgments

   Author is grateful to C.M.Heard and J.Perreault for thoughtful review
   and essential improvements incorporated in this memo.  Author is
   deeply thankful to M.Rose for XML specifications and tools used to
   write this memo.













































Romanov                  Expires April 29, 2003                [Page 21]

Internet-Draft     Developing High Quality SNMP Agents      October 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.

Acknowledgment

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



















Romanov                  Expires April 29, 2003                [Page 22]