community.roxen.com
Not logged in Date: October 14, 2008
 DEMO  DOCS  PIKE
 COMMUNITY  DOWNLOAD
Home Developer tools Internet Documents RFCs www.roxen.com
Newest Categories... 1..499 500..999 1000..1499 1500..1999 2000..2499 2500..2999 3000..3499 3500..3999 4000..4499
[Text version]

Network Working Group
Request for Comments: 4011
Category: Standards Track
S. Waldbusser
Nextbeacon
J. Saperia
JDS Consulting, Inc.
T. Hongal
Riverstone Networks, Inc.
March 2005

Policy Based Management MIB

Status of This Memo

This document specifies an Internet standards track protocol for the Internet community, and requests discussion and suggestions for improvements. Please refer to the current edition of the "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited.

Copyright Notice

Copyright © The Internet Society (2005).

Abstract

This memo defines a portion of the Management Information Base (MIB) for use with network management protocols in TCP/IP-based internets. In particular, this MIB defines objects that enable policy-based monitoring and management of Simple Network Management Protocol (SNMP) infrastructures, a scripting language, and a script execution environment.

Table of Contents

   1.  The Internet-Standard Management Framework ..................   3
   2.  Overview ....................................................   4
   3.  Policy-Based Management Architecture ........................   4
   4.  Policy-Based Management Execution Environment ...............  10
       4.1.  Terminology ...........................................  10
       4.2.  Execution Environment - Elements of Procedure .........  10
       4.3.  Element Discovery .....................................  11
             4.3.1.  Implementation Notes ..........................  12
       4.4.  Element Filtering .....................................  13
             4.4.1.  Implementation Notes ..........................  13
       4.5.  Policy Enforcement ....................................  13
             4.5.1.  Implementation Notes ..........................  14
   5.  The PolicyScript Language ...................................  14
       5.1.  Formal Definition .....................................  15
       5.2.  Variables .............................................  18
             5.2.1.  The Var Class .................................  19
       5.3.  PolicyScript QuickStart Guide .........................  23
             5.3.1.  Quickstart for C Programmers ..................  25
             5.3.2.  Quickstart for Perl Programmers ...............  25
             5.3.3.  Quickstart for TCL Programmers ................  25
             5.3.4.  Quickstart for Python Programmers .............  26
             5.3.5.  Quickstart for JavaScript/ECMAScript/JScript
                     Programmers ...................................  26
       5.4.  PolicyScript Script Return Values .....................  26
   6.  Index Information for `this element' ........................  27
   7.  Library Functions ...........................................  28
   8.  Base Function Library .......................................  29
       8.1.  SNMP Library Functions ................................  29
             8.1.1.  SNMP Operations on Non-Local Systems ..........  30
             8.1.2.  Form of SNMP Values ...........................  32
             8.1.3.  Convenience SNMP Functions ....................  34
                     8.1.3.1.  getVar() ............................  34
                     8.1.3.2.  exists() ............................  34
                     8.1.3.3.  setVar() ............................  35
                     8.1.3.4.  searchColumn() ......................  36
                     8.1.3.5.  setRowStatus() ......................  38
                     8.1.3.6.  createRow() .........................  39
                     8.1.3.7.  counterRate() .......................  42
             8.1.4.  General SNMP Functions ........................  44
                     8.1.4.1.  newPDU() ............................  45
                     8.1.4.2.  writeVar() ..........................  45
                     8.1.4.3.  readVar() ...........................  46
                     8.1.4.4.  snmpSend() ..........................  47
                     8.1.4.5.  readError() .........................  48
                     8.1.4.6.  writeBulkParameters() ...............  48
             8.1.5.  Constants for SNMP Library Functions ..........  49
       8.2.  Policy Library Functions ..............................  51
             8.2.1.  elementName() .................................  51
             8.2.2.  elementAddress() ..............................  51
             8.2.3.  elementContext() ..............................  52
             8.2.4.  ec() ..........................................  52
             8.2.5.  ev() ..........................................  52
             8.2.6.  roleMatch() ...................................  52
             8.2.7.  Scratchpad Functions ..........................  53
             8.2.8.  setScratchpad() ...............................  55
             8.2.9.  getScratchpad() ...............................  56
             8.2.10. signalError() .................................  57
             8.2.11. defer() .......................................  57
             8.2.12. fail() ........................................  58
             8.2.13. getParameters() ...............................  58
       8.3.  Utility Library Functions .............................  59
             8.3.1.  regexp() ......................................  59
             8.3.2.  regexpReplace() ...............................  60
             8.3.3.  oidlen() ......................................  60
             8.3.4.  oidncmp() .....................................  60
             8.3.5.  inSubtree() ...................................  60
             8.3.6.  subid() .......................................  61
             8.3.7.  subidWrite() ..................................  61
             8.3.8.  oidSplice() ...................................  61
             8.3.9.  parseIndex() ..................................  62
             8.3.10. stringToDotted() ..............................  63
             8.3.11. integer() .....................................  64
             8.3.12. string() ......................................  64
             8.3.13. type() ........................................  64
             8.3.14. chr() .........................................  64
             8.3.15. ord() .........................................  64
             8.3.16. substr() ......................................  65
       8.4.  General Functions .....................................  65
   9.  International String Library ................................  65
       9.1.  stringprep() ..........................................  66
             9.1.1.  Stringprep Profile ............................  66
       9.2.  utf8Strlen() ..........................................  67
       9.3.  utf8Chr() .............................................  68
       9.4.  utf8Ord() .............................................  68
       9.5.  utf8Substr() ..........................................  68
   10. Schedule Table ..............................................  69
   11. Definitions .................................................  70
   12. Relationship to Other MIB Modules ........................... 113
   13. Security Considerations ..................................... 114
   14. IANA Considerations ......................................... 117
   15. Acknowledgements ............................................ 118
   16. References .................................................. 118
       16.1. Normative References .................................. 118
       16.2. Informative References ................................ 119
   Authors' Addresses .............................................. 120
   Full Copyright Statement ........................................ 121

1. The Internet-Standard Management Framework

For a detailed overview of the documents that describe the current Internet-Standard Management Framework, please refer to section 7 of RFC 3410 [16].

Managed objects are accessed via a virtual information store, termed the Management Information Base or MIB. MIB objects are generally accessed through the Simple Network Management Protocol (SNMP). Objects in the MIB are defined using the mechanisms defined in the Structure of Management Information (SMI). This memo specifies a MIB module that is compliant to the SMIv2, which is described in STD 58, RFC 2578 [2], STD 58, RFC 2579 [3], and STD 58, RFC 2580 [4].

2. Overview

Large IT organizations have developed management strategies to cope with the extraordinarily large scale and complexity of today's networks. In particular, they have tried to configure the network as a whole by describing and implementing high-level business policies, rather than manage device by device, where orders of magnitude more decisions (and mistakes) may be made.

The following are examples of "business policies":

   - All routers will run code version 6.2.
   - On-site contractors will only be connected to ports that are
     configured with special security restrictions.
   - All voice over cable ports in California must provide free local
     calling.
   - Apply special forwarding to all ports whose customers have paid for
     premium service.

Each of these policies could represent an action applied to hundreds of thousands of variables.

To automate this practice, customers need software tools that will implement business policies across their networks, as well as standard protocols that will ensure that policies can be applied to all of their devices, regardless of the vendor.

This practice is called Policy-Based Management. This document defines managed objects for the Simple Network Management Protocol that are used to distribute policies in a common form throughout the network.

3. Policy-Based Management Architecture

Policy-based management is the practice of applying management operations globally on all managed elements that share certain attributes.

Policies are intended to express a notion of:

      if (an element has certain characteristics) then (apply an
      operation to that element)
Policies take the following normal form:
      if (policyCondition) then (policyAction)

A policyCondition is a script that results in a boolean to determine whether an element is a member of a set of elements upon which an action is to be performed.

A policyAction is an operation performed on an element or a set of elements.

These policies are most often executed on or near managed devices where the elements live (and thus their characteristics may be easily inspected) and where operations on those elements will be performed.

A management station is responsible for distributing an organization's policies to all the managed devices in the infrastructure. The pmPolicyTable provides managed objects for representing a policy on a managed device.

An element is an instance of a physical or logical entity and is embodied by a group of related MIB variables, such as all the variables for interface 7. This enables policies to be expressed more efficiently and concisely. Elements can also model circuits, CPUs, queues, processes, systems, etc.

Conceptually, policies are executed in the following manner:

   for each element for which policyCondition returns true, execute
      policyAction on that element

For example:

   If (interface is fast ethernet)       then (apply full-duplex mode)
   If (interface is access)              then (apply security filters)
   If (circuit w/gold service paid for)  then (apply special queuing)

Each unique combination of policy and element is called an execution context. Within a particular execution context, the phrase 'this element' is often used to refer to the associated element, as most policy operations will be applied to 'this element'. The address of 'this element' contains the object identifier of any attribute of the element, the SNMP context the element was discovered in, and the address of the system on which the element was discovered. Policies can manage elements on the same system:

         -----------------------------------------------------
         |                                                   |
         |              Managed System                       |
         |                                                   |
         |                                                   |
         |   ------------------             Managed Elements |
         |   |                |               interfaces     |
         |   | Policy Manager | manages...    circuits       |
         |   |                |               queues         |
         |   ------------------               processes      |
         |                                    ...            |
         |                                                   |
         -----------------------------------------------------

or they can manage elements on other systems:

                                            --------------------------
                                            |  Managed System        |
     --------------------------             |    Managed Elements    |
     |                        |             |      interfaces        |
     |  Management Station or |             |      circuits          |
     |    Mid-Level Manager   |             |      ...               |
     |                        |             --------------------------
     |   ------------------   | manages...
     |   | Policy Manager |   |             --------------------------
     |   ------------------   |             |  Managed System        |
     |                        |             |    Managed Elements    |
     --------------------------             |      interfaces        |
                                            |      circuits          |
                                            |      ...               |
                                            --------------------------
                                            ...

PolicyConditions have the capability of performing comparison operations on SNMP variables, logical expressions, and other functions. Many device characteristics are already defined in MIB Modules and are easy to include in policyCondition expressions (ifType == ethernet, frCircuitCommittedBurst < 128K, etc). However, there are important characteristics that aren't currently in MIB objects, and, worse, it is not current practice to store this information on managed devices. Therefore, this document defines MIB objects for this information. To meet today's needs there are three missing areas: roles, capabilities, and time.

   Roles

A role is an administratively specified characteristic of a managed element. As a selector for policies, it determines the applicability of the policy to a particular managed element.

Some examples of roles are political, financial, legal, geographical, or architectural characteristics, typically not directly derivable from information stored on the managed system. For example, "paid for premium service" or "is plugged into a UPS" are examples of roles, whereas the "percent utilization of a link" would not be.

Some types of information one would put into a role include the following:

   political - describes the role of a person or group of people, or of
               a service that a group of people uses.  Examples:
               executive, sales, outside-contractor, customer.
        If (attached user is executive) then (apply higher bandwidth)
        If (attached user is outside-contractor) then (restrict access)
   financial/legal - describes what financial consideration was
                     received.  Could also include contractual or legal
                     considerations.  Examples: paid, gold, free, trial,
                     demo, lifeline.
        If (gold service paid for) then (apply special queuing)
   geographical - describes the location of an element.  Examples:
                  California, Headquarters, insecure conduit.
        If (interface leaves the building) then (apply special security)
   architectural - describes the network architects "intent" for an
                   element.  Examples: backup, trunk.
        If (interface is backup) then (set ifAdminStatus = down)
      Roles in this model are human-defined strings that can be
      referenced by policy code.  The role table in this MIB may be used
      to assign role strings to elements and to view all role string
      assignments.  Implementation-specific mechanisms may also be used
      to assign role strings; however, these assignments must be visible
      in the role table.  Multiple roles may be assigned to each
      element.  Because policy code has access to data in MIB objects
      that represent the current state of the system and (in contrast)
      role strings are more static, it is recommended that role strings
      not duplicate information available in MIB objects.  Role strings
      generally should be used to describe information not accessible in
      MIB objects.
      Policy scripts may inspect role assignments to make decisions
      based on whether an element has a particular role assigned to it.
      The pmRoleTable allows a management station to learn what roles
      exist on a managed system.  The management station may choose not
      to install policies that depend on a role that does not exist on
      any elements in the system.  The management station can then
      register for notifications of new roles.  Upon receipt of a
      pmNewRoleNotification, it may choose to install new policies that
      make use of that new role.
   Capabilities
      The capabilities table allows a management station to learn what
      capabilities exist on a managed system.  The management station
      may choose not to install policies that depend on a capability
      that does not exist on any elements in the system.  The management
      station can then register for notifications of new capabilities.
      Upon receipt of a pmNewCapabilityNotification, it may choose to
      install new policies that make use of that new capability.
   Time
      Managers may wish to define policies that are intended to apply
      for certain periods of time.  This might mean that a policy is
      installed and is dormant for a period of time, becomes ready, and
      then later goes dormant again.  Sometimes these time periods will
      be regular (Monday-Friday 9-5), and sometimes ad hoc.  This MIB
      provides a schedule table that can schedule when a policy is ready
      and when it is dormant.
A policy manager contains the following:
         -------------------------------------------------------
         | Policy Manager                                      |
         |                                                     |
         |   ----------------------------------------          |
         |   | Agent                                |          |
         |   |                                      |          |
         |   |  ---------------------------------   |          |
         |   |  | Policy Download and Control   |   |          |
         |   |  |   pmPolicyTable               |   |          |
         |   |  |   pmElementTypeRegTable       |   |          |
         |   |  |   pmSchedTable                |   |          |
         |   |  ---------------------------------   |          |
         |   |                                      |          |
         |   |  ---------------------------------   |          |
         |   |  | Policy Environment Control    |   |          |
         |   |  |   pmRoleTable                 |   |          |
         |   |  |   pmCapabilitiesTables        |   |          |
         |   |  ---------------------------------   |          |
         |   |                                      |          |
         |   |  ---------------------------------   |          |
         |   |  | Policy Monitoring             |   |          |
         |   |  |   pmTrackingTables            |   |          |
         |   |  |   pmDebuggingTable            |   |          |
         |   |  ---------------------------------   |          |
         |   ----------------------------------------          |
         |                                                     |
         |   --------------------------------                  |
         |   | Execution Environment        |                  |
         |   |                              |                  |
         |   |  -----------------------     |                  |
         |   |  | Policy Scheduler    |     |                  |
         |   |  -----------------------     |                  |
         |   |  -----------------------     |                  |
         |   |  | Language            |     |                  |
         |   |  -----------------------     |                  |
         |   |  -----------------------     |                  |
         |   |  | Function Library    |     |                  |
         |   |  -----------------------     |                  |
         |   --------------------------------                  |
         -------------------------------------------------------

4. Policy-Based Management Execution Environment

4.1. Terminology

   Active Schedule - A schedule specifies certain times that it will be
      considered active.  A schedule is active during those times.
   Valid Policy - A valid policy is a policy that is fully configured
      and enabled to run.  A valid policy may run unless it is linked to
      a schedule entry that says the policy is not currently active.
   Ready Policy - A ready policy is a valid policy that either has no
      schedule or is linked to a schedule that is currently active.
   Precedence Group - Multiple policies can be assigned to a precedence
      group with the resulting behavior that for each element, of the
      ready policies that match the condition, only the one with the
      highest precedence value will be active.  For example, if there is
      a default bronze policy that applies to any interface and a
      special policy for gold interfaces, the higher precedence of the
      gold policy will ensure that it is run on gold ports and that the
      bronze policy isn't.
   Active Execution Context - An active execution context is a pairing
      of a ready policy with an element that matches the element type
      filter and the policy condition.  If there are multiple policies
      in the precedence group, it is also necessary that no higher
      precedence policy in the group match the policy condition.
   Run-Time Exception (RTE) - A run-time exception is a fatal error
      caused in language or function processing.  If, during the
      invocation of a script, a run-time exception occurs, execution of
      that script is immediately terminated.  If a policyCondition
      experiences a run-time exception while processing an element, the
      element is not matched by the condition and the associated action
      will not be run on that element.  A run-time exception can cause
      an entry to be added to the pmDebuggingTable and will be reflected
      in the pmTrackingPEInfo object.

4.2. Execution Environment - Elements of Procedure

There are several steps performed in order to execute policies in this environment:
      - Element Discovery
      - Element Filtering
      - Policy Enforcement

4.3. Element Discovery

An element is an instance of a physical or logical entity. Examples of elements include interfaces, circuits, queues, CPUs, and processes. Sometimes various attributes of an entity will be described through tables in several standard and proprietary MIB Modules. As long as the indexing is consistent between these tables, the entity can be modeled as one element. For example, the ifTable and the dot3Stats table both contain attributes of interfaces and share the same index (ifIndex), therefore they can be modeled as one element type.

The Element Type Registration table allows the manager to learn what element types are being managed by the system and to register new types, if necessary. An element type is registered by providing the OID of an SNMP object (i.e., without the instance). Each SNMP instance that exists under that object is a distinct element. The index part of the discovered OID will be supplied to policy conditions and actions so that this code can inspect and configure the element. The agent can determine the index portion of discovered OIDs based on the length of the pmElementTypeRegOIDPrefix for the portion of the MIB that is being retrieved. For example, if the OIDPrefix is 'ifEntry', which has 9 subids, the index starts on the 11th subid (skipping the subidentifier for the column; e.g., ifSpeed).

For each element that is discovered, the policy condition is called with the element's name as an argument to see whether the element is a member of the set the policy acts upon.

Note that agents may automatically configure entries in this table for frequently used element types (interfaces, circuits, etc.). In particular, it may configure elements for which discovery is optimized in one or both of the following ways:

   1. The agent may discover elements by scanning internal data
      structures as opposed to issuing local SNMP requests.  It is
      possible to recreate the exact semantics described in this table
      even if local SNMP requests are not issued.
   2. The agent may receive asynchronous notification of new elements
      (for example, "card inserted") and use that information to create
      elements instantly rather than through polling.  A similar feature
      might be available for the deletion of elements.

Note that upon restart, the disposition of agent-installed entries is described by the pmPolicyStorageType object. A special element type "0.0" represents the "system element". "0.0" represents the single instance of the system itself and provides an execution context for policies to operate on "the system" and on MIB objects modeled as scalars. For example, "0.0" gives an execution context for policy-based selection of the operating system code version (likely modeled as a scalar MIB object). The element type "0.0" always exists. As a consequence, no actual discovery will take place and the pmElementTypeRegMaxLatency object will have no effect for the "0.0" element type. However, if the "0.0" element type is not registered in the table, policies will not be executed on the "0.0" element.

If the agent is discovering elements by polling, it should check for new elements no less frequently than pmElementTypeRegMaxLatency would dictate. When an element is first discovered, all policyConditions are run immediately, and policyConditions that match will have the associated policyAction run immediately. Subsequently, the policyCondition will be run regularly for the element, with no more than pmPolicyConditionMaxLatency milliseconds elapsing between each invocation. Note that if an implementation has the ability to be alerted immediately when a particular type of element is created, it is urged to discover that type of element in this fashion rather than through polling, resulting in immediate configuration of the discovered element.

4.3.1. Implementation Notes

Note that although the external behavior of this registration process is defined in terms of the walking of MIB tables, implementation strategies may differ. For example, commonly used element types (such as interface) may have purpose-built element discovery capability built-in and advertised to managers through an entry in the pmElementTypeRegTable.

Before registering an element type, a manager is responsible for inspecting the table to see whether it is already registered (either by the agent or by another manager). Note that entries that differ only in the last subid (which specifies which object is an entry) are effectively duplicates and should be treated as such by the manager.

The system that implements the Policy-Based Management MIB may not have knowledge of the format of object identifiers in other MIB Modules. Therefore it is inappropriate for it to check these OIDs for errors. It is the responsibility of the management station to register well-formed object identifiers. For example, if an extra sub-identifier is supplied when the ifTable is registered, no elements will be discovered. Similarly, if a sub-identifier is missing, every element will be discovered numerous times (once per column) and none of the element addresses will be well formed.

4.4. Element Filtering

The first step in executing a policy is to see whether the policy is ready to run based on its schedule. If the pmPolicySchedule object is equal to zero, there is no schedule defined, and the policy is always ready. If the pmPolicySchedule object is non-zero, then the policy is ready only if the referenced schedule group contains at least one valid schedule entry that is active at the current time.

If the policy is ready, the next step in executing a policy is to see which elements match the policy condition. The policy condition is called once for each element and runs to completion. The element's name is the only argument that is passed to the condition code for each invocation. No state is remembered within the policy script from the previous invocation of 'this element' or from the previous invocation of the policy condition, except for state accessible through library functions. Two notable examples of these are the scratchpad functions, which explicitly provide for storing state, and the SNMP functions, which can store state in local or remote MIB objects. If any run-time exception occurs, the condition will terminate immediately for 'this element'. If the condition returns non-zero, the corresponding policy action will be executed for 'this element'.

If an element matches a condition and it had not matched that condition the last time it was checked (or if it is a newly discovered element), the associated policyAction will be executed immediately. If the element had matched the condition at the last check, it will remain in the set of elements whose policyAction will be run within the policyActionMaxLatency.

4.4.1. Implementation Notes

Whether policy conditions are multi-tasked is an implementation- dependent matter. Each condition/element combination is conceptually its own process and can be scheduled sequentially, or two or more could be run simultaneously.

4.5. Policy Enforcement

For each element that has returned non-zero from the policy condition, the corresponding policy action is called. The element's name is the only argument that is passed to the policy action for each invocation. Except for state accessible from library functions,
no state is remembered from the policy condition evaluation, or from the previous condition/action invocation of 'this element' or from the previous invocation of the policy condition or action on any other element. If any run-time exception occurs, the action will terminate immediately for 'this element'.

4.5.1. Implementation Notes

How policy actions are multi-tasked is an implementation-dependent matter. Each condition/element combination is conceptually its own process and can be scheduled sequentially, or two or more could be run simultaneously.

5. The PolicyScript Language

Policy conditions and policy actions are expressed with the PolicyScript language. The PolicyScript language is designed to be a small interpreted language that is simple to understand and implement; it is designed to be appropriate for writing small scripts that make up policy conditions and actions.

PolicyScript is intended to be familiar to programmers that know one of several common languages, including Perl and C. Nominally, policyScript is a subset of the C language; however, it was desirable to have access to C++'s operator overloading (solely to aid in documenting the language). Therefore, PolicyScript is defined formally as a subset of the C++ language in which many of the operators are overloaded as part of the "var" class. Note, however, that a PolicyScript program cannot further overload operators, as the syntax to specify overloading is not part of the PolicyScript syntax. A subset was used to provide for easy development of low-cost interpreters of PolicyScript and to take away language constructs that are peculiar to the C/C++ languages. For example, it is expected that both C and Perl programmers will understand the constructs allowed in PolicyScript.

Some examples of the C/C++ features that are not available are function definitions, pointer variables, structures, enums, typedefs, floating point and pre-processor functions (except for comments).

This language is formally defined as a subset of ISO C++ [10] but only allows constructs that may be expressed in the Extended Backus- Naur Form (EBNF) documented here. This is because although EBNF doesn't fully specify syntactical rules (it allows constructs that are invalid) and doesn't specify semantic rules, it can successfully be used to define the subset of the language that is required for conformance to this specification. Unless explicitly described herein, the meaning of any construct expressed in the EBNF can be found by reference to the ISO C++ standard.

The use of comments and newlines are allowed and encouraged in order to promote readability of PolicyScript code. Comments begin with '/*' and end with '*/' or begin with '//' and go until the end of the line.

One subset is not expressible in the EBNF syntax: all variables within an instance of a PolicyScript script are within the same scope. In other words, variables defined in a block delimited with '{' and '}' are not in a separate scope from variables in the enclosing block.

PolicyScript code must be expressed in the ASCII character set.

In the EBNF used here, terminals are character set members (singly or in a sequence) that are enclosed between two single-quote characters or described as a phrase between '<' and '>' characters. Nonterminals are a sequence of letters and underscore characters. A colon (:) following a nonterminal introduces its definition, a production. In a production, a '|' character separates alternatives. The '(' and ')' symbols group the enclosed items. The '[' and ']' symbols indicate that the enclosed items are optional. A '?' symbol following an item indicates that the item is optional. A '*' symbol following an item indicates that the item is repeated zero, one, or more times. A '+' symbol following an item indicates that the item is repeated one or more times. The symbol '--' begins a comment that ends at the end of the line.

5.1. Formal Definition

The PolicyScript language follows the syntax and semantics of ISO C++ [10], but is limited to that which can be expressed in the EBNF below.

The following keywords are reserved words and cannot be used in any policy script. This prevents someone from using a common keyword in another language as an identifier in a script, thereby confusing the meaning of the script. The reserved words are:

      auto, case, char, const, default, do, double, enum, extern, float,
      goto, inline, int, long, register, short, signed, sizeof, static,
      struct, switch, typedef, union, unsigned, void, and volatile.
Any syntax error, use of a reserved keyword, reference to an unknown identifier, improper number of function arguments, error in coercing an argument to the proper type, exceeding local limitations on string length, or exceeding local limitations on the total amount of storage used by local variables will cause an RTE.

PolicyScript permits comments using the comment delimiters, '/*' to '*/', or the start of comment symbol '//'.

-- Lexical Grammar

   letter:       '_' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
               | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm'
               | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't'
               | 'u' | 'v' | 'w' | 'x' | 'y' | 'z'
               | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
               | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M'
               | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T'
               | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z'
   digit:        '0' | '1' | '2' | '3' | '4'
               | '5' | '6' | '7' | '8' | '9'
   non_zero:   '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
   oct_digit:  '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7'
   hex_digit:    digit | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
                       | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
   escape_seq:    '\''   |   '\"'   |   '\?'   |   '\\'
                | '\a'   |   '\b'   |   '\f'   |   '\n'
                | '\r'   |  '\t'    |   '\v'
                | '\' oct_digit+    | '\x' hex_digit+
   non_quote:  Any character in the ASCII character set
               except single quote ('), double quote ("),
               backslash ('\'), or newline.
   c_char:            non_quote | '"' | escape_seq
   string_literal:    '"' s_char* '"'
   s_char:            non_quote | ''' | escape_seq
   char_constant:     ''' c_char '''
   decimal_constant:  non_zero digit*
   octal_constant:    '0' oct_digit*
   hex_constant:      ( '0x' | '0X' ) hex_digit+
   integer_constant:  decimal_constant | octal_constant | hex_constant
   identifier:        letter ( letter | digit )*

-- Phrase Structure Grammar

   -- Expressions
   primary_expr:      identifier | integer_constant | char_constant
                    | string_literal  |  '(' expression ')'
   postfix_expr:      primary_expr
                    | identifier '(' argument_expression_list? ')'
                    | postfix_expr '++'
                    | postfix_expr '--'
                    | postfix_expr '[' expression ']'
   argument_expression_list:
                      assignment_expr
                    | argument_expression_list ',' assignment_expr
   unary_expr:        postfix_expr  |  unary_op unary_expr
   unary_op:          '+' | '-' | '~' | '!' | '++' | '--'
   binary_expr:  unary_expr | binary_expr binary_op unary_expr
   binary_op:       '||' | '&&' | '|'  | '^'  | '&'  | '!='
                  | '==' | '>=' | '<=' | '>'  | '<'  | '>>'
                  | '<<' |  '-' | '+'  | '%'  | '/'  |  '*'
   assignment_expr:      binary_expr
                       | unary_expr assignment_op assignment_expr
   assignment_op:     '=' | '*='  | '/=' | '%=' | '+=' | '-='
                  | '<<=' | '>>=' | '&=' | '^=' | '|='
   expression:    assignment_expr | expression ',' assignment_expr
   -- Declarations
   declaration:       'var' declarator_list ';'
   declarator_list:   init_declarator
                    | declarator_list ',' init_declarator
   init_declarator:   identifier [ '=' assignment_expr ]
   -- Statements
   statement:   declaration
              | compound_statement
              | expression_statement
              | selection_statement
              | iteration_statement
              | jump_statement
   compound_statement:    '{' statement* '}'
   expression_statement:  expression? ';'
   selection_statement:
           'if' '(' expression ')' statement
         | 'if' '(' expression ')' statement 'else' statement
   iteration_statement:
           'while' '(' expression ')' statement
         | 'for' '(' expression? ';' expression? ';' expression? ')'
               statement
   jump_statement:    'continue' ';'
                    | 'break' ';'
                    | 'return' expression? ';'
   -- Root production
   PolicyScript:     statement*

5.2. Variables

To promote shorter scripts and ease in writing them, PolicyScript provides a loosely typed data class, "var", that can store both integer and string values. The native C++ types (char, int, etc.) are thus unnecessary and have not been carried into the subset that comprises this language. The semantics of the "var" type are modeled after those of ECMAScript[17].
      For example:
         var number = 0, name = "IETF";
This language will be executed in an environment where the following typedef is declared. (Note that this typedef will not be visible in the policyCondition or policyAction code.)
      typedef ... var;

Although this declaration is expressed here as a typedef, the 'typedef' keyword itself is not available to be used in PolicyScript code.

5.2.1. The Var Class

A value is an entity that takes on one of two types: string or integer.

The String type is the set of all finite ordered sequences of zero or more 8-bit unsigned integer values ("elements"). The string type can store textual data as well as binary data sequences. Each element is considered to occupy a position within the sequence. These positions are indexed with nonnegative integers. The first element (if any) is at position 0, the next element (if any) at position 1, and so on. The length of a string is the number of elements (i.e., 8-bit values) within it. The empty string has length zero and therefore contains no elements.

The integer type is the set of all integer values in the range -9223372036854775808 (-2^63) to 18446744073709551615 (2^64-1). If an integer operation would cause a (positive) overflow, then the result is returned modulo 2^64. If an integer operation would cause a (negative) underflow, then the result is undefined. Integer division rounds toward zero.

Prior to initialization, a var object has type String and a length of zero.

The policy script runtime system performs automatic type conversion as needed. To clarify the semantics of certain constructs it is useful to define a set of conversion operators: ToInteger(), ToString(), ToBoolean(), and Type(). These operators are not a part of the language; they are defined here to aid the specification of the semantics of the language. The conversion operators are polymorphic; that is, they can accept a value of any standard type.

   ToInteger

The operator ToInteger converts its argument to a value of type Integer according to the following table:

Integer The result equals the input argument (no conversion). String See grammar and note below. integer_constant The result equals the input argument (no conversion). string_literal See grammar and note below. char_constant See grammar and note below.
   ToInteger Applied to Strings

ToInteger applied to the String Type string_literal and to char_constants applies the following grammar to the input. If the grammar cannot interpret the string as an expansion of numeric_string, then an RTE is generated. Note that a numeric_string that is empty or contains only white space is converted to 0.

 -- EBNF for numeric_string
   numeric_string : white_space* numeric? white_space*
   white_space :      <TAB> |  <SP> |  <NBSP> |  <FF> |  <VT>
                    | <CR>  |  <LF> |  <LS>   |  <PS> |  <USP>
   numeric :        signed_decimal |  hex_constant | octal_constant |
                    enum_decimal
   signed_decimal:  [ '-' | '+' ] decimal_constant
   enum_decimal:    [ letter | digit | '-' ]* '(' decimal_constant ')'

-- decimal_constant, hex_constant, and octal_constant are defined -- in the PolicyScript EBNF described earlier.

Note that when the enum_decimal form is converted, the sequence of characters before the parenthesis and the pair of parenthesis themselves are completely ignored, and the decimal_constant inside the parenthesis is converted. Thus, "frame-relay(32)" translates to the integer 32.

Although this will make the script more readable than using the constant "32", the burden is on the code writer to be accurate, as "ethernet-csmacd(32)" and "frame-relay(999)" will also be accepted.

   ToString

The operator ToString converts its argument to a value of type String according to the following table:

      Integer           Return the string containing the decimal
                        representation of the input argument in
                        the form of signed_decimal, except that
                        no leading '+' will be used.
      String            Return the input argument (no conversion)
      integer_constant  Return the string containing the decimal
                        representation of the input argument in the
                        form of signed_decimal except that no
                        leading '+' will be used.
      string_literal    Return the input argument (no conversion)
      char_constant     Return the string of length one containing
                        the value of the input argument.
   ToBoolean

The operator ToBoolean converts its argument to a value of type Integer according to the following table:

      Integer            The result is 0 if the argument is 0.
                         Otherwise the result is 1.
      String             The results is 0 if the argument is the
                         empty string.  Otherwise the result is 1.
      integer_constant   The result is 0 if the argument is 0.
                         Otherwise the result is 1.
      string_literal     The result is 0 if the argument is the
                         empty string.  Otherwise the result is 1.
      char_constant      The result is 1.
   Operators

The rules below specify the type conversion rules for the various operators.

      A++:   A = ToInteger(A); A++;
      A--:   A = ToInteger(A); A--;
      ++A:   A = ToInteger(A); ++A;
      --A:   A = ToInteger(A); --A;
      +A:    ToInteger(A);
      -A:     -1 * ToInteger(A);
      ~A:    ToInteger(A);
      !A:    !ToBoolean(A);
      A * B, A - B, A & B, A ^ B , A | B, A << B, A >> B:
             ToInteger(A) <operator> ToInteger(B)
      A / B, A % B:
             if (ToInteger(B) == 0)
               RTE, terminate;
             else
               ToInteger(A) <operator> ToInteger(B)
      A + B:
             if (Type(A) == String || Type(B) == String)
               ToString(A) concatenated with ToString(B)
             else
               A + B
      Compound Assignment (<operator>=):
              Simply follow rules above.  Note that type of LHS (Left
              Hand Side) may be changed as a result.
      A < B, A > B, A <= B, A >= B, A == B, A != B:
             if (Type(A) == String && Type(B) == String)
                 lexically compare strings with strcmp() logic
             else
                 ToInteger(A) <operator> ToInteger(B)
       A && B:
              if (ToBoolean(A))
                  ToBoolean(B);
              else
                  false;
       A || B:
              if (ToBoolean(A))
                  true;
              else
                  ToBoolean(B);
       if(A):
              if (ToBoolean(A))
       while(A):
              while(ToBoolean(A))
       for(...; A; ...):
             for(...; ToBoolean(A); ...)
       A[B] as a RHS (Right Hand Side) value:
             if (Type(A) != String
                  || ToInteger(B) >= strlen(A))
                RTE, terminate;
             A[ ToInteger(B) ]
             The contents are returned as a string of length one
        A[B] = C as a LHS value:
             if (Type(A) != String
                  || ToInteger(B) >= strlen(A))
                RTE, terminate;
             if (strlen(ToString(C)) == 0)
                RTE, terminate
             A[ ToInteger(B) ] = First octet of ToString(C)
             Note that this is only applicable in a simple assignment.
   For example, in the expression
      "getVar("ifSpeed.1") < 128000"

getVar always returns a string and '128000' is implicitly an integer. The rules for '<' dictate that if either argument is an integer then a 'numeric less than' is performed on ToInteger(A) and ToInteger(B).

If "getVar("ifSpeed.1")" returns "64000", the expression can be translated to:

        ToInteger("64000") < ToInteger(128000); or,
        64000 < 128000; or,
        True

5.3. PolicyScript QuickStart Guide

PolicyScript is designed so that programmers fluent in other languages can quickly begin to write scripts.

One way to become familiar with a language is to see it in action. The following nonsensical script exercises most of the PolicyScript constructs (though it skips some usage options and many arithmetic operators).

      var x, index = 7, str = "Hello World", oid = "ifSpeed.";
      x = 0;
      while(x < 10){
          if (str < "Goodbye") /* string comparison */
              continue;
          else
              break;
          x++;
      }
      if (oidlen(oid) == 10)
          oid += "." + index; // append index to oid
      for(x = 0; x < 7; x++){
            str += "a";
            var y = 12;
            index = ((x * 7) + y) % 3;
            if (str[6] == 'W')
                return index;
      }
      return;

The following examples are more practical:

   For a condition:
      // Return 1 if this is an interface and it is tagged
      // with the role "gold"
      return (inSubtree(elementName(), "ifEntry")
          && roleMatch("gold"))

A condition/action pair: First, register the Host Resources MIB hrSWRunEntry as a new element in the pmElementTypeRegTable. This will cause the policy to run for every process on the system. The token '$*' will be replaced by the script interpreter with a process index (see Section 7 for a definition of the '$*' token).

   The condition:
      // if it's a process and it's an application and it's
      // consumed more than 5 minutes of CPU time
      return (inSubtree(elementName(), "hrSWRunEntry")
              && getVar("hrSWRunType.$*") == 4  // app, not OS or driver
              && getVar("hrSWRunPerfCPU.$*") > 30000) // 300 seconds
   The action:
      // Kill it
      setVar("hrSWRunStatus.$*", 4, Integer); // invalid(4) kills it

A more substantial action to start an RMON2 host table on interfaces that match the condition:

      var pdu, index;
      pdu = newPDU();
      writeVar(pdu, 0, "hlHostControlDataSource.*",
               "ifIndex." + ev(0), Oid);
      writeVar(pdu, 1, "hlHostControlNlMaxDesiredEntries.*", 1000,
               Integer);
      writeVar(pdu, 2, "hlHostControlAlMaxDesiredEntries.*", 1000,
               Integer);
      writeVar(pdu, 3, "hlHostControlOwner.*", "policy", String);
      writeVar(pdu, 4, "hlHostControlStatus.*", "active(1)", Integer);
      if (createRow(pdu, 5, 4, 20, 65535, index) == 0
          || index == -1)
          return;
Because PolicyScript is a least common denominator, it contains nothing that would astonish programmers familiar with C, C++, Perl, Tcl, JavaScript, or Python. Although a new programmer may attempt to use language constructs that aren't available in PolicyScript, s/he should be able to understand any existing PolicyScript and will likely know how to use anything that is valid in PolicyScript. The lists below quickly enumerate the changes of note for programmers coming from some particular languages. These lists won't describe the unavailable constructs, but it is easy to see from the definition above what is available.

5.3.1. Quickstart for C Programmers

   - Character constants (i.e., 'c') are treated as one-character
     strings, not as integers.  So operations such as ('M' - 'A') or (x
     + 'A') will not perform as expected.
   - Functions can change the value of arguments even though they are
     not pointers (or called like '&arg').
   - All variables are in the same scope.

5.3.2. Quickstart for Perl Programmers

   - Comments are '/* comment */' and '// till end of line', not '#'.
   - No need to put a '$' in front of variables.
   - Strings are compared with ==, <=, <, etc. (details in Sec. 6.2.1).
   - Strings are concatenated with '+' (details in Sec. 6.2.1).
   - No variable substitution in "" strings.  '' strings are 1 char
     only.
   - Variables must be declared before use (but no type is necessary).
   - All variables are in the same scope.

5.3.3. Quickstart for TCL Programmers

   - Comments are '/* comment */' and '// till end of line', not '#'.
   - No need to put a '$' in front of variables.
   - Function calls are func-name(arg1, arg2, ...).
   - Square braces [] don't interpret their contents.
   - Double quotes "" surround a string, but no substitutions are
     performed ("" is like { } in TCL ).
   - Statements are terminated by a semicolon (;).
   - Instead of "Set a b", use "b = a;".
   - Strings are concatenated with '+' (details in Sec. 6.2.1).
   - All variables are in the same scope.

5.3.4. Quickstart for Python Programmers

   - Comments are '/* comment */' and '// till end of line', not '#'.
   - Single quotes can be used only for single-character strings ('a').
   - Indentation doesn't matter.  Braces { } define blocks.
   - Variables must be declared before use (but no type is necessary).
   - The expressions for if and while are always surrounded by
     parenthesis, as in "if (x < 5)".
   - 'for' syntax is "for(expression; expression; expression)" (see
     EBNF).
   - All variables are in the same scope.

5.3.5. Quickstart for JavaScript/ECMAScript/JScript Programmers

- Variables must be declared before use. - Functions can change the value of arguments. - All variables are in the same scope.

5.4. PolicyScript Script Return Values

A PolicyScript script execution is normally ended by the execution of a return statement, or by having the flow of execution reach the end of the final statement in the script. A normal script execution always returns a Boolean value. If no explicit value is specified in the return statement, or if the flow of control proceeds through the end of the script, the return value is implicitly zero. If an expression is provided with the return statement, the expression is evaluated, and the result of the expression is implicitly converted with the ToBoolean operator before being returned to the script execution environment.

The return value of a policyCondition script is used to determine whether the associated policyAction script is executed. If the returned value is zero, the associated policyAction script is not executed. If the returned value is one, the associated policyAction script will be executed.

The return value of a policyAction script is ignored.

An RTE or invocation of the fail() function will cause the return value of the script to be set to zero. Note however, that execution of the defer() or fail() functions may set the defer attribute so that the lower precedence script may be executed. This is independent of the return value of the policy script execution.

6. Index Information for 'this element'

PolicyScript code needs a convenient way to get the components of the index for 'this element' so that they can perform SNMP operations on it or on related elements.

Two mechanisms are provided.

   1. For all OID input parameters to all SNMP Library Functions (but
      not OID utility functions), the token "$n" ('$' followed by an
      integer between 0 and 128) can be used in place of any decimal
      sub-identifier.  This token is expanded by the agent at execution
      time to contain the nth subid of the index for the current
      element.  For example, if the element is interface 7, and the
      objectIdentifier is "1.3.6.1.2.1.2.2.1.3.$0", it will be expanded
      to "1.3.6.1.2.1.2.2.1.3.7".  The special token "$*" is expanded to
      contain all of the subidentifiers of the index of the current
      element, separated by '.' characters.
      It is an RTE if a token is specified that is beyond the length of
      the index for the current element.
      Note that the "$n" convention is only active within strings.
   2. The ec() and ev() functions allow access to the components of the
      index for 'this element'.  ec() takes no argument and returns the
      number of index components that exist.  ev() takes an integer
      argument specifying which component of the index (numbered
      starting at 0) and returns an integer containing the value of the
      n'th subidentifier.  Refer to the Library functions section for
      the complete definition of ec() and ev().
         For example, if 'this element' is frCircuitDLCI.5.57
                                           (ifIndex = 5, DLCI = 57)
               then ec()  returns 2
                    ev(0) returns 5
                    ev(1) returns 57
      This is helpful when one wishes to address a related element.
      Extending the previous example, to find the port speed of the
      port, the circuit (above) runs over:
         portSpeed = getVar("ifSpeed." + ev(0));
      A script may check the type of 'this element' by calling the
      elementName() function.  Although it is possible to write a script
      that will work with different types of elements, many scripts will
      assume a particular element type and will work incorrectly if used
      on different element types.

7. Library Functions

Library functions are built-in functions available primarily to provide access to information on the local system or to manipulate this information more efficiently. A group of functions is organized into a library, the unit of conformance for function implementation. In order to claim conformance to a library, an implementation must implement all functions in a library to the specifications of the library.

In order for a management station or a condition or action to understand whether a certain library of functions is implemented, each library will have a name that it registers in the role table as a characteristic of the system element ("0.0") in the default SNMP context. Thus, conformance to a library can be tested with the roleMatch library function (in the base library) with the call roleMatch ("libraryName", "0.0").

Note that in the descriptions of these functions below, the function prototype describes the type of argument expected. Even though variables are not declared with a particular type, their contents must be appropriate for each function argument. If the type is variable, the keyword 'var' will be used. If only a string is appropriate, the keyword 'string' will be used. If only an integer is appropriate, the keyword 'integer' will be used. If the argument is declared as 'string' or 'integer' and a value of a different type is passed, the argument will be coerced with ToInteger() or ToString(). Any failure of this coercion will cause an RTE (in particular for ToInteger(), which will fail if its string-valued argument is not a well-formed integer).

In the function prototype, if the '&' character precedes the identifier for an argument, that argument may be modified by the function (e.g., "integer &result, ...)"). Arguments without the '&' character cannot be modified by the function. In a script, modifiable arguments don't have to be preceded by a '&'. It is an RTE if a constant is passed to a modifiable function argument (regardless of whether the function actually writes to the argument).

In the function prototype, the '[' and ']' characters surround arguments that are optional. In PolicyScript code, the optional argument may only be included if all optional arguments to the left of it are included. The function may place restrictions on when an optional argument must, or must not, be included. In the function prototype, if a type is listed before the name of the function, the function returns a value of that type. If no type is listed, the function returns no value.

8. Base Function Library

A standard base library of functions is available to all systems that implement this specification. This library is registered with the name "pmBaseFunctionLibrary". Although the specification of this library is modularized into 4 separate sections, conformance to the library requires implementation of all functions in all sections.

The sections are:

      - SNMP library functions
      - Policy library functions
      - Utility functions
      - Library Functions

8.1. SNMP Library Functions

Two sets of SNMP Library functions are available with different situations in mind:
   - Convenience SNMP Functions
     In an effort to keep simple things simple, these functions are easy
     to use and code that is easy to understand.  These functions will
     suffice for the majority of situations, where a single variable is
     referenced and the desired error recovery is simply (and
     immediately) to give up (and move to the next policy-element
     combination).  In more complex cases, the General SNMP Functions
     can be used at the cost of several times the code complexity.
     The convenience SNMP functions are getVar, exists, setVar,
     setRowStatus, createRow, counterRate, and searchColumn.
   - General SNMP Functions
     The General SNMP functions allow nearly any legal SNMP Message to
     be generated, including those with multiple varbinds, getNext
     operations, notifications, and messages with explicit addressing or
     security specifications.
     The general SNMP functions are writeVar, readVar, snmpSend,
     readError, and writeBulkParameters.

8.1.1. SNMP Operations on Non-Local Systems

From time to time, a script may have to perform an operation on a different SNMP system than that on which 'this element' resides. Scripts may also have to specify the use of alternate security parameters. In order to do this, the following optional arguments are provided for the SNMP library functions:
   snmp-function(...[, integer mPModel,
                       string tDomain, string tAddress,
                       integer secModel, string secName,
                       integer secLevel, string contextEngineID
   ])

For example:

       getVar("sysDescr.0", "", SNMPv3, "transportDomainUdpIpv4",
              "192.168.1.1:161", USM, "joe", NoAuthNoPriv);

The use of these arguments is denoted in function definitions by the keyword 'NonLocalArgs'. The definitions of these arguments are as follows:

      'mPModel' is the integer value of the SnmpMessageProcessingModel
      to use for this operation.
      'tDomain' is a string containing an ASCII dotted-decimal object
      identifier representing the transport domain to use for this
      operation.
      'tAddress' is a string containing the transport address formatted
      according to the 'tDomain' argument.  The ASCII formats for
      various values of 'tDomain' are defined by the DISPLAY-HINT for a
      TEXTUAL-CONVENTION that represents an address of that type.  The
      DISPLAY-HINTs used are:
         tDomain                    Source of DISPLAY-HINT [5] [11]
         -------                    ----------------------
         transportDomainUdpIpv4     TransportAddressIPv4
         transportDomainUdpIpv6     TransportAddressIPv6
         transportDomainUdpDns      TransportAddressDns
         snmpCLNSDomain             snmpOSIAddress
         snmpCONSDomain             snmpOSIAddress
         snmpDDPDomain              snmpNBPAddress
         snmpIPXDomain              snmpIPXAddress
         rfc1157Domain              snmpUDPAddress
         Other                      Use DISPLAY-HINT "1x:"
      'secModel' is the integer value of the SnmpSecurityModel to use
      for this operation.
      'secName' is a string value representing the SnmpSecurityName to
      use for this operation.
      'secLevel' is the integer value of the SnmpSecurityLevel to use
      for this operation.
      An SNMP operation will be sent to the target system by using
      security parameters retrieved from a local configuration datastore
      based on 'secModel', 'secName', and 'secLevel'.  It is the
      responsibility of the agent to ensure that sensitive information
      in the local configuration datastore is used on behalf of the
      correct principals, as identified by the security credentials of
      the last entity to modify the pmPolicyAdminStatus for a policy.
      To illustrate how this must be configured, consider an example in
      which 'joe' installs a policy on 'PMAgent' that will periodically
      configure objects on 'TargetAgent' with the credentials of
      'Operator'.  The following conditions must be true for this policy
      to execute with the proper privileges:
      - 'Operator's security credentials for TargetAgent must be
        installed in PMAgent's local configuration datastore (e.g.,
        usmUserTable [6]) indexed by TargetAgent's engineID and
        'Operator'.
      - VACM [9] must be configured on PMAgent so that 'joe' has access
        to the above entry in the appropriate MIB for the local
        configuration datastore (e.g., usmUserTable).
      - 'joe' must be the last user to modify the pmPolicyAdminStatus
        object for the policy.
      See the Security Considerations section for more information.
      For convenience, constants for 'mPModel', 'secModel', and
      'secLevel' are defined in the "Constants" section below.
      'contextEngineID' is a string representing the contextEngineID of
      the SNMP entity targeted by this operation.  It is encoded as a
      pair of hex digits (upper- and lowercase are valid) for each octet
      of the contextEngineID.  If 'tDomain' and 'tAddress' are provided
      but 'contextEngineID' is not, then the operation will be directed
      to the SNMP entity reachable at 'tDomain' and 'tAddress'.
      In order for PolicyScript code to use any of these arguments, all
      optional arguments to the left must be included.  'mPModel',
      'tDomain', 'tAddress', 'secModel', 'secName', and 'secLevel' must
      be used as a group; if one is specified, they must all be.
      'contextEngineID' may only be specified if all others are
      specified.
      Note that a function that uses NonLocalArgs must provide a
      parameter for the contextName that will be required when the
      NonLocalArgs are present.  Many functions will have the following
      logic:
      ContextName NonLocalArgs
      Supplied    Supplied
      No          No            Addressed to default context on
                                local system.
      Yes         No            Addressed to named context on
                                local system.
      Yes         Yes           Addressed to named context on
                                potentially remote system.
      No          Yes           Not allowed.

8.1.2. Form of SNMP Values

Many of the library functions have input or output parameters that may be one of the many SMI data types. The actual type is not encoded in the value but is specified elsewhere, possibly by nature of the situation in which it is used. The exact usage for input and output is as follows:
   Any Integer value
      (INTEGER, Integer32, Counter32, Counter64, Gauge32, Unsigned32,
      TimeTicks, Counter64):
      On input:
         An Integer or a String that can be successfully coerced to an
         Integer with the ToInteger() operator.  It is an RTE if a
         string is passed that cannot be converted by ToInteger() into
         an integer.
         A string of the form
           enum_decimal: [ letter | digit | '-' ]* '(' decimal_constant
         ')'
         will also be accepted.  In this case the sequence of characters
         before the parentheses and the parentheses themselves are
         completely ignored, and the decimal_constant inside the
         parentheses is converted.  Thus, "frame-relay(32)" translates
         to the integer 32.
      On output:
         An Integer containing the returned value.
   Octet String
      On input:
         Either a String or an Integer.  If an Integer, it will be
         coerced to a String with the ToString() function.  This string
         will be used as an unencoded representation of the octet string
         value.
      On output:
         A String containing the unencoded value of the octet string.
   Object Identifier
      On input and on output:
         A String containing a decimal ASCII encoded object identifier
         of the following form:
            oid:       subid [ '.' subid ]* [ '.' ]
            subid:     '0' | decimal_constant
      It is an RTE if an Object Identifier argument is not in the form
      above.  Note that a trailing '.' is acceptable and will simply be
      ignored.  (Note, however, that a trailing dot could cause a
      strncmp() comparison of two otherwise-identical OIDs to fail;
      instead, use oidncmp().)
      Note that ASCII descriptors (e.g., "ifIndex") are never used in
      these encodings "over the wire".  They are never returned from
      library functions; nor are they ever accepted by them.  NMS user
      interfaces are encouraged to allow humans to view object
      identifiers with ASCII descriptors, but they must translate those
      descriptors to dotted-decimal format before sending them in MIB
      objects to policy agents.
   Null
      On input:
         The input is ignored.
      On output:
         A zero length string.

8.1.3. Convenience SNMP Functions

8.1.3.1. getVar()

The getVar() function is used to retrieve the value of an SNMP MIB object instance.
      string getVar(string oid [, string contextName, NonLocalArgs])
         'oid' is a string containing an ASCII dotted-decimal
         representation of an object identifier (e.g.,
         "1.3.6.1.2.1.1.1.0").
         The optional 'contextName' argument contains the SNMP context
         on which to operate.  If 'contextName' is not present, the
         contextName of 'this element' will be used.  If 'contextName'
         is the zero-length string, the default context is used.
         The optional 'NonLocalArgs' provide addressing and security
         information to perform an SNMP operation on a system different
         from that of 'this element'.
         It is an RTE if the queried object identifier value does not
         exist.
         This function returns a string containing the returned value,
         encoded according to the returned type.  Note that no actual
         SNMP PDU has to be generated and parsed when the policy MIB
         agent resides on the same system as the managed elements.
         It is recommended that NMS user interfaces display and allow
         input of MIB object names by their descriptor values, followed
         by the index in dotted-decimal form (e.g., "ifType.7").

8.1.3.2. exists()

The exists() function is used to verify the existence of an SNMP MIB object instance.
      integer exists(string oid [, string contextName, NonLocalArgs])
         'oid' is a string containing an ASCII dotted-decimal
         representation of an object identifier (e.g.,
         "1.3.6.1.2.1.1.1.0").
         The optional 'contextName' argument contains the SNMP context
         on which to operate.  If 'contextName' is not present, the
         contextName of 'this element' will be used.  If 'contextName'
         is the zero-length string, the default context is used.
         The optional 'NonLocalArgs' provide addressing and security
         information to perform an SNMP operation on a system different
         from that of 'this element'.
         This function returns the value 1 if the SNMP instance exists
         and 0 if it doesn't exist.  Note that no actual SNMP PDU has to
         be generated and parsed when the policy MIB agent resides on
         the same system as the managed elements.
         It is recommended that NMS user interfaces display and allow
         input of MIB object names by their descriptor values, followed
         by the index in dotted-decimal form (e.g., "ifType.7").

8.1.3.3. setVar()

The setVar() function is used to set a MIB object instance to a certain value. The setVar() function is only valid in policyActions.
      setVar(string oid, var value, integer type
             [, string contextName, NonLocalArgs] )
         'oid' is a string containing an ASCII dotted-decimal
         representation of an object identifier (e.g.,
         "1.3.6.1.2.1.1.1.0").
         'value' is a string encoded in the format appropriate to the
         'type' parameter.  The agent will set the variable specified by
         'oid' to the value specified by 'value'.
         'type' will be the type of the 'value' parameter and will be
         set to one of the values for DataType Constants.
         The optional 'contextName' argument contains the SNMP context
         on which to operate.   If 'contextName' is not present, the
         contextName of 'this element' will be used.  If 'contextName'
         is the zero length string, the default context is used.
         The optional 'NonLocalArgs' provide addressing and security
         information to perform an SNMP operation on a system different
         from that of 'this element'.  Note that no actual SNMP PDU has
         to be generated and parsed when the policy MIB agent resides on
         the same system as the managed elements.
         It is an RTE if the set encounters any error.
         It is recommended that NMS user interfaces display and allow
         input of MIB object names by their descriptor values, followed
         by the index in dotted-decimal form (e.g., "ifType.7").

8.1.3.4. searchColumn()

      integer searchColumn(string columnoid, string &oid,
                           string pattern, integer mode
                           [, string contextName, NonLocalArgs])
         searchColumn performs an SNMP walk on a portion of the MIB
         searching for objects with values equal to the 'pattern'
         parameter.
         'columnoid' constrains the search to those variables that share
         the same OID prefix (i.e., those that are beneath it in the OID
         tree).
         A getnext request will be sent requesting the object identifier
         'oid'.  If 'oid' is an empty string, the value of 'columnoid'
         will be sent.
         The value returned in each response packet will be transformed
         to a string representation of the value of the returned
         variable.  The string representation of the value will be
         formed by putting the value in the form dictated by the "Form
         of SNMP Values" rules, and then by performing the ToString()
         function on this value, forming 'SearchString'.
         The 'mode' value controls what type of match to perform on this
         'SearchString' value.  There are 6 possibilities for mode:
           Mode               Search Action
           ExactMatch         Case sensitive exact match of 'pattern'
                              and 'SearchString'.
           ExactCaseMatch     Case insensitive exact match of 'pattern'
                              and 'SearchString'.
           SubstringMatch     Case sensitive substring match, finding
                              'pattern' in 'SearchString'.
           SubstringCaseMatch Case insensitive substring match, finding
                              'pattern' in 'SearchString'.
           RegexpMatch        Case sensitive regular expression match,
                              searching 'SearchString' for the regular
                              expression given in 'pattern'.
           RegexpCaseMatch    Case insensitive regular expression match,
                              searching 'SearchString' for the regular
                              expression given in 'pattern'.
         Constants for the values of 'mode' are defined in the
         'Constants' section below.
         searchColumn uses the POSIX extended regular expressions
         defined in POSIX 1003.2.
         The optional 'contextName' argument contains the SNMP context
         on which to operate.  If 'contextName' is not present, the
         contextName of 'this element' will be used.  If 'contextName'
         is the zero-length string, the default context is used.
         The optional 'NonLocalArgs' provide addressing and security
         information to perform SNMP operations on a system different
         from that of 'this element'.
         If a match is found, 'oid' is set to the OID of the matched
         value, and 1 is returned.  If the search traverses beyond
         columnoid or returns an error without finding a match, zero is
         returned, and 'oid' isn't modified.
         To find the first match, the caller should set 'oid' to the
         empty string.  To find additional matches, subsequent calls to
         searchColumn should have 'oid' set to the OID of the last
         match, an operation that searchColumn performs automatically.
         For example:
             To find an ethernet interface
             oid = "";
             searchColumn("ifType", oid, "6", 0);
         This sends a getnext request for ifType and continues to walk
         the tree until a value matching 6 is found or a variable
         returns that is not in the 'ifType' subtree.
         To find the next ethernet interface, assuming that interface 3
         was discovered to be the first:
             oid = "ifType.3";
             searchColumn("ifType", oid, "6", 0);
         In a loop to determine all the ethernet interfaces, this looks
         as follows:
             oid = "";
             while(searchColumn("ifType", oid, "6", 0)){
               /* Do something with oid */
             }
         Note that in the preceding examples, "ifType" is used as a
         notational convenience, and the actual code downloaded to the
         policy MIB agent must use the string "1.3.6.1.2.1.2.2.1.3" as
         there may be no MIB compiler (or MIB file) available on the
         policy MIB agent.
         Note that if the value of 'columnoid' is too short and thus
         references too much of the object identifier tree (e.g.,
         "1.3.6"), 'columnoid' could end up searching a huge number of
         variables (if the value was "1.3.6", it would search ALL
         variables on the agent).  It is the responsibility of the
         caller to make sure that 'columnoid' is set appropriately.

8.1.3.5. setRowStatus()

      integer setRowStatus(string oid, integer maxTries
                           [, integer freeOnException , integer seed
                            , string contextName, NonLocalArgs])
         setRowStatus is used to automate the process of finding an
         unused row in a read-create table that uses RowStatus whose
         index contains an arbitrary integer component for uniqueness.
         'oid' is a string containing an ASCII dotted-decimal
         representation of an object identifier, with one of the subids
         replaced with a '*' character (e.g.,
         "1.3.6.1.3.1.99.1.2.1.9.*").  'oid' must reference an
         'instance' of the RowStatus object, and the '*' must replace
         any integer index item that may be set to some random value.
         setRowStatus will come up with a number for the selected index
         item and will attempt to create the instance with the
         createAndWait state.  If the attempt fails, it will retry with
         a different random index value.  It will attempt this no more
         than 'maxTries' times.
         If the optional 'freeOnException' argument is present and equal
         to 1, the agent will free this row by setting RowStatus to
         'destroy' if, later in the same script invocation, this script
         dies with a run-time exception or by a call to fail().  Note
         that this does not apply to exceptions experienced in
         subsequent invocations of the script.
         If the optional 'seed' argument is present, the initial index
         will be set to 'seed'.  Otherwise it will be random.  'seed'
         may not be present if the 'freeOnException' argument is not
         present.
         The optional 'contextName' argument contains the SNMP context
         on which to operate.  If 'contextName' is not present, the
         contextName of 'this element' will be used.  If 'contextName'
         is the zero-length string, the default context is used.
         The optional 'NonLocalArgs' provide addressing and security
         information to perform an SNMP operation on a system different
         from that of 'this element'.
         setRowStatus returns the successful integer value for the
         index.  If it is unsuccessful after 'maxTries', or if zero or
         more than one '*' is in OID, -1 will be returned.
         The createRow function (below) can also be used when adding
         rows to tables.  Although createRow has more functionality,
         setRowStatus may be preferable in certain situations (for
         example, to have the opportunity to inspect default values
         created by the agent).

8.1.3.6. createRow()

      integer createRow(integer reqPDU, integer reqNumVarbinds,
                        integer statusColumn, integer maxTries,
                        integer indexRange,
                        integer &respPDU, integer &respNumVarbinds,
                        integer &index
                        [, integer freeOnException, string contextName,
                        NonLocalArgs])
         createRow is used to automate the process of creating a row in
         a read-create table whose index contains an arbitrary integer
         component for uniqueness.  In particular, it encapsulates the
         algorithm behind either the createAndWait or createAndGo
         mechanism and the algorithm for finding an unused row in the
         table.  createRow is not useful for creating rows in tables
         whose indexes don't contain an arbitrary integer component.
         createRow will perform the operation by sending 'reqPDU' and
         returning the results in 'respPDU'.  Both 'reqPDU' and
         'respPDU' must previously have been allocated with newPDU.
         'reqPDU' and 'respPDU' may both contain the same PDU handle, in
         which case the 'reqPDU' is sent and then replaced with the
         contents of the received PDU.
         'reqNumVarbinds' is an integer greater than zero that specifies
         which varbinds in the PDU will be used in this operation.  The
         first 'reqNumVarbinds' in the PDU are used.  Each such varbind
         must be of a special form in which the object name must have
         one of its subids replaced with a '*' character (e.g.,
         "1.3.6.1.3.1.99.1.2.1.9.*").  The subid selected to be replaced
         will be an integer index item that may be set to some random
         value.  The same subid should be selected in each varbind in
         the PDU.
         'respNumVarbinds' will be modified to contain the number of
         varbinds received in the last response PDU.
         'statusColumn' identifies which varbind in 'pdu' should be
         treated as the RowStatus column, where 0 identifies the 1st
         varbind.
         createRow will come up with a random integer index value and
         will substitute that value in place of the '*' subid in each
         varbind.  It will then set the value of the RowStatus column to
         select the 'createAndGo' mechanism and execute the set.  If the
         attempt fails due to the unavailability of the 'createAndGo'
         mechanism, it will retry with the 'createAndWait' mechanism
         selected.  If the attempt fails because the chosen index value
         is already in use, the operation will be retried with a
         different random index value.  It will continue to retry
         different index values until it succeeds, until it has made
         'maxTries' attempts, or until it encounters an error.  The
         value of 'maxTries' should be chosen to be high enough to
         minimize the chance that as the table fills up an attempt to
         create a new entry will 'collide' too often and fail.
         All random index values must be between 1 and 'indexRange',
         inclusive.  This is so that values are not attempted for an
         index that fall outside of that index's restricted range (e.g.,
         1..65535).
         If the optional 'freeOnException' argument is present and equal
         to 1, the agent will free this row by setting RowStatus to
         'destroy' if, later in the same script invocation, this script
         dies with a run-time exception or by a call to fail().  Note
         that this does not apply to exceptions experienced in
         subsequent invocations of the script.
         The optional 'contextName' argument contains the SNMP context
         on which to operate.  If 'contextName' is not present, the
         contextName of 'this element' will be used.  If 'contextName'
         is the zero-length string, the default context is used.
         The optional 'NonLocalArgs' provide addressing and security
         information to perform an SNMP operation on a system different
         from that of 'this element'.
         Note that no actual SNMP PDU has to be generated and parsed
         when the policy MIB agent resides on the same system as the
         managed elements.  If no PDU is generated, the agent must
         correctly simulate the behavior of the SNMP Response PDU,
         particularly in case of an error.
         This function returns zero unless an error occurs, in which
         case it returns the proper SNMP Error Constant.  If an error
         occurred, respPDU will contain the last response PDU as
         received from the agent unless no response PDU was received, in
         which case respNumVarbinds will be 0.  In any event, readError
         may be called on the PDU to determine error information for the
         transaction.
         The 'index' parameter returns the chosen index.  If successful,
         'index' will be set to the successful integer index.  If no
         SNMP error occurs but the operation does not succeed due to the
         following reasons, 'index' will be set to -1:
            1) Unsuccessful after 'maxTries'.
            2) An object name had no '*' in it.
            3) An object name had more than one '*' in it.
         For example, createRow() might be used as follows:
         var index, pdu = newPDU(), nVars = 0;
         writeVar(pdu, nVars++, "hlHostControlDataSource.*",
                  "ifIndex." + ev(0), Oid);
         writeVar(pdu, nVars++, "hlHostControlNlMaxDesiredEntries.*",
                  1000, Integer);
         writeVar(pdu, nVars++, "hlHostControlAlMaxDesiredEntries.*",
                  1000, Integer);
         writeVar(pdu, nVars++, "hlHostControlOwner.*", "policy",
                  String);
         writeVar(pdu, nVars++, "hlHostControlStatus.*", "active(1)",
                  Integer);
         if (createRow(pdu, nVars, 4, 20, 65535,
                       pdu, nVars, index) != 0
             || index == -1)
             return;
         // index now contains index of new row

8.1.3.7. counterRate()

When a policy wishes to make a decision based on the rate of a counter, it faces a couple of problems:
   1. It may have to run every X minutes but have to make decisions on
      rates calculated over at least Y minutes, where Y > X.  This would
      require the complexity of managing a queue of old counter values.

2. The policy script has no control over exactly when it will run.

The counterRate() function is designed to surmount these problems easily.
      integer counterRate(string oid, integer minInterval
                          [, integer 64bit,
                          string discOid, integer discMethod,
                          string contextName, NonLocalArgs])
         'counterRate' retrieves the variable specified by oid once per
         invocation.  It keeps track of timestamped values retrieved on
         previous invocations by this execution context so that it can
         calculate a rate over a period longer than that since the last
         invocation.
         'oid' is the object identifier of the counter value that will
         be retrieved.  The most recent previously saved value of the
         same object identifier that is at least 'minInterval' seconds
         old will be subtracted from the newly retrieved value, yielding
         a delta.  If 'minInterval' is zero, this delta will be
         returned.  Otherwise, this delta will be divided by the number
         of seconds elapsed between the two retrievals, and the
         integer-valued result will be returned (rounding down when
         necessary).
         If there was no previously saved retrieval older than
         'minInterval' seconds, then -1 will be returned.  It is an RTE
         if the query returns noSuchName, noSuchInstance, or
         noSuchObject or an object that is not of type Counter32 or
         Counter64.
         The delta calculation will allow for 32-bit counter semantics
         if it encounters rollover between the two retrievals, unless
         the optional argument '64bit' is present and equal to 1, in
         which case it will allow for 64-bit counter semantics.
         'discOid' and 'discMethod' may only be present together.
         'discOid' contains an object identifier of a discontinuity
         indicator value that will be retrieved simultaneously with each
         counter value:
            1. If 'discMethod' is equal to 1 and the discontinuity
               indicator is less than the last one retrieved, then a
               discontinuity is indicated.
            2. If 'discMethod' is equal to 2 and the discontinuity
               indicated is different from the last one retrieved, then
               a discontinuity is indicated.
         If this value indicates a discontinuity, this counter value
         (and its timestamp) will be stored, but all previously stored
         counter values will be invalidated and -1 will be returned.
         The implementation will have to store a number of timestamped
         counter values.  The implementation must keep all values that
         are newer than minInterval seconds, plus the newest value that
         is older than minInterval seconds.  Other than this one value
         that is older than minInterval seconds, the implementation
         should discard any older values.
         For example:
           Policy that executes every 60 seconds:
               rate = counterRate("ifInOctets.$*", 300);
               if (rate > 1000000)
                   ...
         Another example, with a discontinuity indicator:
           Policy that executes every 60 seconds:
               rate = counterRate("ifInOctets.$*", 300, 0,
                                  "sysUpTime.0", 1);
               if (rate > 1000000)
                   ...
         Another example, with zero minInterval:
           Policy that executes every 60 seconds:
               delta = counterRate("ifInErrors.$*", 0);
               if (delta > 100)
                   ...
         The optional 'contextName' argument contains the SNMP context
         on which to operate.  If 'contextName' is not present, the
         contextName of 'this element' will be used.  If 'contextName'
         is the zero-length string, the default context is used.

8.1.4. General SNMP Functions

It is desirable that a general SNMP interface have the ability to perform SNMP operations on multiple variables at once and that it allow multiple varbind lists to exist at once. The newPdu, readVar, and writeVar functions exist to provide these facilities in a language without pointers, arrays, and memory allocators.

newPDU is called to allocate a PDU and return an integer handle to it. As PDUs are automatically freed when the script exits and can be reused during execution, there is no freePDU().

readVar and writeVar access a variable length varbind list for a PDU. The PDU handle and the index of the variable within that PDU are specified in every readVar and writeVar operation. Once a PDU has been fully specified by one or more calls to writeVar, it is passed to snmpSend (by referencing the PDU handle) and the number of varbinds to be included in the operation. When a response is returned, the contents of the response are returned in another PDU and may be read by one or more calls to readVar. Error information may be read from the PDU with the readError function. Because GetBulk PDUs send additional information in the SNMP header, the writeBulkParameters function is provided to configure these parameters.

Varbinds in this data store are created automatically whenever they are written by any writeVar or snmpSend operation.

   For example:
     var pdu = newPDU();
     var nVars = 0, oid, type, value;
     writeVar(pdu, nVars++, "sysDescr.0", "", Null);
     writeVar(pdu, nVars++, "sysOID.0", "", Null);
     writeVar(pdu, nVars++, "ifNumber.0", "", Null);
     if (snmpSend(pdu, nVars, Get, pdu, nVars))
         return;
     readVar(pdu, 0, oid, value, type);
     readVar(pdu, 1, oid, value, type);
     readVar(pdu, 2, oid, value, type);
     ...
   or,
     var pdu = newPDU();
     var nVars = 0, oid1, oid2;
     writeVar(pdu, nVars++, "ifIndex", "", Null);
     writeVar(pdu, nVars++, "ifType", "", Null);
     while(!done){
       if (snmpSend(pdu, nVars, Getnext, pdu, nVars))
           continue;
       readVar(pdu, 0, oid1, value, type);
       readVar(pdu, 1, oid2, value, type);
       /* leave OIDs alone, now PDU #0 is set up for next step
          in table walk. */
       if (oidncmp(oid1, "ifIndex", oidlen("ifIndex")))
         done = 0;
       ...
     }

Note that in the preceding examples, descriptors such as ifType and sysDescr are used in object identifiers solely as a notational convenience. The actual code downloaded to the policy MIB agent must use a dotted decimal notation only, as there may be no MIB compiler (or MIB file) available on the policy MIB agent.

To conform to this specification, implementations must allow each policy script invocation to allocate at least 5 PDUs with at least 64 varbinds per list. It is suggested that implementations limit the total number of PDUs per invocation to protect other script invocations from a malfunctioning script (e.g., a script that calls newPDU() in a loop).

8.1.4.1. newPDU()

      integer newPDU()
         newPDU will allocate a new PDU and return a handle to the PDU.
         If no PDU could be allocated, -1 will be returned.  The PDU's
         initial values of nonRepeaters and maxRepetitions will be zero.

8.1.4.2. writeVar()

      writeVar(integer pdu, integer varBindIndex,
               string oid, var value, integer type)
         writeVar will store 'oid', 'value', and 'type' in the specified
         varbind.
         'pdu' is the handle to a PDU allocated by newPDU().
         'varBindIndex' is a non-negative integer that identifies the
         varbind within the specified PDU modified by this call.  The
         first varbind is number 0.
         'oid' is a string containing an ASCII dotted-decimal
         representation of an object identifier (e.g.,
         "1.3.6.1.2.1.1.1.0").
         'value' is the value to be stored, of a type appropriate to the
         'type' parameter.
         'type' will be the type of the value parameter and will be set
         to one of the values for DataType Constants.
         It is an RTE if any of the parameters don't conform to the
         rules above.

8.1.4.3. readVar()

      readVar(integer pdu, integer varBindIndex, string &oid,
              var &value, integer &type)
         readVar will retrieve the oid, the value, and its type from the
         specified varbind.
         'pdu' is the handle to a PDU allocated by newPDU().
         'varBindIndex' is a non-negative integer that identifies the
         varbind within the specified PDU read by this call.  The first
         varbind is number 0.
         The object identifier value of the referenced varbind will be
         copied into the 'oid' parameter, formatted in an ASCII dotted-
         decimal representation (e.g., "1.3.6.1.2.1.1.1.0").
         'value' is the value retrieved, of a type appropriate to the
         'type' parameter.
         'type' is the type of the value parameter and will be set to
         one of the values for DataType Constants.
         It is an RTE if 'pdu' doesn't reference a valid PDU or
         'varBindIndex' doesn't reference a valid varbind.

8.1.4.4. snmpSend()

      integer snmpSend(integer reqPDU, integer reqNumVarbinds,
                       integer opcode,
                       integer &respPDU, integer &respNumVarbinds,
                       [, string contextName , NonLocalArgs] )
         snmpSend will perform an SNMP operation by sending 'reqPDU' and
         returning the results in 'respPDU'.  Both 'reqPDU' and
         'respPDU' must previously have been allocated with newPDU.
         'reqPDU' and 'respPDU' may both contain the same PDU handle, in
         which case the 'reqPDU' is sent and then replaced with the
         contents of the received PDU.  If the opcode specifies a Trap
         or V2trap, 'respPDU' will not be modified.
         'reqNumVarbinds' is an integer greater than zero that specifies
         which varbinds in the PDU will be used in this operation.  The
         first 'reqNumVarbinds' in the PDU are used.  'respNumVarbinds'
         will be modified to contain the number of varbinds received in
         the response PDU, which, in the case of GetBulk or an error,
         may be substantially different from reqNumVarbinds.
         'opcode' is the type of SNMP operation to perform and must be
         one of the values for SNMP Operation Constants listed in the
         'Constants' section below.
         The optional 'contextName' argument contains the SNMP context
         on which to operate.  If 'contextName' is not present, the
         contextName of 'this element' will be used.  If 'contextName'
         is the zero-length string, the default context is used.
         Note that no actual SNMP PDU has to be generated and parsed
         when the policy MIB agent resides on the same system as the
         managed elements.  If no PDU is generated, the agent must
         correctly simulate the behavior of the SNMP Response PDU,
         particul