Windows SNMP Agent The Open Interface for Programming the Extensible SNMP V1 and V2 Agent Under Microsoftr Windows™ Version 0.3 1 December 1994 Aleksey Romanov Paul Freeman Associates, Inc William H. White Digital Equipment Corporation Pete Wilson Paul Freeman Associates, 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@mailbag.intel.com. Requests to be added to the WinSNMP mailing list should be addressed as: To: Majordomo@mailbag.intel.com Subject: Message subscribe winsnmp The binary of the WinSNMP Agent demonstrates the feasilbility of this specification. You can obtain this form of the Agent via anonymous FTP. The demonstration package includes an implementation of the core Agent for the SNMP v1 and v2; the SNMP and System groups of MIB-II; and the required and hrSWRun groups from the Host Resources MIB (RFC 1514). To FTP this binary: ftp ftp.std.com // 192.74.137.7 user ftp or anonymous pass your e-mail address cd vendors/snmp/windows-agent binary Then mget all of the files in that directory. The README file gives further instructions. Authors' Contact Information Aleksey Romanov ralex@world.std.com William H. White b_white@ranger.enet.dec.com Pete Wilson pwilson@world.std.com Version history and status of this version 0.1 1 Jan 1994 Original offering. 0.2 1 Sep 1994 1. Version 0.2 reflects changes made during the implementation of the WinSNMP Agent and its accompanying MIBs (pw). 2. Added feature allowing overlapping MIBs/namespaces (pw). 0.3 1 Dec 1994 Corrected winsnmp mail-list subscription info; add support for hrSWRun Group in HR MIB accompanying WESA, the Windows Extensible SNMP Agent. 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 6.3 Core Agent Mutual Exclusions 7 DECLARATIONS 7.1 Structure Declarations 7.2 Function Prototypes 8 OTHER ISSUES 8.1 Common Service Primitives 8.2 Interface to DMTF 9 REFERENCES APPENDIX A MIB-server code example 1 INTRODUCTION 1.1 Identification This paper is the WinSNMP/Agent Specification, Version 0.2. It suggests 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 Microsoftr Windows™ family of operating systems. 1.2 Trademarks Microsoft, MS, and MS-DOS 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 ™, 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 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 The 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 The 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. Namespaces may overlap. 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 The 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. 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 whatever 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 the appropriate contextLocalEntity. 1.7.3 Primitives Each MIB server implements six callable functions, termed "primitives," which operate on its namespaces. 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 ú 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 reference (pointer or handle) of the view list, the index of the current view, and the 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 static 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 from the two get transactions. In a write transaction, the core agent delivers the name of the variable to be set, its unencoded value, and the type and the length of the value. The MIB server then must find this variable instance, check access rights, check the value, the type, 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 calls 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) in reverse order 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,...) were 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. The core agent then calls set(UNDO_PHASE1, handle, index) for all stored triplets in reverse order. If all calls to set(DO_COMMIT, ...) were successfully performed, the core agentcalls set(DO_RELEASE, handle, index) for all stored triplets in reverse order toinsure orderly release of all locks and resources reserved. 2.1.5 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 2.2.2 GetRequest Processing 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() ------> lok for variable instance, } check access, pass value add result to output packet; back } 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 pointer to packet to the communications layer 2.2.3 SetRequest Processing 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, pass handle back if (handle != 0) { 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 triplets in reverse order){ mib->mib_set(DO_RELEASE, handle, index) --> release resources } 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: WESA_TYPE_UINT32V1. There are some SNMPv1 mibs which use objects of type INTEGER (0...4294967295). The value of this type must be encoded in 5 bytes if it is greater than or equal to 0x80000000. WESA_TYPE_UINT32V1, defined as (0x100 | WESA_TYPE_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 returned to the core agent, the agent generates genErr for delivery to the NMS. The core agent performs no 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. WESA_ERR_INTERNAL-- 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. WESA_ERR_NOERROR -- same as SNMPv2 noError WESA_ERR_GENERR -- same as SNMPv2 genErr WESA_ERR_WRONGTYPE -- same as SNMPv2 wrongType WESA_ERR_WRONGLENGTH -- same as SNMPv2 wrongLength WESA_ERR_WRONGVALUE -- same as SNMPv2 wrongValue WESA_ERR_NOCREATION -- same as SNMPv2 noCreation WESA_ERR_NOACCESS -- same as SNMPv2 noAccess WESA_ERR_INCONSISTENTVALUE -- same as SNMPv2 inconsistentValue WESA_ERR_RESOURCEUNAVAILABLE -- same as SNMPv2 resourceUnvailable WESA_ERR_NOTWRITABLE -- same as SNMPv2 notWritable WESA_ERR_INCONSISTENTNAME -- same as SNMPv2 inconsistentName WESA_ERR_NOSUCHOBJECT -- there is no object which can be matched to the requested name with max_access != non-accessible and either request type is Get, GetNext or Get-Bulk, or request version is SNMPv1 or requested name is mapped in by the current view. WESA_ERR_NOSUCHINSTANCE -- there is no matching instance for a requested name and such an instance cannot be created in the case of a set operation and the requested/found name is mapped in by the current view WESA_ERR_OBJECT_MAPPED_OUT -- there is no matching object with max_access !=non-accessible for current request. WESA_ERR_NAME_NOT_DETECTED -- this error is equivalent of genErr and is used by implementations with overlapped namespaces. TheMIB server must return this code when a generic error occurred before the MIB server detected if the requested name is included in thecurrent namespeace. 3.2 Error Processing and Reporting Any error not listed below for a particular case is an "unexpected error." For example, the error value 1002345 is an unexpected one for any case, and the error WESA_ERR_BADVALUE is an unexpected one for SNMPv1 GetRequest. Unexpected error codes returned for a particular environment are translated to genErr by the core agent. The next subsections of this section will describe error translation performed by the core agent for each environment. In all cases, WESA_ERR_NAME_NOT_DETETCTED and WESA_ERR_INTERNAL are acceptable and expected error codes. 3.2.1 Look Primitive 3.2.1.1 SNMPv2 GetRequest WESA_ERR_NOERROR noError, core agent will create a varbind using returned type and value in the output packet WESA_ERR_GENERR genErr WESA_ERR_NOSUCHOBJECT WESA_ERR_OBJECT_MAPPED_OUT noError, core agent will create a varbind with value noSuchObject in the output packet WESA_ERR_NOSUCHINSTANCE WESA_ERR_INSTANCE_MAPPED_OUT noError, core agent will createa varbind with value noSuchInstancein the output packet 3.2.1.2 SNMPv2 GetNextRequest and GetBulkRequest WESA_ERR_NOERROR noError, core agent will createa varbind using returned type andvalue in the output packet WESA_ERR_GENERR genErr WESA_ERR_NOSUCHOBJECT WESA_ERR_OBJECT_MAPPED_OUT WESA_ERR_NOSUCHINSTANCE WESA_ERR_INSTANCE_MAPPED_OUT (a) If there are namespaces left unprocessedcore agent will continue search in thenext namespaces, otherwise(b) noError, core agent will createa varbind with value endOfMibViewin the output packet 3.2.1.3 SNMPv2 SetRequest WESA_ERR_NOERRORr noErro WESA_ERR_GENERR genErr WESA_ERR_WRONGTYPE wrongType WESA_ERR_WRONGLENGTH wrongLength WESA_ERR_WRONGVALUE wrongValue WESA_ERR_NOCREATION noCreation WESA_ERR_INCONSISTENTVALUE inconsistentValue WESA_ERR_RESOURCEUNAVAILABLE resourceUnvailable WESA_ERR_NOTWRITABLE notWritable WESA_ERR_INCONSISTENTNAME inconsistentName WESA_ERR_NOSUCHOBJECT WESA_ERR_NOSUCHINSTANCE WESA_ERR_NOCREATION noCreation WESA_ERR_OBJECT_MAPPED_OUT WESA_ERR_INSTANCE_MAPPED_OUT WESA_ERR_NOACCESS noAccess 3.2.1.4 SNMPv1 GetRequest and GetNextRequest WESA_ERR_NOERROR noError, core agent will create a varbind using returned type and value in the output packet WESA_ERR_GENERR genErr WESA_ERR_NOSUCHOBJECT WESA_ERR_OBJECT_MAPPED_OUT WESA_ERR_NOSUCHINSTANCE WESA_ERR_INSTANCE_MAPPED_OUT noSuchName 3.2.1.5 SNMPv1 SetRequest WESA_ERR_NOERROR noError WESA_ERR_GENERR genErr WESA_ERR_WRONGTYPE WESA_ERR_WRONGLENGTH WESA_ERR_WRONGVALUE WESA_ERR_INCONSISTENTVALUE badValue WESA_ERR_RESOURCEUNAVAILABLE genErr WESA_ERR_NOCREATION WESA_ERR_NOTWRITABLE WESA_ERR_INCONSISTENTNAME WESA_ERR_NOSUCHOBJECT WESA_ERR_NOSUCHINSTANCE WESA_ERR_OBJECT_MAPPED_OUT WESA_ERR_INSTANCE_MAPPED_OUT WESA_ERR_NOACCESS WESA_ERR_NOCREATION noSuchName 3.3 Set Primitive 3.3.1 DO_PHASE1 The same set of rules is applicable here as in the case of the look primitive in the same environment. 3.3.2 UNDO_PHASE1, RELEASE 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 the 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 the look primitive in the SNMPv2 SetRequest environment, if all CMD_DO_COMMIT calls return WESA_ERR_NOERROR. Otherwise the core agent stores the index passed back by the failed set(CMD_DO_COMMIT). If all subsequent CMD_UNDO_COMMIT calls return WESA_ERR_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 FAR *mib_prev; struct mib FAR *mib_next; struct mib_server FAR *mib_server; INT32 mib_state; OID mib_low_bound[MIB_BOUND_LEN]; INT32 mib_low_bound_len; OID mib_hi_bound[MIB_BOUND_LEN]; INT32 mib_hi_bound_len; UINT32 mib_specific; UINT32 mib_priority; UINT32 (FAR PASCAL *mib_init)(const struct mib FAR *); BOOL (FAR PASCAL * *mib_indicate)(INT32, UINT32, INT32, INT32, BOOL, INT32, INT32, void FAR *, const struct mib FAR *); INT32 (FAR PASCAL *mib_look)(LPOID, LPINT32, INT32, LPUINT8 FAR *, LPUINT16, LPINT32, LPUINT32, const struct mib FAR *); INT32 (FAR PASCAL *mib_set)(INT32, UINT32, LPINT32, const struct mib FAR*); void (FAR PASCAL *mib_tick)(UINT32, const struct mib FAR *); UINT8 mib_entity[MAX_ENTITY_LEN]; INT32 mib_entity_len; INT32 mib_flag; }; mib_prev and mib_next -- are pointers to the previous and next mib structures in the list. NULL means that this structure is the first or final one in the list. mib_server -- is a pointer to the structure defining the MIB server which accesses the namespace that this mib structure defines. 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. mib_low_bound -- is the low boundary 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. If name belongsto the namespace it must be lexicographically greater than or equal to the namespace's mib_low_bound. mib_low_bound_len -- is the length of mib_low_bound. 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 name belongs to a namespace, then it must be lexicographically less than that namespace's mib_high_bound. mib_high_bound_len -- the length of mib_high_bound. mib_specific -- used by MIB-server code in some server-- specific way. For, example, the value of mib_specific (returned by init(), stored by the core agent, and then made visible to the MIB server on each subsequent call) is the only way an external MIB server supporting several entities and/or namespaces can detect what entity and namespace is referenced in the 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. mib_priority -- used by MIB servers handling overlapping namespaces. The highest priority is 0; a namespace with this priority cannot be overlapped with any other namespace. Normal priority is 1000. mib_init -- pointer to the init primitive function in the MIB server for the namespace represented by this mib element. mib_init() returns 0 on failure, else some non- zero UINT32 value of interest only to the MIB server. The core agent stores this value into mib_specific. mib_indicate -- pointer to the indicate primitive function in the MIB server for the namespace represented by this mib element. mib_indicate() returns FALSE on failure and TRUE on success. mib_look -- pointer to the look primitive function in the MIB server for the namespace represented by this mib element. mib_look() returns an error code on failure. mib_set -- pointer to the set primitive function in the MIB server for the namespace represented by this mib element. mib_tick -- pointer to the tick primitive function in the MIB server for the namespace represented by this mib element. mib_entity[] -- the local entity name. mib_entity_len -- the length of mib_entity. mib_flag -- used internally by 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 them abc_xxxx just to logically link the primitive and namespace. The final parameter of each primitive function call is a FAR 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: UINT32 FAR PASCAL abc_init(struct mib FAR * mib); struct mib FAR * mib -- pointer to the mib entry itself which allows MIB-server access to its mib entry. Returns: 0 on failure and some UINT32 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: BOOL FAR PASCAL abc_indicate( INT32 cmd, UINT32 cid, INT32 version, INT32 rw, BOOL exact, INT32 view_ind, INT32 curr_time, void FAR *view_head, const struct mib FAR *mib ); cmd -- CYCLE_INDICATE_START or CYCLE_INDICATE_END cid -- cycle id version -- is either SNMPV1 or SNMPV2 rw -- is either WESA_READ or WESA_WRITE exact -- is either TRUE or FALSE view_ind -- is the index of the current view curr_time -- is either CURRENTTIME, RESTARTTIME or CACHETIME view_head -- is the pointer to the current view list. Returns: indicate returns FALSE on failure and TRUE on success. If indicate returns failure, then the core agent disables the MIB server, and all its mibs, and (if applicable) unload the MIB server. If indicate(START) returns FALSE, then the core agent will respond to the NMS with error-status genError and error- index pointing to the first variable in the packet which has resides in the MIB server's namespace. If indicate(END) returns FALSE, this does not affect the result of current packet processing.But in both cases the core agent disables all the MIBs represented by this 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. 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 32-bit unsigned never-0 value which will never be 0, this value is incremented by the core agent for every packet processed. It wraps on overflow, skipping 0 in the process. The MIB-server can thus detect the very beginning of the packet. The same value is used by the core-agent to detect packets delivered after timeout. rw and exact together tell the MIB server what request is present in this packet. If rw is WESA_READ and exact is TRUE, then the request is GET; if rw is WESA_READ and exact is FALSE, then the request is GET-NEXT or GET-BULK. if rw is WESA_WRITE, then the request is SET and exact is always TRUE and may be ignored. 4.2.3 Look Primitive Function Invoked for all MIB-access operations. Syntax: INT32 FAR PASCAL abc_look( LPOID name, LPINT32 namelen, INT32 index, LPUINT8 FAR *pval, LPUINT16 type, LPINT32 len, LPUINT32 ph, const struct mib FAR *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 Next Transaction: name -- requested name upon return will contain the found 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 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 unencoded value. type -- type points to the ASN type of value (*) len -- len points to the length of value (**) ph -- *ph will contain the non-zero handle upon return at MIB server's choice Returns: The value returned by look() is either WESA_ERR_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. Notes: (*) 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. (**) If *len == -1 then some error was detected during value decoding. 4.2.4 Set Primitive Function Used to set variables. Syntax: INT32 FAR PASCAL abc_set( INT32 cmd, UINT32 hand, LPINT32 pind, const struct mib FAR *mib ); cmd -- can take five values: CMD_DO_PHASE1, CMD_UNDO_PHASE1, CMD_DO_COMMIT, CMD_UNDO_COMMIT, CMD_DO_RELEASE handle -- non-zero handle returned by MIB server's look primitive index -- points to the variable for which the MIB server returned a non-zero. Returns: Return values of this function are described in the section 3. Description: Described in detail in section 3. 4.2.5 Tick Primitive Function Informs the MIB server of the passage of time. Syntax: void FAR PASCAL party_tick(UINT32 cid, const struct mib FAR *mib) cid -- holds a non-zero 32-bit value incremented by one before each functioninvocation. 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 between a WinSNMP core agent and external MIB servers uses a combination of native MS-Windows messaging and shared memory. 5.1 Combined Message/Shared-Memory Mechanism All WinSNMP agent message exchanged between the WinSNMP Agent and its external MIB servers have the following format: struct defWinSNMPMsg { WHND hWnd; /* handle of receiving window */ WORD wMessageType; /* RegisterWindowMessage( "WinSNMPAgentMessageType") */ WORD wParam; /* WESA_ */ LONG lParam; /* HIWORD=shared memory handle */ /* LOWORD=sending window handle */ }; 5.1.1 WinSNMP Agent Windows Message Type There is one type for all WinSNMP Agent messages exchanged between the core agent and external MIB servers via PostMessage and SendMessage. This messages type is determined dynamically on each system by issuing: WORD wMessageType = RegisterWindowMessage("WinSNMPAgentMessageType"); 5.1.2 WinSNMP Agent Message Sub-Type There is a constant message sub-type for each interface primitive's message and its correspondent response: Primitive Primitive Invocation Primitive Response -------------------------------------------------- ------------------------------------------- init() WESA_INIT_REQ WESA_INIT_RSP indicate() WESA_INDICATE_REQ WESA_INDICATE_RSP look() WESA_LOOK_REQ WESA_LOOK_RSP set() WESA_SET_REQ WESA_SET_RSP tick() WESA_TICK_REQ WESA_TICK_RSP The message sub-type is passed in the wParam of all WinSNMP Agent windows messages. 5.1.3. Shared Memory Areas There are three shared-memory areas involved in exchanges of information between the WinSNMP core agent and its correspondent external MIB Servers, known as Areas B, C and D: ú Area B is used to store the list of views known to the system. The actual layout of this area is Agent Core implementation specific and is not seen by the MIB servers. ú Area C is used by the core agent as a link area for core- agent-to-MIB-server transactions. ú Area D is used by the MIB server as a link area for MIB- server-to-core-agent transactions. The MIB-server never writes in areas B and C; the MIB server writes only into Area D. The core-agent never writes into Area D but only into Areas B and C. The shared-memory areas are allocated from Global Heap and are assumed to be moveable. The handle to the shared-memory area is passed in the high order word of lParam in WinSNMP Agent windows messages. 5.1.4 Sending Window's Handle Each message identifies the sending window's handle. This handle issued to identify the message source for response destination and message context selection. The handle is passed in the low-order word of the lParam in WinSNMP Agent windows messages. 5.1.5 Message Id Each message sent in either direction (to the MIB server by the core agent; or to the core agent by the MIB server) must contain an individual Message ID. Message ID is the cycle identifier (cid from indicate() primitive description above). For all other messages it is a non-zero unsigned 32- bit value assigned by the core agent. The message ID is passed in the shared memory associated with message. 5.2 Message 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. In the diagrams below, we place the core agent on the left side of the page and the MIB server on the right. Agent MIB Server ==== ========= ------------------------------------> <------------------------------ The full message format is (wParam) (high lParam) (low lParam) In the abbreviated diagrammatic message format used, only the wParam as and the high order lParam as appear as follows: (wParam) (high lParam) 5.2.1 Init Message Exchange: -------------------- -----> <------------------- --------- Area C format : Offset Type Contents ------------------------------------------------------------ ------------------------------------- 0 UINT32 Message ID 4 UINT32 Length of the Entity Name (0..32) 8 UINT8[] EntityName 40 UINT32 Length of namespace's low boundary (in OIDs)(2..20) 44 OID[] Low boundary 124 UNIT32 Handle of shared area B (obsolete, will be removed) 128 UINT32 Current value of sysUpTime (obsolete, will be removed) Area D format: Offset Type Contents ------------------------------------------------------------ ------------------------------------------------- 0 UINT32 Message ID 4 UINT32 Result of operation (will be stored in mib_specific, see 4.2.1 above) 5.2.2 Indicate Message Exchange: ---------------- ---------> <--------------- ------------ Area C format : Offset Type Contents ------------------------------------------------------------ ---------- 0 UINT32 cycle-id 4 UINT32 mib_specific 8 UINT32 cmd 12 UINT32 version 16 UINT32 rw 20 UINT32 exact 24 UINT32 current view index 28 UINTY32 current time domain 32 UINT32 handle of shared area B (view list) Area D format: Offset Type Contents ----------------------------------------------------- 0 UINT32 cycle-id 4 UINT32 Result of operation 5.2.3. Look a) get transaction Message Exchange: -------------------- -----> <------------------- --------- Area C format : Offset Type Contents -------------------------------------------------------- 0 UINT32 message-id 4 UINT32 mib_specific 8 UINT32 name length (in OIDs) 12 OID[] name Area D format: Offset Type Contents ----------------------------------------------------------- 0 UINT32 message-id 4 UINT32 result of operation 8 UINT32 value type 12 UINT32 value length (unencoded) 16 any value (unencoded) b) next transaction Message Exchange: -------------------- -----> <------------------- -------- Area C format : Offset Type Contents -------------------------------------------------------- 0 UINT32 message -id 4 UINT32 mib_specific 8 UINT32 name length (in OIDs) 12 OID[] name Area D format: Offset Type Contents ------------------------------------------------------------ 0 UINT32 message-id 4 UINT32 result of operation 8 UINT32 name length (in OIDs) 12 OID[] name 524 UINT32 value type 528 UINT32 value length (unencoded) 532 any value (unencoded) c) write transaction Message Exchange: -------------------- -----> <------------------- -------- Area C format: Offset Type Contents ------------------------------------------------------------ 0 UINT32 message-id 4 UINT32 mib_specific 8 UINT32 name length (in OIDs) 12 OID[] name 524 UINT32 variable index 528 UINT32 value type 532 UINT32 value length (unencoded) 536 any value (unencoded) Area D format: Offset Type Contents ---------------------------------------------------- 0 UINT32 message-id 4 UINT32 result of operation 8 UINT32 handle or 0 Note: the core agent communicates the type of the transaction specified in the request packet in the indicate(START) message so the transaction type isn't repeated in the look() invocation. 5.2.4. Set Message Exchange: --------------------- ----> <-------------------- -------- Area C format: Offset Type Contents ----------------------------------------------- 0 UINT32 message-id 4 UINT32 mib_specific 8 UINT32 cmd 12 UINT32 handle 16 UINT32 variable index Area D format: Offset Type Contents ---------------------------------------------------- 0 UINT32 message-id 4 UINT32 result of operation 8 UINT32 variable index 5.2.5 Tick Message Exchange: -------------------- -----> <------------------- --------- Area C format: Offset Type Contents ----------------------------------------------- 0 UINT32 tid 4 UINT32 mib_specific 8 UINT32 sysUpTime Area D format: Offset Type Contents ----------------------------------------------- 0 UINT32 tid 6 PROCEDURES FOR MOUNTING AND UNMOUNTING OF MIB SERVERS 6.1 Loadable MIB Servers Each loadable MIB server defines three functions: BOOL FAR PASCAL server_reg_init(char FAR *init_str); INT32 FAR PASCAL server_reg_next(struct mib FAR *mib); void FAR PASCAL server_unreg(INT32 reason); The function server_reg_init() performs all the operations needed for the MIB server 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. Note: init_str is not used by current implementation, it will be apparently gone very soon. Current implementation assumes that each loadable server is smart enough to read its own .ini file without any help. The server_reg_init() function returns FALSE on failure else TRUE. 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_priority, 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, in which case the function is called again with a pointer to another newly-assigned mib_list entry; ú MSR_NOMORE 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. 6.2.1 External MIB-Server Descriptor Every external MIB server must initialize its namespaces and allocate global shared memory area using its Server Descriptor. The external MIB server needs to maintain its descriptor in a consistent state while it is running. The external MIB server change its descriptor, but these changes have to be done atomically. The Server Descriptor has this format: Server Descriptor format: Offset Type Contents 0 char Unique name of MIB server, null terminated 80 char Description of MIB server, null terminated 160 UINT32 Number of namespaces this MIB server will handle First namespace handled by this MIB server 164 UINT32 mib_specific 168 UINT32 mib_priority 172 UINT32 Length of entity name 176 UINT8[] Entity name 208 UINT32 Length of low boundary in OIDs 212 OID[] Low boundary 292 UINT32 Length of high bound in OIDs 296 OID[] High boundary Second namespace handled by this MIB server 376 UINT32 mib_specific Information for second and subsequent namespaces arranged as for the first 6.2.2 MIB-Server Handshake The MIB Server initiates mounting upon startup and after any changes to the MIB-Server descriptor.To do so it broadcasts the following message: SendMessage( HWND_BROADCAST, wesa_msg_type, WESA_MIB_REG_REQ, MAKELONG(extMIBServerWindow, extMIBServerDescriptor) ); The lParam of this message contains the MIB server's window handle in the LOWORD and the serverdescriptor handle in HIWORD. If there is a core agent running in the system it will respondby a sending message back to the server (while processing the message received fthe rom server, similarto DDE handshake): SendMessage( extMIBServerWindow, wesa_msg_type, WESA_MIB_REG_RSP, MAKELONG(coreAgentWindow, 0) ); The LOWORD of lParam in this message contains the core agent's window handle. If the MIB server terminates, it posts a message to the core agent: PostMessage( coreAgentWindow, wesa_msg_type, WESA_MIB_REG_ABORT, MAKELONG(extMibServerWindow, 0) ); The LOWORD of lParam in this message contains the MIB server's window handle. 6.2.3 Core Agent Handshake The core agent has to broadcasts the following message upon start up: SendMessage( HWND_BROADCAST, wesa_msg_type, WESA_AGENT_REG_REQ, MAKELONG(coreAgentWindow, 0) ); The LOWORD of lParam in this message contains the core agent's window handle. Each external MIB server running in the system it responds by sending a message back to the core-agent (while processing the message received from the core-agent, similar to DDE handshake): SendMessage( coreAgentWindow, wesa_msg_type, WESA_AGENT_REG_RSP, MAKELONG(extMIBServerWindow, extMIBServerDescriptor) ); The lParam of this message contains the server's window handle in the LOWORD and the server descriptor handle in HIWORD. If the core agent is unable to mount a MIB server; or wishes to unmount a MIB-server for some reason, it either sends or posts the following message: SendMessage( extMIBServerWindow, wesa_msg_type, WESA_AGENT_REG_ABORT, MAKELONG(coreAgentWindow, reason) ); PostMessage( extMIBServerWindow, wesa_msg_type, WESA_AGENT_REG_ABORT, MAKELONG(coreAgentWindow, reason) ); The LOWORD of lParam in this message contains the core agent's window handle; the HIWORD of lParam in this message contains the reason for unmounting, where reason can be: ú 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.3 Core Agent Mutual Exclusion Upon startup, the core agent must check that there are no other WESA agents running in the system. To do so it broadcasts the following message: SendMessage( HWND_BROADCAST, wesa_msg_type, WESA_AGENT_EXCL_REQ, MAKELONG(coreAgent1WindowHandle, 0) ); If there is such an agent running in the system, it sends a message back (while processing this message, in DDE handshake style): SendMessage( coreAgent1WindowHandle, wesa_msg_type, WESA_AGENT_EXCL_RSP, MAKELONG(coreAgent2WindowHandle, 0) ); If the core agent receives the message WESA_AGENT_EXCL_RSP it must terminate. 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. INT32 FAR PASCAL compare( LPOID name1, INT32 len1, LPOID name2, INT32 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 Views INT32 FAR PASCAL check_view( LPOID name, INT32 known_len, INT32 total_len, void FAR *view_list, INT32 view_ind ); This function evaluates whether a name is mapped into or out of the view represented by view_ind and view_list. Name is the name in question, total_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, total_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_index is the index of the view of interest. This function returns 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. INT32 FAR PASCAL check_view_sh( LPOID name, INT32 known_len, INT32 total_len, HGLOBAL view_list_h, INT32 view_ind ); The only difference between these two functions is that, in the second case, the handle of the view list head is used instead of a pointer to the view head. 8.1.3 RowStatus State Machine There is also a RowStatus state machine provided by the core agent. INT32 FAR PASCAL _export rs_machine( BOOL new_entry, LPINT32 new_status, INT32 status_index, INT32 old_status, BOOL consistent, INT32 cons_index, LPINT32 pind, LPUINT32 tmout ); This function returns an intermediate error code for the current operation in accord with the RowStatus transition table from RFC 1443. New_entry is FALSE if the current operation is a modification of an existing entry, otherwise it is TRUE. 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. *new_status is the new value of the 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 ignored. Consistent is TRUE if the rest of the new row's values (except status element) are consistent, otherwise it is FALSE. If consistent is FALSE 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. *pind is the value of the index of the first row variable in the current packet and, on output, *pind is the value of error-index. *tmout contains the time when this row should be discarded due to time-out in notInService or notReady status. 8.1.4 Time Service There is a time service provided by agent, which give both UNIX-like time and sysUpTime values available to every MIB server. UINT32 FAR PASCAL wesa_time(void) UINT32 FAR PASCAL wesa_up_time(void) 8.1.5 Traps There are two key elements in trap sending: the trap_register function and the trap data block.Any component of the WESA subsystem can send a trap by calling the trap_register function: BOOL FAR PASCAL trap_register( INT32 tclass, UINT32 trap, UINT32 specific, UINT32 data ); Data for non-generic (non-well known) traps are provided in the form of a trap data block. This block is allocated from shared memory by the element sending the trap. Shared memory is freed by the caller once the trap is sent. The trap data block has the following format: Offset Type Value Description 0 UINT32 trap_oid_len length of next field 4 OID[] trap_oid SNMPv1: enterprise, SNMPv2: trap name 4+trap_oid_len*4 UINT32 trap_var_num number of trap vars 8+trap_oid_len*4 struct trap_var[] trap_vars arry of trap vars Where struct trap_var is defined as : #define MAX_TRAP_VAR_OID 32 /* should be OK for next 15 years */ #define MAX_TRAP_VAL_LEN 256 /* more than enough */ struct trap_var { OID tv_name[MAX_TRAP_VAR_OID]; INT32 tv_name_len; UINT8 tv_val[MAX_TRAP_VAL_LEN]; /* value */ INT32 tv_len; /* tv_val length, non encoded */ UINT16 tv_type; /* pseudo-type */ }; 8.1.5.1 SNMPv1 Generic Traps In order to send a generic SNMPv1 trap, the following values must be passed to thefunction trap_register: tclass - TCLASS_V1_GENERIC trap - generic trap value specific - ignored data - if trap is linkUp or linkDown, data is ifIndex of link if trap is egpNeighborLoss, data is IP address of neighbor in network byte order 8.1.5.2 SNMPv1 Specific Traps In order to send a specific SNMPv1 trap, the following values must be passed to thefunction trap_register: tclass - TCLASS_V1_SPECIFIC trap - generic trap value specific - specific trap value data - handle of trap data block 8.1.5.3 SNMPv2 Well-Known Traps In order to send a well-known SNMPv2 trap, the following values must be passed to thefunction trap_register: tclass - TCLASS_V2_WELL_KNOWN trap - last oid of well-known trap name specific - ignored data - if trap is linkUp or linkDown, data is ifIndex of link if trap is egpNeighborLoss, data is IP address of neighbor in network byte order 8.1.5.4 SNMPv2 Non-Well-Known Traps In order to send any SNMPv2 trap the following values must be passed to thefunction trap_register: tclass - TCLASS_V1_GENERAL trap - ignored specific - specific trap value data - handle of trap data block The trap sending routine will make sysUpTime the first variable in the varbind,the snmpTrapOID will be the second one, and the variables from trap data block follow these two 8.2 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: ú Register itself as a management application with the DMI Service Layer. ú Issue several list commands to obtain information about availability of DMTF managed entities; build appropriate transaction tables (or functions) to translate SNMP names into DMI names. ú Build appropriate namespaces representing discovered DMTF variables, build a registration data block, and register itself with core agent by sending WM_SNMP_IAMH_REQ message. The mapping of WinSNMP/Agent primitives onto DMI primitives is straightforward. There is nothing DMTF specific which has to be done by a MIB server in order to perform init(), indicate(), 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 1155 M. Rose and K. McCloghrie: Structure and Identification of Management Information for 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. 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