Network Working Group A. Romanov Internet-Draft QQI Expires: October 20, 2002 April 21, 2002 Developing High Quality SNMP Agents draft-aromanov-snmp-hiqa-02 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 20, 2002. 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 October 20, 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 GetNext 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 Property . . . . . . . . . . . . . . . . . . . . 8 7. 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 . . . . . . . . . . . . . . . . . 11 A.3 Processing Non-implied String Index . . . . . . . . . . . . . 13 A.4 Putting It All Together . . . . . . . . . . . . . . . . . . . 18 B. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 20 Full Copyright Statement . . . . . . . . . . . . . . . . . . . 21 Romanov Expires October 20, 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 widespread 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 observed, while working as a consultant for many years, that even the most experienced developers frequently miss or 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 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 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, 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 Romanov Expires October 20, 2002 [Page 3] Internet-Draft Developing High Quality SNMP Agents April 2002 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, 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. 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 GetNext 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, when using the TCP MIB 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. 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 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 search has to be Romanov Expires October 20, 2002 [Page 4] Internet-Draft Developing High Quality SNMP Agents April 2002 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' varying 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 practical necessity. And 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 has to 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]. 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 marketplace 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 marketplace pressure to rectify the situation; and if a developer aims too high, it will negatively affect time to market Romanov Expires October 20, 2002 [Page 5] Internet-Draft Developing High Quality SNMP Agents April 2002 and development costs. Do the standards allow implementing an SNMP agent to accept 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 spin- locks) 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. 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) an agent MUST return `inconsistentValue' if the complexity of SetRequest-PDU exceeds the agent's ability to perform consistency checking: e.g. if the PDU contains any other variable. 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. Nothing prevents a developer to gobeyond 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. 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. Fortunately, as a rule Romanov Expires October 20, 2002 [Page 6] Internet-Draft Developing High Quality SNMP Agents April 2002 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 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. 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 the possibility of NMS bugs, which can result in 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. 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 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 Romanov Expires October 20, 2002 [Page 7] Internet-Draft Developing High Quality SNMP Agents April 2002 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 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. 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. So, it is RECOMMENDED to strictly follow design recommendation (1) in the previous section in order to eliminate vulnerabilities associated Romanov Expires October 20, 2002 [Page 8] Internet-Draft Developing High Quality SNMP Agents April 2002 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. 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 will never emit an insecure communication on its own, regardless of its configuration parameters. 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. [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. Romanov Expires October 20, 2002 [Page 9] Internet-Draft Developing High Quality SNMP Agents April 2002 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) { 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) Romanov Expires October 20, 2002 [Page 10] Internet-Draft Developing High Quality SNMP Agents April 2002 { /* 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; } 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 Romanov Expires October 20, 2002 [Page 11] Internet-Draft Developing High Quality SNMP Agents April 2002 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]; exact = 0; if(indexStringLength >= (off + 4)) { /* We have full address specified */ last = &indexString[off+3]; } else { assert(!inBump); last = &indexString[indexStringLength-1]; exact = 1; } Romanov Expires October 20, 2002 [Page 12] Internet-Draft Developing High Quality SNMP Agents April 2002 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); *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 lexicographic string ordering. Romanov Expires October 20, 2002 [Page 13] Internet-Draft Developing High Quality SNMP Agents April 2002 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 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 presense or absense of the next component is irrelevant */ return 0; } Romanov Expires October 20, 2002 [Page 14] Internet-Draft Developing High Quality SNMP Agents April 2002 /* 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; 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) { Romanov Expires October 20, 2002 [Page 15] Internet-Draft Developing High Quality SNMP Agents April 2002 /* 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; *checkExact = 0; return 0; } if(len > (uint32)maxStringLength) { /* Legnth component indicates length which is too big */ *stringLength = 0; *checkExact = 1; return 1; } off++; Romanov Expires October 20, 2002 [Page 16] Internet-Draft Developing High Quality SNMP Agents April 2002 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; inBump = 1; continue; } if(inBump) { *s = (uint8) ((*oid) + 1); inBump = 0; } else { *s = (uint8)(*oid); } Romanov Expires October 20, 2002 [Page 17] Internet-Draft Developing High Quality SNMP Agents April 2002 } 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; } 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, Romanov Expires October 20, 2002 [Page 18] Internet-Draft Developing High Quality SNMP Agents April 2002 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) { exact = 1; remotePort = 0; remoteAddr = 0; } bump = nextprocOid2IpAddr(indexString, indexStringLength, 0, bump, &localAddr, &curExact); if(bump) { return NOTFOUND; } Romanov Expires October 20, 2002 [Page 19] Internet-Draft Developing High Quality SNMP Agents April 2002 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; } Appendix B. Acknowledgements Author is grateful to C.M.Heard and J.Perreault for thoughtful review and essential improvements incorporated in this memo. Romanov Expires October 20, 2002 [Page 20] 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 20, 2002 [Page 21]