RFC 3512

Configuring Networks and Devices with Simple Network Management Protocol (SNMP)

📅 published April 2003, from ietf

Configuring Networks and Devices with Simple Network Management Protocol (SNMP) 🎀

Okay bestie, here's the title block. This is RFC 3512 from the Network Working Group, and it's Informational 🦋. It was published in April 2003, and the authors are:

  • M. MacFaden, Riverstone Networks, Inc.
  • D. Partain, Ericsson
  • J. Saperia, JDS Consulting, Inc.
  • W. Tackabury, Gold Wire Technology, Inc.

The title block doesn't list anything it obsoletes or updates, so it's a standalone doc 💅.

Status of this Memo 📜

This memo gives info for the Internet community. It doesn't specify an Internet standard of any kind, and distribution of it is unlimited 🌈.

Copyright (C) The Internet Society (2003). All Rights Reserved. 🫶

Abstract ✨

This doc is for readers who are into the Internet Standard Management Framework and its protocol, the Simple Network Management Protocol (SNMP). It gives guidance on using SNMP effectively for configuration management. It's relevant to vendors that build network elements, management application developers, and anyone who acquires and deploys this tech in their networks 🧚.

Table of Contents 📚

   1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . .   3
      1.1. The Internet Standard Management Framework. . . . . . . .   3
      1.2. Configuration and the Internet Standard Management
           Frame-work. . . . . . . . . . . . . . . . . . . . . . . .   4
   2. Using SNMP as a Configuration Mechanism. . . . . . . . . . . .   5
      2.1. Transactions and SNMP . . . . . . . . . . . . . . . . . .   6
      2.2. Practical Requirements for Transactional Control. . . . .   6
      2.3. Practices in Configuration--Verification. . . . . . . . .   7
   3. Designing a MIB Module . . . . . . . . . . . . . . . . . . . .   9
      3.1. MIB Module Design - General Issues. . . . . . . . . . . .  10
      3.2. Naming MIB modules and Managed Objects. . . . . . . . . .  11
      3.3. Transaction Control And State Tracking. . . . . . . . . .  12

           3.3.1. Conceptual Table Row Modification Practices. . . .  12
           3.3.2. Fate sharing with multiple tables. . . . . . . . .  13
           3.3.3. Transaction Control MIB Objects. . . . . . . . . .  14
           3.3.4. Creating And Activating New Table Rows . . . . . .  15
           3.3.5. Summary Objects and State Tracking . . . . . . . .  15
           3.3.6. Optimizing Configuration Data Transfer . . . . . .  18
      3.4. More Index Design Issues. . . . . . . . . . . . . . . . .  22
           3.4.1. Simple Integer Indexing. . . . . . . . . . . . . .  23
           3.4.2. Indexing with Network Addresses. . . . . . . . . .  23
      3.5. Conflicting Controls. . . . . . . . . . . . . . . . . . .  24
      3.6. Textual Convention Usage. . . . . . . . . . . . . . . . .  25
      3.7. Persistent Configuration. . . . . . . . . . . . . . . . .  26
      3.8. Configuration Sets and Activation . . . . . . . . . . . .  28
           3.8.1. Operational Activation Considerations. . . . . . .  28
           3.8.2. RowStatus and Deactivation . . . . . . . . . . . .  30
      3.9. SET Operation Latency . . . . . . . . . . . . . . . . . .  31
           3.9.1. Subsystem Latency, Persistence Latency,
                  and Activation Latency . . . . . . . . . . . . . .  33
      3.10. Notifications and Error Reporting. . . . . . . . . . . .  33
           3.10.1. Identifying Source of Configuration Changes . . .  34
           3.10.2. Limiting Unnecessary Transmission of
                   Notifications . . . . . . . . . . . . . . . . . .  34
           3.10.3. Control of Notification Subsystem . . . . . . . .  36
      3.11 Application Error Reporting . . . . . . . . . . . . . . .  36
      3.12 Designing MIB Modules for Multiple Managers . . . . . . .  37
      3.13 Other MIB Module Design Issues. . . . . . . . . . . . . .  39
           3.13.1. Octet String Aggregations . . . . . . . . . . . .  39
           3.13.2 Supporting multiple instances of a MIB Module. . .  40
           3.13.3 Use of Special Optional Clauses. . . . . . . . . .  41
   4. Implementing SNMP Configuration Agents . . . . . . . . . . . .  41
      4.1. Operational Consistency . . . . . . . . . . . . . . . . .  41
      4.2. Handling Multiple Managers. . . . . . . . . . . . . . . .  43
      4.3. Specifying Row Modifiability. . . . . . . . . . . . . . .  44
      4.4. Implementing Write-only Access Objects. . . . . . . . . .  44
   5. Designing Configuration Management Software. . . . . . . . . .  44
      5.1. Configuration Application Interactions
           with Managed Systems. . . . . . . . . . . . . . . . . . .  45
           5.1.1. SET Operations . . . . . . . . . . . . . . . . . .  46
           5.1.2. Configuration Transactions . . . . . . . . . . . .  46
           5.1.3. Tracking Configuration Changes . . . . . . . . . .  47
           5.1.4. Scalability of Data Retrieval. . . . . . . . . . .  48
   6. Deployment and Security Issues . . . . . . . . . . . . . . . .  48
      6.1. Basic assumptions about Configuration . . . . . . . . . .  48
      6.2. Secure Agent Considerations . . . . . . . . . . . . . . .  49
      6.3. Authentication Notifications. . . . . . . . . . . . . . .  49
      6.4. Sensitive Information Handling. . . . . . . . . . . . . .  50
   7. Policy-based Management. . . . . . . . . . . . . . . . . . . .  51
      7.1. What Is the Meaning of 'Policy-based' . . . . . . . . . .  51
      7.2. Organization of Data in an SNMP-Based Policy System . . .  53
      7.3. Information Related to Policy-based Configuration . . . .  54
      7.4. Schedule and Time Issues. . . . . . . . . . . . . . . . .  56
      7.5. Conflict Detection, Resolution and Error Reporting. . . .  56
           7.5.1. Changes to Configuration Outside of the
                  Policy System. . . . . . . . . . . . . . . . . . .  57
      7.6. More about Notifications in a Policy System . . . . . . .  57
      7.7. Using Policy to Move Less Configuration Data. . . . . . .  57
   8. Example MIB Module With Template-based Data. . . . . . . . . .  58
      8.1. MIB Module Definition. . . . . . . . .  . . . . . . . . . .  61
      8.2. Notes on MIB Module with Template-based Data. . . . . . .  73
      8.3. Examples of Usage of the MIB . . . . . . .. . . . . . . .  74
   9. Security Considerations . . . . . . . . . . .. . . . . . . . .  77
   10. Acknowledgments. . . . . . . . . . . . . . .  . . . . . . . .  78
   11. Normative References. . . . . . . . . . . . . . . . . . . . .  78
   12. Informative References. . . . . . . . . . . . . . . . . . . .  79
   13. Intellectual Property . . . . . . . . . . . . . . . . . . . .  81
   14. Editors' Addresses. . . . . . . . . . . . . . . . . . . . . .  82
   15. Full Copyright Statement. . . . . . . . . . . . . . . . . . .  83

1. Introduction ✨

1.1. The Internet Standard Management Framework 🌸

The Internet Standard Management Framework has a lot of components, and this doc is all about the good ways to apply them to configuration management problems 💖.

For reference, the framework currently has five major components:

  • An overall architecture, described in RFC 3411 [1].
  • Mechanisms for describing and naming objects and events for management purposes. The first version of this Structure of Management Information (SMI) is called SMIv1 and is described in STD 16, RFC 1155 [15], STD 16, RFC 1212 [16] and RFC 1215 [17]. The second version, called SMIv2, is described in STD 58, RFC 2578 [2], STD 58, RFC 2579 [3] and STD 58, RFC 2580 [4].
  • Message protocols for transferring management information. The first version of the SNMP message protocol is called SNMPv1 and is described in STD 15, RFC 1157 [18]. A second version of the SNMP message protocol, which is not an Internet standards track protocol, is called SNMPv2c and is described in RFC 1901 [19]. The third version of the message protocol is called SNMPv3 and is described in RFC 3417 [5], RFC 3412 [6] and RFC 3414 [7].
  • Protocol operations for accessing management information. The first set of protocol operations and associated PDU formats is described in STD 15, RFC 1157 [18]. A second set of protocol operations and associated PDU formats is described in RFC 3416 [8].
  • A set of fundamental applications described in RFC 3413 [9], and the view-based access control mechanism described in RFC 3415 [10].

If you want a more detailed intro to the current SNMP Management Framework, check out RFC 3410 [12] 🦋.

Managed objects are accessed through a virtual information store called the Management Information Base, or MIB. Objects in the MIB are defined using the mechanisms defined in the SMI 🧚.

1.2. Configuration and the Internet Standard Management Framework ✨

Okay so data networks have gotten SO big over the past decade 🌈, and it shows up in three main ways:

  • Scale - networks have more network elements, and those elements are literally bigger and put more demands on the systems managing them 💪. Like, look at how many interfaces a typical core network element has these days 😭. A managed metropolitan area network switch can have a port density way higher than what the management systems from before were built to expect. There are also way more interrelationships within and between devices and device functions 🕸️.
  • Functionality - network devices do more functions now 🧚. More protocols and network layers are required for the network services that depend on them to actually work.
  • Rate of Change - the way modern network services work means device configuration info gets updated, added, and deleted way more often than before 🔄. You can't just assume a configuration gets specified once and then rarely updated anymore. The trend is totally toward much more frequent configuration changes 😤.

Correct configuration of the network elements that make up data networks is a prerequisite for successfully deploying the services on them 💅. Bigger, more complicated networks make it even more important to have a standard configuration mechanism that's tightly integrated with performance and fault management systems 🫶.

The Internet Standard Management Framework has been used successfully to build configuration management systems for a broad range of devices and networks 🎀. A standard configuration mechanism that tightly integrates with performance and fault systems is needed not only to cut down on management complexity, but also to enable verification of configuration activities that create revenue-producing services 💰.

This document describes Current Practices that have been used when designing effective configuration management systems using the Internet Standard Management Framework (colloquially known as SNMP) 🦋. It covers lots of basic practices, plus the more complex agent and manager design issues that configuration management raises. We are not trying to be a full how-to for generalized SNMP agent, MIB module, or management application design and development 🙅. We will, however, cover points of generalized SNMP software design and implementation practice where that practice has been seen to benefit configuration management software. So, for example, the requirement for management applications to be aware of agent limitations is discussed in the context of configuration operations, but a lot of the issues a management application developer should consider about manager-agent interactions are left for other documents and resources 📚.

Significant experience has been gained over the past ten years configuring public and private data networks with SNMP 🙌. During that time, networks grew a ton, as described above. A response to this explosive growth has been the development of policy-based configuration management. Policy-Based Configuration Management is a methodology where configuration information is derived from rules and network-wide objectives, and is distributed to potentially many network elements with the goal of achieving consistent network behavior throughout an administrative domain 🌟.

This document presents lessons learned from these experiences and applies them to both conventional and policy-based configuration systems based on SNMP ✨.


2. Using SNMP as a Configuration Mechanism 🔥

Configuration activity causes one or more state changes in an element. Even though it often takes an arbitrary number of commands and amount of data to make up a configuration change, it is critical that the configuration system treats the overall change operation atomically, so that the number of states an element transitions into is minimized 🧩. The goal is for a change request to either be completely executed or not at all. This is called transactional integrity 💜. Transactional integrity is what makes it possible to build reliable configuration systems that can invoke transactions, keep track of an element's overall state, and keep working in the presence of error states 💪.

2.1. Transactions and SNMP 🎀

Transactions can happen at super fine-grained levels, like one individual object instance, or in really big aggregations that could include many object instances spread across many tables on a managed device 🌸. For this reason, relying on transactional integrity only at the SNMP protocol level is insufficient 🙅.

2.2. Practical Requirements for Transactional Control 💅

A well-designed and deployed configuration system should have the following features with regard to transactions and transactional integrity 📝.

  1. Provide for flexible transaction control at many different levels of granularity. At one extreme, an entire configuration may be delivered and installed on an element, or alternately one small attribute may be changed 🍓.
  2. The transaction control component should work at and understand a notion of the kind of multi-level "defaulting" as described in Section 7.1. The key point here is that it may make most sense to configure systems at an abstract level rather than on an individual instance by instance basis, as has been commonly practiced 🧠. In some cases it is more effective to send a configuration command to a system that contains a set of 'defaults' to be applied to instances that meet certain criteria ✨.
  3. An effective configuration management system must allow flexibility in the definition of a successful transaction. This cannot be done at the protocol level alone, but rather must be provided for throughout the application and the information that is being managed. In the case of SNMP, the information would be in properly defined MIB modules 📦.
  4. A configuration management system should provide time-indexed transaction control. For effective rollback control, the configuration transactions and their successful or unsuccessful completion status must be reported by the managed elements and stored in a repository that supports such time indexing and can record the user that made the change, even if the change was not carried out by the system recording the change ⏰.
  5. The managed system must support transactional security. This means that depending on who is making the configuration request and where it is being made, it may be accepted or denied based on the security policy that is in effect in the managed element 🔐.

Effective transactional control is a responsibility shared between design, implementation, and operational practice 🤝. Transaction control techniques for MIB module design are discussed in Section 3.3. Transaction control considerations for the agent implementation are discussed in Section 5.2.2 💖.

2.3. Practices in Configuration--Verification ✨

okay so verifying what happens after you commit a change is literally part of the configuration process, bestie 💅 to avoid dumb config mistakes, a lot of orgs follow this change management procedure:

pre-test - verify that the system is presently working properly

change   - make configuration changes and wait for convergence
           (system or network stability)

re-test  - verify once again that the system is working properly

this is super common for critical stuff like the domain name system (DNS) 🌸 DNS software kits come with diagnostic tools so you can run automatic test procedures and scripts. iconic behavior.

if the pre-configuration test shows the system is unstable, you can abort the planned config sequence 🛑 and honestly? Debugging the unintended effects of two sets of changes in big systems is way harder than analyzing a single set after you've ended the test. no thank you 💀

networks and devices under SNMP configuration can totally run this change management procedure because SNMP gives you monitoring, configuration, and diagnostics all in one place 🦋 the key is sequencing your SNMP protocol operations so they make up an integrated change procedure like the one above. this is usually a well-bounded affair for changes inside one network element or node. BUT there are times when configuring one element can impact other elements in the network. configuring network protocols like IEEE 802.1D Spanning Tree or OSPF is especially tricky, since a configuration change can directly affect the stability (convergence) of the network the device is connected to. main character problems 😭

an integrated view of configuration and monitoring is an ideal platform for evaluating these changes 🧚 for example, the MIB module that governs IEEE 802.1D Spanning Tree (RFC 1493 [24]) gives you this object to monitor stability per logical bridge:

   dot1dStpTopChanges OBJECT-TYPE
       SYNTAX  Counter
       ACCESS  read-only
       STATUS  mandatory
       DESCRIPTION
          "The total number of topology changes detected by
          this bridge since the management entity was last
          reset or initialized."
       REFERENCE
          "IEEE 802.1D-1990: Section 6.8.1.1.3"
       ::= { dot1dStp 4 }

and the OSPF MIB module has a similar metric for stability per OSPF area 🍓

   ospfSpfRuns OBJECT-TYPE
       SYNTAX   Counter32
       MAX-ACCESS   read-only
       STATUS   current
       DESCRIPTION
          "The number of times that the intra-area route
          table has been calculated using this area's
          link-state database.  This is typically done
          using Dijkstra's algorithm."
      ::= { ospfAreaEntry 4 }

these object types are great examples of how to facilitate the principles described in Section 2.3 🎀 basically you need to understand how a subsystem behaves before a configuration change, and then use the same tools to retest and verify it's still working properly after the change. ✨ that's the whole loop, babe.

the operational effects of a given implementation often differ from one to another for any given standard configuration object 🌈 the impact of a change on stability of systems like OSPF should be documented in an agent-capabilities statement that's consistent with "Requirements for IP Version 4 Routers" [22], Section 1.3.4:

A vendor needs to provide adequate documentation on all configuration parameters, their limits and effects.

the model above is not fail-safe 🙈 especially when configuration errors get masked by long latencies, or when they lead to oscillations in network stability. for example, picture loading a new software version on a device that causes small, slow, cumulative memory leaks triggered by a certain traffic pattern that didn't get caught during vendor and customer test lab trials. ngl that's a nightmare.

in a network-based example, convergence in an autonomous system can't be guaranteed when configuration changes are made, because there are factors outside the operator's control, like the state of other network elements. problems affecting convergence may not be detected for a long time after the change. and even for factors inside the operator's control, there's often little verification done to prevent mis-configuration (as the following example shows) 🥲

consider a change to the ospfIfHelloInterval and ospfIfRtrDeadInterval [24] timers in OSPF so that both are set to the same value. two routers might form an adjacency, then start cycling in and out of it, and never reach a stable (converged) state 💀 if you'd used the configuration process described at the start of this section, you would've discovered this without hurting the production network. saved!

the big takeaway from all this 👑 configuration systems should be designed and implemented with verification tests in mind.


3. Designing a MIB Module 🎀

carefully considered MIB module designs are crucial for practical configuration with SNMP. as we just saw, MIB objects designed for configuration can be super effective because they can be associated with integrated diagnostic, monitoring, and fault objects. MIB modules for configuration also scale when they expose their notion of template object types 📈 template objects can represent information at a higher level of abstraction than instance-level ones. this has the benefit of reducing how much instance-level data needs to move from the management application to the agent on the managed element, when that instance-level data is produced by applying a template object on the agent. put it all together and you get a robust configuration subsystem. slay.

the rest of this section covers specific practices for MIB module design using SMIv2 and SNMPv3 💜

3.1. MIB Module Design - General Issues 🧩

one of the first tasks in defining a MIB module is creating a model that reflects the scope and organization of the management information an agent will expose.

MIB modules can be thought of as logical models that provide one or more aspects/views of a subsystem. the objective for all MIB modules should be to serve one or more operational requirements, such as accounting information collection, configuration of one or more parts of a system, or fault identification. however, it's important to include only those aspects of a subsystem that are proven to be operationally useful 🫶

in 1993, one of the most widely deployed MIB modules supporting configuration was published: RFC 1493, which contained the BRIDGE-MIB. it defined the criteria used to develop the MIB module, as follows:

To be consistent with IAB directives and good engineering practice, an explicit attempt was made to keep this MIB as simple as possible. This was accomplished by applying the following criteria to objects proposed for inclusion:

(1) Start with a small set of essential objects and add only as further objects are needed.

(2) Require objects be essential for either fault or configuration management.

(3) Consider evidence of current use and/or utility.

(4) Limit the total (sic) of objects.

(5) Exclude objects which are simply derivable from others in this or other MIBs.

(6) Avoid causing critical sections to be heavily instrumented. The guideline that was followed is one counter per critical section per layer.

over the past eight years, additional experience has shown a need to expand these criteria as follows 🔥

(7) Before designing a MIB module, identify goals and objectives for the MIB module. How much of the underlying system will be exposed depends on these goals.

(8) Minimizing the total number of objects is not an explicit goal, but usability is. Be sure to consider deployment and usability requirements.

(9) During configuration, consider supporting explicit error state, capability and capacity objects.

(10) When evaluating rule (5) above, consider the impact on a management application. If an object can help reduce a management application's complexity, consider defining objects that can be derived.

3.2. Naming MIB modules and Managed Objects 🏷️

naming MIB modules and objects informally follows a set of best practices. originally, standards track MIB modules used RFC names. as MIB modules evolved, the practice shifted to more descriptive names. nowadays, Standards Track MIB modules define a given area of technology, such as ATM-MIB, and vendors then extend those MIB modules by prefixing their company name, as in ACME-ATM-MIB 🌸

object descriptors (the "human readable names" assigned to object identifiers [2]) defined in standard MIB modules should be unique across all MIB modules. generally, a prefix is added to each managed object that can help reference the MIB module it was defined in. for example, the IF-MIB uses the "if" prefix for descriptors of object types such as ifTable, ifStackTable and so forth 🦋

MIB module object type descriptors can include an abbreviation for the function they perform. for example, the objects that control configuration in the example MIB module in Section 8 include "Cfg" as part of the object descriptor, as in bldgHVACCfgDesiredTemp 🍓

this is more fully realized when the object descriptors that include the fault, configuration, accounting, performance and security [33] abbreviations are combined with an organized OID assignment approach. for example, a vendor could create a configuration branch in their private enterprises area. in some cases, this might be best done on a per product basis. whatever approach is used, "Cfg" might be included in every object descriptor in the configuration branch. this has two operational benefits 🎀 first, for those who do look at instances of MIB objects, descriptors seen through MIB browsers or other command line tools help convey the meaning of the object type. secondly, management applications can be pointed at specific subtrees for fault or configuration, which causes a more efficient retrieval of data and a simpler management application with potentially better performance ✨

3.3. Transaction Control And State Tracking 🔄

transactions, and keeping track of their state, are an important consideration when performing any type of configuration activity, regardless of the protocol 🧠 here are a few areas to think about when designing transaction support into an SNMP-based configuration system.

3.3.1. Conceptual Table Row Modification Practices ✨

okay so whenever people talk about transaction control in MIB module design, they almost always start with how creating or changing object instances in a conceptual row gets controlled 💅

RowStatus [3] is the standard textual convention for managing conceptual rows in a table 🎀 The RowStatus textual convention, used as the SYNTAX of a single column in a table, is what controls creating, deleting, activating, and deactivating the conceptual rows of that table. When a table has a RowStatus object as one of its columns, changing an instance of that object to 'active' makes the row it appears in become 'committed' ✨

In a multi-table scenario where the configuration data has to be spread over lots of columnar objects, a RowStatus object in one table can cause the entire set of data to be put in operation or stored, based on how the objects are defined 🧚

Sometimes really huge amounts of data need to be 'committed' all at once 😭 In those cases, another approach is to configure all of the rows in all the tables that are required, and then have an "activate" object with a set method that commits all the modified rows 🫶

The RowStatus textual convention says that, when it's used in a conceptual row, a description must define what can be modified. The description of the conceptual row and its columnar object types is the correct place to figure out instance modifiability, but it's often wrongly assumed in some implementations that:

  1. objects either must all be presently set or none need be set to make a conceptual RowStatus object transition to active(1)
  2. objects in a conceptual row cannot be modified once a RowStatus object is active(1). Restricting instance modifiability like this, so that nothing can change after a RowStatus object is set to active(1), is honestly a reasonable limitation 💜 That's because such a RowStatus set may have agent system side-effects that depend on committed columnar object instance values. However, where this restriction exists on an object, it should be made clear in a DESCRIPTION clause such as the following:
protocolDirDescr OBJECT-TYPE
  SYNTAX      DisplayString (SIZE (1..64))
  MAX-ACCESS  read-create
  STATUS      current
  DESCRIPTION
      "A textual description of the protocol encapsulation.
      A probe may choose to describe only a subset of the
      entire encapsulation (e.g., only the highest layer).

      This object is intended for human consumption only.

      This object may not be modified if the associated
      protocolDirStatus object is equal to active(1)."
  ::= { protocolDirEntry 4 }

Any such restrictions on columnar object instance modification while a row's RowStatus object instance is set to active(1) should appear in the DESCRIPTION clause of the RowStatus columnar OBJECT-TYPE as well 🌸

3.3.2. Fate sharing with multiple tables

An important principle tied to transaction control is fate sharing of rows in different tables 🦋 Imagine a relationship has been specified between two conceptual tables of a MIB module (or tables in two different MIB modules). Here, fate sharing means that when a row of one table is deleted, the corresponding row in the other table is deleted too 👑 In a transaction control context, fate sharing can also be used when activating very large configuration changes. If we have two tables that hold a set of configuration information, a row in one table might have to be put in the 'ready' state before the second one can be put in the 'ready' state. Once that second table can be placed in the 'ready' state, the entire transaction can be considered to have been 'committed' 🎀

Fate sharing of SNMP table data should be explicitly defined where possible using the SMI index qualifier AUGMENTS. If the relationship between tables can't be defined using SMIv2 macros, then the DESCRIPTION clause of the object types that particularly affect the cross-table relationship should define what should happen when rows in related tables are added or deleted 🧠

Let's look at the relationship between the dot1dBasePortTable and the ifTable. These tables have a sparse relationship. If a given ifEntry supports 802.1D bridging, then there is a dot1dBasePortEntry that has a pointer to it via dot1dBasePortIfIndex.

Now, what should happen if an ifEntry that can bridge gets deleted? Should the object dot1dBasePortIfIndex simply be set to 0, or should the dot1dBasePortEntry be deleted as well? 🤔 A number of acceptable design and practice techniques can provide the answer to these questions, so it's important for the MIB module designer to give guidance that guarantees consistency and interoperability 🫶

So, when two tables are related in this way, ambiguities like this should be avoided by having the DESCRIPTION clauses of the pertinent row object types define the fate sharing of entries in the respective tables 💅

3.3.3. Transaction Control MIB Objects

When a MIB module is defined that includes configuration object types, consider providing transaction control objects ✨ These objects can be used to cause a large transaction to be committed. For example, we might have several tables that define the configuration of a portion of a system. To avoid churn in the operational state of the system, we might create a single scalar object that, when set to a particular value, causes the activation of the rows in all the necessary tables 🧚 Here are some examples of further usage for such object types:

  • Control objects that are the 'write' or 'commit' objects.

Such objects can cause all pending transactions (change MIB object values as a result of SET operations) to be committed to a permanent repository or operational memory, as defined by the semantics of the MIB objects 💾

  • Control objects at different levels of configuration granularity.

One of the decisions for a MIB module designer is what levels of granularity make sense in practice 🤓 For example, in the routing area, would changes be allowed on a per protocol basis, such as BGP? If they're allowed at the BGP level, are sub-levels permitted, such as per autonomous system? The design of these control objects will be impacted by the underlying software design. RowStatus (see Section 3.3.1) also has important relevance as a general transaction control object 🌈

3.3.4. Creating And Activating New Table Rows

When designing read-create objects in a table, a MIB module designer should first consider the default state of each object in the table when a row is created. If an implementation of a standard MIB module varies in terms of the objects that need to be set in order to create an instance of a given row, an agent capabilities statement should be used to name the additional objects in that table using the CREATION-REQUIRES clause 📝

It's useful when configuring new rows to use the notReady status to indicate that row activation cannot proceed 🌷

When creating a row instance of a conceptual table, one should consider the state of instances of required columnar objects in the row. The DESCRIPTION clause of such a required columnar object should specify it as such 🎀

While a management application is trying to create a row, there may be a period of time when not all of these required (and non-defaultable) columnar object instances have been set. Throughout this time, an agent should return a noSuchInstance error for a GET of any object instance of the row, until all of these required instance values are set 😳 The exception is the RowStatus object instance, for which a notReady(3) value should be returned during this period ✨

You only need to be concerned with the notReady value returned for a RowStatus object when the row under creation doesn't yet have all of the required, non-defaultable instance values for the row 🫶 One approach to simplifying in-row configuration transactions when designing MIB modules is to construct table rows that have no more instance data for columnar objects than will fit inside a single SET PDU. In this case, the createAndWait() value for the RowStatus columnar object is not required. It's possible to use createAndGo() in the same SET PDU, which simplifies transactional management 🦋

3.3.5. Summary Objects and State Tracking ✨

okay so, before a management app starts a fresh batch of config changes, it's lowkey smart for it to checkpoint the state of the managed devices it's about to touch 🧠 there are a bunch of tricks a MIB designer can build in to help with (re-)syncing the managed systems 🔄 these objects can also double-check that the app's idea of the device state matches what the device actually has 💖

the techniques are:

  1. Provide an object that reports the number of rows in a table
  2. Provide an object that flags when data in the table was last modified.
  3. Send a notification message (InformRequests are preferable) to deliver configuration change.

okay the row count object is cute because apps can decide how to grab a table's data and switch strategies depending on how big it is 📊 BUT ngl it's not enough to tell if anything changed 🙈 if you create and delete the same number of rows during the checkpoint window, the count looks exactly the same even though everything changed 💀 and if the table changes without the row count moving at all, monitoring that object won't catch it either 😭

so instead, you can track any table data change with an object that monitors table change state as a function of time 🦋 RFC 2790, Host Resources MIB, has a great example:

hrSWInstalledLastUpdateTime OBJECT-TYPE
    SYNTAX     TimeTicks
    MAX-ACCESS read-only
    STATUS     current
    DESCRIPTION
        "The value of sysUpTime when the hrSWInstalledTable
        was last completely updated.  Because caching of this
        data will be a popular implementation strategy,
        retrieval of this object allows a management station
        to obtain a guarantee that no data in this table is
        older than the indicated time."
    ::= { hrSWInstalled 2 }

a similar pattern shows up in tons of standards track MIB modules, it's called the "LastChange" type object 💅

ENTITY-MIB, RFC 2737 [34], gives us this one:

entLastChangeTime OBJECT-TYPE
  SYNTAX      TimeStamp
  MAX-ACCESS  read-only
  STATUS      current
  DESCRIPTION
          "The value of sysUpTime at the time a conceptual row is
          created, modified, or deleted in any of these tables:
                  - entPhysicalTable
                  - entLogicalTable
                  - entLPMappingTable
                  - entAliasMappingTable
                  - entPhysicalContainsTable"
  ::= { entityGeneral 1 }

this convention isn't formalized, so the LastChanged objects tend to differ a little in what they actually reflect 🎀 IF-MIB (RFC 2863 [20]) defines this one:

ifTableLastChange  OBJECT-TYPE
    SYNTAX      TimeTicks
    MAX-ACCESS  read-only
    STATUS      current
    DESCRIPTION
            "The value of sysUpTime at the time of the last
            creation or deletion of an entry in the ifTable.  If
            the number of entries has been unchanged since the
            last re-initialization of the local network management
            subsystem, then this object contains a zero value."
    ::= { ifMIBObjects 5 }

so, if an agent changes a row with an SNMP SET on ifAdminStatus, ifTableLastChange does NOT update 😳 that's why it's so important to be specific about what makes an object update, so management apps can detect and react to these changes properly 🫶

the final way to keep distributed config data consistent is an event-driven model, where config changes get communicated as they happen 🔥 when config changes don't happen very often, or polling a cache object isn't wanted, think about defining a notification that reports all the config change details 🌸

when you do that, an SNMPv3 (or SNMPv2c) agent gets to send the notification as either a trap or an inform ✨ which PDU goes to the recipient is generally a local configuration thing 🧚 vendors SHOULD recommend informs over traps for NOTIFICATION-TYPE data, since the agent can use whether a response comes back to figure out if it needs to retransmit 💜 overall, an inform is the better pick so changes have a higher chance of confirmed end-to-end delivery 🎀

bestie, the spec is serious about this: vendors SHOULD recommend informs, and the reason is that a response (or no response) is how the agent knows whether to retransmit. keep that in mind 💅

as a MIB design thing, when it's practical, the NOTIFICATION-TYPE SHOULD include all the modified columnar objects in a table row 🍓 that makes it way easier for the receiving app to track what changed in the row and do additional analysis on the state of the managed elements 🧠

BUT notifications aren't always ideal for communicating the state of a rapidly changing object 😵‍💫 which brings us back to the MIB design question of what the right level of granularity to expose is 🤔

finally, polling a bunch of "LastChange" objects doesn't scale well 💀 so consider providing a global LastChange type object that represents the overall config of a given agent implementation 👑


3.3.6. Optimizing Configuration Data Transfer 🚀

config management software should keep track of the current config of every device it controls 💖 it should make sure the result is a consistent view of the network's config, which helps cut down on accidental config errors 🙈

on devices with huge amounts of config data, it can get expensive for both the agent and the manager if the manager keeps polling the entire config tables just to stay in sync 😩 a big win from good sync between manager and agent is that the manager can figure out the smallest, most effective set of data to send to managed devices when changes are needed 🦋 depending on how the table is organized on the managed device and how the agent is implemented, this can lighten the load on the device when activating those changes ✨

in the last section we talked about the "LastChange" style of object 🎀 measured against the requirements we just went over, LastChange is NOT enough for large amounts of data 💅

there are three design options that can help the manager and the managed device stay in sync on config data:

  1. Design multiple indices to partition the data in a table logically or break a table into a set of tables to partition the data based on what an application will use the table for 🧩
  2. Use a time-based indexing technique ⏰
  3. Define a control MIB module that manages a separate data delivery protocol 📦

3.3.6.1. Index Design ✨

Okay bestie, index design is lowkey one of the biggest deals in the whole MIB 💅 It decides how much data has to travel between SNMP entities, and it's the secret weapon for handling tables that get huge 🔥

Most tables in standard MIB modules use one of two indexing vibes:

  • Indexing with increasing Integer32 or Unsigned32 values, kinda like an array 📦
  • Associative indexing, where the indices are potentially sparse and based on a "key," like a hash table 🗝️

When a table gets really big, associative indexing is so iconic because you can grab just the rows you care about, super efficiently 🦋

Say an SNMP entity exposes a copy of the default-free Internet routing table, as defined in the ipCidrRouteTable. It's currently sitting at around 100,000 rows. Literally wild 💀

The ipCidrRouteTable uses associative indexing, so you can pull out, for example, all the routes for a given IPv4 destination 192.0.2/24. Main character energy 👑

BUT, if your goal is to pull down a copy of the whole table, associative indexing can actually slow down throughput and possibly performance of retrieval 😭 Why? Because every index object gets tacked onto the object identifiers of every object instance that comes back. That's a lot of extra baggage 🎒

Here's the ipCidrRouteEntry definition:

   ipCidrRouteEntry OBJECT-TYPE
      SYNTAX   IpCidrRouteEntry
      MAX-ACCESS not-accessible
      STATUS   current
      DESCRIPTION
       "A particular route to a particular destination,
       under a particular policy."
      INDEX {
        ipCidrRouteDest,
        ipCidrRouteMask,
        ipCidrRouteTos,
        ipCidrRouteNextHop
        }

A simple array-like index works super efficiently because it keeps the index small and simple, and it lets more rows fit into a single PDU 🧚 If the indexing isn't sparse, you can gain concurrency by sending multiple asynchronous, non-overlapping collection requests. RFC 2819 [32] explains this on Page 41 (in the section about Host Group indexing) 🌸

Should your requirements ever call for new ways of accessing data, you can define multiple indices so both associative and simple indexing can coexist and access a single logical table. 🫶

Here are two examples, and they're giving "same tea, different cup" energy 🍓

The first one is the ifStackTable from RFC 2863 [20] and the ifInvStackTable from RFC 2864 [33]. They're logical equivalents, with the order of the auxiliary (index) objects simply reversed 🔄

   ifStackEntry  OBJECT-TYPE
        SYNTAX        IfStackEntry
        MAX-ACCESS    not-accessible
        STATUS        current
        DESCRIPTION
                "Information on a particular relationship between
                two sub-layers, specifying that one sub-layer runs
                on 'top' of the other sub-layer.  Each sub-layer
                corresponds to a conceptual row in the ifTable."
                INDEX { ifStackHigherLayer, ifStackLowerLayer }
        ::= { ifStackTable 1 }

   ifInvStackEntry  OBJECT-TYPE
      SYNTAX        IfInvStackEntry
      MAX-ACCESS    not-accessible
      STATUS        current
      DESCRIPTION
          "Information on a particular relationship between two
          sub-layers, specifying that one sub-layer runs underneath
          the other sub-layer.  Each sub-layer corresponds to a
          conceptual row in the ifTable."
          INDEX { ifStackLowerLayer, ifStackHigherLayer }
      ::= { ifInvStackTable 1 }

The second one is about factoring data into multiple tables that have well-defined relationships, which can help cut down on overall data transfer 📉 The RMON-MIB from RFC 2819 [32] shows a really useful technique: organizing tables into control and data components. Control tables hold the objects that get configured and change infrequently, and data tables hold the information being collected, which can be large and change quite frequently 💜

For example, the RMON hostControlTable lets you specify how to collect MAC addresses learned as a source or destination from a given port that does transparent bridging of Ethernet packets 🌈

Configuration happens through the hostControlTable. It's indexed by a simple integer. That might seem array-like, but it's common practice for command generators to encode the ifIndex into this simple integer so they get associative lookup capability too. Galaxy brain 🧠✨

The RMON hostTable and hostTimeTable are dependent tables holding results indexed by the hostControlTable entry 🦋

The hostTable is further indexed by the MAC address, which makes it reasonable to search for a collection, like by the Organizationally Unique Identifier (OUI), which is the first three octets of the MAC address 🔍

The hostTimeTable is designed specifically for fast transfer of bulk RMON data. It shows how to deal with collecting a large number of rows when deletions and insertions are happening, by providing hostControlLastDeleteTime ⏱️

   hostControlLastDeleteTime OBJECT-TYPE
   SYNTAX     TimeTicks
   MAX-ACCESS read-only
   STATUS     current
   DESCRIPTION
       "The value of sysUpTime when the last entry
       was deleted from the portion of the hostTable
       associated with this hostControlEntry.  If no
       deletions have occurred, this value shall be zero."
   ::= { hostControlEntry 4 }

3.3.6.2. Time Based Indexing 🕰️

The TimeFilter, defined in RFC 2021 [44] and used in RMON2-MIB and Q-BRIDGE-MIB (RFC 2674 [26]), gives you a way to get only the rows that changed on or after some specified period of time has passed. Very targeted, very cute 🎯

One drawback of TimeFilter index tables is that a given row can show up at many points in time. That artificially inflates the table's size when you're doing standard getNext or getBulk retrievals 😩

3.3.6.3. Alternate Data Delivery Mechanisms 📬

If the amount of data you need to transfer is bigger than current SNMP design restrictions allow, like with OCTET STRINGS (64k minus overhead of IP/UDP header plus SNMP header plus varbind list plus varbind encoding), consider delivering the data through an alternate method, such as FTP, and use a MIB module to control that delivery process 🚚 Often you can dodge this problem with effective MIB design. So object types that need this kind of transfer size should be used judiciously, if at all 🙈

Lots of enterprise MIB modules control TFTP or FTP. Usually the SNMP part defines what to send where, and setting an object kicks off the operation. For an example, check out the CISCO-FTP-CLIENT-MIB, discussed in [38] 💖

There are also different approaches that let a local agent process running inside the managed node take a template for an object instance (say, for a set of interfaces), then adapt it and apply it to all the actual instances on the node 🧩 This is an architecture for one form of policy-based configuration (see [36], for example). It has to be designed into the agent and some parts of the MIB module, and it gives you the efficiency of specifying lots of copies of instance data just once, plus the execution efficiency of distributing that instance data to the agent 🚀

Other work is currently underway to improve efficiency for bulk SNMP transfer operations [37]. The goal is simply to convey more information with less overhead 🌟

3.4. More Index Design Issues 💅

Section 3.3.5 covered considerations for table row index design as it relates to synchronizing changes within sizable table rows. This section just looks at how to specify that syntactically and how to manage indices semantically 🧠

Honestly, index design issues in a MIB module are a lot like those in a database 🗄️ You have to figure out, during the design phase, how often and what kind of information needs to be set or retrieved. The next few points give some guidance 🫶

3.4.1. Simple Integer Indexing 🔢

When indexing tables with simple Integer32 or Unsigned32, start with one (1) and specify the maximum range of the value. Object identifiers are unsigned long values, so you might wonder why not index from zero (0) instead of one (1)? 🤔

RFC 2578 [2], Section 7.7, page 28, says this:

Instances identified by use of integer-valued objects should be numbered
starting from one (i.e., not from zero).  The use of zero as a value
for an integer-valued index object type should be avoided, except in
special cases.

It also points to the following textual convention from the Interfaces Group MIB module [33]:

   InterfaceIndexOrZero ::= TEXTUAL-CONVENTION
       DISPLAY-HINT "d"
       STATUS       current
       DESCRIPTION
           "This textual convention is an extension of the
           InterfaceIndex convention.  The latter defines a greater
           than zero value used to identify an interface or interface
           sub-layer in the managed system.  This extension permits the
           additional value of zero.  the value zero is object-specific
           and must therefore be defined as part of the description of
           any object which uses this syntax.  Examples of the usage of
           zero might include situations where interface was unknown,
           or when none or all interfaces need to be referenced."
       SYNTAX       Integer32 (0..2147483647)

3.4.2. Indexing with Network Addresses 🌐

Lots of objects use IPv4 addresses (SYNTAX IpAddress) as indexes. One such table is the ipAddrTable from RFC 2011 [14] IP-MIB. The problem is that this limits the MIB module to IPv4 only 😬 To avoid that, use the addressing textual conventions in INET-ADDRESS-MIB [13] (or updates to that MIB module). They give you a generic way to represent addresses for Internet Protocols 🦋 When you use the InetAddress textual convention in your MIB, though, pay heed to this advisory from its description clause:

      When this textual convention is used as the syntax of an index
      object, there may be issues with the limit of 128 sub-identifiers
      specified in SMIv2, STD 58.  In this case, the OBJECT-TYPE
      declaration MUST include a 'SIZE' clause to limit the number of
      potential instance sub-identifiers.

Keep in mind the SMI limit of 128 sub-identifiers when you use certain kinds of network address index types 🚨 The most likely real-world liability has been with DNS names, which can actually be longer than 128 bytes 😭 And it can get even more compounded when a table has multiple indices of this type.

Let's keep the vibes high, bestie 💖

3.5. Conflicting Controls ✨

okay bestie, rule one of MIB design: don't make two read-write objects that overlap in function, partly or completely 🙅‍♀️💅

Here's a messy example 💀. A dot1dBasePortEntry can have a matching ifEntry, and the BRIDGE-MIB has this object:

dot1dStpPortEnable OBJECT-TYPE
    SYNTAX  INTEGER {
                enabled(1),
                disabled(2)            }
    ACCESS  read-write
    STATUS  mandatory
    DESCRIPTION
        "The enabled/disabled status of the port."
    REFERENCE
        "IEEE 802.1D-1990: Section 4.5.5.2"
    ::= { dot1dStpPortEntry 4 }

And the IF-MIB has a very similar one:

ifAdminStatus OBJECT-TYPE
    SYNTAX  INTEGER {
                up(1),       -- ready to pass packets
                down(2),
                testing(3)   -- in some test mode
            }
    MAX-ACCESS  read-write
    STATUS      current
    DESCRIPTION
        "The desired state of the interface.  The testing(3)
        state indicates that no operational packets can be
        passed.  When a managed system initializes, all
        interfaces start with ifAdminStatus in the down(2) state.
        As a result of either explicit management action or per
        configuration information retained by the managed system,
        ifAdminStatus is then changed to either the up(1) or
        testing(3) states (or remains in the down(2) state)."
    ::= { ifEntry 7 }

Now the drama 😭. If ifAdminStatus gets set to testing(3), nobody knows what dot1dStpPortEnable is supposed to return. Nothing says so. So management software has to watch both objects whenever bridging shows up and then figure out the relationship itself 🫠 that's a lot of extra work for the poor app.

The dot1dStpPortEnable definition could have been way more helpful 🎀. It could have said exactly what it returns for each possible ifAdminStatus value. Like, spell it out for every single one.

So the rule is: if overlap is unavoidable, describe the interaction in the DESCRIPTION of each object 🙌. And if you're the designer of a newer object that comes after an older one that already exists, you need to put those interaction details in the DESCRIPTION of your new object. No excuses, periodt 💅

3.6. Textual Convention Usage 🦋

Textual conventions should be used whenever possible. They give you one consistent meaning for a datatype that keeps popping up over and over, which is so iconic 🧚

Lots of MIB modules have a binary state object, like enable/disable or on/off 💜. The current move is to use an existing Textual Convention and define the read-write object in terms of a TruthValue from SNMPv2-TC [3]. For example, the Q-BRIDGE-MIB [26] has this:

dot1dTrafficClassesEnabled OBJECT-TYPE
    SYNTAX      TruthValue
    MAX-ACCESS  read-write
    STATUS      current
    DESCRIPTION
       "The value true(1) indicates that Traffic Classes are
       enabled on this bridge.  When false(2), the bridge
       operates with a single priority level for all traffic."
    DEFVAL      { true }
    ::= { dot1dExtBase 2 }

Also, Textual Conventions that could realistically get reused by other MIB modules are ideally defined in their own separate MIB module 🌸. That makes sharing way easier. For instance, every ATM MIB module pulls from the ATM-TC-MIB [39] to grab shared stuff like addressing, service class values, and so on. Main character behavior of reusable code 👑.

It's recommended to use existing SNMPv2-TC based definitions when you can, because it keeps management simpler 🫶. Compare this object:

acmePatioLights OBJECT-TYPE
    SYNTAX  INTEGER {
                on(1),
                off(2),
    }
    MAX-ACCESS  read-write
    STATUS      current
    DESCRIPTION
        "Current status of outdoor lighting."
    ::= { acmeOutDoorElectricalEntry 3 }

With the existing SNMPv2-TC TruthValue, it could be defined like this:

acmePatioLightsOn OBJECT-TYPE
    SYNTAX  TruthValue
    MAX-ACCESS  read-write
    STATUS      current
    DESCRI2096PTION
        "Current status of outdoor lighting.  When set to true (1),
        this means that the lights are enabled and turned on.
        When set to false (2), the lights are turned off."
     ::= { acmeOutDoorElectricalEntry 3 }

3.7. Persistent Configuration 💾

Lots of network devices have two levels of persistence for configuration data 🧠. In the first level, the config you send to the device only sticks around until a later config operation changes it, or until the system gets reinitialized. In the second level, the data becomes persistent as part of simply being accepted. Some config is both at once: when it's accepted it gets saved permanently and kept in memory 🔥. But neither of those guarantees the operational code is actually using it. Sometimes you need separate objects to activate the new config for the operational code 😤.

Here's the tea ☕: a lot of SNMP agents today use simple persistence models that don't match the full relationship between config data and the real persistence model I just described. Some SET requests against MIB objects with MAX-ACCESS read-write get written to persistent storage automatically, and some don't. For the ones that don't, you sometimes need enterprise MIB objects just to get standard config saved 😭. That makes life really hard for a generic application that wants consistent results 💀.

Good news though, there are standard conventions for saving config data 🎀. The first method uses the StorageType Textual Convention [3], which explicitly says how persistent a given row is supposed to be. Examples include the RFC 3231 [25] definition for the schedTable row object schedStorageType (syntax StorageType), plus similar row objects in virtually all the tables of the SNMP View-based Access Control Model MIB [10].

A second method just uses the DESCRIPTION clause to explain how instance data should persist ✨. RFC 2674 [26] spells out Dot1qVlanStaticEntry data persistence like this:

dot1qVlanStaticTable OBJECT-TYPE
    SYNTAX      SEQUENCE OF Dot1qVlanStaticEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
     "A table containing static configuration information for
     each VLAN configured into the device by (local or
     network) management.  All entries are permanent and will
     be restored after the device is reset."
    ::= { dot1qVlan 3 }

The current practice is a dual persistence model 🌈. You can change the run-time config, and there's also a non-volatile config that gets read when the device initializes. The DISMAN-SCHEDULE-MIB module [25] is a good example. A row in its SchedTable sets the parameters for when an agent MIB variable instance gets set to a specific value, governed by other constraints and directives too. One of those objects is:

schedStorageType OBJECT-TYPE
     SYNTAX      StorageType
     MAX-ACCESS  read-create
     STATUS      current
     DESCRIPTION
         "This object defines whether this scheduled action is kept
          in volatile storage and lost upon reboot or if this row is
          backed up by non-volatile or permanent storage.
          Conceptual rows having the value `permanent' must allow
          write access to the columnar objects schedDescr,
          schedInterval, schedContextName, schedVariable, schedValue,
          and schedAdminStatus.  If an implementation supports the
          schedCalendarGroup, write access must be also allowed to
          the columnar objects schedWeekDay, schedMonth, schedDay,
          schedHour, schedMinute."
     DEFVAL { volatile }
     ::= { schedEntry 19 }

Real talk though 🫠: how persistence actually works is decided by what the underlying system can do, meaning its storage model for management data. This is kind of like how a Counter's storage size on the system might not match its MIB object type. So the final say on the "when" and "how" of storing persistent data comes down to the system and whoever implements the agent on it 🧚‍♀️.

3.8. Configuration Sets and Activation 🎬

Okay, this one is essential 🔑. When you configure a network element with SNMP, you have to keep in mind that setting a group of config objects is different from activating those changes in the actual subsystem 💅. Usually it only makes sense to activate a group of objects together, as one single 'transaction' ✨.

3.8.1. Operational Activation Considerations ✨

okay bestie, so a MIB module design has to think about what happens across the whole subsystem when changes get activated 🧠. This matters SO much for complex config changes, whether that complexity is in the config data itself or in what turning it on does to the managed subsystem 😭. A practical move is to partition the config sets that get activated as changes 🎀. Any complex config should have a master on/off switch (a MIB object type) plus well-placed on/off switches that split up how the config data gets activated in the subsystem 🔥. These controls are a big deal during configuration AND later during diagnostics 💅.

Also, a run of set operations shouldn't make the agent activate every object, because then you'd get operational instability with every single changed object instance 🙈. To dodge that, a series of Set PDUs can install the configuration, and then one final set series of PDUs can activate the changes 🫶.

During diagnostics, some on/off switches can be flipped to localize the perceived error, so you don't have to rip the whole configuration out 🦋.

Here's an example from the OSPF Version 2 MIB [29], the global ospfAdminStat 👑:

ospfAdminStat OBJECT-TYPE
    SYNTAX   Status
    MAX-ACCESS   read-write
    STATUS   current
    DESCRIPTION
       "The administrative status of  OSPF  in the
       router.  The value 'enabled' denotes that the
       OSPF Process is active on at least one interface;
       'disabled' disables it on all interfaces."
   ::= { ospfGeneralGroup 2 }

The semantics of setting ospfAdminStat to enabled(2) are clearly spelled out elsewhere in the OSPF MIB 🌸.

The Scheduling MIB [25] does something similar: each entry in the scheduled actions table gets this kind of object, plus the matching stats object type (read-only ACCESS) on the scheduled actions row instance 🍓.

This is a pattern that keeps showing up, and it makes object type usage way clearer 💜. A table can expose one columnar object type that's strictly for administrative control. When you read it, you get back its last set or defaulted value. Then there's a companion operational columnar object type with MAX-ACCESS of read-only, which tells you the current state of activation or deactivation from the last set of the administrative column 🦋. It's totally expected that the administrative and operational instances can show different values for a while during activation latency, which is exactly why they're separate objects 🙈. Later sections show what goes wrong when you mash the operational and administrative row columns into one object type 😭.

Heads up, all of this is independent of the RowStatus columnar object, and of the idea of 'activation' as it applies to RowStatus 🎀. A RowStatus object type should be strictly about managing the table row itself (where 'activation' means "the conceptual row is available for use by the managed device" [3]), and it should NOT be confused with any operational activation semantics 💅.

In this example, schedAdminStatus controls activation of the scheduled action, and schedOperStatus reports its operational status 🧚:

schedAdminStatus OBJECT-TYPE
    SYNTAX      INTEGER {
                    enabled(1),
                    disabled(2)
                }
    MAX-ACCESS  read-create
    STATUS      current
    DESCRIPTION
        "The desired state of the schedule."
    DEFVAL { disabled }
    ::= { schedEntry 14 }

schedOperStatus OBJECT-TYPE
    SYNTAX      INTEGER {
                    enabled(1),
                    disabled(2),
                    finished(3)
                }
    MAX-ACCESS  read-only
    STATUS      current
    DESCRIPTION
        "The current operational state of this schedule.  The state
         enabled(1) indicates this entry is active and that the
         scheduler will invoke actions at appropriate times.  The
         disabled(2) state indicates that this entry is currently
         inactive and ignored by the scheduler.  The finished(3)
         state indicates that the schedule has ended.  Schedules
         in the finished(3) state are ignored by the scheduler.
         A one-shot schedule enters the finished(3) state when it
         deactivates itself."
    ::= { schedEntry 15 }

3.8.2. RowStatus and Deactivation 🙃

okay so RowStatus objects should not be used to control activation/deactivation of a configuration 🚫. I know it looks perfect for the job, since an app can set a row to active(1), flip it to notInService(2) to disable it, then flip it back to active(1) 💖. BUT there's no guarantee the agent won't discard the row while it's sitting in notInService(2) 😭. RFC 2579 [3], page 15 says:

   The agent must detect conceptual rows that have been in either
   state for an abnormally long period of time and remove them.  It
   is the responsibility of the DESCRIPTION clause of the status
   column to indicate what an abnormally long period of time would
   be.

The DISMAN-SCHEDULE-MIB's schedAdminStatus shows how to separate row control from row activation 🦋. Setting schedAdminStatus to disabled(2) does NOT cause the row to get aged out or removed from the table ✨.

Last thing: a reasonable agent implementation needs to think about how many rows can be created in the notReady/notInService state, so an errant application can't exhaust resources 🔥.


3.9. SET Operation Latency ⏳

Lots of standards track and enterprise MIB modules have read-write objects that assume an agent can finish a set operation as fast as it can send back the set's status to the application 🙈. That's not always true, bestie.

Let's look at the subtle operational problems with this object, which reports the current state AND lets a SET change it to a possibly new state 👀:

wheelRotationState  OBJECT-TYPE
      SYNTAX        INTEGER { unknown(0),
                              idle(1),
                              spinClockwise(2),
                              spinCounterClockwise(3)
                             }
      MAX-ACCESS    read-write
      STATUS        current
      DESCRIPTION
      "The current state of a wheel."
  ::= { XXX 2 }

With that object defined, here's one possible transaction 🎡:

Time  Command Generator --------> <--- Command Responder
----- -----------------                -----------------
|
A  GetPDU(wheelRotationState.1.1)
|
|                          ResponsePDU(error-index 0,
|                                       error-code 0)
|
B                          wheelRotationState.1.1 == spinClockwise(2)
|
C  SetPDU(wheelRotationState.1.1 =
|                   spinCounterClockwise(3)
|
|                          ResponsePDU(error-index 0,
|                                       error-code 0)
|
D                          wheelRotationState.1.1
                                           == spinCounterClockwise(3)
|
E  GetPDU(wheelRotationState.1.1)
|
F                          ResponsePDU(error-index 0,
|                                       error-code 0)
|
V                          wheelRotationState.1.1 == spinClockwise(2)
   ....some time, perhaps seconds, later....
|
G       GetPDU(wheelRotationState.1.1)
|
H                         ResponsePDU(error-index 0,
|                                      error-code 0)
|                       wheelRotationState.1.1
V                                          == spinCounterClockwise(3)

The GET response at time E is going to confuse management apps that assume the object should already say spinCounterClockwise(3) 😭. In reality, the wheel is slowing down so it can reach the idle state and THEN start spinning counter clockwise 🌀.

This kind of confusing, paradoxical behaviour is basically inevitable whenever one object type is used to both control AND report on the same state 💀. The common fix we've already seen is to split the desired (settable) state from the current state 🫶. ifAdminStatus and ifOperStatus from RFC 2863 [20] are a great example of that split 👑.

3.9.1. Subsystem Latency, Persistence Latency, and Activation Latency 🐢

A second source of SET latency is delay in agent implementations that have to talk to loosely coupled subsystems 🔌. It can take the instrumented system a while to accept the new config from the SNMP agent, process it, and 'install' the updated config (or otherwise process the directives), and that can easily be longer than the SNMP response timeout 😬.

In these cases, it's a good idea to provide a "current state" object type that the management app can poll to find out the control state of the loosely coupled subsystem affected by the config update 💜.

More generally, some MIB objects can have high latency when their values change 🌸. That might come from saving the changed value to a persistent storage type, from activating a subsystem that's inherently high latency (like we just talked about), or both 🍓. When you define objects like this, it might be wise to have the agent process set operations in the managed subsystem as soon as the Set PDU is processed, and then update the appropriate status objects once saving to persistent storage and (if applicable) activation has succeeded or is otherwise complete ✨. Another option is to send a notification saying the operation has finished 🔔.

When you describe an activation object, its DESCRIPTION clauses should give a hint about how long the operation will likely take to complete ⏱️. Keep in mind that from a management software point of view (like the schedAdminStatus example in Section 3.8.1), the latency of saving to persistence and the latency of activation can't be told apart when they're part of a single operation 🧩. Iconic behaviour, honestly 🫶🌈.

3.10. Notifications and Error Reporting 🔔

Okay bestie, for this section a 'notification' is whatever gets defined with the NOTIFICATION-TYPE macro in SMIv2, which is RFC 2578 [2]. 🧚 Notifications can be sent in SNMPv2c [19] or as SNMPv3 TRAP or InformRequest PDUs. Config info is super sensitive, so it is recommended that config operations always happen over SNMPv3, because it has the enhanced security capabilities 🔐. InformRequest PDUs should be used in preference to TRAP PDUs, since the recipient sends back a Response PDU. That ack can help avoid unnecessary retransmission of NOTIFICATION-TYPE information when retransmissions are actually required 💅.

Using InformRequest instead of TRAP is not up to the MIB module designer or the agent implementor 🙈. Whether an SNMPv2c or SNMPv3 agent sends a TRAP or an InformRequest is generally decided by the agent's local configuration, though it can be controlled with MIB objects in SNMPv3. If notification timeout and retry values come from local configuration parameters, care should be taken to avoid unnecessary retransmission of InformRequest PDUs ⏱️.

Config change and error info sent in InformRequest PDUs can be a huge part of a good SNMP-based management system 🌸. They can also get overused, though. This section gives some guidance on defining NOTIFICATION-TYPE info about config changes that can ride along in InformRequest PDUs. Notifications also play a key role in all kinds of error reporting, from hardware failures to config and general policy errors. Those notifications should be designed as described in Section 3.11 (Application Error Reporting) 🦋.

3.10.1. Identifying Source of Configuration Changes

A NOTIFICATION-TYPE designed to report config changes should report the identity of the management entity that started the change 👑. If the entity is known to be an SNMP command generator, the transport address and SNMP parameters found in the snmpTargetParamsTable from RFC 3413 SNMP-TARGET-MIB should be reported where possible. For config changes made outside the SNMP domain, the change mechanism used (for example, CLI vs. HTTP-based management client access) should be reported, along with whatever notion of "user ID" the change initiator has, if it applies and is available 💻.

3.10.2. Limiting Unnecessary Transmission of Notifications

Event-driven synchronization models are essential to config management, and they can use notifications as a super important enabling technique 🔄. When notifications are used properly, the manager's view of the managed element's config stays in close sync with the element's actual config state ✨.

When designing new NOTIFICATION-TYPEs, consider how to limit the number of notification PDUs sent with the info defined in the NOTIFICATION-TYPE in response to a config change or error event 🎯.

InformRequest PDUs have an inherent advantage over TRAP PDUs when the goal is cutting down on unnecessary messages from the system generating the NOTIFICATION-TYPE data, in cases where retransmission is actually needed 🫶. An InformRequest PDU gets acknowledged by the receiving entity with a Response PDU. Getting that response lets the sender verify (and record an audit entry, where such facilities exist on the agent system) that the message was received 📝. As a matter of notification protocol, that receipt guarantee is not available with TRAP PDUs. If it's required, the agent has to handle it with some mechanism outside SNMP, which usually means the penalty of polling 😭.

No matter which PDUs carry them, one way to limit unnecessary notifications is to build the situations where a notification need not be sent right into the NOTIFICATION-TYPE definition 🧠. A great example is frDLCIStatusChange, defined in FRAME-RELAY-DTE-MIB, RFC 2115 [21]:

frDLCIStatusChange NOTIFICATION-TYPE
    OBJECTS  { frCircuitState }
    STATUS      current
    DESCRIPTION
       "This trap indicates that the indicated Virtual Circuit
       has changed state.  It has either been created or
       invalidated, or has toggled between the active and
       inactive states.  If, however, the reason for the state
       change is due to the DLCMI going down, per-DLCI traps
       should not be generated."
::= { frameRelayTraps 1 }

There are a bunch of other techniques for cutting down on unwanted NOTIFICATION-TYPE info too 🌈. When defining notifications, the designer can put time-based limits on how often specific instances of a NOTIFICATION-TYPE get generated. For example, a definition could say messages won't be sent more often than once every 60 seconds while the condition that caused the notification keeps going. Or a NOTIFICATION-TYPE DESCRIPTION clause could set a fixed cap on how many messages get sent for the duration of the condition that triggered the notification 🔢.

If NOTIFICATION-TYPE transmission is "aggregated" in some way, bounded either by time or by absolute system state change as described above, the best design move is to have the data delivered with the notification reference the actual number of underlying managed element transitions that caused it 🍓. No matter which threshold is picked to govern when NOTIFICATION-TYPEs actually get sent, the goal is to describe an aggregated event, or a related set of events, in as few PDUs as possible 💜.

3.10.3. Control of Notification Subsystem

There are standards track MIB modules that define objects that either augment or overlap with control of notifications 🎀. For instance, FRAME-RELAY-DTE-MIB (RFC 2115) defines frTrapMaxRate, and DOCS-CABLE-DEVICE-MIB defines a set of objects in docsDevEvent that handle rate limiting and filtering of notifications ⭐.

Back in the day, agents didn't have a standard way to configure a notification generator 🕰️. Now that the SNMP-NOTIFICATION-MIB module exists in RFC 3413 [9], it is strongly recommended that its filtering functions get used. This MIB makes it possible to map given NOTIFICATION-TYPEs to their intended recipients 📬.

If the SNMP-NOTIFICATION-MIB mechanisms aren't suitable for your application, an explanation of why they aren't suitable should be included in the DESCRIPTION clause of any replacement control objects 📝.


3.11. Application Error Reporting 🚨

MIB module designers should not rely on SNMP protocol error reporting alone to report application layer error state for objects that accept SET operations 🙅‍♀️.

Most MIB modules out there today give very little detail about why a config request failed 😤. Often the only info provided is an SNMP protocol error, which generally doesn't give enough detail about why an agent rejected a SET request. Typically, the config application has a big, real burden of figuring out whether the request failed because of a resource issue, a security issue, or an application error 💀.

Ideally, when a "badValue" error happens for a given SET request, an application can ask the agent for more details about the error 🔍. A badValue doesn't necessarily mean the command generator sent bad data. The agent could be the one at fault! Additional detailed diagnostic info can help figure out what's going on in the integrated system 🧩.

Let's think about conveying error info about a MIB expression 'object' set within DISMAN-EXPRESSION-MIB [40], which happens when the expression gets evaluated 🧮. Protocol errors clearly aren't relevant for an error that happens during expression evaluation. Instead, DISMAN-EXPRESSION-MIB provides objects to report those errors, the expErrorTable. In other words, the expErrorTable keeps info about errors that happen at evaluation time 🫧:

expErrorEntry OBJECT-TYPE
    SYNTAX      ExpErrorEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
    "Information about errors in processing an expression.
    Entries appear in this table only when there is a matching
    expExpressionEntry and then only when there has been an
    error for that expression as reflected by the error codes
    defined for expErrorCode."
INDEX       { expExpressionOwner, expExpressionName }

More specifically, a MIB module can give config applications info about errors on the managed device by creating columnar object types in log tables that hold error info specific to errors that happen on row activation 🌷.

Notifications with detailed failure info objects can also be used to signal config failures 📣. If you go this route, the config of destinations for NOTIFICATION-TYPE data generated from config failures should be considered independently of the destinations for other NOTIFICATION-TYPEs generated for other operational reasons 🙌. Basically, in lots of management environments, the network operators who care about NOTIFICATION-TYPEs from config failures may not fully overlap with the operators who care about NOTIFICATION-TYPEs from, say, network interface failures 🫶.

3.12. Designing MIB Modules for Multiple Managers ✨

okay so when you're designing a MIB module for configuration, there are a few things you need to think about so that multiple managers can play nice together 💅🎀

First up, you have to avoid race conditions between two or more authorized management apps that are sending SET protocol operations spanning more than a single PDU. Not cute. 😭

The standard textual convention document [3] defines TestAndIncr, which people call a spinlock 🔒, and it's the thing that stops those race conditions. A MIB module designer may explicitly define a synchronization object with the syntax TestAndIncr, or may choose to rely on snmpSetSerialNo (a global spinlock object) from SNMPv2-MIB. Here's how snmpSetSerialNo is defined:

snmpSetSerialNo OBJECT-TYPE
SYNTAX     TestAndIncr
MAX-ACCESS read-write
STATUS     current
DESCRIPTION
        "An advisory lock used to allow several cooperating
        command generator applications to coordinate their
        use of the SNMP set operation.

        This object is used for coarse-grain coordination.
        To achieve fine-grain coordination, one or more similar
        objects might be defined within each MIB group, as
        appropriate."
::= { snmpSet 1 }

Another totally iconic TestAndIncr example lives in SNMP-TARGET-MIB [9], in snmpTargetSpinLock. 🌸

Secondly, an agent should be able to report configuration that was set by different entities, so it's distinguishable from configuration defined outside the SNMP domain. Think a default being applied, or changes made through an alternate management interface like a command line interface. 🧚 Section 3.10.1 gives you considerations for doing this when you're designing NOTIFICATION-TYPEs. The OwnerString textual convention from RMON-MIB RFC 2819 [32] has been used successfully for this purpose. More recently, RFC 3411 [1] introduced SnmpAdminString, which is designed as a UTF8 string. That makes it way more suitable for representing names in lots of languages 🌈

Experience has shown that using OwnerString to represent row ownership can be a useful diagnostic tool too. Honestly, the string "monitor" has been used a lot, and it's been really helpful in apps, for identifying configuration set by an agent or local management. 🦋

Thirdly, think about whether multiple managers need to configure the same set of tables. If they do, an "OwnerString" may be used as the first component of a table's index, so VACM can protect access to subsets of rows, at least at the level of securityName or groupName provided. RFC 3231 [25], Section 6 walks through this technique in detail. ngl, this does add complexity to the managed device and to the configuration management application, since the manager has to know about these extra columnar objects in configuration tables and set them properly. Also, the agent must be configured to provide the right instance-level restrictions on whether the instances can be modified. 🔥


3.13. Other MIB Module Design Issues 💜

3.13.1. Octet String Aggregations

The OCTET STRING syntax can be an extremely flexible and useful datatype when you're defining managed objects that allow SET operations. An octet string can model a lot of things, but it's limited in size to 65535 octets by SMIv2[2]. 🍓

Because OCTET STRINGS are so flexible, you have to define them carefully so they're actually useful to applications. Otherwise, apps will at most just be able to display and set them. 😭

Take this object from RFC 3418 SNMPv2-MIB [11], for example:

sysLocation OBJECT-TYPE
SYNTAX      DisplayString (SIZE (0..255))
MAX-ACCESS  read-write
STATUS      current
DESCRIPTION
        "The physical location of this node (e.g., `telephone
        closet, 3rd floor').  If the location is unknown, the value
        is the zero-length string."
::= { system 6 }

Informational object types like this have come to be colloquially known as "scratch pad objects" 🙈 They're often useful, but if an application needs to do more with the information than read and set the value, you need a more precise definition of what's in the OCTET STRING. The actual format of an instance for this kind of object is unstructured, so it's a whole mood. The alternative is to split the object type into several object type definitions, which gives you the extra structural detail you need. 👑

When you're using OCTET STRINGS, avoid platform dependent data formats. Also avoid using OCTET STRINGS where a more precise SMI syntax, like SnmpAdminString or BITS, would work. 💅

A lot of MIB modules try to optimize how much data gets sent and received in a SET or GET PDU by packing octet strings with aggregate data. For example, the PortList syntax from the Q-BRIDGE-MIB (RFC 2674 [26]) is defined like this:

PortList ::= TEXTUAL-CONVENTION
 STATUS      current
 DESCRIPTION
     "Each octet within this value specifies a set of eight
     ports, with the first octet specifying ports 1 through
     8, the second octet specifying ports 9 through 16, etc.
     Within each octet, the most significant bit represents
     the lowest numbered port, and the least significant bit
     represents the highest numbered port.  Thus, each port
     of the bridge is represented by a single bit within the
     value of this object.  If that bit has a value of '1'
     then that port is included in the set of ports; the port
     is not included if its bit has a value of '0'."
 SYNTAX      OCTET STRING

This compact representation saves on data transfer, but it has some limitations. Complex instance info like this is hard to reference outside of the object or to use as an index to a table. Plus, with this approach, if one value inside the aggregate needs to change, you have to write the entire aggregated object instance. Ugh. 🥲

Providing an SNMP table to represent aggregate data avoids those limitations, so it's the better general practice. ✨

Finally, as mentioned earlier in Section 3.3.6.3, you should think about the practical ramifications of instance transfer for object types of SYNTAX OCTET STRING where their typical instance data requirements are close to the upper boundary of SMIv2 OCTET STRING instance encoding. Where those object types are truly necessary at all, SNMP/UDP may not be a very scalable means of transfer, so alternatives should be explored. 🚀

3.13.2. Supporting multiple instances of a MIB Module

When you're defining new MIB modules, consider whether there could ever be multiple instances of that MIB module in a single SNMP entity. 🧚

Some MIB modules do assume a one-to-many relationship. MIBs for routing protocols are a good example, since they can handle multiple "processes" of the underlying protocol and its administrative framework. But honestly, most MIB modules assume a one-to-one relationship between the objects in the MIB module and how many instances will exist on a given SNMP agent. The OSPF-MIB, IP-MIB, and BRIDGE-MIB are all defined for a single instance of their technology. 🌸

Single instancing like this clearly limits implementations that might support multiple instances of OSPF, IP stacks, or logical bridges. 😤

In those cases, the ENTITY-MIB [RFC2737] can support the one-to-many relationship through naming scopes, using the entLogicalTable. Just keep in mind that this approach has some drawbacks:

  1. One cannot issue a PDU request that spans naming scopes. For example, if there are two instances of BRIDGE-MIB active in a single agent, one PDU cannot contain a request for dot1dBaseNumPorts from both the first and second instances. 🙅‍♀️
  2. Relying on this technique creates a dependency on the Entity MIB for an application to access multiple instances of information. 💀

Alternatively, completely independent of the Entity MIB, multiple MIB module instances can be scoped by different SNMP contexts. Heads up though, this does require coordinating the technique with the administrative establishment of contexts in the configured agent system. 🫶

3.13.3. Use of Special Optional Clauses

When you're defining integer-based objects for read-create, read-write, and read-only semantics, using the UNITS clause is recommended, along with the DESCRIPTION clause spelling out the particular details of how UNITs are to be interpreted. 🎀

The REFERENCE clause is recommended too, since it helps an implementer track down related information on a given object. If you add a REFERENCE clause pointing to the specific underlying technology document, multiple separate implementations are more likely to interoperate. Iconic behavior, honestly. 👑


4. Implementing SNMP Configuration Agents 🛠️

4.1. Operational Consistency 🌈

Successful deployment of SNMP configuration systems depends on understanding the roles of MIB module design and agent design. 💖

Both module and agent design need to be done with an understanding of how UDP/IP-based SNMP behaves. A current practice in MIB design is to think about the idempotency of settable objects. Basically, idempotency means you can invoke the same set operation over and over but it only results in a single activation. It's giving reliable vibes ✨

Here's idempotency in action 👇

Manager                              Agent
--------                             ------
Set1 (Object A, Value B) --->        receives set OK and responds
                          X<-------- Response PDU(OK) is dropped by
                                     network
Manager times out
and sends again
Set2 (Object A, Value B) --->       receives set OK (does nothing),
                                    responds
                          <-------- with a Response PDU(OK)
Manager receives OK

Okay so, the set operation could've gone sideways here 😭 Had object A been defined in a stateful way, the Set2 operation might have failed because of how it interacted with Set1. If the agent implementation doesn't know about that possibility on the second request, it can behave poorly by performing the set request again instead of doing nothing. Not it 🙈

The big takeaway: all the software running on a managed element and in managed applications should be designed in concert, when possible. Extra attention goes at the logical boundaries of the management system components so everything keeps working correctly 🫶

  1. The first interface is between SNMP agents in managed devices and the management applications themselves. The MIB document is a contract between these two entities that defines expected behavior. It's a type of API 📜✨
  2. The second interface is between the agent and the instrumented subsystem. Sometimes the instrumented subsystem needs modification to allow for the dynamic nature of SNMP-based configuration, control, and monitoring operations. Agent implementors also have to be sensitive to the operational code and the device, so they can minimize the impact of management on the primary subsystems 🌸

Additionally, the SNMP protocol-level and MIB module-level modeling of configuration operations may be idempotent and stateless from one set operation to another, but the underlying subsystem may not be that way at all 👀 An agent may need to manage this state explicitly in these subsystem architectures when it has placed the underlying subsystem into an "intermediate" state while processing a series of SET PDUs. Alternatively, depending on the underlying subsystem, the agent may be able to buffer all of the configuration set operations and activate them in the subsystem all at once, to fit the nature of that subsystem 🧚

Here's an example that's giving VPN chaos 🌈 It would be reasonable to define a MIB module to control Virtual Private Network (VPN) forwarding, where a management station could set a set of ingress/egress IP addresses for the VPN gateway. The MIB module might assume the level of transactionality is creating a single row in a table that defines the address of the ingress/egress gateway, along with some prefix information to help with routing at the VPN layer to that gateway. But in an underlying Layer 2 VPN subsystem, the requirement could be that all existing gateways for a VPN get deleted before a new one can be defined. In other words, to add a new gateway g(n) to a VPN, gateways g(1)..g(n-1) need to be removed, and then all n gateways get reestablished with the VPN forwarding service 😵‍💫 In that case, an agent could have some sort of timer that opens a bounded window for receiving SETs for new VPN gateways, then activates them all in this removal-then-reestablishment of existing and new gateways at the end of the window ⏰

4.2. Handling Multiple Managers 🤝

Devices are often modified by multiple management entities using different management techniques. Sometimes an element is managed by different organizations, like when a device sits between administrative domains 🏢

There are a bunch of approaches management software can use to keep information in sync between the manager(s) and the managed elements ✨

An agent should report configuration changes made by different entities. It should also tell apart configuration that's defined locally, like a default or a locally specified configuration made through an alternate management interface such as a command line interface. When a change has been made to the system via SNMP, CLI, or another method, a managed element should send a notification to the manager(s) configured as recipients of those notifications 📣 Those management applications should then update their local configuration repositories and take whatever additional action is appropriate. This approach can also be an early warning for undesired configuration changes 🚨

Managers should also develop mechanisms to make sure they stay synchronized with each other 💜

4.3. Specifying Row Modifiability 🎀

Once a RowStatus value is active(1) for a given row, the management application should be able to figure out what the semantics are for making additional changes to that row. The RMON MIB control table objects spell out exactly which managed objects in a row can and cannot be changed once a given RowStatus goes active 📋

As described earlier, some operations take time to complete. Some systems also need to stay in a particular state for some period before moving on to another. In some cases, changing one value may require re-initialization of the system. In all of these cases, the DESCRIPTION clause should contain information about the requirements of the managed system and any special restrictions managers should observe 💅

4.4. Implementing Write-only Access Objects 🙈

The second version of the SNMP SMI dropped direct support for a write-only object. That means you have to return something when reading an object you might have wanted to be write-only. Such objects should have a DESCRIPTION clause that details what the return values should be. Regardless of the approach, though, the value returned when reading the object instance should still be meaningful in the context of the object's semantics 🫠


5. Designing Configuration Management Software 🛠️

In this section, we go over practices that should be used when creating and deploying management software that configures one or more systems using SNMP. Functions that all configuration management software should provide, no matter how the configuration information gets conveyed to the managed systems, are backup, fail-over, and restoration. A management system should have the following features 💖

  1. A method for restoring a previous configuration to one or more devices. Ideally this restoration should be time indexed, so a network can be restored to a configured state as of a specific time and date ⏪
  2. A method for saving back-up versions of the configuration data in case of hardware or software failure 💾
  3. A method of providing fail-over to a secondary (management) system in case of a primary failure. This capability should be deployed in a way that does not cause duplicate polling of configuration 🔁

These three capabilities are obviously important for other types of management that aren't the focus of this BCP 🌟

5.1. Configuration Application Interactions with Managed Systems 🦋

From the point of view of the management application's design, there are three basic requirements to evaluate that relate to SNMP protocol operations and configuration:

  • Set and configuration activation operations
  • Notifications from the device
  • Data retrieval and collection

Depending on the requirements of the specific services being configured, many other requirements may (and probably will) also be present 🙌

The design of the system should not assume that the objects in a device that represent configuration data will stay unchanged over time 😬

As standard MIB modules evolve and vendors add private extensions, the specific configuration parameters for a given operation are likely to change over time. Even for a configuration application designed for a single vendor, the management application should allow for variability in the MIB objects used to configure the device for a particular purpose. The best way to do this is to separate, as much as possible, the operational semantics of a configuration operation from the actual data. Some applications do this by making the specific configuration objects for a particular device table driven rather than hard coded 📊 Ideally, management software should verify support in the devices it's expected to manage and report any unexpected deviations to the operator 🔍 This approach is especially valuable when developing applications that are meant to support equipment or software from multiple vendors 🍓

5.1.1. SET Operations 🧁

Management software should be mindful of the environment in which SET operations are being deployed. The goal here is to move configuration information to the managed device as efficiently as possible. There are many ways to get efficiency, and some are specific to given devices. One general case all management software should use is reducing the number of SET PDU exchanges between the managed device and the management software to the smallest reasonable number. One approach is to check the largest number of variable bindings that can fit into a SET PDU for a managed device. In some cases, the number of variable bindings sent in a particular PDU will depend on the device, the specific MIB objects, and other factors 🧠

Maximizing the number of variable bindings in a SET PDU also helps with management application transaction initiation, which we'll talk about in the following section 🔜

There are agents that may have implementation limits on the number and order of varbinds they can handle in a single SET PDU. In that case, sending fewer varbinds will be necessary 🥲

As stated at the start of this section, the management application software designer has to be sensitive to the design of the SNMP software in the managed device. For example, the software in the managed device may require that all related configuration information for an operation be conveyed in a single PDU, because it has no concept of a transaction beyond a single SNMP PDU 🚫 Another example is the RowStatus textual convention. Some SNMP agents implement only a subset of the available features, so the management application must avoid using features that may not be supported in a specific table implementation, such as createAndWait ⚠️

5.1.2. Configuration Transactions ✨

okay bestie, so SNMP-based configuration apps can run a few different kinds of transactions 💅 they can hit a scalar object, do stuff inside a single table (within a row and across rows), span several tables on one managed device, or even go across many devices 🌈 How well your manager pulls these off is kinda based on how the MIB objects were designed in the first place, ngl.

To make transaction semantics actually work, the management software has to know what's inside the MIB modules it's configuring. That way it can use RowStatus objects properly to control transactions on one or more tables 🎀 It also needs to know about any control tables the device has that manage the status of other tables. Main character knowledge, fr 🧠

To the greatest extent possible, the management application should provide the facility to support transactions across multiple devices 🦋 So if you want a configuration operation across several devices, the manager can coordinate them so they become active as close to simultaneously as possible. Literally the goal ⏰

SNMP has a few practical tools that support management application level transactions. One was mentioned in the preceding section: transactions can be optimized by including the maximum number of SET variable bindings possible in a single PDU sent to the agent 🍓

There's an important refinement to that. The set of read-create row data objects for tables should be sent in a single PDU, and only placed across multiple PDUs if absolutely necessary 🫶 The success of these set operations should be verified through the response(s) to the Set PDU or through subsequent polling of the row data objects. The applicable RowStatus object(s), may be set to active only after this verification ✅ This is the only tractable means of affording an opportunity for per-row rollback, especially when the configuration change spans table row instances on multiple managed devices. This part is so important, bestie 😭

Finally, where a MIB module exposes the kind of helpful transaction management object types discussed in Section 3.3.5, it is clearly beneficial to the integrity of the management application's ability to handle transactions to make use of them 👑


5.1.3. Tracking Configuration Changes 🔍

As previously described in Section 3.3.5 (Summary Objects and State Tracking), agents should provide the capability to send notifications to their configured management systems whenever a configuration operation is completed or is detected to have failed 📣 The management application must be prepared to accept these notifications so it knows the current configured state of the devices under its control.

When a notification comes in, the management application should use getBulk or getNext to retrieve the configuration from the agent and store the relevant contents in the management application database 💾 The GetBulkRequest-PDU is super useful for this whenever the managed device supports it, since it's more efficient than the GetNextRequest-PDU when you're pulling large amounts of data 🚀 For backward compatibility, the management station should also support and use the GetNextRequest-PDU when the agent doesn't support the GetBulkRequest-PDU.

Management systems should also offer configuration options with defaults for users who tend to retrieve the smallest amount of data needed to hit their goal. This avoids unnecessary load on managed devices for the most common retrieval operations 🌸


5.1.4. Scalability of Data Retrieval 📈

The efficient retrieval techniques in the sections above are only one piece of what application developers should think about when building configuration apps 🧚 Management applications should provide for distributed processing of configuration operations. This also extends to management functions that aren't the focus of this document.

Distributed processing can also give you resilience when the network fails 🛡️ An SNMP-based configuration management system might be deployed in a distributed way, where three systems in different locations keep each other synchronized. That synchronization can happen without extra polling of network devices, through a variety of techniques. If one fails, a 'backup' system can take over the configuration responsibilities from the failed manager without having to re-synchronize with the managed elements, since it's already up to date. Iconic behavior, honestly 👑


6. Deployment and Security Issues 🏗️

Now that we've looked at designing SNMP MIB data for configuration, agent implementation of its access, and management application issues in configuration using SNMP, we turn to a bunch of operational considerations that cut across all three areas 💖

6.1. Basic assumptions about Configuration 🧸

The following basic assumptions are made about real world configuration models.

  1. Operations must understand and must be trained in the operation of a given technology. No configuration system can stop an untrained operator from causing outages due to misconfiguration 🙈
  2. Systems undergoing configuration changes must be able to cope with unexpected loss of communication at any time.

During configuration operations, network elements must take appropriate measures to leave the configuration in a consistent/recognizable state, either by rolling back to a previously valid state or by changing to a well-defined or default state. Resilience era 💪

  1. Configuration exists on a scale from relatively unchanging to a high volume, high rate of change. The former is often referred to as "set and forget", meaning the configuration changes quite infrequently. The latter, "near real-time change control", implies a high frequency of configuration change. Design of configuration management must take into account the rate and volume of change expected in a given configuration subsystem 📊

6.2. Secure Agent Considerations 🔐

Vendors should not ship a device with a community string 'public' or 'private', and agents should not define default community strings except when needed to bootstrap devices that don't have secondary management interfaces. Defaults have led to security issues that have been recognized and exploited, so please don't be that vendor 💀 When using SNMPv1, supporting read-only community strings is a common practice.

Version 3 of SNMP represents the current standard for the Internet Management Framework and is recommended for all network management applications 💜 In particular, SNMPv3 provides authorization, authentication, and confidentiality protection, and it's essential to meeting the security considerations for all management of devices that support SNMP-based configuration. Non-negotiable, bestie 🫶


6.3. Authentication Notifications 🚨

The default state of RFC 1215 [17] Authentication notifications should be off. You do not want to risk accidentally sending out authentication failure information, which by itself could be a security liability 🙅‍♀️ Enabling Authentication Notifications should be done in the context of a management security scheme that considers the proper recipients of this information.

There are other liabilities when authentication notifications get generated without proper security infrastructure. When notifications are sent in SNMPv1 trap PDUs, unsolicited packets to a device can cause one or more trap PDUs to be created and sent to management stations. If these traps flow over shared access media and links, the community string from the trap may be gleaned and exploited to gain access to the device 😱 At the very least, this risk should be mitigated by having the authentication trap PDU conveyed with a community string that's only used for authentication traps from the agent, and that would be useless for inbound access to the agent to get at other management data.

A further liability shows up when authentication traps are generated in the face of a Denial Of Service (DOS) attack, in the form of a flood of PDUs with invalid community strings, on the agent system. If it's bad enough that the system has to respond to and recover from the invalid agent data accesses, it gets worse if a separate Authentication notification PDU is sent to each recipient on the management network. Not the vibe, not the vibe at all 🥀


6.4. Sensitive Information Handling 🗝️

Some MIB modules contain objects that may hold data for keys, passwords, and other sensitive information, so they must be protected from unauthorized access 🔒 MIB documents created in the IETF must have a 'Security Considerations' section that details how sensitive information should be protected. Similarly, MIB module designers who create MIB documents for private MIB objects should include similar information so that users of the products containing these objects can take appropriate precautions ✨

Even if a device does support DES, it should be noted that configuration of keys for other protocols via SNMP Sets protected by DES should not be allowed if the other keys are longer than the 56 bit DES keys protecting the SNMP transmission. Yikes, bestie 😬

The DESCRIPTION clause for these object types, and their Security Considerations sections in the documents that define them, should make it clear how and why these specific objects are sensitive, and that a user should only make them accessible for encrypted SNMP access. Vendors should also document sensitive objects in a similar fashion 📝

Confidentiality is not a mandatory portion of the SNMPv3 management framework [6] 🤷‍♀️

Prior to SNMPv3, providing customized views of MIB module data was difficult. This led to objects being defined such as the following, from [41] 🦖

docsDevNmAccessEntry OBJECT-TYPE
    SYNTAX      DocsDevNmAccessEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
        "An entry describing access to SNMP objects by a
         particular network management station.  An entry in
         this table is not readable unless the management station
         has read-write permission (either implicit if the table
         is empty, or explicit through an entry in this table.
         Entries are ordered by docsDevNmAccessIndex.  The first
         matching entry (e.g., matching IP address and community
         string) is used to derive access."
    INDEX { docsDevNmAccessIndex  }
    ::= {  docsDevNmAccessTable 1 }

New MIB modules should capitalize on the existing security capabilities of the SNMPv3 Framework 🌈 One way they can do this is by indicating the level of security appropriate to different object types. For example, objects that change the configuration of the system might be protected by using the authentication mechanisms in SNMPv3. Specifically, it's useful to design MIB module object grouping with VACM views definition in mind, so users can define and properly scope which tables are visible to a given user and view 🦋

7. Policy-based Management ✨

okay so bestie, here's the part where things get really fun 💅 In some designs and implementations, people use SNMP as a control channel and pair it with other protocols that are built to move bulk data. Lowkey this is sub-optimal, because it raises a bunch of security and other concerns 😬 Moving big piles of configuration data over SNMP can be done efficiently with several of the techniques we already covered earlier in this document. This policy section shows how to get even more efficiency using a set of relatively new design mechanisms 🚀 It gives some background, defines the terms that matter in this space, and describes some ways to deploy it 🧚

7.1. What Is the Meaning of 'Policy-based'?

Okay, so the word 'policy' has been absolutely everywhere in standards documents and networking vendor marketing for the last few years 🙃 It's been stretched and twisted into so many meanings that the true meaning of 'policy' is unclear unless you add more qualifiers every time you use it. Ngl, it's a mess 💀

[42] gives the term 'policy' two explicit definitions:

  • A definite goal, course or method of action to guide and determine present and future decisions. "Policies" are implemented or executed within a particular context (such as policies defined within a business unit).
  • Policies as a set of rules to administer, manage, and control access to network resources.

Note that these two views are not contradictory, since individual rules may be defined in support of business goals. 🎀

As it pertains to our discussion of the term 'policy-based configuration', the meaning gets way more specific ✨ In this context, we're talking about a way of integrating data and the management actions that use it, so that:

  • there is the ability to specify "default" configuration data for a number of instances of managed elements, where those instances can be correlated in some data driven or algorithmic way. The engine that does this correlation and activates instances from defaults may live in the agent or outside it. Where the representation of these defaults is in the MIB design itself, the object types supporting this notion are called "template objects" 🦋
  • the activation of instance data derived from template object types results from minimal activation directives from the management application, once the instances of the template object types have been established 💖
  • somewhat independently, the architecture of the overall management agent may accommodate the definition and evaluation of management and configuration policies. The side-effects of evaluating these policies typically include the activation of certain configuration directives. Where management data design exposes template object types, the policy-driven activation can (and ideally, should) include applying template object instances to the analogous managed element instance-level values 👑

As it pertains to template object data, these underlying notions have been around for a while in non-SNMP management regimes 🌸 A common feature of many command line interfaces for configuring routers is specifying one or more access control lists. These usually provide a set of IP prefixes, BGP autonomous system numbers, or other similar identifying constructs (see, for example, [42]). Once the access control lists are assembled, their application to various interfaces, routing processes, and the like is specified, typically in the configuration of whatever the access control list is applied to.

Consistent with the properties above that define our use of policy-based configuration, a) the access list is defined independently from its point of application, and b) its application is independent of the access list definition 🙌 For example, changing the application of an access list from one interface to the other does not require any change to the access list itself. The first point suggests what is necessary for template-based data organization. The second suggests how it gets applied in a policy-based manner 🧠

Let's now look at the motivation for such a system or subsystem (perhaps bounded at the level of a 'template-enabled' MIB module, given the definition above). Let's explore why policy-based techniques matter for configuration specifically 😤

7.2. Organization of Data in an SNMP-Based Policy System

The number of configurable parameters and 'instances' such as interfaces has grown as equipment got bigger and more complex 📈

At the same time, many of these systems need to be configured to operate in a coordinated fashion 🫶 This is what makes it possible to deliver new specialized services that need that coordination. Examples include delivering virtual private networks and connections that guarantee specific service levels 💜

The growth in size and complexity of configuration information has big implications for how it gets organized, and for how efficiently it gets transferred to the management agent 😭 As an example, an agent that implements the Bridge MIB [24] could be used to represent a large VLAN with some 65,000 port entries. Configuring a VLAN like that would require establishing dot1dStpPortTable and dot1DStaticTable entries for each of those virtual ports, and each table entry would contain several parameters. A much more efficient approach is to provide default values for creating new entries that fit the VLAN environment in our example. The local management infrastructure should then iterate across the system, setting the default values on the selected ports as groups 🧚‍♀️

To date, this kind of large-scale configuration has been done with file transfer, by setting individual MIB objects, or with lots of CLI commands. In every one of these approaches, the details for each instance are stored in the file, the CLI commands, or the MIB objects. That means they contain not only the value and type of the object, but also the exact instance of the object the value should be applied to 🙈 This is the property that makes configuration operations explode as the number of instances (such as interfaces) grows. This per-instance approach can work for a few machines set up by experts, but there's a real need for a more scalable solution 🔥 Template-based data organization and policy-based management abstract away the details above the instance level, which means fewer SET requests get sent to a managed device 💖

Realizing a policy-driven system requires agents that can take defaults and apply them to instances, based on a rule that defines the conditions under which the defaults (policy) should be applied. A policy-driven configuration system that is meant to be scalable needs to expose a way of layering its application of defaults at discrete ranges of granularity. The spectrum of that granularity might start at the breadth of a network service 🎀

Ultimately, that layering ends up with features that support instance-level object instance data within the running agent 🌈

An example of this kind of layering is implicit in how a SNMPCONF Policy-Based Management MIB [36] (PM-MIB) implementation works. However, other entity management systems have been using these kinds of techniques end-to-end for a long time, sometimes with SNMP and sometimes with other encodings and transfer technologies 🍓 What the PM-MIB sets out to establish, in an environment that's ideal for its deployment, is an adaptation between MIB module data that wasn't designed with template object types and the ability to let the PM-MIB agent engine apply instances of that data as though it were template-based ⭐

okay so, for policy management to actually work, you need a LOT of info about the network elements, or you'll make bad policy calls 🙈 and ngl, even if you're NOT doing policy-based config, this stuff is still super useful for deciding what config operations to do 💅

you have to be really clear about three different things: distribution of a policy to a system, activation of a policy in a system, and changes or failures that happen while the policy is supposed to be active 🎀 for example, if an interface is down but it's part of a policy you just distributed, that's not an error, because the policy might not be scheduled to turn on until later 🦋

BUT if a policy is distributed and applied to an interface that should be operational and it isn't, that's clearly a problem 😭 (it's just not a problem with the policy config itself!). with that backstory, here are the areas to think about so you can make good policy config decisions and figure out when a policy has 'failed' 🌸

  • The operational state of network elements that are to be configured.

Make sure the sub-components you want to configure are actually available for use 👀 because sometimes they just aren't. The policy config software should figure out whether availability is a prerequisite for installing the policy, or whether it's even acceptable for them to be unavailable. This decision is separate from the one about activation! Installation is when the policy gets sent from the policy manager to the managed device, and activation is turning the policy on 💖 If a policy is distributed while a sub-component like an interface or disk isn't available, the managed system should send a notification to the designated management station when the policy is set to become active, or if the resource is still not available 🚨

  • The capabilities of the devices in the network.

A capability can be almost any unit of work a network element can perform 🧠 Think routing protocols supported, Web server and OS versions, queuing mechanisms supported on each interface that can be used for different qualities of service, and so many more. You can get this info from the capabilities table of the Policy MIB module [36] 📋

Historically, management apps had to get this kind of info by issuing get requests for every object they might want to use 😩 That's sooo inefficient, since it takes a ton of get requests, and it's more error prone because some instances won't exist until they're configured. The new capabilities table is a huge upgrade over the old technique 🙌

  • The capacity of the devices to perform the desired work.

Capability is the ability to perform the desired work, while capacity is a measure of how much of that capability the system has 📏 Wherever possible, the policy config application should evaluate the capacity of the network element to do the work identified by the policy 🔍 In some systems you can't get the capacity of the managed elements directly, even though you might be able to monitor how much work the element is doing. In those cases, the management application may benefit from pre-configured info about the capacity of different network elements, so it can evaluate available resources before distributing new policies 🧮

Utilization refers to how much of a particular capability has been consumed 🥤 For devices that have been under policy config control for any amount of time, a certain percentage of their available capacity will already be used. Policies should not be distributed to systems that don't have the resources to carry out the policy in a reasonable period of time 🫶


7.4. Schedule and Time Issues ⏰

this section applies equally to systems that aren't policy-based and to policy-based systems, since config operations often need to be synchronized across time zones 🌍 Wherever possible, network elements should support time info using the standard DateAndTime TC, which includes local time zone info 💜 Policy-based management often needs more complex time expressions than the DateAndTime TC can convey, so check out the Policy-Based Management MIB document [36] for more 📚 Some deployed systems don't store complex notions of local time, so they may not be able to process policy directives that contain time zone relevant data 😬 For this reason, policy management applications should be able to find out the timekeeping abilities of the managed system, and make adjustments to the policy for those systems that are time-zone challenged 🕰️


7.5. Conflict Detection, Resolution and Error Reporting ⚡

policies sent to a device may contain conflicting instructions 🥴 Detection can happen at the device or the management level, and conflicts can be resolved using a bunch of mechanisms (like, last configuration set wins, or abort change) 🔧 These unintended conflicts should be reported 📣 Conflicts can happen at different levels in a chain of commands, and each 'layer' in a policy management system should be able to check for some errors and report them 🧱 This is basically the same as programs raising an exception and passing that info on to software that can do something meaningful with it 💻

at the instance level, conflict detection has been done in a limited way for a while in software that realizes MIB objects at this level of resolution 🫧 This detection is independent of policy. The 'conflicts' that usually get checked for are resource availability and the validity of set operations ✅ In a policy-enabled system, there are no additional requirements for this software, assuming good error detection and reporting appropriate to this level are already in place 💅

7.5.1. Changes to Configuration Outside of the Policy System 🚪

it is essential to consider changes to configuration that are initiated outside of the policy system 👀 A goal of SNMP-based policy management is to coexist with other kinds of management software that have historically been instance based, and the best example is the command line interface 💻

a notification should be sent whenever an out-of-policy control change is made to an element that is under policy control 🔔 This notification should include the policy that was affected, the instance of the element that was changed, and the object and value it was changed to 📝

even for systems with no concept of policy control, these ideas make sense 🫶 If SNMP coexists with other access methods like a CLI, it's essential that the management station stays synchronized with changes that might have been made to the managed device using those other methods 🔄 So sending a notification when another access method makes a change is a really good approach. Of course, the user should be able to configure this 🎛️


7.6. More about Notifications in a Policy System 📬

notifications can be useful for figuring out that a policy has failed because of an error in the policy, or in the element(s) under policy control 🕵️‍♀️ As with all notifications, they should be defined and controlled so they don't create a problem by sending more than is helpful over a specific period of time 🙅‍♀️ For example, if a policy is controlling 1,000 interfaces and fails, one notification instead of 1,000 may be the better approach 🎯 Also, these notifications should include as much info as possible to help with problem resolution 🧩


7.7. Using Policy to Move Less Configuration Data 📦

one of the big advantages of policy-based configuration with SNMP is that many config operations can be conveyed with a tiny amount of data 🤏 Changing a single config parameter for each of 100 interfaces on a system might take 100 CLI commands, or 100 SNMP variable bindings using conventional techniques 😵

with policy-based configuration and SNMP, a single SET PDU can carry the policy info needed to apply a config change to 100 similar interfaces 🎉 This efficiency comes from not having to send the value for each instance that needs to be configured 🙌 The 'default' for each of the instances included in the policy is sent, and the rule for selecting which instances the default applies to can also be carried along (see the Policy MIB module [36]) 🧠

let's stretch the example a bit 🧶 Say there are 10 parameters that need to change. With conventional techniques, that's 1,000 variable bindings, one for each instance of each new value for each interface 🥵 With policy-based configuration and SNMP, all of that info can still probably fit in one SET PDU 🙌 The only difference is that the ten parameters sent are the 'template' used to create instances on the managed interfaces 🍓

this efficiency gain applies to GET operations too, not just SET operations 🔍 Since the policy is also held in storage for cross-instance defaults (for example, the pmPolicyTable in [36]), an entire data set that could control hundreds of rows of info can be retrieved in a single GET request 🤯

this kind of policy-friendly data organization is consistent and plays nicely with MIB module objects that support "summary" activation and activation reporting, of the kind discussed in Section 3.3.5 💖


8. Example MIB Module With Template-based Data 🌈

this section defines a MIB module that controls the heating and air conditioning system for a large building 🏢 It contains both config and counter objects, so operators can see how much cooling or heating a particular configuration has consumed 🥶🔥 Objects that represent the config info at a "default" level (as referenced above) are included too 🧚

these tables, combined with applying the tables' row instance data as templated 'defaults', let operators configure and monitor many rooms at once 🏠✨ They can also change config parameters based on time of day, and make a bunch of other sophisticated decisions based on the 'policy' implied by these defaults and how they're applied 🧠👑 That's why these config controls get their instances specified from template object types 💅

in our simplified Heating Ventilation and Air Conditioning (HVAC) model, we'll create three tables based on a simple analysis 🌬️ More complicated systems will need more tables, but the principles will be the same 🦋

Okay bestie, so picking up right where Step 1 starts 💅✨

Step 1: Same as any MIB module, we start by figuring out which objects we need for configuration and control 🎀 The first table is a pretty traditional monitoring table. It has indices so we know which rooms the counters and status objects belong to 🏠 It also has an object that's a RowPointer to a table holding the configuration info. These are the objects for the bldgHVACTable, our first table in the HVAC MIB module 🌸

Index objects that tell us which floor and office we're managing:

bldgHVACFloor
bldgHVACOffice

A single index reference to a table that 'glues' the configuration defaults to the descriptive info:

bldgHVACCfgTemplate

A set of objects for status and units of work (bldgHVACCoolOrHeatMins), plus the standard per-row SnmpAdminString, StorageType, and RowStatus columns:

bldgHVACFanSpeed
bldgHVACCurrentTemp
bldgHVACCoolOrHeatMins
bldgHVACDiscontinuityTime
bldgHVACOwner
bldgHVACStatus

Step 2: This is the configuration description table 🧚 Its whole job is to give templates a unique string identifier, and those templates may be driven by policies in the network. Say you want devices to deliver a particular quality of service. The index string here could be the name, and the description part could be a short note about the motivation behind it, like "provides extra heat to corner offices to counteract excessive exterior wind chill" 🥶 Standard owner and status objects may also be helpful, so they're included here too ✨ The row columns are:

bldgHVACCfgTemplateInfoIndex
bldgHVACCfgTemplateInfoID
bldgHVACCfgTemplateInfoDescr
bldgHVACCfgTemplateInfoOwner
bldgHVACCfgTemplateInfoStatus

Notice that up to this point we haven't given any configuration info at all 👀 That lives in the next table. Some people might wonder why this table isn't just combined with the configuration template table from the next step. Honestly, they can be combined 🤷‍♀️ The reason to keep them separate is that as systems get more complex, there may be more than one configuration table pointing at these descriptions. Another reason for two tables is that the description isn't reproduced for every template and instance, which can save some extra data movement 🦋 Every designer has to weigh the tradeoffs between the number of objects and data movement efficiency, same as with any other MIB module. No shortcuts, bestie 💜

Step 3: The bldgHVACCfgTemplateTable holds the specific configuration parameters that the bldgHVACConfigPtr object points to 🎯 Lots of rows in the bldgHVACTable can point at one entry in this table. It's also possible for an entry to be used by 1 or 0 rows of the bldgHVACTable. This is honestly the iconic part: being able to have multiple rows (instances) in the bldgHVACTable point at the same row in this table is what gives you those efficiency gains from policy-based management with SNMP 📈 Also notice that the configuration data is tied directly to the counter data, so people can actually see how configurations affect behavior 👑

The objects in this table are all that's needed for configuration and for connecting to the other tables, plus the usual SnmpAdminString, StorageType, and RowStatus objects 💅 A simple index for the table:

bldgHVACCfgTemplateIndex

The configuration objects:

bldgHVACCfgTemplateDesiredTemp
bldgHVACCfgTemplateCoolOrHeat

The administrative objects for SnmpAdminString and RowStatus:

bldgHVACCfgTemplateInfo
bldgHVACCfgTemplateOwner
bldgHVACCfgTemplateStorage
bldgHVACCfgTemplateStatus

8.1. MIB Module Definition

Now for the actual MIB module 🦋 Here it is in full:

BLDG-HVAC-MIB DEFINITIONS ::= BEGIN
IMPORTS
    MODULE-IDENTITY, Counter32,
    Gauge32, OBJECT-TYPE, Unsigned32, experimental
        FROM SNMPv2-SMI
    MODULE-COMPLIANCE, OBJECT-GROUP
       FROM SNMPv2-CONF
    TEXTUAL-CONVENTION,
    TimeStamp, RowStatus, StorageType
        FROM SNMPv2-TC
    SnmpAdminString
        FROM SNMP-FRAMEWORK-MIB;


bldgHVACMIB MODULE-IDENTITY
    LAST-UPDATED "200303270000Z"
    ORGANIZATION "SNMPCONF working group
                  E-mail: snmpconf@snmp.com"
    CONTACT-INFO
        "Jon Saperia
        Postal:     JDS Consulting
                    174 Chapman Street
                    Watertown, MA 02472
                    U.S.A.
        Phone:      +1 617 744 1079
        E-mail:     saperia@jdscons.com

        Wayne Tackabury
        Postal:     Gold Wire Technology
                    411 Waverley Oaks Rd.
                    Waltham, MA 02452
                    U.S.A.
        Phone:      +1 781 398 8800
        E-mail:     wayne@goldwiretech.com
        Michael MacFaden
        Postal:     Riverstone Networks
                    5200 Great America Pkwy.
                    Santa Clara, CA 95054
                    U.S.A.
        Phone:      +1 408 878 6500
        E-mail:     mrm@riverstonenet.com

        David Partain
        Postal:     Ericsson AB
                    P.O. Box 1248
                    SE-581 12  Linkoping
                    Sweden
        E-mail:     David.Partain@ericsson.com"
    DESCRIPTION
        "This example MIB module defines a set of management objects
        for heating ventilation and air conditioning systems.  It
        also includes objects that can be used to create policies
        that are applied to rooms.  This eliminates the need to send
        per-instance configuration commands to the system.

        Copyright (C) The Internet Society (2003).  This version of
        this MIB module is part of RFC 3512; see the RFC itself for
        full legal notices."

    REVISION "200303270000Z"
    DESCRIPTION
        "Initial version of BLDG-HVAC-MIB as published in RFC 3512."
    ::= { experimental 122 }

bldgHVACObjects         OBJECT IDENTIFIER ::= { bldgHVACMIB 1 }
bldgConformance         OBJECT IDENTIFIER ::= { bldgHVACMIB 2 }

--
-- Textual Conventions
--

BldgHvacOperation  ::= TEXTUAL-CONVENTION
    STATUS             current
    DESCRIPTION
        "Operations supported by a heating and cooling system.
        A reference to underlying general systems would go here."
    SYNTAX      INTEGER {
                         heat(1),
                         cool(2)
                }
--
-- HVAC Objects Group
--

bldgHVACTable    OBJECT-TYPE
    SYNTAX      SEQUENCE OF BldgHVACEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
        "This table is the representation and data control
        for building HVAC by each individual office.
        The table has rows for, and is indexed by a specific
        floor and office number.  Each such row includes
        HVAC statistical and current status information for
        the associated office.  The row also contains a
        bldgHVACCfgTemplate columnar object that relates the
        bldgHVACTable row to a row in the bldgHVACCfgTemplateTable.
        If this value is nonzero, then the instance in the row
        that has a value for how the HVAC has been configured
        in the associated template (bldgHVACCfgTeplateTable row).
        Hence, the bldgHVACCfgTeplateTable row contains the
        specific configuration values for the offices as described
        in this table."
    ::= { bldgHVACObjects 1 }

bldgHVACEntry  OBJECT-TYPE
    SYNTAX       BldgHVACEntry
    MAX-ACCESS   not-accessible
    STATUS       current
    DESCRIPTION
        "A row in the bldgHVACTable.  Each row represents a particular
        office in the building, qualified by its floor and office
        number.  A given row instance can be created or deleted by
        set operations  upon its bldgHVACStatus columnar
        object instance."
    INDEX { bldgHVACFloor, bldgHVACOffice }
        ::= { bldgHVACTable 1 }

BldgHVACEntry ::= SEQUENCE {
        bldgHVACFloor             Unsigned32,
        bldgHVACOffice            Unsigned32,
        bldgHVACCfgTemplate       Unsigned32,
        bldgHVACFanSpeed          Gauge32,
        bldgHVACCurrentTemp       Gauge32,
        bldgHVACCoolOrHeatMins    Counter32,
        bldgHVACDiscontinuityTime TimeStamp,
        bldgHVACOwner             SnmpAdminString,
        bldgHVACStorageType       StorageType,
        bldgHVACStatus            RowStatus
        }

bldgHVACFloor    OBJECT-TYPE
    SYNTAX      Unsigned32 (1..1000)
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
        "This portion of the index indicates the floor of the
         building.  The ground floor is considered the
         first floor.  For the purposes of this example,
         floors under the ground floor cannot be
         controlled using this MIB module."
    ::= { bldgHVACEntry 1 }

bldgHVACOffice    OBJECT-TYPE
    SYNTAX      Unsigned32 (1..2147483647)
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
        "This second component of the index specifies the
        office number."
    ::= { bldgHVACEntry 2 }

bldgHVACCfgTemplate  OBJECT-TYPE
    SYNTAX          Unsigned32
    MAX-ACCESS      read-create
    STATUS          current
    DESCRIPTION
        "The index (bldgHVACCfgTemplateIndex instance)
        of an entry in the 'bldgHVACCfgTemplateTable'.
        The bldgHVACCfgTable row instance referenced
        is a pre-made configuration 'template'
        that represents the configuration described
        by the bldgHVACCfgTemplateInfoDescr object.  Note
        that not all configurations will be under a
        defined template.  As a result, a row in this
        bldgHVACTable may point to an entry in the
        bldgHVACCfgTemplateTable that does not in turn
        have a reference (bldgHVACCfgTemplateInfo) to an
        entry in the bldgHVACCfgTemplateInfoTable.  The
        benefit of this approach is that all
        configuration information is available in one
        table whether all elements in the system are
        derived from configured templates or not.

        Where the instance value for this colunmar object
        is zero, this row represents data for an office
        whose HVAC status can be monitored using the
        read-only columnar object instances of this
        row, but is not under the configuration control
        of the agent."
    ::= { bldgHVACEntry 3 }

bldgHVACFanSpeed  OBJECT-TYPE
    SYNTAX            Gauge32
    UNITS             "revolutions per minute"
    MAX-ACCESS        read-only
    STATUS            current
    DESCRIPTION
        "Shows the revolutions per minute of the fan.  Fan speed
        will vary based on the difference between
        bldgHVACCfgTemplateDesiredTemp and bldgHVACCurrentTemp.  The
        speed is measured in revolutions of the fan blade per minute."
    ::= { bldgHVACEntry 4 }

bldgHVACCurrentTemp  OBJECT-TYPE
    SYNTAX            Gauge32
    UNITS             "degrees in celsius"
    MAX-ACCESS        read-only
    STATUS            current
    DESCRIPTION
        "The current measured temperature in the office.  Should
        the current temperature be measured at a value of less
        than zero degrees celsius, a read of the instance
        for this object will return a value of zero."
    ::= { bldgHVACEntry 5 }

Okay bestie, this part is all about the counters and the template tables, so let's dig in! 🎀

bldgHVACCoolOrHeatMins is the one counting heating or cooling minutes. 🔥 It only starts counting once the row is activated, and whether those minutes are heating or cooling depends on how the row is set up. If the system switches between cooling and heating, the count starts over, kind of like a network interface card getting reconfigured. 💅 Discontinuities can show up when the management system re-initializes, and at other times too, which the bldgHVACDiscontinuityTime tells you about. 🦋

bldgHVACCoolOrHeatMins  OBJECT-TYPE
    SYNTAX            Counter32
    UNITS             "minutes"
    MAX-ACCESS        read-only
    STATUS            current
    DESCRIPTION
        "The total number of heating or cooling minutes that have
        been consumed since the row was activated.  Notice that
        whether the minutes represent heating or cooling is a
        function of the configuration of this row.  If the system
        is re-initialized from a cooling to heating function or
        vice versa, then the counter would start over again.  This
        effect is similar to a reconfiguration of some network
        interface cards.  When parameters that impact
        configuration are changed, the subsystem must be
        re-initialized.  Discontinuities in the value of this counter
        can occur at re-initialization of the management system,
        and at other times as indicated by the value of
        bldgHVACDiscontinuityTime."
    ::= { bldgHVACEntry 6 }

bldgHVACDiscontinuityTime OBJECT-TYPE
    SYNTAX      TimeStamp
    MAX-ACCESS  read-only
    STATUS      current
    DESCRIPTION
        "The value of sysUpTime on the most recent occasion at which
        any heating or cooling operation for the office designated
        by this row instance experienced a discontinuity.  If
        no such discontinuities have occurred since the last re-
        initialization of the this row, then this object contains a
        zero value."
    ::= { bldgHVACEntry 7 }

bldgHVACOwner  OBJECT-TYPE
    SYNTAX            SnmpAdminString
    MAX-ACCESS        read-create
    STATUS            current
    DESCRIPTION
        "The identity of the operator/system that
        last modified this entry.  When a new entry
        is created, a valid SnmpAdminString must
        be supplied.  If, on the other hand, this
        entry is populated by the agent 'discovering'
        unconfigured rooms, the empty string is a valid
        value for this object."
    ::= { bldgHVACEntry 8 }

bldgHVACStorageType  OBJECT-TYPE
    SYNTAX            StorageType
    MAX-ACCESS        read-create
    STATUS            current
    DESCRIPTION
        "The persistence of this row of the table in system storage,
        as it pertains to permanence across system resets.  A columnar
        instance of this object with value 'permanent' need not allow
        write-access to any of the columnar object instances in the
        containing row."
    ::= { bldgHVACEntry 9  }

bldgHVACStatus  OBJECT-TYPE
    SYNTAX            RowStatus
    MAX-ACCESS        read-create
    STATUS            current
    DESCRIPTION
        "Controls and reflects the creation and activation status of
        a row in this table.

        No attempt to modify a row columnar object instance value in
        the bldgHVACTable should be issued while the value of
        bldgHVACStatus is active(1).  Should an agent receive a SET
        PDU attempting such a modification in this state, an
        inconsistentValue error should be returned as a result of
        the SET attempt."
    ::= { bldgHVACEntry 10 }
--
-- HVAC Configuration Template Table
--

bldgHVACDiscontinuityTime is the sysUpTime snapshot of the most recent discontinuity in any heating or cooling operation for that office. ✨ If nothing has gone wonky since the last re-initialization, it just reads zero. 🌸

bldgHVACOwner is the who-did-it column 🙈, storing the identity of the operator or system that last touched the entry. When someone creates a new entry, they have to supply a valid SnmpAdminString, but if the agent discovers unconfigured rooms on its own, an empty string is totally fine. 💖

bldgHVACStorageType tells you how permanent the row is in system storage across resets. 🧚 A row marked 'permanent' doesn't have to allow write access to the other columns in that row. 🍓

bldgHVACStatus is the on-off switch for the row's creation and activation. 👑 While it's active(1), you're not supposed to try modifying the row's columns. If an agent gets a SET PDU trying that anyway, it sends back an inconsistentValue error. 💀

Now we're moving on to the HVAC Configuration Template Table, which is giving very organised, very iconic energy. 💜

bldgHVACCfgTemplateInfoTable  OBJECT-TYPE
    SYNTAX      SEQUENCE OF BldgHVACCfgTemplateInfoEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
        "This table provides unique string identification for
        HVAC templates in a network.  If it were necessary to
        configure rooms to deliver a particular quality of climate
        control with regard to cooling or heating, the index string
        of a row in this table could be the template name.
        The bldgHVACCfgCfgTemplateInfoDescription
        contains a brief description of the template service objective
        such as: provides summer cooling settings for executive
        offices.  The bldgHVACCfgTemplateInfo in the
        bldgHVACCfgTemplateTable will contain the pointer to the
        relevant row in this table if it is intended that items
        that point to a row in the bldgHVACCfgTemplateInfoTable be
        identifiable as being under template control though this
        mechanism."

    ::= { bldgHVACObjects 2 }

bldgHVACCfgTemplateInfoEntry  OBJECT-TYPE
    SYNTAX       BldgHVACCfgTemplateInfoEntry
    MAX-ACCESS   not-accessible
    STATUS       current
    DESCRIPTION
        "Each row represents a particular template and
        description.  A given row instance can be created or
        deleted by set operations upon its
        bldgHVACCfgTemplateInfoStatus columnar object
        instance."
    INDEX { bldgHVACCfgTemplateInfoIndex }
    ::= { bldgHVACCfgTemplateInfoTable 1 }

BldgHVACCfgTemplateInfoEntry ::= SEQUENCE {
        bldgHVACCfgTemplateInfoIndex          Unsigned32,
        bldgHVACCfgTemplateInfoID             SnmpAdminString,
        bldgHVACCfgTemplateInfoDescr          SnmpAdminString,
        bldgHVACCfgTemplateInfoOwner          SnmpAdminString,
        bldgHVACCfgTemplateInfoStatus         RowStatus,
        bldgHVACCfgTemplateInfoStorType       StorageType
        }

bldgHVACCfgTemplateInfoIndex   OBJECT-TYPE
       SYNTAX       Unsigned32 (1..2147483647)
       MAX-ACCESS   not-accessible
       STATUS       current
       DESCRIPTION
           "The unique index to a row in this table."
        ::= { bldgHVACCfgTemplateInfoEntry 1 }

bldgHVACCfgTemplateInfoID  OBJECT-TYPE
    SYNTAX       SnmpAdminString
    MAX-ACCESS   read-create
    STATUS       current
    DESCRIPTION
        "Textual identifier for this table row, and, consequently
        the template.  This should be a unique name within
        an administrative domain for a particular template so that
        all systems in a network that are under the same template
        can have the same 'handle' (e.g., 'Executive Offices',
        'Lobby Areas')."
    ::= { bldgHVACCfgTemplateInfoEntry 2 }


bldgHVACCfgTemplateInfoDescr   OBJECT-TYPE
    SYNTAX       SnmpAdminString
    MAX-ACCESS   read-create
    STATUS       current
    DESCRIPTION
        "A general description of the template.  One example might
        be - Controls the cooling for offices on higher floors
        during the summer."
    ::= { bldgHVACCfgTemplateInfoEntry 3 }

bldgHVACCfgTemplateInfoOwner  OBJECT-TYPE
    SYNTAX            SnmpAdminString
    MAX-ACCESS        read-create
    STATUS            current
    DESCRIPTION
        "The identity of the operator/system that last modified
        this entry."
    ::= { bldgHVACCfgTemplateInfoEntry 4 }

bldgHVACCfgTemplateInfoStatus  OBJECT-TYPE

    SYNTAX            RowStatus
    MAX-ACCESS        read-create
    STATUS            current
    DESCRIPTION
        "The activation status of this row.

        No attempt to modify a row columnar object instance value in
        the bldgHVACCfgTemplateInfo Table should be issued while the
        value of bldgHVACCfgTemplateInfoStatus is active(1).
        Should an agent receive a SET PDU attempting such a modification
        in this state, an inconsistentValue error should be returned as
        a result of the SET attempt."
    ::= { bldgHVACCfgTemplateInfoEntry 5 }

bldgHVACCfgTemplateInfoStorType   OBJECT-TYPE
    SYNTAX            StorageType
    MAX-ACCESS        read-create
    STATUS            current
    DESCRIPTION
        "The persistence of this row of the table in system storage,
         as it pertains to permanence across system resets.  A columnar
        instance of this object with value 'permanent' need not allow
        write-access to any of the columnar object instances in the
        containing row."
    ::= { bldgHVACCfgTemplateInfoEntry 6  }

bldgHVACCfgTemplateInfoTable is basically the name tag 🏷️ for each HVAC template. Think of it as a unique string that lets every system in a network under the same template share the same handle, like 'Executive Offices' or 'Lobby Areas'. 🦋 Each row also gets a short description of what the template is for, such as summer cooling settings for executive offices. 😭 The bldgHVACCfgTemplateInfo column in bldgHVACCfgTemplateTable points back to a row here when that template should be identifiable as template-controlled. 💅

Each row in that table has an index, an ID, a description, an owner, a status, and a storage type. ✨ The index is the unique number for the row, the ID is the textual handle, the description says what it does, the owner is whoever last changed it, the status controls creation and activation, and the storage type handles persistence across resets. 🌈 The status rules are the same vibe as before: no modifying the row while it's active(1), or the agent throws an inconsistentValue error. 🙅‍♀️

Last part of this chunk: the template table itself, which holds the actual settings that get applied to offices. 🔥

--
-- HVAC Configuration Template Table
--
bldgHVACCfgTemplateTable    OBJECT-TYPE
    SYNTAX      SEQUENCE OF BldgHVACCfgTemplateEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
        "This table contains the templates, which
        can be used to set defaults that will
        be applied to specific offices.  The application
        of those values is accomplished by having a row
        instance of the bldgHVACTable reference a row of
        this table (by the value of the former's
        bldgHVACCfgTemplate columnar instance).  Identifying
        information concerning a row instance of this table
        can be found in the columnar data of the row instance
        of the bldgHVACCfgTemplateInfoTable entry referenced
        by the bldgHVACCfgTemplateInfo columnar object of
        this table."
    ::= { bldgHVACObjects 3 }

bldgHVACCfgTemplateEntry  OBJECT-TYPE
    SYNTAX       BldgHVACCfgTemplateEntry
    MAX-ACCESS   not-accessible
    STATUS       current
    DESCRIPTION
        "Each row represents a single set of template parameters
        that can be applied to selected instances - in this case
        offices.  These policies will be turned on and off by the
        policy module through its scheduling facilities.

        A given row instance can be created or
        deleted by set operations upon its
        bldgHVACCfgTemplateStatus columnar object instance."
    INDEX { bldgHVACCfgTemplateIndex }
    ::= { bldgHVACCfgTemplateTable 1 }

BldgHVACCfgTemplateEntry ::= SEQUENCE {
        bldgHVACCfgTemplateIndex           Unsigned32,
        bldgHVACCfgTemplateDesiredTemp     Gauge32,
        bldgHVACCfgTemplateCoolOrHeat      BldgHvacOperation,
        bldgHVACCfgTemplateInfo            Unsigned32,
        bldgHVACCfgTemplateOwner           SnmpAdminString,
        bldgHVACCfgTemplateStorage         StorageType,
        bldgHVACCfgTemplateStatus          RowStatus
}

bldgHVACCfgTemplateIndex    OBJECT-TYPE
    SYNTAX      Unsigned32 (1..2147483647)
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION
        "A unique value for each defined template in this
        table.  This value can be referenced as a row index
        by any MIB module that needs access to this information.
        The bldgHVACCfgTemplate will point to entries in this
        table."
    ::= { bldgHVACCfgTemplateEntry 1 }

bldgHVACCfgTemplateDesiredTemp OBJECT-TYPE
    SYNTAX            Gauge32
    UNITS             "degrees in celsius"
    MAX-ACCESS        read-create
    STATUS            current
    DESCRIPTION
        "This is the desired temperature setting.  It might be
        changed at different times of the day or based on
        seasonal conditions.  It is permitted to change this value
        by first moving the row to an inactive state, making the
        change and then reactivating the row."
    ::= { bldgHVACCfgTemplateEntry 2 }

bldgHVACCfgTemplateCoolOrHeat  OBJECT-TYPE
    SYNTAX             BldgHvacOperation
    MAX-ACCESS         read-create
    STATUS             current
    DESCRIPTION
        "This controls the heating and cooling mechanism and is
        set-able by building maintenance.  It is permitted to
        change this value by first moving the row to an inactive
        state, making the change and then reactivating the row."
    ::= { bldgHVACCfgTemplateEntry 3 }

bldgHVACCfgTemplateInfo OBJECT-TYPE
    SYNTAX             Unsigned32
    MAX-ACCESS         read-create
    STATUS             current
    DESCRIPTION
        "This object points to a row in the
        bldgHVACCfgTemplateInfoTable.  This controls the
        heating and cooling mechanism and is set-able by
        building maintenance.  It is permissible to change
        this value by first moving the row to an inactive
        state, making the change and then reactivating
        the row.  A value of zero means that this entry
        is not associated with a named template found
        in the bldgHVACCfgTemplateInfoTable."
    ::= { bldgHVACCfgTemplateEntry 4 }

bldgHVACCfgTemplateTable is where the template settings live, and each row is a set of parameters that can be applied to offices, with policies switched on and off by the policy module through its scheduling. 🧚 The index is the unique number for each template and the thing other MIB modules can reference. 💖

bldgHVACCfgTemplateDesiredTemp is the desired temperature in degrees Celsius 🌡️, and it can change by time of day or season. To change it, you first move the row to an inactive state, make your edit, and then reactivate it. 🍓

bldgHVACCfgTemplateCoolOrHeat controls the heating and cooling mechanism, and building maintenance is the one who sets it. 👑 Same deal as before: deactivate the row, change it, then reactivate. 🫶

bldgHVACCfgTemplateInfo is the pointer to a row in the template info table. 🔗 A value of zero means the entry isn't tied to any named template. 🌸

bldgHVACCfgTemplateOwner  OBJECT-TYPE
    SYNTAX            SnmpAdminString
    MAX-ACCESS        read-create
    STATUS            current
    DESCRIPTION
        "The identity of the administrative entity
        that created this row of the table."
    ::= { bldgHVACCfgTemplateEntry 5 }

bldgHVACCfgTemplateStorage  OBJECT-TYPE
    SYNTAX            StorageType
    MAX-ACCESS        read-create
    STATUS            current
    DESCRIPTION
        "The persistence of this row of the table across
         system resets.  A columnar instance of this object with
         value 'permanent' need not allow write-access to any
         of the columnar object instances in the containing row."

    ::= { bldgHVACCfgTemplateEntry 6 }

bldgHVACCfgTemplateStatus  OBJECT-TYPE
    SYNTAX            RowStatus
    MAX-ACCESS        read-create
    STATUS            current
    DESCRIPTION
        "The activation status of this row of the table.

        No attempt to modify a row columnar object instance value in
        the bldgHVACCfgTemplateTable should be issued while the
        value of bldgHVACCfgTemplateStatus is active(1).
        Should an agent receive a SET PDU attempting such a modification
        in this state, an inconsistentValue error should be returned as
        a result of the SET attempt."
    ::= { bldgHVACCfgTemplateEntry 7 }

--
-- Conformance Information
--

bldgCompliances  OBJECT IDENTIFIER ::= { bldgConformance 1 }
bldgGroups       OBJECT IDENTIFIER ::= { bldgConformance 2 }

-- Compliance Statements

bldgCompliance MODULE-COMPLIANCE
    STATUS current
    DESCRIPTION
        "The requirements for conformance to the BLDG-HVAC-MIB.  The
         bldgHVACObjects group must be implemented to conform to the
         BLDG-HVAC-MIB."
    MODULE -- this module

    GROUP bldgHVACObjectsGroup
    DESCRIPTION
        "The bldgHVACObjects is mandatory for all systems that
        support HVAC systems."
    ::= { bldgCompliances 1 }

bldgHVACObjectsGroup OBJECT-GROUP
    OBJECTS {
        bldgHVACCfgTemplate,
        bldgHVACFanSpeed, bldgHVACCurrentTemp,
        bldgHVACCoolOrHeatMins, bldgHVACDiscontinuityTime,
        bldgHVACOwner, bldgHVACStatus,
        bldgHVACStorageType, bldgHVACCfgTemplateInfoID,
        bldgHVACCfgTemplateInfoDescr, bldgHVACCfgTemplateInfoOwner,
        bldgHVACCfgTemplateInfoStatus,
        bldgHVACCfgTemplateInfoStorType,
        bldgHVACCfgTemplateDesiredTemp,
        bldgHVACCfgTemplateCoolOrHeat,
        bldgHVACCfgTemplateInfo,
        bldgHVACCfgTemplateOwner,bldgHVACCfgTemplateStorage,
        bldgHVACCfgTemplateStatus
    }
    STATUS current
    DESCRIPTION
        "The bldgHVACObjects Group."
    ::= { bldgGroups 1 }

END

8.2. Notes on MIB Module with Template-based Data ✨

The main job of this example "HVAC" MIB module 🌡️ is to show how you can squish configuration, template, counter and state info into one single module. It's giving efficiency! If this were a 'real' module, we would also have included definitions for notifications for the configuration change operations, as we talked about before. We'd also have included notifications for faults and other counter threshold events. 🔔

Implementation and Instance Extensions: 💅

Just like with networking technologies, vendors may wish to add extensions so they can stand out from the competition. Iconic behaviour. 👑 If an HVAC vendor also wanted to support humidity control, they could add that facility to their equipment and use AUGMENTS for the bldgHVACTemplateTable with two objects: one for the desired humidity and one for the actual humidity. 💧 The bldgHVACTemplateTable could also be extended using this same approach, so HVAC policies could easily be extended to support this vendor. 🫶

8.3. Examples of Usage of the MIB 🏢

The following two examples use two templates to configure the temperature in executive offices and in conference rooms. 🌸 The "conference rooms" template is applied to all conference rooms (which just so happen to be office 104 on each floor), and the "executive offices" template is applied to executive offices. 💼

If offices 24, 25, and 26 on the third floor are executive offices, the values in the bldgHVACTable might be:

bldgHVACCfgTemplate.3.24 = 2
bldgHVACFanSpeed.3.24 = 2989
bldgHVACCurrentTemp.3.24 = 24
bldgHVACCoolOrHeatMins.3.24 = 123
bldgHVACDiscontinuityTime.3.24 = sysUpTime + 12h + 21m
bldgHVACOwner.3.24 = "policy engine"
bldgHVACStorageType.3.24 = nonVolatile(3)
bldgHVACStatus.3.24 = active(1)

bldgHVACCfgTemplate.3.25 = 2
bldgHVACFanSpeed.3.25 = 0
bldgHVACCurrentTemp.3.25 = 22
bldgHVACCoolOrHeatMins.3.25 = 298
bldgHVACDiscontinuityTime.3.25 = sysUpTime + 4h + 2m
bldgHVACOwner.3.25 = "policy engine"
bldgHVACStorageType.3.25 = nonVolatile(3)
bldgHVACStatus.3.25 = active(1)

bldgHVACCfgTemplate.3.26 = 2
bldgHVACFanSpeed.3.26 = 0
bldgHVACCurrentTemp.3.26 = 22
bldgHVACCoolOrHeatMins.3.26 = 982
bldgHVACOwner.3.26 = "policy engine"
bldgHVACStorageType.3.26 = nonVolatile(3)
bldgHVACStatus.3.26 = active(1)

The second entry in the bldgHVACCfgTemplateTable, which all of the above point to, might have the following configuration: 🎀

bldgHVACCfgTemplateDesiredTemp.2 = 22
bldgHVACCfgTemplateCoolOrHeat.2 = cool(2)
bldgHVACCfgTemplateInfo.2 = 2
bldgHVACCfgTemplateOwner.2 = "Senior Executive assistant"
bldgHVACCfgTemplateStorage.2 = nonVolatile(3)
bldgHVACCfgTemplateStatus.2 = active(1)

and the associated template information ("executive offices") might be:

bldgHVACCfgTemplateInfoID.2 = "executive offices"
bldgHVACCfgTemplateInfoDescr.2 = "Controls temperature for executive
                                  offices"
bldgHVACCfgTemplateInfoOwner.2 = "Senior Executive assistant"
bldgHVACCfgTemplateInfoStorType.2 = nonVolatile(3)
bldgHVACCfgTemplateInfoStatus.2 = active(1)

The policy engine can now associate instances of executive offices with the template called "executive offices" and apply the values in the second entry of the bldgHVACCfgTemplateTable to each of the instances of the executive offices. 🤖 This will then attempt to set the temperature in executive offices to 22 degrees celsius. 🌡️✨

It's also possible that there may be an office configured for a particular temperature, but without using a template. 🧐 For example, office 28 on the third floor might look like this:

bldgHVACCfgTemplate.3.28 = 3
bldgHVACFanSpeed.3.28 = 50
bldgHVACCurrentTemp.3.28 = 26
bldgHVACCoolOrHeatMins.3.28 = 0
bldgHVACDiscontinuityTime.3.28 = 0
bldgHVACOwner.3.28 = "Executive with poor circulation"
bldgHVACStorageType.3.28 = nonVolatile(3)
bldgHVACStatus.3.28 = active(1)

The entry in the bldgHVACCfgTemplateTable (which bldgHVACCfgTemplate.3.28 points to) might instead look like:

bldgHVACCfgTemplateDesiredTemp.3 = 28
bldgHVACCfgTemplateCoolOrHeat.3 = cool(2)
bldgHVACCfgTemplateInfo.3 = 0.0
bldgHVACCfgTemplateOwner.3 = "Executive with poor circulation"
bldgHVACCfgTemplateStorage.3 = nonVolatile(3)
bldgHVACCfgTemplateStatus.3 = active(1)

Note that this entry does not point to a template. 🙅‍♀️

If the executive's circulation improves so that the temperature should be aligned with other executive offices, this is accomplished by changing the value of bldgHVACCfgTemplate.3.28 from bldgHVACCfgTemplateInfoID.3 to bldgHVACCfgTemplateInfoID.2 (shown above). Main character glow-up arc. 🦋

Finally, there might be offices for which there is no configured temperature, but management applications can still read the current temperature, fan speed, and cooling or heating minutes from the bldgHVACTable. 📖 In that case, the value of bldgHVACCfgTemplate will be a zero index ("null"), as will the value of bldgHVACOwner. 🍓

bldgHVACCfgTemplate.4.2 = 0
bldgHVACFanSpeed.3.28 = 50
bldgHVACCurrentTemp.3.28 = 26
bldgHVACCoolOrHeatMins.3.28 = 0
bldgHVACDiscontinuityTime.3.28 = 0
bldgHVACOwner.3.28 = ""
bldgHVACStorageType.3.28 = nonVolatile(3)
bldgHVACStatus.3.28 = active(1)

As a second example, the conference rooms on several floors are configured using the "conference rooms" template. 🧚 When the values in the bldgHVACTable pertaining to conference rooms are read, it might look like:

bldgHVACCfgTemplate.12.104 = bldgHVACCfgTemplateDesiredTemp.1
bldgHVACFanSpeed.12.104 = 1423
bldgHVACCurrentTemp.12.104 = 21
bldgHVACCoolOrHeatMins.12.104 = 2193
bldgHVACDiscontinuityTime.12.104 = sysUpTime + 36h + 15m
bldgHVACOwner.12.104 =  = "Bob the Conference Guy"
bldgHVACStorageType.12.104 = nonVolatile(3)
bldgHVACStatus.12.104 = active(1)

bldgHVACCfgTemplate.14.104 = bldgHVACCfgTemplateDesiredTemp.1
bldgHVACFanSpeed.14.104 = 1203
bldgHVACCurrentTemp.14.104 = 20
bldgHVACCoolOrHeatMins.14.104 = 293
bldgHVACDiscontinuityTime.14.104 = sysUpTime + 5h + 54m
bldgHVACOwner.14.104 =  = "Bob the Conference Guy"
bldgHVACStorageType.14.104 = nonVolatile(3)
bldgHVACStatus.14.104 = active(1)

bldgHVACCfgTemplate.15.104 = bldgHVACCfgTemplateDesiredTemp.1
bldgHVACFanSpeed.15.104 = 12
bldgHVACCurrentTemp.15.104 = 19
bldgHVACCoolOrHeatMins.15.104 = 1123
bldgHVACDiscontinuityTime.15.103 = sysUpTime + 2d + 2h + 7m
bldgHVACOwner.15.104 =  = "Bob the Conference Guy"
bldgHVACStorageType.15.104 = nonVolatile(3)
bldgHVACStatus.15.104 = active(1)

The desired temperature and whether to heat or cool is configured in the first entry of the bldgHVACCfgTemplateTable, which tries to set the temperature to 19 degrees celsius in conference rooms: 🧊

bldgHVACCfgTemplateDesiredTemp.1 = 19
bldgHVACCfgTemplateCoolOrHeat.1 = cool(2)
bldgHVACCfgTemplateInfo.1 = bldgHVACCfgTemplateInfoID.1
bldgHVACCfgTemplateOwner.1 = "Bob the Conference Guy"
bldgHVACCfgTemplateStorage.1 = nonVolatile(3)
bldgHVACCfgTemplateStatus.1 = active(1)

The associated template information would then have:

bldgHVACCfgTemplateInfoID.1 = "conference rooms"
bldgHVACCfgTemplateInfoDescr.1 = "Controls temperature in conference
rooms" bldgHVACCfgTemplateInfoOwner.1 = "Bob the Conference Guy"
bldgHVACCfgTemplateInfoStorType.1 = nonVolatile(3)
bldgHVACCfgTemplateInfoStatus.1 = active(1)

The policy system can then apply this template (cool to 19 degrees Celsius) to its notion of all of the conference rooms in the building. 🏆✨


9. Security Considerations 🔐

This document discusses practices and methods for using the SNMP for management and distribution of configuration information for network elements. 🛰️ Any effective use of the SNMP in this application must concern itself with issues of authentication of the management entities initiating configuration change and management, in addition to the integrity of the configuration data itself. Other, more subtle considerations also exist. 🫣

To that end, the section of this document entitled "Deployment and Security Issues" covers these security considerations to the extent they affect the current practices described throughout this document. 📚 In particular, in the subsection entitled "Secure Agent Considerations", there is a recommendation for the usage of Version 3 of the SNMP, and its essential presumption as a foundation for other practices described throughout. 🛡️ With the exception of a small number of cases where a mention is made to the contrary to illustrate techniques for coexistence with application entities dependent upon earlier versions of the SNMP, that recommendation of usage of Version 3 of the SNMP is reiterated here. 💜

10. Acknowledgments 🎀

okay so this document was produced by the SNMPCONF Working Group 🫶 and the editors wanted to give a shoutout to these amazing people, bestie 💖:

  • Christopher Anderson
  • Andy Bierman
  • Greg Bruell
  • Dr Jeffrey Case
  • Chris Elliott
  • Joel Halpern
  • Pablo Halpern
  • Wes Hardaker
  • David Harrington
  • Harrie Hazewinkel
  • Thippanna Hongal
  • Bob Moore
  • David T. Perkins
  • Randy Presuhn
  • Dan Romascanu
  • Shawn Routhier
  • Steve Waldbusser
  • Bert Wijnen

the way they all showed up for this 😭✨ iconic behaviour, no cap.

11. Normative References 📚

these are the must-read references the spec leans on, so no skipping 💅

[1] Harrington, D., Presuhn, R. and B. Wijnen, "An Architecture for Describing Simple Network Management Protocol (SNMP) Management Frameworks", STD 62, RFC 3411, December 2002.

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

[3] McCloghrie, K., Perkins, D. and J. Schoenwaelder, "Textual Conventions for SMIv2", STD 58, RFC 2579, April 1999.

[4] McCloghrie, K., Perkins, D. and J. Schoenwaelder, "Conformance Statements for SMIv2", STD 58, RFC 2580, April 1999.

[5] Presuhn, R. (Ed.), "Transport Mappings for the Simple Network Management Protocol (SNMPv2)", STD 62, RFC 3417, December 2002.

[6] Case, J., Harrington D., Presuhn R. and B. Wijnen, "Message Processing and Dispatching for the Simple Network Management Protocol (SNMP)", STD 62, RFC 3412, December 2002.

[7] Blumenthal, U. and B. Wijnen, "User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3)", STD 62, RFC 3414, December 2002.

[8] Presuhn, R. (Ed.), "Version 2 of the Protocol Operations for the Simple Network Management Protocol (SNMP)", STD 62, RFC 3416, December 2002.

[9] Levi, D., Meyer, P., and B. Stewart, "Simple Network Management Protocol Applications", STD 62, RFC 3413, December 2002.

[10] Wijnen, B., Presuhn, R. and K. McCloghrie, "View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP)", STD 62, RFC 3415, December 2002.

[11] Presuhn, R. (Ed.), "Management Information Base for the Simple Network Management Protocol (SNMPv2)", STD 62, RFC 3418, December 2002.

[12] Case, J., Mundy, R., Partain, D. and B. Stewart, "Introduction and Applicability Statements for Internet-Standard Management Framework", RFC 3410, December 2002.

[13] Daniele, M., Haberman, B., Routhier, S. and J. Schoenwaelder, "Textual Conventions for Internet Network Addresses", RFC 3291, May 2002.

[14] McCloghrie, K. (Ed.), "SNMPv2 Management Information Base for the Internet Protocol using SMIv2", RFC 2011, November 1996.

12. Informative References 🧠

these ones are extra context, not required reading, but they're giving history and vibes 🌈

[15] Rose, M. and K. McCloghrie, "Structure and Identification of Management Information for TCP/IP-based Internets", STD 16, RFC 1155, May 1990.

[16] Rose, M. and K. McCloghrie, "Concise MIB Definitions", STD 16, RFC 1212, March 1991.

[17] Rose, M., "A Convention for Defining Traps for use with the SNMP", RFC 1215, March 1991.

[18] Case, J., Fedor, M., Schoffstall, M. and J. Davin, "Simple Network Management Protocol", STD 15, RFC 1157, May 1990.

[19] Case, J., McCloghrie, K., Rose, M., and S. Waldbusser, "Introduction to Community-based SNMPv2", RFC 1901, January 1996.

[20] McCloghrie, K. and F. Kastenholz, "The Interfaces Group MIB", RFC 2863, June 2000.

[21] Brown, C. and F. Baker, "Management Information Base for Frame Relay DTEs Using SMIv2", RFC 2115, September 1997.

[22] Baker, F. (Ed.), "Requirements for IP Version 4 Routers", RFC 1812, June 1995.

[23] Hawkinson, J. and T. Bates, "Guidelines for Creation, Selection, and Registration of an Autonomous System (AS)", BCP 6, RFC 1930, March 1996.

[24] Decker, E., Langille, P., Rijsinghani, A. and K. McCloghrie, "Definitions of Managed Objects for Bridges", RFC 1493, July 1993.

[25] Levi, D. and J. Schoenwaelder "Definitions of Managed Objects for Scheduling Management Operations", RFC 3231, January 2002.

[26] Bell, E., Smith, A., Langille, P., Rijsinghani, A. and K. McCloghrie, "Definitions of Managed Objects for Bridges with Traffic Classes, Multicast Filtering and Virtual LAN Extensions", RFC 2674, August 1999.

[27] Baker, F., "IP Forwarding Table MIB", RFC 2096, January 1997.

[28] St. Johns, M. (Ed.), "Radio Frequency (RF) Interface Management Information Base for MCNS/DOCSIS compliant RF interfaces", RFC 2670, August 1999.

[29] Baker, F. and R. Coltun, "OSPF Version 2 Management Information Base", RFC 1850, November 1995.

[30] Blake, S., Black, D., Carlson M., Davies, E., Wang, Z. and W. Weiss, "An Architecture for Differentiated Services ", RFC 2475, December 1998.

[31] Willis, S., Burruss, J. and J. Chu (Ed.), "Definitions of Managed Objects for the Fourth Version of the Border Gateway Protocol (BGP-4) using SMIv2", RFC 1657, July 1994.

[32] Waldbusser, S., "Remote Network Monitoring Management Information Base", RFC 2819, May 2000.

[33] McCloghrie, K. and G. Hanson, "The Inverted Stack Table Extension to the Interfaces Group MIB", RFC 2864, June 2000.

[34] McCloghrie, K. and A. Bierman, "Entity MIB (Version 2)", RFC 2737, December 1999.

[35] ITU-T,, Recommendation M.3010., PRINCIPLES FOR A TELECOMMUNICATIONS MANAGEMENT NETWORK. February, 2000.

[36] Waldbusser, S., Saperia, J., and Hongal, T., "Policy Based Management MIB", Work-in-progress.

[37] Heintz, L., "SNMP Row Operations Extensions", Work-in-progress.

[38] Zeltserman, D., "A Practical Guide to Snmpv3 and Network Management", Prentice Hall, 1999.

[39] Noto, M., Spiegel, E. and K. Tesink, "Definitions of Textual Conventions and OBJECT-IDENTITIES for ATM Management", RFC 2514, February 1999.

[40] Kassaveri, R., Editor, "Distributed Management Expression MIB", RFC 2982, October 2000.

[41] St. Johns, M., "DOCSIS Cable Device MIB Cable Device Management Information Base for DOCSIS compliant Cable Modems and Cable Modem Termination Systems", RFC 2669, August 1999.

[42] Westerinen, A., Schnizlein, J., Strassner, J., Scherling, M., Quinn, B., Herzog, S., Huynh, A., Carlson, M., Perry, J. and S. Waldbusser, "Terminology for Policy-Based Management", RFC 3198, November 2001.

[43] http://wwww.cisco.com/univercd/cc/td/product/software/ios113ed/ 11ed_cr/secur_c/scprt/scacls.pdf.

[44] Waldbusser, S., "Remote Network Monitoring Management Information Base Version 2 using SMIv2", RFC 2021, January 1997.

13. Intellectual Property 🔒

okay the IETF is basically saying it takes no position on whether any intellectual property or other rights might apply to the tech in this document, and it hasn't checked for any of that either 🙈 the info on their rights procedures for standards-track and standards-related docs is in BCP-11. copies of any claimed rights, assurances of licenses, or results of attempts to get a general license can be obtained from the IETF Secretariat.

the IETF also invites anyone interested to tell them about any copyrights, patents, patent applications, or other proprietary rights that might cover technology required to practice this standard. send that info to the IETF Executive Director 💌

14. Editors' Addresses 🏡

Michael R. MacFaden Riverstone Networks, Inc 5200 Great America Parkway Santa Clara, CA 95054

EMail: mrm@riverstonenet.com

David Partain Ericsson AB P.O. Box 1248 SE-581 12 Linkoping Sweden

EMail: David.Partain@ericsson.com

Jon Saperia JDS Consulting 174 Chapman Street Watertown, MA 02472

EMail: saperia@jdscons.com

Wayne F. Tackabury Gold Wire Technology 411 Waverley Oaks Rd. Waltham, MA 02452

EMail: wayne@goldwiretech.com

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

this document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. however, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations, except as needed for the purpose of developing Internet standards in which case the procedures for copyrights defined in the Internet Standards process must be followed, or as required to translate it into languages other than English.

the limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assigns.

this document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Acknowledgement 💜

funding for the RFC Editor function is currently provided by the Internet Society. the end, bestie 🦋✨

📦 where this page comes from