Windows SNMP Agent The Open Interface for Programming the Extensible SNMP V1 and V2 Agent Under Microsoft(r) Windows(tm) Version 0.1 1 January 1994 Aleksey Romanov Paul Freeman Associates, Inc. William H. White Digital Equipment Corporation Pete Wilson Paul Freeman Associates, Inc. Dennis Young Telebit, Inc. Copyright (c) 1993, 1994 by Paul Freeman Associates, Inc. 14 Pleasant Street P. O. Box 2067 Westford, Massachusetts 01886-5067 All Rights Reserved This document may be freely distributed in any form whatever, including the form of computer-readable electronic medium, provided that it is distributed in its entirety and that the copyright and this notice are included. Comments and questions may be submitted by electronic mail to winsnmp@sunsite.unc.edu. Requests to be added to the WinSnMP mailing list should be addressed as: To: listserve@sunsite.unc.edu Subject: Message: subscribe winsnmp This specification, archives of the mailing list, and other information on Windows SNMP are available via anonymous FTP from the host sunsite.unc.edu in directory: /pub/micro/pc-stuff/ms-windows/winsnmp Authors' Contact Information Aleksey Romanov ralex@world.std.com William H. White b_white@ranger.enet.dec.com Pete Wilson pwilson@world.std.com Dennis Young young@telebit.com Version history and status of this version 0.1 1 Jan 1994 Original offering. 1 INTRODUCTION 1.1 Identification 1.2 Trademarks 1.3 Background 1.4 Goals 1.5 Non-Goals 1.6 What is the WinSNMP/Agent? 1.7 Naming Conventions 1.8 Glossary 2 OVERVIEW OF THE CORE-AGENT-TO-MIB-SERVER INTERFACE 2.1 Major Components 2.2 Program Flow 2.3 Message Types 3 ERROR CODES AND PSEUDOTYPES 3.1 Intermediate Error Codes 3.2 Error Processing and Reporting 3.3 Set Primitive 3.4 SNMPv1 CMD_DO_COMMIT 3.5 SNMPv2 CMD_DO_COMMIT/CMD_UNDO_COMMIT 4 FUNCTION-CALL INTERFACE FOR LOADABLE MIB SERVERS 4.1 Basic Structures 4.2 Function Definitions 5 MESSAGE-PASSING INTERFACE FOR EXTERNAL MIB SERVERS 5.1 Shared Memory Mechanism 5.2 Message Structures 6 MIB-SERVER MOUNTING AND UNMOUNTING PROCEDURES 6.1 Loadable MIB Servers 6.2 External MIB Servers 7 DECLARATIONS 7.1 Structure Declarations 7.2 Function Prototypes 8 OTHER ISSUES 8.1 Common Service Primitives 8.2 Agent Configuration MIB 8.3 Interface to DMTF 9 REFERENCES APPENDIX A MIB-server code example 1 INTRODUCTION 1.1 Identification This paper is the WinSNMP/Agent Specification, Draft Version 0.1. It proposes the rules for the implementation of extensible, interoperable, vendor-neutral SNMP agent software that observes the rules of the SNMP Version 1 and Version 2 and that operates under the Microsoft(r) Windows(tm) family of operating systems. 1.2 Trademarks Microsoft, MS, and MS-DOS are are registered trademarks; and Windows is a trademark of Microsoft Corporation. The Universal SNMP v1+v2 Agent and Open Agent Architecture are trademarks of Paul Freeman Associates, Inc. 1.3 Background The architecture described in this paper is the Open Agent Architecture developed as part of the Universal SNMP v1+v2 Agent by Paul Freeman Associates, Inc., which makes the technology available to the WinSNMP/Agent effort. Paul Freeman Associates, Inc., is the developer of the Universal SNMP v1+v2 Agent (tm), a commercial implementation of the agent part of the SNMP. In order to advance the state of the network- management art, PFA is making available to the WinSNMP/Agent effort the part of its Agent which realizes the interface between the core part of its Agent and MIB implementation. It is this interface which enables independent third-party MIBs to operate correctly with the PFA core Agent. The PFA interface is referred to below as the "Interface." The impetus for the revelation of this trade-secret material is the recent effort to standardize SNMP components for operation under the Microsoft Windows operating system: because the Interface has proven useful and complete, PFA offers it as the interface for the emergent Win/SNMP Agent standard. 1.4 Goals of This Paper 1.4.1 The paper describes a robust, powerful, and understandable framework for the implementation of extensible, interoperable, vendor-neutral Windows-based SNMP agents. 1.4.2 The paper defines a standard interface between the WinSNMP's core agent and its MIB-server modules and thereby supports the independent development of core agents and MIB servers. 1.4.3 The paper encourages the development of interoperable Windows-based agent components by different vendors so as to allow the dynamic selection of core-agent and MIB-server configurations by the end user from a set of fully interoperable core-agent and MIB-server components. 1.4.4 The paper describes standardized elements which are focused enough to permit the creation of a broad range of product- specific, value-added agents by conforming yet competing vendors, but proposes no further implementation policy. 1.4.5 The paper hopes to offer APIs that are as compatible and consistent as possible with the WinSNMP/Manager and WinSNMP/MIB APIs. 1.4.6 The paper proposes a DMTF interface. 1.5 Non-Goals of This Paper 1.5.1 The paper sets no implementation policy, but only presents an implementation framework. 1.5.2 The paper is silent on issues of correct and compliant SNMP-agent development. 1.5.3 The paper proposes no communications scheme or transport layer of any kind, except to say by implication that some (conceptual) transport layer is present. The choice of transport layer and its realization is entirely the province of each agent implementor. 1.6 What is the WinSNMP/Agent? The WinSNMP/Agent is an extensible SNMPv1- and SNMPv2-compliant agent that runs under Microsoft Windows. This specification describes a model for implementing the extensible agent. This model strictly partitions the agent into three parts: -- a protocol-specific part, called the "core agent"; -- one or more MIB-specific parts, called "MIB servers"; and -- the interface between the two, called the "core-agent-to-MIB- server interface" or, more often and more simply, the "interface" or the "API." 1.6.1 WinSNMP/Agent Core Agent A core agent is a Windows application that binds to a transport library for receiving or sending SNMP packets and for processing the SNMP v1/v2 PDU header. The core agent never accesses any MIB variables directly, but relies completely upon one or more MIB servers to perform MIB-variable accesses. The core agent completely manages SNMP-packet processing: -- it receives the SNMP request packet from the communications stack. -- it verifies the packet's authenticity, privacy, and context. -- it locates the appropriate MIB server for each variable in the packet. -- it passes each variable binding and protocol operation, in turn, to the appropriate MIB server. -- it manages the synchronization of multiple-phase (SET) and multiple-MIB-server (GETNEXT, GETBULK and SET) processing. -- it receives the result of each protocol operation for each variable binding from the MIB server and, when appropriate, encodes the result in an SNMP response packet. -- it returns the complete response packet to the communications stack for delivery to the NMS. The core agent also provides a number of service primitives for general use including, among possibly many others: -- trap sending. -- BER parsing. -- oid comparison. -- common RowStatus service. -- view evaluation. -- error logging. 1.6.2 WinSNMP/Agent MIB Server A MIB server is a DLL module or MS-Windows process that provides access to MIB variables at the request of and on behalf of the core agent. A MIB server implements all of the functions necessary to carry out all of the variable-access operations requested in any SNMP packet for one or more variables. A MIB server may operate on part of a MIB; on a complete MIB; or on multiple MIBs. The MIB variables under the purview of a MIB server are said to comprise that MIB server's "namespace." Namespaces are ordered and contiguous sets of variable instances and may not overlap: one and only one MIB server may legally access any single MIB variable. A MIB server is bound to a core agent in either of two ways: -- The MIB server can be a dynamically-linkable (loadable) DLL which can be loaded and then unloaded at run time. A MIB server bound in this way is said to be "loadable." -- The MIB server can exist as a process separate from the core agent. Such a MIB server communicates with the core agent using shared memory and messages and is said to be "external." 1.6.3 WinSNMP/Agent Core-Agent-to-MIB-Server Interface The core-agent-to-MIB-server interface consists of four primitive types: -- an initialization primitive that lets each MIB server initialize its namespaces; -- variable-access primitives, including primitives that support GET and SET operations; -- a synchronization primitive that tells a MIB server when packet processing is starting or is complete; and -- general housekeeping primitives for timekeeping and views maintenance. Currently, this specification defines two general primitive implementations: -- Primitives which the core agent accesses through direct function calls. This is the scheme used for loadable MIB servers. -- Primitives which the core agent accesses via the combined use of shared memory and the messaging system native to the Windows environment. This is the scheme used for external MIB servers. +----------+ +-------+ +-----+ +--------+ | Request | | | | +--Call->| MIB | +-->| Packet +->| | Requests | |<- Ret -+ Server | | | from NMS | | +-- with ->| | +--------+ | +----------+ | | Varbinds | | +--+-------------+ | | | | +--------+ | Communications | | Core | | I/F +- Call->| MIB | | Layer | | Agent | | |<- Ret -+ Server | +----------------+ | | | | +--------+ A +----------+ | |<-Results-+ | | | Response | | | | | +--------+ +---+ Packet |<-+ | | +- Call->| MIB | | for NMS | | | | |<- Ret -+ Server | +----------+ +-------+ +-----+ +--------+ Figure 1.1 Control and data flow through some communications layer, the Core Agent, the Interface, and the MIB Server(s). 1.7 Naming Conventions This section aims precisely to define terms which are local to this paper. 1.7.1 Mib Each namespace (see just below) is represented to the core agent by one structure, called "mib" in this paper. There is a linked list of such mib structures, and each structure holds the complete definitions of: -- the single namespace under the control of the corresponding MIB server; and -- all MIB-server-resident primitives which operate on that namespace. The core agent views all MIB servers mounted at any moment as a set of mib entries. The core agent has no interest in the ways in which namespace access might be implemented within any MIB server. 1.7.2 Namespace A namespace, equivalent to a range of MIB variables, is an ordered and contiguous set of variable instances accessible to one and only one MIB server. The set is characterized, defined, and bounded by a low boundary, a high boundary, and an entity name. The low boundary is that ASN.1 name which is less than or equal to the ASN.1 name of the first variable instance in the set. The high boundary is the least ASN.1 name which lies beyond the set ("beyond" in a lexicographic way). There is no variable instance whose ASN.1 name is greater than or equal to the low bound and less than the high bound which is accessed by any other MIB server. The entity name is the value of appropriate contextLocalEntity. 1.7.3 Primitives Each MIB server implements six callable functions, termed "primitives," which operate on its namespaces. No primitive may operate on the namespace of any other MIB server. The MIB-server namespace primitives are: -- init() Initialize (one time) namespace. -- indicate() Let the MIB server know packet processing is starting or is finished. -- look() Process GETs and phase one of SETs in the namespace. -- set() Process phase two of SETs in the namespace. -- view() Handle views for the namespace. -- tick() Keep track of elapsed time. There is no connection between the name of the primitive and the name of the function implementing the primitive operation. We use primitive names as function names in order to avoid another level of indirection. For a loadable MIB server, the core agent calls the primitives using the function pointers associated with the namespace. These function pointers are stored in the namespace's mib structure. For an external MIB server, the core agent sets up shared memory and then passes messages to the server which cause the primitives to be invoked. 1.8 Glossary This section glosses terms in general use in the SNMP and Windows communities. 2 OVERVIEW OF THE CORE-AGENT-TO-MIB-SERVER INTERFACE This section is a brief, high-level overview of the "what" and "why" of the interface. A formal, detailed description of each interface component (the "how") appears later. This section concentrates on the interface as it exists for loadable MIB servers. The external MIB server interface, which depends upon messaging, is treated in detail below, but is functionally equivalent. 2.1 Major components The main part of the interface is a set of six primitives: init(), indicate(), look(), set(), view(), and tick(). Each MIB server must offer this set of primitive functions. 2.1.1 Init Function The init function for each namespace is called only once, and before any other MIB-server functions are called, to allow the MIB server to initialize itself and the namespaces it supports. 2.1.2 Indicate Function The indicate function is called twice during the processing of each received SNMP packet. It is called, first, before calling any other mib primitives involved in processing the current received packet; and it's next called, second, after all varbinds in the request packet have been dealt with and the core agent is ready to return a response packet to the communications layer. The indicate function is called with an argument START or END, so will designate the first call as indicate(START) and the second and indicate(END). The purpose of the function is to allow the MIB server to allocate and free resources synchronously with packet processing. For example, the MIB server might allocate temporary resources or locks at indicate(START) time and release them at indicate(END) time. The call also conveys information which is not going to change during the current packet processing: the version of this current request (SNMPv1 or SNMPv2), the type of operation performed, the bit representation of the current view, and temporal domain. 2.1.3 Look Function The MIB server's look function must perform the search and access validation for the requested name. The details of the operations performed depend upon the pdu type being processed, pdu type established by indicate(START). For the get (GetRequest pdu), the core agent supplies the name of the variable instance to find. The MIB server needs to find this variable instance, check access rights (the tools to do so having been delivered by indicate(START)), possibly copy this variable into some temporary area, pass back its type and length (both unencoded) and a pointer to the variable, and return success or the appropriate error code. The next (Get-Next or Get-Bulk pdu) transaction performs similarly, except that the name of the found variable is also returned. The write (Set pdu) transaction differs substantially from the two get transactions. In a write transaction, the core agent delivers the name of the variable to be set, its BER encoded value, and the type and length of the value. The MIB server then must find this variable instance, check access rights, check the value, the type, and and the length provided; and then must store this value into shadow memory, along with all the information needed for a successful later low-level set operation. If any error is found, the MIB server returns the appropriate error code. If the server finds that it can successfully process the request, the MIB server must return a non-zero 32-bit handle at least once for each packet for use by the core agent in subsequent set primitives. 2.1.4 Set Function The core agent saves all (mib,handle,index) triplets for all non- zero handles passed back by the look primitive. Once the look primitive has been successfully performed for all varbinds in the current packet, the core agent core call set(DO_PHASE1) for each stored (mib, handle,index) triplet. The MIB server now has the chance to verify the consistency of all variable instances associated with the handle value and/or all other variables in the server's namespaces (new values for all variables being known at this point), having made all arrangements necessary to insure a successful later commit. The MIB server returns an indication of success or the appropriate error code. In case of error, the core agent, remembering the error code and the index, calls set(UNDO_PHASE1, handle, index) for each stored and already processed triplet. The MIB server must undo all reservations made during the previous set(DO_PHASE1,...) calls. If all calls to set(DO_PHASE1,...) had been successfully performed, the core agent calls set(DO_COMMIT,handle,index) for all triplets. If a set(DO_COMMIT,...) fails, the core agent calls set(UNDO_COMMIT,handle,index) for each already committed triplet. 2.1.5 View Function The general assumption is the viewTable layout is relatively stable. This allows the MIB-server implementor to mark all variables accessible to a particular view instead of calculating access rights every time a variable is referenced. The view function allows the MIB server to learn of changes in the current viewTable layout. It is called once after init function in order to allow MIB server to learn initial layout of views; and is called, with no intervening primitive calls, after the successful processing of each SET packet which affects viewTable layout. 2.1.6 Tick Function The tick function is called between packets every 10-15 seconds in order to allow MIB servers to perform any time related functions: clean up old notInService rows, initiate trap sending, etc. 2.2 Program Flow This section gives an overview of the program flows of a simple, straightforward conceptual core agent and a cooperating MIB server. For clarity and simplicity, this description omits error handling. 2.2.1 Initialization Core Agent MIB server -------------------------------------- ------------ for(all loadable MIB servers){ add MIB server to MIB-server list; for(all MIB-server name spaces){ for(all entities supported){ add mib to the list; } } } for(all mibs known){ mib->mib_specific = mib->mib_init(); -----> reset to known state, } according to init string, remember pointers to global data for(all mibs known){ mib->mib_view() -----------------> learn initial viewTable } (if needed) 2.2.2 GetRequest Processing ... check access rights and local entity; for(all mibs for particular entity){ mark mib as not referenced; } ... for(each requested varbind) find first and last mib entries to search; for( mib=first; mib!=last; mib=mib->mib_next ){ if(not referenced yet){ mib->mib_indicate(START) ---------> remember pdu type, version, mark as referenced; view and temporal domain, perform all operations } required to start packet processing mib->mib_look() ----------> look for variable instance, } check access, pass value back add result to output packet; } for(all mibs for particular entity){ if(not referenced){ continue; } mib->mib_indicate(END) -----------> perform all operations } required to end packet processing build response packet and return packet pointer to the communications layer 2.2.3 SetRequest Processing ... check access rights and local entity; for(all mibs for particular entity){ mark mib as not referenced; } ... for(each requested varbind) find first and last mib entries to search; for( mib=first; mib!=last; mib=mib->mib_next ){ if(not referenced yet){ mib->mib_indicate(START) ---------> remember pdu type, version, mark as referenced; view, and temporal domain, perform all } operations required to start packet processing mib->mib_look() ----------> look for variable instance, } check access, check value, if (handle != 0) { pass handle back store mib, handle, index triplet } } for(all triplets){ mib->mib_set(DO_PHASE1, handle, index) --> check consistency, reserve } resources for(all triplets){ mib->mib_set(DO_COMMIT, handle, index) --> perform commit } for(all mibs for particular entity){ if(not referenced){ continue; } mib->mib_indicate(END) -----------> perform all operations } required to end packet processing build response packet and return a packet pointer to the communications layer 2.3 Message Types For external MIB servers, there are corresponding request and response messages for each of the primitives listed above. The set of messages is defined in a later section. 3 ERROR CODES AND PSEUDO-TYPES The Open Agent architecture defines a consistent set of intermediate error codes used throughout the agent. Below is the description of the error-codes applicable to the core-agent-to- MIB-server interface. The Open Agent architecture uses an extended set of types internally. This set of types includes the base set of types defined by SMI and pseudo-types. The pseudo-type is a subtype of the base type which requires additional processing from the core agent. There is just a single pseudo-type defined currently: UINT32V1. There are some SNMPv1 mibs which use objects of type INTEGER (xxx...4294967295). The value of this type must be encoded in 5 bytes if it is greater than or equal to 0x80000000. UINT32V1, defined as (0x100 | INTEGER), gives the core agent the information required for appropriate encoding in this specific case. Extended-type encoding is straightforward: the SMI type is encoded in byte 0 (LSB) of the extended type, and byte 1 is used to provide the core agent with additional information. If byte 1 is 0 then this is a basic SMI type, otherwise it is a pseudo- type. See section 4.2.3 for precise descriptions of where and how extended types are used. If some undefined extended type is given to core agent it will generate genErr. The core agent does not perform any conversions of SNMPv2 SMI types to SNMPv1 SMI types. It is the responsibility of the MIB server to use types in accordance with the version of the request packet being processed. 3.1 Intermediate Error Codes: Core-Agent-to-MIB-Server Interface. INTL_ERROR -- the core agent will perform indicate(END) primitive for all namespaces already touched in the current packet processing and will drop processing of the request without any other action. This is meant as a hook for non-native proxies and multi-threaded implementations and should hardly ever be used in WinSNMP. DATA_ERROR -- The size of data area provided for result of parsing of integer value has to be 4, otherwise this error will be generated by set_variable(). UAG_NOERROR -- same as SNMPv2 noError UAG_GENERR -- same as SNMPv2 genErr UAG_WRONGTYPE -- same as SNMPv2 wrongType UAG_WRONGLENGTH -- same as SNMPv2 wrongLength UAG_WRONGENCODING -- same as SNMPv2 wrongEncoding UAG_WRONGVALUE -- same as SNMPv2 wrongValue UAG_NOCREATION -- same as SNMPv2 noCreation UAG_INCONSISTENTVALUE -- same as SNMPv2 inconsistentValue UAG_RESOURCEUNAVAILABLE -- same as SNMPv2 resourceUnvailable UAG_NOTWRITABLE -- same as SNMPv2 notWritable UAG_INCONSISTENTNAME -- same as SNMPv2 inconsistentName UAG_NOSUCHOBJECT -- There is no object which can be matched to the requested name with max_access != non-accessible and the requested name mapped in by current view UAG_NOSUCHINSTANCE -- There is a matching object with max- access != non-accessible, there are no instance which can be match/found to the requested name and such instance cannot be created in case of set operation and the requested/found name is mapped in by current view UAG_OBJECT_MAPPED_OUT -- There are two cases which can yield this error: a) there is no object which can be match to the requested name with max- access != non-accessible and the requested name is mapped out by current view b) the object part of the requested name is mapped out by current view UAG_INSTANCE_MAPPED_OUT -- There is a matching object with max- access != non-accessible, the object part of the name is mapped in, the whole name is mapped out. There are some other intermediate error codes used by the core agent and listed here for information purposes only: PARSE_ERROR, AUTH_ERROR, UAG_TOOBIG, UAG_NOACCESS, UAG_COMMITFAILED, UAG_UNDOFAILED, UAG_AUTHORIZATIONERROR. In addition, the core agent has further uses and reason for DATA_ERROR. 3.2 Error Processing and Reporting Any error not listed below for particular case is unexpected. For example, the error value 1002345 is an unexpected one for any case, and the error UAG_BADVALUE is an unexpected one for SNMPv1 GetRequest. Unexpected error codes returned for a particular environment will be translated into genErr by the core agent. The next subsections of this section will describe error translation performed by core agent for each environment. 3.2.1 Look Primitive 3.2.1.1 SNMPv2 GetRequest INTL_ERROR see explanation in 3.1. UAG_NOERROR -> noError, core agent will create a varbind using returned type and value in the output packet UAG_GENERR -> genErr UAG_NOSUCHOBJECT -> noError, core agent will create UAG_OBJECT_MAPPED_OUT a varbind with value noSuchObject in the output packet UAG_NOSUCHINSTANCE -> noError, core agent will create UAG_INSTANCE_MAPPED_OUT a varbind with value noSuchInstance in the output packet 3.2.1.2 SNMPv2 GetNextRequest and GetBulkRequest INTL_ERROR see explanation in 3.1. UAG_NOERROR -> noError, core agent will create a varbind using returned type and value in the output packet UAG_GENERR -> genErr UAG_NOSUCHOBJECT -> (a) If there are namespaces left unprocessed UAG_OBJECT_MAPPED_OUT core agent will continue search in the UAG_NOSUCHINSTANCE next namespaces, otherwise UAG_INSTANCE_MAPPED_OUT (b) noError, core agent will create a varbind with value endOfMibView in the output packet 3.2.1.3 SNMPv2 SetRequest INTL_ERROR see explanation in 3.1. DATA_ERROR -> genErr UAG_NOERROR -> noError UAG_GENERR -> genErr UAG_WRONGTYPE -> wrongType UAG_WRONGLENGTH -> wrongLength UAG_WRONGENCODING -> wrongEncoding UAG_WRONGVALUE -> wrongValue UAG_NOCREATION -> noCreation UAG_INCONSISTENTVALUE -> inconsistentValue UAG_RESOURCEUNAVAILABLE -> resourceUnvailable UAG_NOTWRITABLE -> notWritable UAG_INCONSISTENTNAME -> inconsistentName UAG_NOSUCHOBJECT -> noCreation UAG_NOSUCHINSTANCE UAG_OBJECT_MAPPED_OUT -> noAccess UAG_INSTANCE_MAPPED_OUT 3.2.1.4 SNMPv1 GetRequest and GetNextRequest INTL_ERROR see explanation in 3.1. UAG_NOERROR -> noError, core agent will create a varbind using returned type and value in the output packet UAG_GENERR -> genErr UAG_NOSUCHOBJECT -> noSuchName UAG_OBJECT_MAPPED_OUT UAG_NOSUCHINSTANCE UAG_INSTANCE_MAPPED_OUT 3.2.1.5 SNMPv1 SetRequest INTL_ERROR see explanation in 3.1. DATA_ERROR -> genErr UAG_NOERROR -> noError UAG_GENERR -> genErr UAG_WRONGTYPE -> badValue UAG_WRONGLENGTH UAG_WRONGVALUE UAG_INCONSISTENTVALUE UAG_WRONGENCODING -> the core agent will discard packet, update snmpStatsWrongEncodings counter UAG_RESOURCEUNAVAILABLE -> genErr UAG_NOCREATION -> noSuchName UAG_NOTWRITABLE UAG_INCONSISTENTNAME UAG_NOSUCHOBJECT UAG_NOSUCHINSTANCE UAG_OBJECT_MAPPED_OUT UAG_INSTANCE_MAPPED_OUT 3.3 Set Primitive 3.3.1 DO_PHASE1 The same set of rules is applicable here as in case of the look primitive in the same environment. 3.3.2 UNDO_PHASE1 The return value and index value are ignored. 3.3.3 SNMPv1 CMD_DO_COMMIT The same set of rules is applicable here as in case of the look primitive in the SNMPv1 SetRequest environment. 3.4 SNMPv1 CMD_UNDO_COMMIT The return value and index value are ignored. 3.5 SNMPv2 CMD_DO_COMMIT/CMD_UNDO_COMMIT The same set of rules is applicable here as in case of look primitive in the SNMPv2 SetRequest environment, if all CMD_DO_COMMIT calls return UAG_NOERROR. Otherwise the core agent will store the index passed back by failed set(CMD_DO_COMMIT). If all subsequent CMD_UNDO_COMMIT calls return UAG_NOERROR, then the core agent generates commitFailed and error-index is set equal to the stored index value. Otherwise the core agent generates undoFailed with error-index 0. 4 FUNCTION-CALL INTERFACES FOR LOADABLE MIB SERVERS This section describes the basic structures and primitives used in the core-agent-to-MIB-server interface when the MIB server is implemented as a loadable MIB server. 4.1 Basic Structures 4.1.1 The mib structure The mib is the basic structure of the interface. To be accessible to the core agent, each mib namespace mounted under the agent must be represented by a single entry in a doubly-linked list of mib elements. struct mib { struct mib *mib_prev; struct mib *mib_next; struct mib_server *mib_server; int mib_state; oid mib_low_bound[MIB_BOUND_LEN]; int mib_low_bound_len; oid mib_high_bound[MIB_BOUND_LEN]; int mib_high_bound_len; u_long (*mib_init)( const struct collection *, const struct mib* ); int (*mib_indicate)(int,u_short,int,int,int,int, const struct mib*); long (*mib_look)(oid*, int*, long, u_char**, u_int*, int*, u_short*, const struct mib*); long (*mib_set)(int, u_short, long*, const struct mib*); void (*mib_view)(u_short, const c_set*, const struct mib*); void (*mib_tick)(u_short, const struct mib*); u_char mib_entity[MAX_ENTITY_LEN]; int mib_entity_len; int mib_flag; u_long mib_specific; }; (mib *) mib_prev and (mib *) -- mib_next are pointers to the previous and next mib structures in the list. (mib *) NULL means that this structure is the first or final one in the list. (mib_server *) mib_server -- is a pointer to the structure defining the MIB server which accesses the namespace that this mib structure defines. (int) mib_state -- describes the current state of the namespace represented by this mib structure. The core agent maintains one of several legal values in mib_state, including: -- namespace is loaded, but it's inactive and can't be used: mib functions can't be called. -- namespace is loaded and active: mib functions can be called. -- namespace is loaded but disabled by the core agent due to some malfunction discovered by the core agent; mib functions can't be called. (oid) mib_low_bound[] -- is the low bound of the namespace represented by and subsumed under this mib entry. The core agent looks at mib_low_bound to decide which active MIB server should be called to get or set each variable in the current request. (int) mib_low_bound_len is the length of mib_low_bound. (oid) mib_high_bound[] -- the high bound of the namespace represented by this mib entry. The core agent uses mib_high_bound and mib_low_bound to detect whether the namespace of a newly- mounted MIB server overlaps the namespace of any other MIB server. If so, then the MIB server will not be mounted. (int) mib_high_bound_len -- the length of mib_high_bound. ((u_long) ()*) mib_init -- pointer to the init primitive function in the MIB server for the namespace represented by this mib element. mib_init() returns (u_long) 0 on failure, else some non-zero unsigned long value of interest only to the MIB server. The core agent stores this value into mib_specific. ((int) ()*) mib_indicate -- pointer to the indicate primitive function in the MIB server for the namespace represented by this mib element. mib_indicate() returns (int) 0 on failure and (int) 1 on success. ((long) ()*) mib_look -- pointer to the look primitive function in the MIB server for the namespace represented by this mib element. mib_look() returns (long) error code on failure. ((long) ()*) mib_set -- pointer to the set primitive function in the MIB server for the namespace represented by this mib element. ((void) ()*) mib_view -- pointer to view primitive function in the MIB server for the namespace represented by this mib element. mib_view() returns no value. ((void) ()*) mib_tick -- pointer to the tick primitive function in the MIB server for the namespace represented by this mib element. mib_tick() returns no value. (u_char) mib_entity[] -- the local entity name. (int) mib_entity_len -- the length of mib_entity. (int) mib_flag -- used internally by the core agent. (u_long) mib_specific -- used by MIB-server code in some implementation-specific way. The value of mib_specific is the only way the external MIB-server primitive supporting several entities and/or namespaces can detect what entity and namespace is referenced in current invocation. It is the responsibility of the MIB server to return some useful value from the mib_init() function which the core agent will then store into mib_specific. 4.1.2 c_set Structure c_set is a bitmap representation of views defined as follows. #define MAX_SET 32 #define C_SET_LEN ((MAX_SET+7)/8) #define C_SET_BITS (C_SET_LEN*8) typedef struct c_set_tag { u_char c_bits[C_SET_LEN]; } c_set; Each view family has a bit assigned in the c_bits array. The offset has to be greater or equal to 1 andless or equal that MAX_SET. The offset value 0 denotes non-existing view. 4.1.3 The Collection The "collection," so called, includes a set of data of interest both to the core agent and to the MIB server. A pointer to this collection structure is passed as the first argument to the mib_init() primitive. struct collection { void **c_view_head; c_set *c_view_set; u_long c_a_id; void *c_a_attach; u_long c_b_id; void *c_b_attach; u_long c_core_id; }; (void **) c_view_head -- a pointer to the head of the list of views. (c_set *) c_view_set -- a pointer to the bit representation of all views. (u_long) c_a_id -- the handle of shared-memory region A (see below for definitions of shared memory regions). (void *) c_a_attach -- pointer to shared-memory region A in the core agent's address space. (u_long) c_b_id -- id handle shared-memory region B. (void *) c_b_attach -- pointer to shared-memory region B in the core agent's address space. (u_long) c_core_id -- id of the input message queue (window) of the core agent. 4.2 Function Definitions The formal definitions of all core-agent-to-MIB-server primitives are given below with reference to a fictitious example namespace called the "abc" namespace. The agent calls the functions by reference to a pointer in the MIB server's mib structure, so the "name" is that of the pointer, not the MIB server's actual function name. As we said above, the actual name of the function is a matter of choice for MIB server designer. We called tham abc_xxxx just to logically link the primitive and namespace. The final parameter of each primitive function call is a pointer to the mib entry itself which allows MIB-server access to its mib entry. 4.2.1 Init Primitive Function Called by the core agent at the time the MIB server is mounted and before any other primitives are called. Syntax: u_long init( struct collection *colp, struct mib* mib ); (collection *) colp -- a pointer to the collection structure described in 4.1.4. struct mib* mib -- pointer to the mib entry itself which allows MIB-server access to its mib entry. Returns: (u_long) 0 on failure and some (u_long) non-zero value on success. The core agent stores this returned value in mib_specific for later reference and use by the MIB server. If the MIB server returns zero, then this namespace is marked inactive and won't be referenced again. The return value is otherwise not interpreted in any way by the core agent. Description: The core agent issues init at the time the MIB server is mounted and before any other primitives are called. 4.2.2 Indicate Primitive Function Called by the core agent for each namespace to indicate the start and end of processing for each received request packet. Syntax: int indicate( int cmd, u_short cid, int version, int rw, int exact, int view_off, int temp_domain, struct mib* mib ); (int) cmd -- CYCLE_INDICATE_START or CYCLE_INDICATE_END (u_short) cid -- 16-bit unsigned cycle id (int) version -- is either SNMPV1 or SNMPV2 (int) rw -- is either READ or WRITE (int) exact -- is either TRUE or FALSE (int) view_off -- is the bit offset of the current view in c_Set (int) temp_domain -- is either CURRENTTIME, RESTARTTIME or CACHETIME struct mib* mib -- pointer to the mib entry itself which allows MIB-server access to its mib entry. Returns: indicate returns (int) 0 on failure and (int) 1 on success. If indicate returns failure, then the core agent will disable the MIB server, in all its instantiations in mib, and (if applicable) unload the MIB server. If indicate(START) returns 0, then the core agent will respond with error-status genError and error-index pointing to the first variable in a packet which has to be looked for in the mibs's name space. If indicate(END) returns 0, it does not affect the result of current packet processing, however in both cases core agent will disable the all mibs representing current MIB server. Description: The core agent invokes the MIB server's indicate function for each namespace when a request packet is delivered from the communications layer, and before any other MIB-server functions are invoked for that namespace and packet; and when all the varbinds in that packet for each namespace have been processed and no further MIB-server functions will be called for that packet. The function intends to let each MIB server synchronize on the arrival of packets and the end of packet processing. (int) cmd takes the value CYCLE_INDICATE_START to signal the start of packet processing and CYCLE_INDICATE_END to signal the end of the current packet. Recall that there may be several namespaces represented by a single MIB server and therefore several instantions of that MIB server in mib. What happens in this case? Imagine a MIB server which represents two namespaces and which therefore has two entries in mib: "abc_A" and "abc_B." Suppose further that the order of variables in a GetRequest pdu is as: abc_A_1, abc_A_2, abc_B_1, abc_A_3, abc_B_2 Processing will be as: abc_A_indicate(START) -- server is told of a new packet referencing the abc_A namespace. abc_A_look(abc_A_1) -- process GetRequest of abc_A_1; abc_A_indicate(START) is guaranteed to be invoked before abc_A_look(). abc_A_look(abc_A_2) -- process GetRequest of abc_A_2. abc_B_indicate(START) -- core agent finds a reference to the abc_B namespace, again tells server of this packet. abc_B_look(abc_B_1) -- process GetRequest of abc_B_1; again, abc_B_indicate(START) is guaranteed to be invoked before abc_B_look(). abc_A_look(abc_A_3) abc_B_look(abc_B_2) abc_B_indicate(END) -- the order of invocations to abc_A_indicate(END) abc_X_indicate(END) is unpredictable, but it is guaranteed that there will be no call to abc_X_look() after any call to abc_X_indicate(END) for any namespace. In order to distinguish the very first call (and the very beginning of the packet processing) cid is used. This is 16-bit unsigned value which will never be 0, this value is incremented for every packet processed, it will wrap over and it will skip 0 in the process. So, the MIB-server can detect the very beginning of the packet. The same value used by core-agent to detect packets delivered after timeout. i_rw and i_exact together tell the MIB server what request is present in this packet. If i_rw is READ and i_exact is TRUE, then the request is GET; if i_rw is READ and i_exact is FALSE, then the request is GET-NEXT or GET-BULK. if i_rw is WRITE, then the request is SET and i_exact is ignored. 4.2.3 Look Primitive Function Invoked for all MIB-access operations. Syntax: long look( oid *name, int *namelen, long index, u_char **pval, u_int *type, int *len, u_long *ph, struct mib *mib ); Get Transaction: name -- requested name namelen -- pointer to its length index -- of current variable (ignored) pval -- *pval will point to unencoded value upon return type -- *type will contain extended type of variable found upon return len -- *len will contain length of variable value upon return ph -- ignored struct mib* mib -- pointer to the mib entry itself which allows MIB-server access to its mib entry. Next Transaction: name -- requested name upon return will contain thefound name namelen -- pointer to its length index -- of current variable (ignored) pval -- *pval will point to unencoded value on return type -- *type will contain extended type of variable found len -- *len will contain length of variable value ph -- ignored struct mib* mib -- pointer to the mib entry itself which allows MIB-server access to its mib entry. Write Transaction: name -- name of variable namelen -- pointer to its length index -- of current variable, can be stored for following use pval -- *pval points to BER encoded value type -- type points to the ASN type of value len -- len points to the asn_length of value ph -- *ph will contain the non-zero handle if applicable struct mib* mib -- pointer to the mib entry itself which allows MIB-server access to its mib entry. Returns: The value returned by look() is either UAG_NOERROR for success or some error code from Section 3. If this return value is an error code, then the core agent discards the other values passed back by the MIB server. Description: The look function is invoked for all MIB-access operations: GetRequest, GetNextRequest, GetBulkRequest, and SetRequest. The core agent communicates the type of transaction called out in the request packet in the invocation of indicate(), so the transaction type isn't repeated in the look() invocation. The MIB server has to support instance-level granularity while calculating access rights for the particular name in all cases. Note: 1. At the time abc_look is called, the core agent has already checked for valid tag and length encodings and no message overflow. 2. The SMI type is used here, not an extended type. It is the responsibility of the MIB-server designer to provide appropriate handling for the case if pseudo-type is used by variables of this MIB-server. 4.2.4 Set Primitive Function Used to set variables. Syntax: long abc_set( int cmd, u_long handle, long *index, struct mib *mib ); cmd -- can have four values CMD_DO_PHASE1, CMD_UNDO_PHASE1, CMD_DO_COMMIT, CMD_UNDO_COMMIT handle -- non-zero handle returned by mib server. index -- points to the index of the variable for which mib server the returned a non-zero handle to allow the mib server to change it in case of an error. struct mib* mib -- pointer to the mib entry itself which allows MIB-server access to its mib entry. Returns: Return values of this function are described in the section 3. Description: Described in the section 3. 4.2.5 View Primitive Function Used to set variables' views. Syntax: void view(u_short vid, const c_set *cs, struct mib*mib); vid -- is non-zero 16-bit value indetifying particular view_update operation: each time views had been changed this value will be incremented. This value allows MIB server handling multiple namespaces save some time. cs -- is a bitmap representation of changed views. struct mib* mib -- pointer to the mib entry itself which allows MIB-server access to its mib entry. Returns: Nothing. Description: The general assumption is that views will be changed relatively seldom, so the module can remember what variable is mapped in and what variable is mapped out by each view and use this information in order to improve performance. From the other side module implementer can elect to check each variable against current view on as need basis in the latter case this function can be an empty one. This function will be called by core-agent right after the Set Request which changed views had been processed and before any other packet. It is guarantied that this function will be called right after abc_init() with all bits in cs set to 1. 4.2.6 Tick Primitive Function Informs the MIB server of the passage of time. Syntax: void abc_tick(u_short tid, struct mib *mib); u_short tid -- holds a non-zero 16-bit value incremented by one before each function invocation. struct mib* mib -- pointer to the mib entry itself which allows MIB-server access to its mib entry. Returns: Nothing. Description: The core agent invokes the tick function once every few (nominally ten) seconds between packets to allow the MIB server to perform any needed time-related functions. The tick function is never called while any packet is being processed, but only between request packets. 5 MESSAGE-PASSING INTERFACE FOR EXTERNAL MIB SERVERS This section describes the invocation of primitives as it's realized for external MIB servers. The message-passing mechanism in this paper uses a combination of native MS-Windows messaging and shared memory. 5.1 Combined Message/Shared Memory Mechanism This section makes some general observations about about data arrangements, configuration information, common message format, and error codes. There is a constant message type defined for each interface primitive described above and for each corresponding response, thus: Primitive Primitive Invocation Primitive Response ------------ ---------------------- ---------------------- init() WM_SNMP_INIT_REQ WM_SNMP_INIT_RSP indicate() WM_SNMP_INDICATE_REQ WM_SNMP_INDICATE_RSP look() WM_SNMP_LOOK_REQ WM_SNMP_LOOK_RSP set() WM_SNMP_SET_REQ WM_SNMP_SET_RSP view() WM_SNMP_VIEW_REQ WM_SNMP_VIEW_RSP tick() WM_SNMP_TICK_REQ WM_SNMP_TICK_RSP There are four shared-memory areas involved in each exchange of information known as Areas A, B, C, and D. -- Area A used to store the received SNMP packet to be processed; -- Area B is used to store the list of views known to the system and the bitmap representation for these views; -- Area C is used by the core agent as a link area for the core-agent-to-MIB-server transactions; and -- Area D is used by the MIB server as a link area for core- agent-to-MIB-server transactions. The MIB server never writes into areas A,B, and C; the MIB server writes into Area D only. Likewise, the core agent never writes into Area D but into Areas A, B, and C only. Each message sent in either direction (i.e., to the MIB server by the core agent or to the core agent by the MIB server) must contain two valid parameters to be authentic: the source window id and the individual message id. Message id for WM_SNMP_INDICATE_REQ and its response is the cycle identifier (cid from the indicate() primitive description above). For all other messages, the message id is an unsigned non-zero 16-bit value assigned by the core agent. This value is encoded as wParam of each message passed across the interface, unless otherwise specified below. The MSW and LSW of lParam are used to transfer different data depending upon the particular message. We'll show the messages as: If lParam is used to pass a single piece of data, we'll show it as: We omit wParam value if it contains the message id. 5.2 Messages and Data Structures The messaging mechanism in this section is a direct mapping of the functional interface described above, so this section will not repeat any general information about the primitives themselves. The MIB server primitives return the same values as described above, except for the init() primitive. The return value for init() is shown explicitly in the diagram. All pointers used as data types are FAR pointers to core-agent address space. In the diagrams below, we place the core agent on the left side of the page and the MIB server on the right. 5.2.1 Init Area C format: Offset Type Contents ------------------------------------------------------------ 0 u_long Length of the entity name 4 u_char[] Entity name 36 u_long Length of low boundary (in oids) 40 oid[] Low boundary 120 Pointer Pointer to the head of list of views* 124 Pointer Pointer to bit representation of views* 128 u_long Id of shared area A 132 Pointer Pointer to shared area A 136 u_long Id of shared area B 140 Pointer Pointer to shared area B 144 u_long Id of shared area C 148 Pointer Pointer to shared area C 152 u_long current value of sysUpTime * Located in area B Area D format: Offset Type Contents --------------------------------------- 0 u_long mib_specific ----- -----> <---- ----- In a success return, WM_SNMP_INIT_RSP includes the handle of Area D else 0. Note: If the MIB server represents several namespaces, then all init responses use the same Area D and the id of Area D must be returned by all WM_INIT_RSP messages. 5.2.2 Indicate A) indicate(START) Area C format: Offset Type Contents --------------------------------- 0 u_long mib_specific 4 u_long version 8 u_long rw 12 u_long exact 16 u_long view_off 20 u_long curr_time ----- <1 |> -------> <--------- B) indicate(END) Area C format: Offset Type Contents --------------------------------- 0 u_long mib_specific -----<2 |> --------> <--------- 5.2.3 Look a) get transaction Area C format: Offset Type Contents --------------------------------- 0 u_long mib_specific 4 u_long Name length 8 oid[] Name Area D format (filled by MIB server): Offset Type Contents --------------------------------------------- 0 u_long Value type 4 u_long Value length (unencoded) 8 any Value -----<1 | >---------> <---------- b) next transaction Area C format: Offset Type Contents --------------------------------- 0 u_long mib_specific 4 u_long Name length 8 oid[] Name Area D format (filled by MIB server): Offset Type Contents -------------------------------------------- 0 u_long Name length 4 oid[] Name found 516 u_long Value type 520 u_long Value length (unencoded) 524 any Value -----<2 |>--------> <--------- b) write transaction Area C format: Offset Type Contents ------------------------------------- 0 u_long mib_specific 4 u_long Name length 8 oid[] Name 520 u_long Value type 524 u_long ASN value length 528 Pointer Value* * Located in area A: -----<3 | index>------------> <--------- Area D format (filled by MIB server): Offset Type Contents ------------------------------- 0 u_long handle | 0 -----<3 | index>----> <--------- 5.2.4 Set a) PHASE1 Area C format: Offset Type Contents --------------------------------- 0 u_long mib_specific 4 u_long handle -----<1 | index>---------> <--------- a) UNDO_PHASE1 Area C format: Offset Type Contents ---------------------------------- 0 u_long mib_specific 4 u_long handle -----<2 |>---------> <--------- a)COMMIT Area C format: Offset Type Contents --------------------------------- 0 u_long mib_specific 4 u_long handle -----<3 | index>---------> <--------- a) UNDO_COMMIT Area C format: Offset Type Contents --------------------------------- 0 u_long mib_specific 4 u_long handle -----<4 |>---------> <--------- 5.2.5 View Area C format: Offset Type Contents ---------------------------------- 0 u_long vid 4 u_long mib_specific 8 c_set Changed views -----<|>----> <----<|>----- 5.2.6 Tick Area C format: Offset Type Contents --------------------------------- 0 u_long tid 4 u_long mib_specific 8 u_long sysUpTime -----<|>----> <----<|>----- 6 PROCEDURES FOR MOUNTING AND UNMOUNTING OF MIB SERVERS 6.1 Loadable MIB Servers Each loadable MIB server defines three functions: int server_reg_init(char *init_str); int server_reg_next(struct mib *mib); int server_unreg(int reason); The function server_reg_init() performs all the operations needed to mount itself. init_str is initialization data for use at MIB-server mounting time and which depends on the needs of the MIB server. The value of init_str is retrieved by the core agent from the configuration database, and this value is passed to this function without any further processing. This value can be the name of MIB server private configuration file or anything else. The server_reg_init() function returns 0 on failure else 1. Upon successful invocation of server_reg_init(), the core agent allocates a new mib structure entry and passes a pointer to it as a parameter to function server_reg_next(). That function stores appropriate values in the following members of the pointed mib_list entry: mib_low_bound, mib_high_bound, mib_low_bound_len, mib_high_bound_len, mib_init, mib_indicate, mib_look, mib_set, mib_view, mib_tick, mib_entity, and mib_entity_len. There are three legal return values from the function: -- MSR_SUCC on success and if there are more namespaces to be registered, in which case the function is called again with a pointer to another newly-assigned mib_list entry; -- MSR_NOMORE on success if there are no further namespaces to register; -- MSR_ERROR on failure. The function server_unreg causes the MIB server to perform any operations needed to terminate its activities. The reason parameter can take three value: -- MSU_NOMOUNT, meaning there is some overlapping namespace; -- MSU_COMMAND, meaning a command to unmount the MIB server was received; -- MSU_OTHER, meaning any other case. 6.2 External MIB Servers There are two ways the external MIB server can be brought to life. It can be started by the core agent or it can be started by the user as part of any application. The same handshake procedure is used in either case. The external MIB server must SendMessage() WM_SNMP_IAMH_REQ upon startup. Th lParam of this message contains the handle of shared memory holding MIB-server data. The core agent then responds by sending WM_SNMP_IAMH_RSP in a way similar to DDE initial handshake. This is a signal for MIB server to wait for WM_SNMP_INIT_REQ. If this WM_SNMP_IAMH_RSP message fails to appear, then the MIB server repeats the attempt for some implementation-specific number of times after some implementation-specific interval. If no connection to the core agent can be made, then the MIB server frees its shared data area. If there is a successful connect, then the core agent frees data area. Data Area format: Offset Type Contents ------------------------------------------------------------ 0 char Unique name of MIB server, null terminated 80 char Description of MIB server, null terminated 160 u_long Number of namespaces MIB server will handle 164 u_long Length of entity name of the first namespace 168 u_char Entity name of the first namespace 200 u_long Length of low bound of the first namespace* 204 oid[] Low boundary of the first namespace 284 u_long Length of high bound of the first namespace* 288 oid[] High boundary of the first namespace 368 u_long Length of entity name of second namespace .... * Lengths of the boundaries are in oids The core agent sends WM_SNMP_TERM_REQ message to the MIB server if it wishes to mib server to terminate. The lParam of this message contains reason for termination. There are three reasons defined at present time: MSU_COMMAND -- the command to terminate this MIB server was received from management information source. MSU_NOMOUNT -- terminate MIB server because it is impossible to mount it. MSU_OTHER -- for all other reasons to terminate. The external MIB server sends WM_SNMP_IAMO_REQ message if wishes to terminate itself. However, such a message is not required: the core agent takes the appropriate implementation-specific actions in case of a nonresponding external MIB server. 7 DECLARATIONS 8 OTHER ISSUES This section describes the execution environment of MIB servers and the integration of WinSNMP/Agent with DMTF. 8.1 Common services There are several common services provided with the core agent, outlined below. 8.1.1 OID Comparison The compare() function compares two oids. int compare(oid *name1, int len1, oid *name2, int len2) The function returns 1 if name1 is lexically greater than name2; returns 0 if names are the same; and returns -1 otherwise. 8.1.2 Manipulation of View Representations These functions manipluate the bitmap representations of views. void c_rise(int off, c_set *cs) The function will set the bit representing offset off in the c_set structure pointed by cs. void c_clear(int off, c_set *cs) The function will clear the bit representing offset off in the c_set structure pointed by cs. void c_zero(c_set *cs) The function will clear all bits of the c_set structure pointed by cs. int c_have(int off, c_set *cs) The function will return the value of the bit representing offset off in the c_set structure pointed by cs. int c_avail(int off, c_set *cs) The function will return the offset of first zero bit in the c_set structure pointed by cs. If there is no such bit, the function will return 0. int c_is_zero(c_set *cs) The function will return 1 if all bits of the c_set structure pointed by cs are 0s, otherwise it will return 0. 8.1.3 Decode an Encoded Variable int set_variable( u_char *var_val, u_int var_val_type, int var_val_len, u_char *stat_p, int stat_len long *perr ) This function will parse the ASN.1-encoded (tag,len,value) value pointed by var_val of type var_val_type and of length var_val_len into the buffer pointed by stat_p; the length of buffer is in stat_len. In case of any error this function will return -1 and it will set *perr to the appropriate intermediate error code. Otherwise it will return the length of decoded value and *perr will be set to UAG_NOERROR. 8.1.4 Views int check_view( oid *name, int name_len, int known_len, void *view_list, int view_off ) This function will evaluate whether a name is mapped into or out of the view represented by view_off and view_list. Name is the name in question, name_len is the total length of the name, known_len is the length of the part of the name which is known currently. For example, name_len can be the length of the variable name including the index part; and known_len can be the length of the object part of the name. View_list is a pointer to the list of view entries to be used. View_off is the bit representation of the view of interest. This function will return CHV_YES if the name is mapped into the view; CHV_NO if the name is mapped out of the view; and CHV_MAYBE if there is not enough information to detect whether it is mapped in or out. There is a variant of this function intended for use by external MIB servers. int check_view_sh( oid *name, int name_len, int known_len, void *view_list, int view_off, int delta ) The problem to be addressed in this case is that the shared memory area can be attached at different addresses in the core- agent and MIB-server address spaces. To solve this problem, the MIB server must provide one more parameter, called delta. Delta is the difference between the address of the same shared-memory location in the MIB-server and core-agent address space. Thus: Pserver = Pcore + delta The value of view_list is a a pointer to a list of viewEntries taken in core-agent address space. 8.1.5 RowStatus State Machine There is also a RowStatus state machine provided by the core agent. long rs_machine( int new_entry, long *new_status, long status_index, long old_status, int consistent, long cons_index, long *pind, u_long *tmout ) This function returns an intermediate error code for the current operation in accordance with the RowStatus transition table from RFC 1443. New_entry is 0 if the current operation is a modification of an existing entry, otherwise it is 1. If the status element is provided within the current packet, then *new_status is equal to this value on input; otherwise *new_status is equal to the old value of the status element on input. The *new_status is the new value of status element on output, which can be different from the value provided in the packet (CreateAndGo -> active) or different from the old value (notInService -> notReady). If the status element is provided within the current packet, then status_index is its index in the packet, otherwise it is 0. Old_status is the old value of the status element if this is a modification of an existing row, otherwise it is ignored. Consistent is 1 if the rest of new row's values (except status element) are consistent, otherwise it is 0. If consistent is 0 and the inconsistency is related to any of the variables (except status element) in the current packet cons_index is the index of this variable otherwise it is 0. The *pind is the value of index of the first row variable in the current packet. The *pind is value of error-index on output. The *tmout contains the time when this row has to be discarded due to timeout in notInServece or notReady status. 8.1.6 Time Service There is a time service provided by agent, u_long local_up_time(void) will give a sysUpTime value and u_long local_time(void) will give the local Epoch time in seconds. These time functions are available only for loadable MIB servers; each external MIB server needs to maintain its own notion of time. The current value of sysUpTime is passed to each external MIB servers every 10-15 seconds in a WM_SNMP_TICK message in order to maintain a uniform sysUpTime notion throughout the system. 8.1.7 Traps There are trap sending functions provided by the core agent. Traps are limited to well-known traps only. void send_trap2(int trap, u_long data) This function will add this trap to the list of SNMPv2 trap requests which will be sent by core agent in the interval between packets. Trap is a value of last oid of well-known trap. The data is an appropriate ifIndex value if trap is linkDown or linkUp or it is IP address in network byte order if the trap is egpNeighborLoss, otherwise it is ignored. void send_trap1(int trap, u_long data) This function will add this trap to the list of SNMPv1 trap requests which will be sent by the core agent in the interval between packets. Trap is the value of a generic trap; data has the same meaning as above. These functions can be used by loadable MIB servers which can call these functions from inside their tick primitive functions. All other trap sources must use the message mechanism. There is a WM_SNMP_TRAP2 message defined and such a message can be sent to the core agent by any source. wParam value of this message is the same as trap in send_trap2(); the lParam value of this message is the same as data in send_trap2(). There is also a WM_SNMP_TRAP1 message defined and such a message can be sent to the core agent by any source. wParam value of this message is the same as trap in send_trap1(); the lParam value of this message is the same as data in send_trap2(). 8.2 Agent Configuration MIB The agent can be dynamically reconfigured, so the next logical step is to allow remote control over agent configuration. An appropriate MIB needs to be developed to allow this remote configuration and this MIB is to be added when it emerges. 8.3 Interface to DMTF This section describes the proposed interface between the WinSNMP/Agent and DMTF. Because a DLL implementation does not offer the capabilities that DMTF requires, DMTF must be implemented as an external MIB server. This external MIB server will assume the role of a DMTF management application. On startup, the DMTF MIB server does the following: 1. Register itself as a management application with the DMI Service Layer. 2. Issue several list commands to obtain information about availability of DMTF managed enitities; build appropriate transaction tables (or functions) to translate SNMP names into DMI names. 3. Build appropriate namespaces representing discovered DMTF variabless, build a registration data block, and register itself with core agent by sending WM_SNMP_IAMH_REQ message. The mapping of of WinSNMP/Agent primitives onto DMI primitives is straightforward. There is nothing DMTF specific which has to be done by MIB-server in order to perform init(), indicate(), view(), and tick() primitives. The mapping of the get and next transactions of the look() primitive is clear: perform name translation and call the appropriate DMTF list function(s). Then call the appropriate DMTF get-attribute function(s), store value in the static area and pass the pointer to this value, type, and length to the core agent. The mapping of the write transaction is as: Check the new value. Perform name translation and call the appropriate DMTF list function(s), then call the appropriate DMTF get-attribute function(s). Store the old and new values in a shadow area and pass the non-zero handle (if needed) to the core agent. The function set(DO_PHASE1) is directly mapped onto the DMTF reserve commands. The function set(UNDO_PHASE1) is directly mapped onto the DMTF release commands. The function set(DO_COMMIT) is directly mapped onto the DMTF set commands. The function set(UNDO_COMMIT) is mapped onto the DMTF set commads restoring the old values. All non-solicited events indicated to the DMTF MIB server are translated by the MIB server into WM_SNMP_TRAP1_REQ and WM_SNMP_TRAP2_REQ messages. 9 References 9.1 Requests for Comment 1089 M. Schoffstall, C. Davin, M. Fedor, and J. Case: SNMP over Ethernet, Feb 1989. 1155 M. Rose and K. McCloghrie: Structure and Identification of Management Information for TCP/IP-based Internets, May 1990. 1156 K. McCloghrie: Management Information Base for Network Management of TCP/IP-based Internets, May 1990. 1157 J. Case, M. Fedor, M. Schoffstall, and C. Davin: Simple Network Management Protocol (SNMP), May 1990. 1213 K. McCloghrie and M. Rose: Management Information Base for Network Management of TCP/IP-based Internets: MIB-II, Mar 1991. 1215 M. Rose: Convention for defining traps for use with the SNMP, Mar 1991. 1227 M. Rose: SNMP MUX protocol and MIB, May 1991. 1270 F. Kastenholz: SNMP communications services, Oct 1991. 1283 M. Rose: SNMP over OSI, Dec 1991. 1284 J. Cook: Definitions of Managed Objects for the Ethernet- like Interface Types, Dec 1991. 1285 J. Case: FDDI Management Information Base, Jan 1992. 1286 E. Decker, P. Langille, A. Rijsinghani, and K. McCloghrie: Definitions of Managed Objects for Bridges, Dec 1991. 1289 J. Saperia: DECnet Phase IV MIB Extensions, Dec 1991. 1303 K. McCloghrie and M. Rose: A Convention for Describing SNMP-based Agents, Feb 1992. 1351 J. Davin, J. Galvin and K. McCloghrie: SNMP Administrative Model, Jul 1992. 1352 J. Galvin, K. McCloghrie, and J. Davin: SNMP Security Protocols, Jul 1992. 1381 D. Throop and F. Baker: SNMP MIB Extension for X.25 LAPB, Nov 1992. 1382 D. Throop: SNMP MIB Extension for the X.25 Packet Layer, Nov 1992. 1407 T. Cox and Kaj Tesink: Definitions of Managed Objects for the DS3/E3 Interface Type, Jan 1993. 1414 M. St.Johns and M. Rose: Identification MIB, Jan 1993. 1418 M. Rose: SNMP over OSI, Feb 1993. 1419 G. Minshall and M. Ritter: SNMP over AppleTalk, Feb 1993. 1420 S. Bostock: SNMP over IPX, Feb 1993. 1441 J. Case, K. McCloghrie, M. Rose, and S. Waldbusser: Introduction to version 2 of the Internet-standard Network Management Framework, Apr 1993. 1442 J. Case, K. McCloghrie, M. Rose, and S. Waldbusser: Structure of Management Information for version 2 of the Simple Network Management Protocol (SNMPv2), Apr 1993. 1443 J. Case, K. McCloghrie, M. Rose, and S. Waldbusser: Textual Conventions for version 2 of the Simple Network Management Protocol (SNMPv2), Apr 1993. 1444 J. Case, K. McCloghrie, M. Rose, and S. Waldbusser: Conformance Statements for version 2 of the Simple Network Management Protocol (SNMPv2), Apr 1993. 1445 J. Galvin and K. McCloghrie: Administrative Model for version 2 of the Simple Network Management Protocol (SNMPv2), Apr 1993. 1446 J. Galvin and K. McCloghrie: Security Protocols for version 2 of the Simple Network Management Protocol (SNMPv2), Apr 1993. 1447 K. McCloghrie and J. Galvin: Party MIB for version 2 of the Simple Network Managment Protocol (SNMPv2), Apr 1993. 1448 J. Case, K. McCloghrie, M. Rose, and S. Waldbusser: Protocol Operations for version 2 of the Simple Network Managment Protocol (SNMPv2), Apr 1993. 1449 J. Case, K. McCloghrie, M. Rose, and S. Waldbusser: Transport Mappings for version 2 of the Simple Network Managment Protocol (SNMPv2), Apr 1993. 1450 J. Case, K. McCloghrie, M. Rose, and S. Waldbusser: Management Information Base for version 2 of the Simple Network Managment Protocol (SNMPv2), Apr 1993. 1451 J. Case, K. McCloghrie, M. Rose, and S. Waldbusser: Manager-to-Manager Management Information Base, Apr 1993. 1452 J. Case, K. McCloghrie, M. Rose, and S. Waldbusser: Coexistence between version 1 and version 2 of the Internet- standard Network Management Framework, Apr 1993. 9.2 Other Reference Sources B. Natale: Windows SNMP: An Open Interface for Programming Network Management Applications using the Simple Network Management Protocol under Microsoft Windows, v1.0, Sep 13 1993. S. Pendse: WinSNMP/MIB: An Interface for Programming using the Management Information Base of SNMP under Microsoft Windows, v1.0d, Oct 1993. D. Perkins: Understanding SNMP MIBs, Revision 1.1.5, Jul 7 1992. M. Rose: The Simple Book: An Introduction to Management of TCP/IP-base Internets, Prentice-Hall, 1990. M. Rose: The Simple Book: An Introduction to Internet Management, Prentice-Hall, 1994. W. Stallings: SNMP, SNMPv2, and CMIP: The Practical Guide to Network Management Standards, Addison Wesley, 1993. APPENDIX A MIB-SERVER CODE EXAMPLE