RFC 3780

SMIng - Next Generation Structure of Management Information

πŸ“… published May 2004, from ietf

Title Block πŸŽ€

Okay bestie, here's the vibe check on this one before we dive in. πŸ’…

  • RFC number and title: RFC 3780, "SMIng - Next Generation Structure of Management Information" ✨
  • Series: Network Working Group
  • Authors: F. Strauss (TU Braunschweig) and J. Schoenwaelder (International University Bremen) πŸ‘‘
  • Category: Experimental πŸ§ͺ
  • Date: May 2004 🌸
  • Obsoletes / Updates: the title block doesn't list anything this RFC obsoletes or updates 🫢

(Part 1 of 12) πŸ¦‹


Status of This Memo πŸ“œ

okay so this memo defines an Experimental Protocol for the Internet community πŸ§ͺ. it does NOT specify an Internet standard of any kind, so don't go calling it one πŸ™ˆ. they're actively asking for discussion and suggestions for improvement, so if you've got thoughts, bring them πŸ’–. and distribution of this memo is unlimited, so share it with everyone who needs it 🌈.

Copyright (C) The Internet Society (2004). All Rights Reserved. πŸ’œ

Abstract ✨

this memo defines the base SMIng (Structure of Management Information, Next Generation) language πŸ’…. SMIng is a data definition language that gives you a protocol-independent way to represent management information, so it's not stuck to one protocol πŸ¦‹. separate RFCs handle the mappings from SMIng to specific management protocols, including SNMP πŸ“.

Table of Contents πŸ—‚οΈ

   1.  Introduction . . . . . . . . . . . . . . . . . . . . . . . . .  3
       1.1.  The History of SMIng . . . . . . . . . . . . . . . . . .  4
       1.2.  Terms of Requirement Levels. . . . . . . . . . . . . . .  5
   2.  SMIng Data Modeling. . . . . . . . . . . . . . . . . . . . . .  5
       2.1.  Identifiers. . . . . . . . . . . . . . . . . . . . . . .  6
   3.  Base Types and Derived Types . . . . . . . . . . . . . . . . .  7
       3.1.  OctetString. . . . . . . . . . . . . . . . . . . . . . .  8
       3.2.  Pointer. . . . . . . . . . . . . . . . . . . . . . . . .  9
       3.3.  ObjectIdentifier . . . . . . . . . . . . . . . . . . . .  9
       3.4.  Integer32. . . . . . . . . . . . . . . . . . . . . . . . 10
       3.5.  Integer64. . . . . . . . . . . . . . . . . . . . . . . . 11
       3.6.  Unsigned32 . . . . . . . . . . . . . . . . . . . . . . . 12
       3.7.  Unsigned64 . . . . . . . . . . . . . . . . . . . . . . . 13
       3.8.  Float32. . . . . . . . . . . . . . . . . . . . . . . . . 13
       3.9.  Float64. . . . . . . . . . . . . . . . . . . . . . . . . 14
       3.10. Float128 . . . . . . . . . . . . . . . . . . . . . . . . 15
       3.11. Enumeration. . . . . . . . . . . . . . . . . . . . . . . 17
       3.12. Bits . . . . . . . . . . . . . . . . . . . . . . . . . . 17
       3.13. Display Formats. . . . . . . . . . . . . . . . . . . . . 18
   4.  The SMIng File Structure . . . . . . . . . . . . . . . . . . . 20
       4.1.  Comments . . . . . . . . . . . . . . . . . . . . . . . . 20
       4.2.  Textual Data . . . . . . . . . . . . . . . . . . . . . . 21
       4.3.  Statements and Arguments . . . . . . . . . . . . . . . . 21
   5.  The module Statement . . . . . . . . . . . . . . . . . . . . . 21
       5.1.  The module's import Statement. . . . . . . . . . . . . . 22
       5.2.  The module's organization Statement. . . . . . . . . . . 23
       5.3.  The module's contact Statement . . . . . . . . . . . . . 23
       5.4.  The module's description Statement . . . . . . . . . . . 23
       5.5.  The module's reference Statement . . . . . . . . . . . . 23
       5.6.  The module's revision Statement. . . . . . . . . . . . . 23
             5.6.1. The revision's date Statement . . . . . . . . . . 24
             5.6.2. The revision's description Statement. . . . . . . 24
       5.7.  Usage Example. . . . . . . . . . . . . . . . . . . . . . 24
   6.  The extension Statement. . . . . . . . . . . . . . . . . . . . 25
       6.1.  The extension's status Statement . . . . . . . . . . . . 25
       6.2.  The extension's description Statement. . . . . . . . . . 26
       6.3.  The extension's reference Statement. . . . . . . . . . . 26
       6.4.  The extension's abnf Statement . . . . . . . . . . . . . 26
       6.5.  Usage Example. . . . . . . . . . . . . . . . . . . . . . 26
   7.  The typedef Statement. . . . . . . . . . . . . . . . . . . . . 27
       7.1.  The typedef's type Statement . . . . . . . . . . . . . . 27
       7.2.  The typedef's default Statement. . . . . . . . . . . . . 27
       7.3.  The typedef's format Statement . . . . . . . . . . . . . 27
       7.4.  The typedef's units Statement. . . . . . . . . . . . . . 28
       7.5.  The typedef's status Statement . . . . . . . . . . . . . 28
       7.6.  The typedef's description Statement. . . . . . . . . . . 29
       7.7.  The typedef's reference Statement. . . . . . . . . . . . 29
       7.8.  Usage Examples . . . . . . . . . . . . . . . . . . . . . 29
   8.  The identity Statement . . . . . . . . . . . . . . . . . . . . 30
       8.1.  The identity's parent Statement. . . . . . . . . . . . . 30
       8.2.  The identity's status Statement. . . . . . . . . . . . . 30
       8.3.  The identity' description Statement. . . . . . . . . . . 31
       8.4.  The identity's reference Statement . . . . . . . . . . . 31
       8.5.  Usage Examples . . . . . . . . . . . . . . . . . . . . . 31
   9.  The class Statement. . . . . . . . . . . . . . . . . . . . . . 32
       9.1.  The class' extends Statement . . . . . . . . . . . . . . 32
       9.2.  The class' attribute Statement . . . . . . . . . . . . . 32
             9.2.1. The attribute's type Statement. . . . . . . . . . 32
             9.2.2. The attribute's access Statement. . . . . . . . . 32
             9.2.3. The attribute's default Statement . . . . . . . . 33
             9.2.4. The attribute's format Statement. . . . . . . . . 33
             9.2.5. The attribute's units Statement . . . . . . . . . 33
             9.2.6. The attribute's status Statement. . . . . . . . . 34
             9.2.7. The attribute's description Statement . . . . . . 34
             9.2.8. The attribute's reference Statement . . . . . . . 34
       9.3.  The class' unique Statement. . . . . . . . . . . . . . . 35
       9.4.  The class' event Statement . . . . . . . . . . . . . . . 35
             9.4.1. The event's status Statement. . . . . . . . . . . 35
             9.4.2. The event's description Statement . . . . . . . . 35
             9.4.3. The event's reference Statement . . . . . . . . . 36
       9.5.  The class' status Statement. . . . . . . . . . . . . . . 36
       9.6.  The class' description Statement . . . . . . . . . . . . 36
       9.7.  The class' reference Statement . . . . . . . . . . . . . 37
       9.8.  Usage Example. . . . . . . . . . . . . . . . . . . . . . 37
   10. Extending a Module . . . . . . . . . . . . . . . . . . . . . . 38
   11. SMIng Language Extensibility . . . . . . . . . . . . . . . . . 39
   12. Security Considerations. . . . . . . . . . . . . . . . . . . . 41
   13. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 41
   14. References . . . . . . . . . . . . . . . . . . . . . . . . . . 42
       14.1. Normative References . . . . . . . . . . . . . . . . . . 42
       14.2. Informative References . . . . . . . . . . . . . . . . . 42
   Appendix A.  NMRG-SMING Module . . . . . . . . . . . . . . . . . . 44
   Appendix B.  SMIng ABNF Grammar. . . . . . . . . . . . . . . . . . 53
   Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 63
   Full Copyright Statement . . . . . . . . . . . . . . . . . . . . . 64

1. Introduction 🌷

okay so picture this: in traditional management systems, the management information is treated as a bunch of managed objects chilling in a virtual information store called the Management Information Base (MIB) πŸ—ƒοΈ. related objects get grouped into MIB modules, and those modules are written in a specification language called the Structure of Management Information (SMI) πŸ’…. there's more than one version of the SMI. SMIv1 is defined in [RFC1155], [RFC1212], and [RFC1215], and SMIv2 is defined in [RFC2578], [RFC2579], and [RFC2580]. both are built on adapted subsets of OSI's Abstract Syntax Notation One, ASN.1 [ASN1] 🧠.

now the same energy shows up with policy provisioning info πŸŽ€. it's treated as a collection of Provisioning Classes (PRCs) and Provisioning Instances (PRIs), living in a virtual information store called the Policy Information Base (PIB). related Provisioning Classes get grouped into PIB modules, which are written using the Structure of Policy Provisioning Information (SPPI) [RFC3159]. SPPI is an adapted subset of SMIv2 πŸ¦‹.

the SMIv1 and SMIv2 are bound to the Simple Network Management Protocol (SNMP) [RFC3411], while SPPI is bound to the Common Open Policy Service Provisioning (COPS-PR) Protocol [RFC3084] 🌈. even though the languages share common rules, it's really hard to use common data definitions with both protocols 😭. so this document is here to define a common data definition language called SMIng, which can formally specify data models that don't depend on any specific protocol or application πŸ’–. the appendix of this document defines a core module that supplies common SMIng definitions 🧚.

there's also a companion document that contains an SMIng language extension to define SNMP-specific mappings of SMIng definitions, compatible with SMIv2 MIB modules [RFC3781] πŸ“. more language extensions might be added later, for example to define COPS-PR-specific mappings of SMIng definitions in a way that's compatible with SPPI PIBs ⭐.

Section 2 gives you an overview of the basic concepts of data modeling with SMIng πŸ”₯. the subsequent sections go deep on the SMIng language: the base types, the SMIng file structure, and all of the SMIng core statements πŸ‘‘. the rest of the document covers the language's extensibility features and the rules to follow when a module gets changed 🫢. and Appendix B has the SMIng grammar written in ABNF [RFC2234] notation πŸ’….

1.1. The History of SMIng πŸ•°οΈ

so SMIng started in 1999 as a research project to fix some drawbacks in SMIv2, the current data modeling language for management information bases πŸ”¬. the main problems were its partial dependence on ASN.1 and a bunch of exception rules, which turned out to be kinda messy 😬. in 2000, the work got handed over to the IRTF Network Management Research Group, where it was seriously fleshed out πŸ’œ. then the RAP Working Group's work on COPS-PR and SPPI showed up in 1999/2000, so SMIng got split into two parts: a core data definition language (which is what this document defines) and protocol mappings, so core definitions can be applied through (potentially) multiple management protocols πŸ¦‹. replacing SMIv2 and SPPI with one merged data definition language was also a main goal of the IETF SMING Working Group, which got chartered at the end of 2000 🌸.

the requirements for a new data definition language got discussed several times within the IETF SMING Working Group, and they changed a lot over time [RFC3216] πŸŒ€. that's how another proposal, SMI Data Structures (SMI-DS), showed up alongside SMIng πŸŽ€. in the end, neither proposal got enough consensus and support, and the attempt to merge the existing concepts didn't work out either πŸ’”. so the Working Group got closed down in April 2003 πŸ₯€.

to keep a record of the NMRG's (Network Management Research Group's) work on SMIng, this memo and the accompanying memo on the SNMP protocol mapping [RFC3781] were published for informational purposes πŸ“š.

quick note bestie: throughout these documents, "SMIng" refers to the specific data modeling language specified in this document, while "SMING" refers to the general IETF Working Group effort to define a new management data definition language as an SMIv2 successor (and probably an SPPI merger). "SMIng" and "SMI-DS" were two specific proposals for that effort πŸ§šβ€β™€οΈβœ¨

(to be continued in part 2 πŸ’–)

1.2. Terms of Requirement Levels πŸŽ€

Okay so the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be read the way [RFC2119] describes. Bestie, these are the official power words, so pay attention to them! πŸ’…


2. SMIng Data Modeling ✨

SMIng is a language made to specify management information in a structured way. The vibe is that it's readable by computer programs (think MIB compilers) and by actual humans too. Both can read it, which is honestly iconic 🫢

Management information gets modeled in classes. You can define a class from scratch, or you can derive it from a parent class. Derivation from multiple parent classes is not possible, so one parent only, periodt πŸ’€ The concept of classes is covered in Section 9.

Each class has a bunch of attributes. Each attribute holds one atomic piece of information. That piece can be a base type, a sub-type of a base type, or another class. The concept of attributes is covered in Section 9.2.

The base types of SMIng include signed and unsigned integers, octet strings, enumeration types, bitset types, and pointers. Pointers are references to class instances, attributes of class instances, or arbitrary identities. The SMIng type system is covered in Section 3.

Related class and type definitions live in modules. A module can refer to definitions from other modules by importing identifiers from them. Each module can serve one or several purposes πŸ¦‹

  • the definition of management classes,
  • the definition of events,
  • the definition of derived types,
  • the definition of arbitrary untyped identities that serve as values of pointers,
  • the definition of SMIng extensions. These let the local module, or other modules, specify information beyond the scope of base SMIng in a machine readable notation. Some extensions for using SMIng in the SNMP framework are defined in [RFC3781],
  • the definition of information beyond the scope of the base SMIng statements, built on locally defined or imported SMIng extensions.

Each module is identified by an upper-case identifier. The names of all standard modules must be unique, but different versions of the same module should share the same name. Developers of enterprise modules are encouraged to pick names that are unlikely to collide with standard or other enterprise modules. One way to do that is to use the enterprise or organization name as a prefix 🌸

2.1. Identifiers 🧚

Identifiers are used to name different kinds of SMIng items. Each identifier is valid in a namespace, and which namespace depends on the type of SMIng item being defined:

  • The global namespace contains all module identifiers.
  • Each module defines a new namespace. A module's namespace may contain definitions of extension identifiers, derived type identifiers, identity identifiers, and class identifiers. A module can also import identifiers of these kinds from other modules. All of these identifiers are also visible within all inner namespaces of the module.
  • Each class within a module defines a new namespace. A class' namespace may contain definitions of attribute identifiers and event identifiers.
  • Each enumeration type and bitset type defines a new namespace of its named numbers. These named numbers are visible in each expression of a corresponding value, such as default values and sub-typing restrictions.
  • Extensions may define additional namespaces and may have additional rules about the visibility of other namespaces.

Within every namespace, each identifier MUST be unique. πŸ”₯

Each identifier starts with an upper-case or lower-case character, depending on the kind of SMIng item. After that it can have zero or more letters, digits, and hyphens.

All identifiers defined in a namespace MUST be unique, and they SHOULD NOT only differ in case. Identifiers MUST NOT exceed 64 characters in length. The set of all identifiers defined in all modules of a single standardization body or organization SHOULD be unique and mnemonic. This helps people have a common language when they talk about a module πŸ“

To reference an item defined in the local module, its definition MUST sequentially precede the reference. So there MUST NOT be any forward references. No time travel, bestie ⏳

To reference an item defined in an external module, it MUST be imported (Section 5.1). Identifiers that are neither defined nor imported MUST NOT be visible in the local module.

When identifiers from external modules get referenced, name collisions are possible. If different items with the same identifier are imported, or if imported identifiers collide with identifiers of locally defined items, the ambiguity is fixed by prefixing those identifiers with the names of their modules and the namespace operator ::, so it looks like Module::item. You can use this notation even when there's no name collision at all, so you do you πŸ’–

Note that SMIng core language keywords MUST NOT be imported. The ...Keyword rules of the SMIng ABNF grammar in Appendix B list all of those keywords πŸ“–


3. Base Types and Derived Types πŸ’…

SMIng has a set of base types. They're kind of like the ones in lots of programming languages, but with some differences that come from the management information model's special requirements πŸ¦‹

You can define additional types too. They can be derived from those base types or from other derived types. Derived types can use subtyping to formally restrict the set of possible values. A starter set of commonly used derived types is defined in the SMIng standard module NMRG-SMING [RFC3781].

The different base types and their derived types allow different kinds of subtyping. These are size restrictions of octet strings (Section 3.1), range restrictions of numeric types (Section 3.4 through Section 3.10), restricted pointer types (Section 3.2), and restrictions on the sets of named numbers for enumeration types (Section 3.11) and bit sets (Section 3.12).

3.1. OctetString 🌈

The OctetString base type represents arbitrary binary or textual data. SMIng has a theoretical size limit of 2^16-1 (65535) octets for this base type, but module designers should realize that there may be implementation and interoperability limits for sizes over 255 octets. Watch out for that one πŸ‘€

Values of octet strings can be written as textual data enclosed in double quotes. They can also be written as arbitrary binary data, shown as a 0x-prefixed hexadecimal value with an even number of at least two hexadecimal digits. Each pair of hex digits represents a single octet. Letters in hexadecimal values MAY be upper-case, but lower-case characters are RECOMMENDED πŸ’– Textual data may contain any number (possibly zero) of any 7-bit displayable ASCII characters. That includes tab characters, spaces, and line terminator characters (nl or cr & nl). Some characters need a special encoding (see Section 4.2). Textual data may span multiple lines, and each subsequent line's prefix, which contains only white space up to the column where the first line's data starts, SHOULD be skipped by parsers for better text formatting ✨

When you define a type derived (directly or indirectly) from the OctetString base type, you can restrict the size in octets. Do this by appending a list of size ranges or explicit size values, separated by pipe | characters, with the whole list enclosed in parentheses. A size range has a lower bound, two consecutive dots .., and an upper bound. Each value can be written in decimal or 0x-prefixed hexadecimal notation. Hexadecimal numbers must have an even number of at least two digits. Size restricting values MUST NOT be negative. If multiple values or ranges are given, they all MUST be disjoint and MUST be in ascending order. If you restrict the size of a size-restricted octet string, the new restriction MUST be equal or more limiting. That means raising the lower bounds, reducing the upper bounds, removing explicit size values or ranges, or splitting ranges into multiple ranges with intermediate gaps 🫢

Value Examples:

   "This is a multiline
    textual data example."         // legal
   "This is "illegally" quoted."   // illegal quotes
   "This is \"legally\" quoted."   // legally encoded quotes
   "But this is 'ok', as well."    // legal apostrophe quoting
   ""                              // legal zero length
   0x123                           // illegal odd hex length
   0x534d496e670a                  // legal octet string

Restriction Examples:

   OctetString (0 | 4..255)        // legal size spec
   OctetString (4)                 // legal exact size
   OctetString (-1 | 1)            // illegal negative size
   OctetString (5 | 0)             // illegal ordering
   OctetString (1 | 1..10)         // illegal overlapping

3.2. Pointer 🧚

The Pointer base type represents values that reference class instances, attributes of class instances, or arbitrary identities. The only Pointer values that can be present in a module refer to identities. They're written as the identifiers of the identities involved πŸ’œ

When you define a type derived (directly or indirectly) from the Pointer base type, you can restrict the values to a specific class, attribute, or identity. The restriction covers all items derived from it, directly or indirectly. You do this by appending the identifier of the right construct, enclosed in parentheses 🌸

Value Examples:

   null                          // legal identity name
   snmpUDPDomain                 // legal identity name

Restriction Examples:

   Pointer (snmpTransportDomain) // legal restriction

3.3. ObjectIdentifier πŸ¦‹

The ObjectIdentifier base type represents administratively assigned names for use with SNMP and COPS-PR. This type SHOULD NOT be used in protocol independent SMIng modules. It's meant for the SNMP and COPS-PR mappings of attributes of type Pointer (Section 3.2) πŸ™ˆ

Values of this type can be written as a sequence of numerical non-negative sub-identifier values. Each sub-identifier MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers can be written in decimal or 0x-prefixed hexadecimal. They're separated by single dots, with no white space in between. Another option, and usually the preferred one, is to start with a previously defined or imported lower-case identifier. That identifier represents a static object identifier prefix πŸ“

Although SMIng object identifiers don't have a limit on the number of sub-identifiers, module designers should realize that there may be implementations that stick with the SMIv1/v2 limit of 128 sub-identifiers. Plan accordingly πŸ’…

Object identifier derived types cannot be restricted in any way.

Value Examples:

   1.3.6.1                     // legal numerical oid
   mib-2.1                     // legal oid with identifier prefix
   internet.4.1.0x0627.0x01    // legal oid with hex subids
   iso.-1                      // illegal negative subid
   iso.org.6                   // illegal non-heading identifier
   IF-MIB::ifNumber.0          // legal fully qualified instance oid

3.4. Integer32 πŸ’–

okay so Integer32 is giving range πŸŽ€ it holds integer values from -2^31 (-2147483648) all the way up to 2^31-1 (2147483647). bestie, that's a lot of numbers 😭

Integer32 values can be written in decimal or hexadecimal, but only decimal numbers can be negative 🧚 Decimal numbers other than zero MUST NOT have leading zero digits. Hex numbers start with 0x and MUST have an even number of at least two hexadecimal digits. Letters MAY be upper-case, but lower-case characters are RECOMMENDED ✨

When you derive a type from Integer32 (directly or indirectly), you can narrow down the allowed values by adding a list of ranges or explicit values. Put a pipe | between each entry and wrap the whole list in parentheses πŸ’… A range is a lower bound, two consecutive dots .., then an upper bound. Each value can be decimal or 0x-prefixed hex, and hex numbers still need an even number of at least two digits. If you give multiple values or ranges, they all MUST be disjoint and MUST be in ascending order. And if you restrict something that's already restricted, the new restriction MUST be equal or more limiting. That means raising lower bounds, reducing upper bounds, removing explicit values or ranges, or splitting ranges into multiple ranges with intermediate gaps πŸ¦‹

Value Examples:

015                         // illegal leading zero
-123                        // legal negative value
- 1                         // illegal intermediate space
0xabc                       // illegal hexadecimal value length
-0xff                       // illegal sign on hex value
0x80000000                  // illegal value, too large
0xf00f                      // legal hexadecimal value

Restriction Examples:

Integer32 (0 | 5..10)       // legal range spec
Integer32 (5..10 | 2..3)    // illegal ordering
Integer32 (4..8 | 5..10)    // illegal overlapping

3.5. Integer64 🌸

Integer64 is the bigger sibling πŸ‘‘ It represents integer values from -2^63 (-9223372036854775808) up to 2^63-1 (9223372036854775807).

The same vibe applies here. Only decimal numbers can be negative. Decimal numbers other than zero MUST NOT have leading zero digits. Hex numbers are prefixed by 0x and MUST have an even number of hexadecimal digits. Letters MAY be upper-case, but lower-case characters are RECOMMENDED πŸ“

For derived types, the restriction rules are the same as Integer32, just with Integer64 as the base. You can use a pipe-separated list of ranges or explicit values, wrapped in parentheses. A range is a lower bound, .., and an upper bound. Each value can be decimal or 0x-prefixed hex, and hex numbers must have an even number of at least two digits. If multiple values or ranges are given, they all MUST be disjoint and MUST be in ascending order. If a value restriction is applied to an already restricted type, the new restriction MUST be equal or more limiting, that is raising the lower bounds, reducing the upper bounds, removing explicit values or ranges, or splitting ranges into multiple ranges with intermediate gaps 🫢

Value Examples:

015                         // illegal leading zero
-123                        // legal negative value
- 1                         // illegal intermediate space
0xabc                       // illegal hexadecimal value length
-0xff                       // illegal sign on hex value
0x80000000                  // legal value

Restriction Examples:

Integer64 (0 | 5..10)       // legal range spec
Integer64 (5..10 | 2..3)    // illegal ordering
Integer64 (4..8 | 5..10)    // illegal overlapping

3.6. Unsigned32 πŸ¦‹

Unsigned32 is the no-negatives girlie 🧚 It represents positive integer values between 0 and 2^32-1 (4294967295).

Values can be decimal or hexadecimal, but there's no negative sign to worry about. Decimal numbers other than zero MUST NOT have leading zero digits. Hex numbers are prefixed by 0x and MUST have an even number of hexadecimal digits. Letters MAY be upper-case, but lower-case characters are RECOMMENDED πŸ’œ

Derived types work the same way as before. Wrap a pipe-separated list of ranges or explicit values in parentheses, with each range written as a lower bound, .., then an upper bound. Each value can be decimal or 0x-prefixed hex, and hex numbers must have an even number of at least two digits. If multiple values or ranges are given, they all MUST be disjoint and MUST be in ascending order. If a value restriction is applied to an already restricted type, the new restriction MUST be equal or more limiting, that is raising the lower bounds, reducing the upper bounds, removing explicit values or ranges, or splitting ranges into multiple ranges with intermediate gaps 🌈

Value Examples:

015                         // illegal leading zero
-123                        // illegal negative value
0xabc                       // illegal hexadecimal value length
0x80000000                  // legal hexadecimal value
0x8080000000                // illegal value, too large

Restriction Examples:

Unsigned32 (0 | 5..10)       // legal range spec
Unsigned32 (5..10 | 2..3)    // illegal ordering
Unsigned32 (4..8 | 5..10)    // illegal overlapping

3.7. Unsigned64 ✨

Unsigned64 is Unsigned32's even bigger cousin πŸ”₯ It represents positive integer values between 0 and 2^64-1 (18446744073709551615).

Same rules as Unsigned32. Decimal numbers other than zero MUST NOT have leading zero digits. Hex numbers are prefixed by 0x and MUST have an even number of hexadecimal digits. Letters MAY be upper-case, but lower-case characters are RECOMMENDED πŸŽ€

For derived types, you can wrap a pipe-separated list of ranges or explicit values in parentheses. Each range is a lower bound, two consecutive dots .., and an upper bound. Each value can be decimal or 0x-prefixed hex, and hex numbers must have an even number of at least two digits. If multiple values or ranges are given, they all MUST be disjoint and MUST be in ascending order. If a value restriction is applied to an already restricted type, the new restriction MUST be equal or more limiting, that is raising the lower bounds, reducing the upper bounds, removing explicit values or ranges, or splitting ranges into multiple ranges with intermediate gaps 🧚

Value Examples:

015                         // illegal leading zero
-123                        // illegal negative value
0xabc                       // illegal hexadecimal value length
0x8080000000                // legal hexadecimal value

Restriction Examples:

Unsigned64 (1..10000000000) // legal range spec
Unsigned64 (5..10 | 2..3)   // illegal ordering

3.8. Float32 🌈

Float32 is the floating point queen πŸ‘‘ It represents floating point values of single precision, as described by [IEEE754].

You can write Float32 values as a decimal fraction with an optional exponent, the way lots of programming languages do it. For the exact syntax, check the grammar rule floatValue in Appendix B πŸ’– There are four special values: snan (signalling Not-a-Number), qnan (quiet Not-a-Number), neginf (negative infinity), and posinf (positive infinity). Heads up: -0.0 and +0.0 are different floating point values, but 0.0 is equal to +0.0 πŸ™ˆ

For derived types, the same pipe-and-parentheses trick applies. A range is a lower bound, two consecutive dots .., and an upper bound. If multiple values or ranges are given, they all MUST be disjoint and MUST be in ascending order. If a value restriction is applied to an already restricted type, the new restriction MUST be equal or more limiting, that is raising the lower bounds, reducing the upper bounds, removing explicit values or ranges, or splitting ranges into multiple ranges with intermediate gaps. The special values snan, qnan, neginf, and posinf must be explicitly listed in restrictions if they shall be included, and snan and qnan cannot be used in ranges πŸ¦‹

Encoding is NOT part of this specification. Whatever transports Float32 objects has to describe the encoding itself. Also, most floating point encodings can't represent lots of values that look nice as decimal fractions in SMIng, so explicit values in floating point type restrictions should be handled with care πŸ’€

Value Examples:

00.1                       // illegal leading zero
3.1415                     // legal value
-2.5E+3                    // legal negative exponential value

Restriction Examples:

Float32 (-1.0..1.0)        // legal range spec
Float32 (1 | 3.3 | 5)      // legal, probably unrepresentable 3.3
Float32 (neginf..-0.0)     // legal range spec
Float32 (-10.0..10.0 | 0)  // illegal overlapping

3.9. Float64 ✨

okay so Float64 is the double precision float type πŸ’– It's the chunky, precise one, and [IEEE754] is the spec that defines how it works.

Float64 values can be written as a decimal fraction with an optional exponent, just like in lots of programming languages πŸ¦‹ The grammar rule floatValue in Appendix B has the exact syntax. There are four special values: snan (signalling Not-a-Number), qnan (quiet Not-a-Number), neginf (negative infinity), and posinf (positive infinity). Also, -0.0 and +0.0 are different floating point values, but 0.0 is equal to +0.0. wild, I know πŸ™ˆ

When you define a type that's derived (directly or indirectly) from Float64, you can restrict the set of values you allow. You do this by appending a list of ranges or explicit values, separated by pipe | characters, and wrapping the whole list in parenthesis. A range is a lower bound, two consecutive dots .., and an upper bound. If you give multiple values or ranges, they MUST be disjoint and MUST be in ascending order. If you restrict a type that's already restricted, the new restriction MUST be equal or more limiting. That means raising the lower bounds, reducing the upper bounds, removing explicit values or ranges, or splitting ranges into multiple ranges with intermediate gaps πŸ’… The special values snan, qnan, neginf, and posinf must be explicitly listed in restrictions if they shall be included, and snan and qnan cannot be used in ranges.

Encoding is NOT part of this spec, so protocols that transport Float64 objects have to describe it themselves 🫢 Also, most floating point encodings can't represent lots of values that look nice as decimal fractions (which is what SMIng uses so humans can read it). So explicit values in floating point restrictions should be handled with care 😭

Value Examples:

00.1                       // illegal leading zero
3.1415                     // legal value
-2.5E+3                    // legal negative exponential value

Restriction Examples:

Float64 (-1.0..1.0)        // legal range spec
Float64 (1 | 3.3 | 5)      // legal, probably unrepresentable 3.3
Float64 (neginf..-0.0)     // legal range spec
Float64 (-10.0..10.0 | 0)  // illegal overlapping

3.10. Float128 🌈

Float128 is the quadruple precision float type, and it's also defined by [IEEE754] πŸŽ€ It's basically Float64's bigger sibling.

Float128 values use the same decimal fraction plus optional exponent format. Check the grammar rule floatValue in Appendix B for the exact syntax 🌸 The special values are the same as before: snan (signalling Not-a-Number), qnan (quiet Not-a-Number), neginf (negative infinity), and posinf (positive infinity). -0.0 and +0.0 are different, and 0.0 is equal to +0.0 πŸ‘‘

Restricting a derived Float128 type works exactly like Float64. You use pipe | separated ranges or explicit values in parenthesis, and a range is a lower bound, .., and an upper bound. Multiple values or ranges MUST be disjoint and MUST be in ascending order. A restriction on an already restricted type MUST be equal or more limiting, so raise the lower bounds, reduce the upper bounds, remove explicit values or ranges, or split ranges with intermediate gaps. The special values snan, qnan, neginf, and posinf must be explicitly listed if they shall be included, and snan and qnan cannot be used in ranges ✨

Encoding is not covered here either. Protocols transporting Float128 objects have to describe it, and most floating point encodings can't represent many nice-looking decimal fractions. So be careful with explicit values in restrictions πŸ’€

Value Examples:

00.1                       // illegal leading zero
3.1415                     // legal value
-2.5E+3                    // legal negative exponential value

Restriction Examples:

Float128 (-1.0..1.0)        // legal range spec
Float128 (1 | 3.3 | 5)      // legal, probably unrepresentable 3.3
Float128 (neginf..-0.0)     // legal range spec
Float128 (-10.0..10.0 | 0)  // illegal overlapping

3.11. Enumeration πŸ“

Enumeration is for values from a set of integers between -2^31 (-2147483648) and 2^31-1 (2147483647), where every value gets an assigned name. It's giving labels 🧚

The named numbers go in a comma-separated list, enclosed in parenthesis, and appended to the Enumeration keyword. Each named number is a lower-case identifier followed by its integer value in parenthesis. That value can be decimal or a 0x-prefixed hexadecimal number, and hex numbers must have an even number of at least two digits. Every name and every number in an enumeration type MUST be unique. It is RECOMMENDED that values be positive, start at 1, and be numbered contiguously. All named numbers MUST be given in ascending order πŸ’œ

When you write enumeration values, you can use decimal or 0x-prefixed hex numbers, but the iconic move is using the assigned names πŸ’… Hex numbers still need an even number of at least two digits.

Derived types are where it gets fun. A type derived (directly or indirectly) from an enumeration can keep the same named numbers or remove some of them. What it can NOT do is add named numbers or change a name, a value, or both πŸ™ˆ

Type and Value Examples:

Enumeration (up(1), down(2), testing(3))
Enumeration (down(2), up(1)) // illegal order

0                            // legal (though not recommended) value
up                           // legal value given by name
2                            // legal value given by number

3.12. Bits πŸ¦‹

Bits is the base type for bit sets. A Bits value is a set of flags, each identified by a small integer number starting at 0 🌸 Every bit number gets an assigned name, and that works the same way as Enumeration. The named numbers go in a comma-separated list, enclosed in parenthesis, and appended to the Bits keyword. Each named number is a lower-case identifier followed by its integer value in parenthesis, decimal or 0x-prefixed hex with an even number of at least two digits.

Every name and every number in a bits type MUST be unique. It is RECOMMENDED that numbers start at 0 and be numbered contiguously. Negative numbers are forbidden, and all named numbers MUST be given in ascending order 🫢

Bits values are written as a comma-separated list of decimal or 0x-prefixed hex numbers, or (preferably) their assigned names, all enclosed in parenthesis. Hex numbers still need an even number of at least two digits. There MUST NOT be any element, by name or number, listed more than once. Elements MUST be listed in ascending order πŸ™ˆ

When you derive a type from a bits type, you can restrict the set of named numbers by removing one or more of them. You still can't add named numbers or change a name, a value, or both πŸ‘‘

Type and Value Examples:

Bits (readable(0), writable(1), executable(2))
Bits (writable(1), readable(0) // illegal order

()                          // legal empty value
(readable, writable, 2)     // legal value
(0, readable, executable)   // illegal, readable(0) appears twice
(writable, 4)               // illegal, element 4 out of range

3.13. Display Formats ✨

Okay so attribute and type definitions can say how a value should be displayed πŸ’…, and those format specs are just textual data, bestie.

If the base type is Integer32, Integer64, Unsigned32, or Unsigned64, the format is an integer-format spec with two parts. Part one is a single character picking the look: x for hexadecimal, d for decimal, o for octal, or b for binary. For all types, leading zeros get dropped when rendering, and negative values get a minus sign right before the digits πŸ”₯. Part two is always left out for x, o, and b, and it's optional for d. When it's there, it starts with a hyphen and then a decimal number that says where the implied decimal point goes. So d-2 means 1234 shows up as 12.34 πŸŽ€.

If the base type is OctetString, the format is one or more octet-format specs. Each spec has five parts, and each part eats zero or more of the next octets from the value and spits out zero or more characters. The octets get processed in order of significance, most significant first 🧚.

The five parts of an octet-format spec are:

  1. The (optional) repeat indicator. If it's there, it's a *, and it means the current octet is the repeat count. That count is an unsigned integer (zero is totally allowed) saying how many times the rest of this spec gets applied in a row. No repeat indicator means the repeat count is one 🌸.
  2. The octet length: one or more decimal digits for how many octets this spec uses and formats. It can be zero πŸ™ˆ. If fewer octets are left than that number, you just use the lesser number that remain.
  3. The display format: x for hexadecimal, d for decimal, o for octal, a for ASCII, or t for UTF-8 [RFC3629]. If the octet length is greater than one and the display format is a numeric one, network byte-ordering (big-endian encoding) is used to read the octets. The octets handled by t don't always form a whole number of UTF-8 characters, and trailing octets that don't make a valid UTF-8 encoded character get thrown out πŸ¦‹.
  4. The (optional) display separator character. If present, it's one character shown after each application of this spec. Hot tip from the original: it doesn't get shown if it would sit right in front of this spec's repeat terminator character. It can be any character except a decimal digit or * ✨.
  5. The (optional) repeat terminator character. It can only show up if there's a display separator and this spec starts with a repeat indicator. If present, it's one character shown after all the repeated applications (the repeat count) are done. Same rule: anything except a decimal digit or * πŸ’œ.

Output of a display separator or repeat terminator gets suppressed if it would be the very last character of the display. Iykyk πŸ’€.

If the octets run out before all the specs have been used, the leftover specs get ignored. If there are still octets left after every spec has been interpreted, the last spec gets re-interpreted to process them, over and over, until none remain 🌈.

Here's the tea: some types don't have any format specs defined. For derived types and attributes based on those, format specs SHOULD be omitted. Implementations MUST ignore any format specs they can't interpret πŸ™…β€β™€οΈ. Also, the SMIng grammar (Appendix B) doesn't spell out the syntax of format specs, so that's a separate thing.

Display Format Examples:

Base Type   Format              Example Value    Rendered Value
----------- ------------------- ---------------- -----------------
OctetString 255a                "Hello World."   Hello World.
OctetString 1x:                 "Hello!"         48:65:6c:6c:6f:21
OctetString 1d:1d:1d.1d,1a1d:1d 0x0d1e0f002d0400 13:30:15.0,-4:0
OctetString 1d.1d.1d.1d/2d      0x0a0000010400   10.0.0.1/1024
OctetString *1x:/1x:            0x02aabbccddee   aa:bb/cc:dd:ee
Integer32   d-2                 1234             12.34

4. The SMIng File Structure πŸ“š

Okay so the topmost container of SMIng info is a file πŸ—‚οΈ. An SMIng file can hold zero, one, or more modules. It is RECOMMENDED that modules be stored in separate files named by their module names, wherever that's possible. For dedicated purposes though, it might make sense to bundle several modules into a single file πŸ’–.

The top-level construct is the module statement (Section 5), which defines one single SMIng module. A module is a sequence of sections in an obligatory order, each holding different kinds of definitions. Whether those sections actually contain statements or stay empty mostly depends on what the module is for ✨.

4.1. Comments πŸ’¬

Comments can go pretty much anywhere in an SMIng file, except between the characters of one token, like inside a quoted string. However, it is RECOMMENDED that all the substantive descriptions go in an appropriate description clause, so SMIng parsers can actually get at that info 🫢.

Comments start with a pair of adjacent slashes // and run until the end of the line.

4.2. Textual Data πŸ“

Some statements get a textual argument: organization, contact, description, reference, abnf, format, and units πŸŽ€. That text, plus any representations of OctetString values, have to be wrapped in double quotes. Inside, they can hold basically any characters, with these special encoding rules:

A backslash introduces a special character, and what that character is depends on whatever comes right after the backslash:

\n      new line
\t      a tab character
\"      a double quote
\\      a single backslash

Now, if the text has a line break followed by whitespace that's just there to indent it according to the layout of the SMIng file, that prefixing whitespace gets stripped out. Layout doesn't leak into the text 🧹.

4.3. Statements and Arguments 🧩

SMIng runs on a very small set of basic grammar rules built around the idea of statements πŸ’…. Each statement starts with a lower-case keyword naming it, followed by a number (possibly zero) of arguments. An argument can be quoted text, an identifier, a value of any base type, a list of identifiers in parentheses ( ), or a statement block in curly braces { }. Since statement blocks count as valid arguments, you can nest statement sequences inside each other πŸͺ†. Every statement ends with a semicolon ;.

The core set of statements can be extended using the SMIng extension statement. See Sections 6 and 11 for the details 🌟.

Where a statement is expected but an unknown lower-case word shows up, those statements MUST be skipped up to the proper semicolon, nested statement blocks included. No crashing, no drama πŸ’œ.


5. The module Statement πŸ¦‹

The module statement is the container for all the definitions of a single SMIng module πŸ‘‘. It gets two arguments: an upper-case module name and a statement block with mandatory and optional statements, plus sections of statements, all in an obligatory order:

     module <MODULE-NAME> {

         <optional import statements>
         <organization statement>
         <contact statement>
         <description statement>
         <optional reference statement>
         <at least one revision statement>

         <optional extension statements>

         <optional typedef statements>

         <optional identity statements>

         <optional class statements>

     };

So the optional import statements (Section 5.1) come first, then the mandatory organization (Section 5.2), contact (Section 5.3), and description (Section 5.4) statements, plus the optional reference statement (Section 5.5). After those, you need at least one mandatory revision statement (Section 5.6). Everything up to this point is the module's meta information, meaning it describes the whole module without defining the items apps use right away 🧠. The main definitions come after: SMIng extensions (Section 6), derived types (Section 7), identities (Section 8), and classes (Section 9) 🌸.

See the moduleStatement rule of the SMIng grammar (Appendix B) for the formal syntax of the module statement πŸ“œ.

5.1. The module's import Statement πŸ“₯

The optional import statement is how you bring identifiers in from external modules into the local module's namespace πŸ›Έ. It gets two arguments: the name of the external module, and a comma-separated list of one or more identifiers to import, all enclosed in parentheses.

Multiple import statements for the same module with disjoint lists of identifiers are allowed, though NOT RECOMMENDED πŸ™ƒ. The same identifier from the same module MUST NOT be imported more than once. Importing identifiers with the same name from different modules might be necessary, and that's allowed too. To tell them apart in the local module, you refer to them by qualified names 🫠. And importing identifiers the local module never uses is NOT RECOMMENDED.

See the importStatement rule of the SMIng grammar (Appendix B) for the formal syntax of the import statement πŸ’–.

5.2. The module's organization Statement 🏒

The module's organization statement must be present πŸ’…. It gets one argument, a textual description of the organization(s) under whose auspices this module was developed.

5.3. The module's contact Statement πŸ“‡

The contact statement must be present too 🫢. It gets one argument that spells out the name, postal address, telephone number, and electronic mail address of the person technical queries about this module should go to.

5.4. The module's description Statement πŸ“–

The description statement must be present, obviously πŸŽ€. It gets one argument: a high-level textual description of what's in this module.

5.5. The module's reference Statement πŸ”—

The reference statement need not be present ✨. It gets one argument, a textual cross-reference to some other document. That could be another module that defines related management information, or some other document that gives extra info relevant to this module 🌈.

5.6. The module's revision Statement πŸŽ€

okay so the revision statement is the module's edit history, and it's used again and again to note the editorial revisions, including the very first one πŸ’… It takes one argument, a statement block with detailed info in a fixed order. A module MUST have at least one initial revision statement, no cap. And every time there's an editorial change, a brand new one MUST be added in front of the revisions sequence, so everything stays in reverse chronological order ⏳ (newest slay goes first πŸ’–)

See the revisionStatement rule of the SMIng grammar (Appendix B) for the formal syntax of the revision statement 🧚

5.6.1. The revision's date Statement

okay the revision's date statement is a must-have, since it must be present 😭 It gets one argument that records when the revision happened, written as YYYY-MM-DD HH:MM or just YYYY-MM-DD. If you leave off the time, it's read as 00:00. And the time is always UTC, bestie 🌍

See the date rule of the SMIng grammar (Appendix B) for the formal syntax of the revision's date statement.

5.6.2. The revision's description Statement

The revision's description statement, which must be present, gets one argument that gives a high-level textual description of the revision πŸ“

5.7. Usage Example 🌸

Consider how a skeletal module might be constructed:

module ACME-MIB {

  import NMRG-SMING (DisplayString);

  organization
            "IRTF Network Management Research Group (NMRG)";

  contact   "IRTF Network Management Research Group (NMRG)
             http://www.ibr.cs.tu-bs.de/projects/nmrg/

             Joe L. User

             ACME, Inc.
             42 Anywhere Drive
             Nowhere, CA 95134
             USA

             Phone: +1 800 555 0815
             EMail: joe@acme.example.com";

  description
            "The module for entities implementing the ACME protocol.

             Copyright (C) The Internet Society (2004).
             All Rights Reserved.
             This version of this MIB module is part of RFC 3780,
             see the RFC itself for legal notices.";

  revision {
    date            "2003-12-16";
    description
            "Initial revision, published as RFC 3780.";
  };

  // ... further definitions ...

}; // end of module ACME-MIB.

6. The extension Statement 🧚

the extension statement is how you define brand new statements. You can use them in the local module right after this definition, or in external modules that import it πŸ’œ It gets two arguments: a lower-case extension statement identifier, and a statement block that holds detailed extension info in a fixed order.

Extension statement identifiers SHOULD NOT contain any upper-case characters πŸ™ˆ

Note that the SMIng extension feature doesn't let you formally spell out the context, or the argument syntax and semantics, of an extension. Its only job is to declare that the extension exists and to give it a unique reference. See Section 11 for detailed info on extensions, and [RFC3781] for the mappings of SMIng definitions to SNMP, which is formally defined as an extension ✨

See the extensionStatement rule of the SMIng grammar (Appendix B) for the formal syntax of the extension statement.

6.1. The extension's status Statement

The extension's status statement, which must be present, gets one argument that says whether this extension definition is current or historic. The value current means the definition is current and valid. The value obsolete means the definition is obsolete, so it shouldn't be implemented, and it can be removed if it was implemented before. The value deprecated also marks an obsolete definition, but it still allows new or continued implementation so that things keep working with older, existing implementations 🫢

6.2. The extension's description Statement

The extension's description statement, which must be present, gets one argument that gives a high-level textual description of the extension statement.

It is RECOMMENDED that information on the extension's context, its semantics, and implementation conditions be included. See also Section 11 πŸ’…

6.3. The extension's reference Statement

The extension's reference statement, which need not be present, gets one argument that's a textual cross-reference to some other document. That could be another module with related extension definitions, or any other document with extra info about this extension πŸ“š

6.4. The extension's abnf Statement

The extension's abnf statement, which need not be present, gets one argument that gives a formal ABNF [RFC2234] grammar definition of the extension. The grammar can reference rule names from the core SMIng grammar (Appendix B) 🌈

Note that the abnf statement should contain only pure ABNF and no additional text. Comments prefixed by a semicolon are allowed, but they should probably be moved to the description statement. Also, double quotes within the ABNF grammar have to be represented as \" according to Section 4.2 πŸŽ€

6.5. Usage Example πŸ“

extension severity {
  status  current;
  description
         "The optional severity extension statement can only
          be applied to the statement block of an SMIng class'
          event definition. If it is present it denotes the
          severity level of the event in a range from 0
          (emergency) to 7 (debug).";
  abnf
         "severityStatement = severityKeyword sep number optsep \";\"
          severityKeyword   = \"severity\"";
};

7. The typedef Statement πŸ¦‹

the typedef statement is how you define new data types for the local module or for external modules to use πŸ’– It gets two arguments: an upper-case type identifier, and a statement block that holds detailed type info in a fixed order.

Type identifiers SHOULD NOT consist of all upper-case characters, and they SHOULD NOT contain hyphens πŸ™…β€β™€οΈ

See the typedefStatement rule of the SMIng grammar (Appendix B) for the formal syntax of the typedef statement.

7.1. The typedef's type Statement

The typedef's type statement, which must be present, gets one argument that says which type this new type is derived from. You can optionally restrict the new type by adding subtyping info, following the rules of the base type. See Section 3 for SMIng base types and their type restrictions 🧁

7.2. The typedef's default Statement

The typedef's default statement, which need not be present, gets one argument that gives an acceptable default value for attributes of this type. A default value may be used when an attribute instance gets created. Basically, the value is a "hint" for implementors πŸ’‘

The value of the default statement must, of course, correspond to the (probably restricted) type given in the typedef's type statement.

The default value of a type may be overwritten by a default value of an attribute of that type.

Note that for some types, default values make no sense πŸ€·β€β™€οΈ

7.3. The typedef's format Statement

The typedef's format statement, which need not be present, gets one argument that gives a hint about how the value of an instance of an attribute of this type might be displayed. See Section 3.13 for a description of format specifications πŸ‘€

If no format is specified, it's inherited from the type given in the type statement. On the other hand, the format specification of a type may be semantically refined by a format specification of an attribute of that type ✨

7.4. The typedef's units Statement

The typedef's units statement, which need not be present, gets one argument that gives a textual definition of the units associated with attributes of this type πŸ“

If no units are specified, they're inherited from the type given in the type statement. On the other hand, the units specification of a type may be semantically refined by a units specification of an attribute of that type.

The units specification has to be appropriate for values displayed according to the typedef's format specification, if one is present. For example, say the type defines frequency values of type Unsigned64 measured in thousands of Hertz. Then the format specification should be d-3, and the units specification should be Hertz or Hz. If the format specification were omitted, the units specification should be Milli-Hertz or mHz. Authors of SMIng modules should pay attention to keep format and units specifications in sync 🎢 Application implementors MUST NOT implement units specifications without implementing format specifications. No exceptions, bestie πŸ™…β€β™€οΈ

7.5. The typedef's status Statement

The typedef's status statement, which must be present, gets one argument that says whether this type definition is current or historic. The value current means the definition is current and valid. The value obsolete means the definition is obsolete, so it shouldn't be implemented, and it can be removed if it was implemented before. The value deprecated also marks an obsolete definition, but it still allows new or continued implementation so that things keep working with older, existing implementations 🌷

Derived types SHOULD NOT be defined as current if their underlying type is deprecated or obsolete. Similarly, they SHOULD NOT be defined as deprecated if their underlying type is obsolete. Nevertheless, subsequent revisions of the underlying type can't be avoided, but they SHOULD be taken into account in subsequent revisions of the local module πŸ”₯

7.6. The typedef's description Statement

The typedef's description statement, which must be present, gets one argument that gives a high-level textual description of the newly defined type.

It is RECOMMENDED that all semantic definitions needed for implementation be included, along with anything that would otherwise be communicated in commentary annotations attached to this type definition 🧠

7.7. The typedef's reference Statement

The typedef's reference statement, which need not be present, gets one argument that's a textual cross-reference to some other document. That could be another module that defines related type definitions, or any other document that gives extra info about this type definition πŸ¦‹

7.8. Usage Examples ✨

okay bestie, here are the typedef examples πŸ’… Look at how RptrOperStatus is built, with its enum values and a default of other. πŸ“

typedef RptrOperStatus {
  type            Enumeration (other(1), ok(2), rptrFailure(3),
                               groupFailure(4), portFailure(5),
                               generalFailure(6));
  default         other;       // undefined by default.
  status          deprecated;
  description
          "A type to indicate the operational state
           of a repeater.";
  reference
          "[IEEE 802.3 Mgt], 30.4.1.1.5, aRepeaterHealthState.";
};

typedef SnmpTransportDomain {
  type            Pointer (snmpTransportDomain);
  status          current;
  description
          "A pointer to an SNMP transport domain identity.";
};

typedef DateAndTime {
  type            OctetString (8 | 11);
  format          "2d-1d-1d,1d:1d:1d.1d,1a1d:1d";
  status          current;
  description
          "A date-time specification.
           ...

           Note that if only local time is known, then timezone
           information (fields 8-10) is not present.";
  reference
          "RFC 2579, SNMPv2-TC.DateAndTime.";
};

typedef Frequency {
  type            Unsigned64;
  format          "d-3"
  units           "Hertz";
  status          current;
  description
          "A wide-range frequency specification measured
           in thousands of Hertz.";
};

8. The identity Statement ✨

okay so the `identity' statement is how you make a brand-new abstract and untyped identity πŸ¦‹ Its whole job is to say what it's called, what it means, and that it exists. You can build one from scratch, or you can derive it from a parent identity. πŸ’–

The `identity' statement takes two arguments. The first is a lower-case identity identifier. The second is a statement block with detailed identity info, and that info has to come in a fixed, required order. πŸŽ€

For the formal syntax, check the `identityStatement' rule of the SMIng grammar (Appendix B). It's giving precise 🫢

8.1. The identity's parent Statement

The `parent' statement is a must for a derived identity, and it must be left out for an identity defined from scratch. It takes one argument, which names the parent identity this one shall be derived from. 🌸

8.2. The identity's status Statement

The status' statement is required, and it takes one argument that says whether the identity definition is current or historic. ✨ current' means the definition is valid and up to date. obsolete' means the definition is out of date and should not be implemented, and it can be removed if it was implemented before. deprecated' is also an obsolete definition, but it still allows new or continued implementation so it can keep working with older, existing implementations. It's a whole vibe πŸ’…

Derived identities SHOULD NOT be defined as current' if their parent identity is deprecated' or obsolete'. Same energy here: they SHOULD NOT be defined as deprecated' if their parent identity is `obsolete'. Nevertheless, changes to the parent identity can't be avoided, but they SHOULD be taken into account in later revisions of the local module. πŸ”₯

8.3. The identity' description Statement

The `description' statement is required and takes one argument, which is a high-level text description of the new identity. πŸ“

It is RECOMMENDED that all the semantic definitions needed for implementation get included, along with any info that would otherwise have gone in commentary annotations attached to this identity definition. Don't leave your bestie guessing 🫢

8.4. The identity's reference Statement

The `reference' statement doesn't have to be there. It takes one argument, which is a text cross-reference to another document. That could be another module with related identity definitions, or any other document with more info on this identity. 🌈

8.5. Usage Examples

identity null {
  status  current;
  description
          "An identity used to represent null pointer values.";
};

identity snmpTransportDomain {
  status  current;
  description
          "A generic SNMP transport domain identity.";
};

identity snmpUDPDomain {
  parent  snmpTransportDomain;
  status  current;
  description
          "The SNMP over UDP transport domain.";
};

9. The class Statement ✨

the `class' statement is how you define a new class, which is basically a container for related attributes and events (see Section 9.2 and Section 9.4) 🧚 A class can be built from scratch or derived from a parent class. A derived class inherits all of the parent's attributes and events, and it can add more of its own. Main character energy πŸ‘‘

The `class' statement takes two arguments. The first is an upper-case class identifier. The second is a statement block with detailed class info, which has to come in a fixed, required order. πŸŽ€

For the formal syntax, check the `classStatement' rule of the SMIng grammar (Appendix B). πŸ’œ

9.1. The class' extends Statement

The `extends' statement is a must for a class derived from a parent class, and it must be left out for a class defined from scratch. It takes one argument, which names the parent class this class shall be derived from. πŸ¦‹

9.2. The class' attribute Statement

The `attribute' statement can show up zero, one, or multiple times. It takes two arguments: the attribute name, and a statement block with detailed attribute info that has to come in a fixed, required order. 🌸

9.2.1. The attribute's type Statement

The `type' statement is required. It takes at least one argument that says what type the attribute is, either a type name or a class name. If it's a type name, a second argument can restrict it, following the restriction rules in Section 3. ✨

9.2.2. The attribute's access Statement

The `access' statement is required for attributes typed by a base type or a derived type, and it must be left out for attributes typed by a class. It takes one argument that says whether it makes sense to read and/or write an instance of the attribute, or to include its value in an event. This is the maximum level of access, and it doesn't depend on any administrative authorization policy. πŸ”

readwrite' means read and write both make sense. readonly' means read makes sense, but write is never possible. `eventonly' means the object can only be reached through an event. πŸ’…

These values are ordered from least to greatest access level: eventonly', readonly', `readwrite'. 🌈

9.2.3. The attribute's default Statement

The `default' statement doesn't have to be there for attributes typed by a base type or a derived type, and it must be left out for attributes typed by a class. It takes one argument, which is an acceptable default value for the attribute. A default can be used when an attribute instance gets created, so it's basically a "hint" for implementors πŸ“

The value in the default' statement must, of course, match the type from the attribute's type' statement, which might be restricted. No mismatches allowed, bestie πŸ™ˆ

If both are present, the attribute's default overrides the default of the underlying type definition. πŸ‘‘

9.2.4. The attribute's format Statement

The `format' statement doesn't have to be there for attributes typed by a base type or a derived type, and it must be left out for attributes typed by a class. It takes one argument that gives a hint about how an instance's value might be displayed. Check Section 3.13 for the details on format specifications. πŸŽ€

If both are present, the attribute's format specification overrides the format specification of the underlying type definition. πŸ’–

9.2.5. The attribute's units Statement

The `units' statement doesn't have to be there for attributes typed by a base type or a derived type, and it must be left out for attributes typed by a class. It takes one argument, which is a text definition of the units for this attribute. πŸ“

If both are present, the attribute's units specification overrides the units specification of the underlying type definition. πŸ¦‹

The units specification has to match the values displayed under the attribute's format specification, if one is present. For example, say the attribute is a frequency value of type Unsigned64 measured in thousands of Hertz. Then the format specification should be d-3', and the units specification should be Hertz' or Hz'. If the format specification were left out, the units specification should be Milli-Hertz' or `mHz'. Authors of SMIng modules should keep the format and units specs of type and attribute definitions in sync, because nobody likes a mismatch 😭 Application implementors MUST NOT implement units specifications without implementing format specifications. This one's a hard rule, no cap πŸ”₯

9.2.6. The attribute's status Statement

The status' statement must be there. It takes one argument that says whether the attribute definition is current or historic. current' means the definition is valid. obsolete' means it's out of date and should not be implemented, and it can be removed if it was implemented before. deprecated' is also obsolete, but it still allows new or continued implementation so it works with older, existing implementations. πŸ’€

Attributes SHOULD NOT be defined as current' if their type or their containing class is deprecated' or obsolete'. Similarly, they SHOULD NOT be defined as deprecated' if their type or their containing class is `obsolete'. Nevertheless, changes to the type definition they use can't be avoided, but they SHOULD be taken into account in later revisions of the local module ✨

9.2.7. The attribute's description Statement

The `description' statement is required and takes one argument, which is a high-level text description of this attribute. πŸ“

It is RECOMMENDED that all the semantic definitions needed to implement this attribute get included. Future you will thank present you 🫢

9.2.8. The attribute's reference Statement

The `reference' statement doesn't have to be there. It takes one argument, which is a text cross-reference to another document. That could be another module with related attribute definitions, or any other document with more info on this attribute definition. 🌸

9.3. The class' unique Statement πŸ”‘

The class' `unique' statement is optional bestie, so it doesn't have to show up. When it does, it takes one argument: a comma-separated list of attributes of this class, wrapped in parenthesis. If you include it, that list becomes the unique ID for every possible instance of the class, and underlying protocols can use it as a unique key πŸ—οΈ.

If the list is empty, the class should be regarded as a scalar class with only a single instance ✨.

If the `unique' statement isn't there at all, the class isn't meant to be instantiated directly. Instead it gets contained in other classes, or it's the parent class of other refining classes 🧩.

If the statement is present, the attribute list MUST NOT contain any attribute more than once, and the attributes should be ordered where that makes sense, so the ones that matter most in most situations come first πŸ’….

9.4. The class' event Statement πŸ””

The class' `event' statement defines an event related to an instance of this class that can happen asynchronously, so it's giving "surprise, something happened" 😭. It takes two arguments: a lower-case event identifier, and a statement block that holds detailed info in a required order πŸŽ€.

See the eventStatement' rule of the SMIng grammar (Appendix B) for the formal syntax of the event' statement πŸ“œ.

9.4.1. The event's status Statement 🚦

The event's status' statement must be present. It takes one argument that says whether this event definition is current or historic. The value current' means the definition is current and valid βœ…. The value obsolete' means the definition is obsolete, so it should not be implemented, and it can be removed if it was implemented before πŸ—‘οΈ. The value deprecated' also means obsolete, but it still permits new or continued implementation so it can keep playing nice with older, existing implementations 🫢.

9.4.2. The event's description Statement πŸ“

The event's `description' statement must be present. It takes one argument that's a high-level textual description of this event πŸ’œ.

It is RECOMMENDED that all semantic definitions needed to implement this event be included 🌸. In particular, which instance of the class is associated with an event of this type SHOULD be documented 🧁.

9.4.3. The event's reference Statement πŸ”—

The event's `reference' statement need not be present. It takes one argument: a textual cross-reference to some other document. That document could be another module with related event definitions, or any other doc with extra info about this event definition πŸ“š.

9.5. The class' status Statement 🚦

The class' status' statement must be present. It takes one argument that says whether this class definition is current or historic. The value current' means the definition is current and valid ✨. The value obsolete' means it's obsolete, so it should not be implemented, and it can be removed if it was implemented before πŸ₯€. The value deprecated' also means obsolete, but it still permits new or continued implementation to keep interoperability going with older, existing implementations πŸ•°οΈ.

Derived classes SHOULD NOT be defined as current' if their parent class is deprecated' or obsolete' πŸ™…β€β™€οΈ. Similarly, they SHOULD NOT be defined as deprecated' if their parent class is `obsolete' πŸ™ˆ. Nevertheless, later revisions of the parent class can't be avoided, but they SHOULD be taken into account in later revisions of the local module πŸ”„.

9.6. The class' description Statement πŸ“–

The class' `description' statement must be present. It takes one argument that's a high-level textual description of the newly defined class 🌈.

It is RECOMMENDED that all semantic definitions needed for implementation be included, along with anything that would otherwise be shared in commentary annotations attached to this class definition πŸ’–.

9.7. The class' reference Statement πŸ”—

The class' `reference' statement need not be present. It takes one argument: a textual cross-reference to some other document. That could be another module that defines related class definitions, or some other doc with extra info relevant to this class definition πŸ¦‹.

9.8. Usage Example 🌷

Consider how an event might be described that signals a status change of an interface πŸ‘€:

class Interface {
  // ...
  attribute speed {
    type        Gauge32;
    access      readonly;
    units       "bps";
    status      current;
    description
         "An estimate of the interface's current bandwidth
          in bits per second.";
  };
  // ...
  attribute adminStatus {
    type        AdminStatus;
    access      readwrite;
    status      current;
    description
         "The desired state of the interface.";
  };
  attribute operStatus {
    type        OperStatus;
    access      readonly;
    status      current;
    description
         "The current operational state of the interface.";
  };

  event linkDown {
    status      current;
    description
            "A linkDown event signifies that the ifOperStatus
             attribute for this interface instance is about to
             enter the down state from some other state (but not
             from the notPresent state).  This other state is
             indicated by the included value of ifOperStatus.";

  };

  status        current;
  description
            "A physical or logical network interface.";

};

10. Extending a Module 🌟

As experience is gained with a module, it may be desirable to revise that module 🧠. However, changes aren't allowed if they could cause interoperability problems between an implementation using the original specification and an implementation using the updated specification(s) 🚫.

For any change, some statements near the top of the module MUST be updated to include information about the revision. Specifically, a new revision' statement (Section 5.6) must be included in front of the revision' statements πŸ“…. Furthermore, any necessary changes MUST be applied to other statements, including the organization' and contact' statements (Section 5.2, Section 5.3) 🏷️.

Note that any definition in a module can be imported by any other module, and it's referenced in an import' statement by the module name. So a module name MUST NOT be changed. Specifically, the module name (e.g., ACME-MIB' in the example of Section 5.7) MUST NOT be changed when revising a module, except to fix typos, and definitions MUST NOT be moved from one module to another 🚚.

Also note that obsolete definitions MUST NOT be removed from modules, since their identifiers may still be referenced by other modules πŸͺ¦.

A definition may be revised in any of the following ways 🎨:

  • In typedef' statement blocks, a type' statement containing an Enumeration' or Bits' type may have new named numbers added πŸ”’.
  • In typedef' statement blocks, the value of a type' statement may be replaced by another type if the new type is derived (directly or indirectly) from the same base type, has the same set of values, and has identical semantics πŸ”.
  • In attribute' statements where the type' sub-statement specifies a class, the class may be replaced by another class if the new class is derived (directly or indirectly) from the base class and both classes have identical semantics 🧬.
  • In attribute' statements where the type' sub-statement specifies a base type, a defined type, or an implicitly derived type (i.e., not a class), that type may be replaced by another type if the new type is derived (directly or indirectly) from the same base type, has the same set of values, and has identical semantics πŸͺ„.
  • In any statement block, a status' statement value of current' may be revised as deprecated' or obsolete'. Similarly, a status' statement value of deprecated' may be revised as obsolete'. When making such a change, the description' statement SHOULD be updated to explain the rationale πŸ“£.
  • In typedef' and attribute' statement blocks, a `default' statement may be added or updated 🎯.
  • In typedef' and attribute' statement blocks, a `units' statement may be added πŸ“.
  • A class may be augmented by adding new attributes βž•.
  • In any statement block, clarifications and additional information may be included in the `description' statement πŸ’¬.
  • In any statement block, a `reference' statement may be added or updated πŸ”—.
  • Entirely new extensions, types, identities, and classes may be defined, using previously unassigned identifiers πŸ†•.

Otherwise, if the semantics of any previous definition are changed (i.e., if a non-editorial change is made to any definition other than those specifically allowed above), then this MUST be achieved by a new definition with a new identifier 🚨. In the case of a class where the semantics of any attributes are changed, the new class can be defined by derivation from the old class, refining the changed attributes 🧁.

Note that changing the identifier of an existing definition is considered a semantic change, since these strings may be used in an `import' statement πŸ’….

11. SMIng Language Extensibility ✨

okay so the core SMIng language has a really clean set of statements (Section 5 through Section 9.4) for all the stuff people agree is needed without any management protocol specific info πŸ’… but ngl people always wanna express MORE. Writing extra details in description statements is kinda meh bc a program can't parse any of it 😭

SMIng lets modules include statements that a parser doesn't recognise but that still fit the core grammar rules (Section 4.3). You can also make brand new statements with the `extension' statement (Section 6) 🧚 Extensions can be used in the local module or in other modules that import the extension. Here's why that's iconic:

  • A parser can tell extensions apart from unknown statements, so it can complain about unknown statements, like typos πŸ™ˆ
  • If an extension's definition has a formal ABNF grammar and the parser can read that ABNF, the parser can also complain when someone uses an extension wrong πŸ”₯
  • Since lots of orgs might want the same kinda extension, name collisions are pretty likely as long as there's no standardized extension for that purpose. Having to explicitly import extension statements means you can tell them apart πŸ’–
  • The supported extensions of an SMIng implementation, e.g., an SMIng module compiler, can be clearly expressed 🌸

The only formal effect of defining an extension statement is declaring that it exists and what its status is, plus optionally its ABNF grammar. All the other stuff SHOULD be described in the `description' statement:

  • The detailed semantics of the new statement SHOULD be described πŸŽ€
  • The contexts where the new statement can be used SHOULD be described, e.g., a new statement may be designed to be used only in the statement block of a module, but not in other nested statement blocks. Others may be applicable in multiple contexts. Also, the point in the sequence of an obligatory order of other statements where the new statement may be inserted might be prescribed πŸ‘‘
  • The circumstances that make the new statement mandatory or optional SHOULD be described πŸ¦‹
  • The syntax of the new statement SHOULD at least be described informally, if not supplied formally in an `abnf' statement ✨
  • It might be reasonable to give some suggestions about the conditions under which the implementation of the new statement is adequate and how it could be integrated into existing implementations πŸ“

Some possible extension applications are:

  • The formal mapping of SMIng definitions into the SNMP [RFC3781] framework is defined as an SMIng extension. Other mappings may follow in the future 🌈
  • Inlined annotations to definitions. For example, a vendor may wish to describe additional information to class and attribute definitions in private modules. An example is severity levels of events in the statement block of an `event' statement πŸ’œ
  • Arbitrary annotations to external definitions. For example, a vendor may wish to describe additional information to definitions in a "standard" module. This lets a vendor implement "standard" modules as well as additional private features, without redundant module definitions, but on top of "standard" module definitions 🫢

12. Security Considerations πŸ”’

okay this part is short but it's a whole vibe. This document defines a language for writing and reading descriptions of management information. The language itself has no security impact on the Internet πŸ₯Ή no notes, just a clean slate for the whole Internet 🌍✨

13. Acknowledgements πŸ™Œ

SMIng started out as a close successor to SMIv2, so some paragraphs and phrases were directly taken from the SMIv2 specifications [RFC2578], [RFC2579], [RFC2580], written by Jeff Case, Keith McCloghrie, David Perkins, Marshall T. Rose, Juergen Schoenwaelder, and Steven L. Waldbusser. Real OGs, honestly πŸ’–

The authors would like to thank everyone who took part in the 7th NMRG meeting, held in Schloss Kleinheubach from 6-8 September 2000. That meeting was a major step toward where this memo is now, and the people who made it happen were Heiko Dassow, David Durham, Keith McCloghrie, and Bert Wijnen πŸŽ€

Furthermore, several discussions within the SMING Working Group reflected experience with SMIv2 and influenced this specification at some points. The group chat was clearly lively πŸ“

14. References πŸ“š

14.1. Normative References

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119, March 1997.

[RFC2234]  Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
              Specifications: ABNF", RFC 2234, November 1997.

14.2. Informative References

[RFC3216]  Elliott, C., Harrington, D., Jason, J., Schoenwaelder, J.,
              Strauss, F. and W. Weiss, "SMIng Objectives", RFC 3216,
              December 2001.

[RFC3781]  Strauss, F. and J. Schoenwaelder, "Next Generation
              Structure of Management Information (SMIng) Mappings to
              the Simple Network Management Protocol (SNMP)", RFC 3781,
              May 2004.

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

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

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

[RFC3159]  McCloghrie, K., Fine, M., Seligson, J., Chan, K., Hahn,
              S., Sahita, R., Smith, A. and F. Reichmeyer, "Structure of
              Policy Provisioning Information (SPPI)", RFC 3159, August
              2001.

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

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

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

[ASN1]     International Organization for Standardization,
              "Specification of Abstract Syntax Notation One (ASN.1)",
              International Standard 8824, December 1987.

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

[IEEE754]  Institute of Electrical and Electronics Engineers, "IEEE
              Standard for Binary Floating-Point Arithmetic", ANSI/IEEE
              Standard 754-1985, August 1985.

[RFC3629]  Yergeau, F., "UTF-8, a transformation format of ISO
              10646", STD 63, RFC 3629, November 2003.

[RFC3084]  Chan, K., Seligson, J., Durham, D., Gai, S., McCloghrie,
              K., Herzog, S., Reichmeyer, F., Yavatkar, R. and A. Smith,
              "COPS Usage for Policy Provisioning", RFC 3084, March
              2001.

Appendix A. NMRG-SMING Module 🧁

most SMIng modules are built on top of some commonly used derived types, and those are defined in the NMRG-SMING module below πŸ’… these types are pretty generally useful for modelling all areas of management information. Among them are counter types, string types, and date and time related types ✨

This module is derived from RFC 2578 [RFC2578] and RFC 2579 [RFC2579].

module NMRG-SMING {

    organization    "IRTF Network Management Research Group (NMRG)";

    contact         "IRTF Network Management Research Group (NMRG)
                     http://www.ibr.cs.tu-bs.de/projects/nmrg/

                     Frank Strauss
                     TU Braunschweig
                     Muehlenpfordtstrasse 23
                     38106 Braunschweig
                     Germany
                     Phone: +49 531 391 3266
                     EMail: strauss@ibr.cs.tu-bs.de

                     Juergen Schoenwaelder
                     International University Bremen
                     P.O. Box 750 561
                     28725 Bremen
                     Germany
                     Phone: +49 421 200 3587
                     EMail: j.schoenwaelder@iu-bremen.de";

    description     "Core type definitions for SMIng. Several
                     type definitions are SMIng versions of
                     similar SMIv2 or SPPI definitions.

                     Copyright (C) The Internet Society (2004).
                     All Rights Reserved.
                     This version of this module is part of
                     RFC 3780, see the RFC itself for full
                     legal notices.";

    revision {
        date        "2003-12-16";
        description "Initial revision, published as RFC 3780.";
    };

    typedef Gauge32 {
        type        Unsigned32;
        description
           "The Gauge32 type represents a non-negative integer,
            which may increase or decrease, but shall never
            exceed a maximum value, nor fall below a minimum
            value.  The maximum value can not be greater than
            2^32-1 (4294967295 decimal), and the minimum value
            can not be smaller than 0.  The value of a Gauge32
            has its maximum value whenever the information
            being modeled is greater than or equal to its
            maximum value, and has its minimum value whenever
            the information being modeled is smaller than or
            equal to its minimum value.  If the information
            being modeled subsequently decreases below
            (increases above) the maximum (minimum) value, the
            Gauge32 also decreases (increases).";
        reference
           "RFC 2578, Sections 2. and 7.1.7.";
    };

    typedef Counter32 {
        type        Unsigned32;
        description
           "The Counter32 type represents a non-negative integer
            which monotonically increases until it reaches a
            maximum value of 2^32-1 (4294967295 decimal), when it
            wraps around and starts increasing again from zero.

            Counters have no defined `initial' value, and thus, a
            single value of a Counter has (in general) no information
            content.  Discontinuities in the monotonically increasing
            value normally occur at re-initialization of the
            management system, and at other times as specified in the
            description of an attribute using this type.  If such
            other times can occur, for example, the creation of a
            class instance that contains an attribute of type
            Counter32 at times other than re-initialization, then a
            corresponding attribute should be defined, with an
            appropriate type, to indicate the last discontinuity.
            Examples of appropriate types include: TimeStamp32,
            TimeStamp64, DateAndTime, TimeTicks32 or TimeTicks64
            (other types defined in this module).

            The value of the access statement for attributes with
            a type value of Counter32 should be either `readonly'
            or `eventonly'.
            A default statement should not be used for attributes
            with a type value of Counter32.";
        reference
           "RFC 2578, Sections 2. and 7.1.6.";
    };

    typedef Gauge64 {
        type        Unsigned64;
        description
           "The Gauge64 type represents a non-negative integer,
            which may increase or decrease, but shall never
            exceed a maximum value, nor fall below a minimum
            value.  The maximum value can not be greater than
            2^64-1 (18446744073709551615), and the minimum value
            can not be smaller than 0.  The value of a Gauge64
            has its maximum value whenever the information
            being modeled is greater than or equal to its
            maximum value, and has its minimum value whenever
            the information being modeled is smaller than or
            equal to its minimum value.  If the information
            being modeled subsequently decreases below
            (increases above) the maximum (minimum) value, the
            Gauge64 also decreases (increases).";
    };

    typedef Counter64 {
        type        Unsigned64;
        description
           "The Counter64 type represents a non-negative integer
            which monotonically increases until it reaches a
            maximum value of 2^64-1 (18446744073709551615), when
            it wraps around and starts increasing again from zero.

            Counters have no defined `initial' value, and thus, a
            single value of a Counter has (in general) no
            information content.  Discontinuities in the
            monotonically increasing value normally occur at
            re-initialization of the management system, and at
            other times as specified in the description of an
            attribute using this type.  If such other times can
            occur, for example, the creation of a class
            instance that contains an attribute of type Counter32
            at times other than re-initialization, then
            a corresponding attribute should be defined, with an
            appropriate type, to indicate the last discontinuity.
            Examples of appropriate types include: TimeStamp32,
            TimeStamp64, DateAndTime, TimeTicks32 or TimeTicks64
            (other types defined in this module).

            The value of the access statement for attributes with
            a type value of Counter64 should be either `readonly'
            or `eventonly'.

            A default statement should not be used for attributes
            with a type value of Counter64.";
        reference
           "RFC 2578, Sections 2. and 7.1.10.";
    };

    typedef Opaque {
        type        OctetString;
        status      obsolete;
        description
           "******* THIS TYPE DEFINITION IS OBSOLETE *******

            The Opaque type is provided solely for
            backward-compatibility, and shall not be used for
            newly-defined attributes and derived types.

            The Opaque type supports the capability to pass
            arbitrary ASN.1 syntax.  A value is encoded using
            the ASN.1 Basic Encoding Rules into a string of
            octets.  This, in turn, is encoded as an
            OctetString, in effect `double-wrapping' the
            original ASN.1 value.

            Note that a conforming implementation need only be
            able to accept and recognize opaquely-encoded data.
            It need not be able to unwrap the data and then
            interpret its contents.

            A requirement on `standard' modules is that no
            attribute may have a type value of Opaque and no
            type may be derived from the Opaque type.";
        reference
           "RFC 2578, Sections 2. and 7.1.9.";
    };

    typedef IpAddress {
        type        OctetString (4);
        status      deprecated;
        description

           "******* THIS TYPE DEFINITION IS DEPRECATED *******

            The IpAddress type represents a 32-bit Internet
            IPv4 address.  It is represented as an OctetString
            of length 4, in network byte-order.

            Note that the IpAddress type is present for
            historical reasons.";
        reference
           "RFC 2578, Sections 2. and 7.1.5.";
    };

    typedef TimeTicks32 {
        type        Unsigned32;
        description
           "The TimeTicks32 type represents a non-negative integer
            which represents the time, modulo 2^32 (4294967296
            decimal), in hundredths of a second between two epochs.
            When attributes are defined which use this type, the
            description of the attribute identifies both of the
            reference epochs.

            For example, the TimeStamp32 type (defined in this
            module) is based on the TimeTicks32 type.";
        reference
           "RFC 2578, Sections 2. and 7.1.8.";
    };

    typedef TimeTicks64 {
        type        Unsigned64;
        description
           "The TimeTicks64 type represents a non-negative integer
            which represents the time, modulo 2^64
            (18446744073709551616 decimal), in hundredths of a second
            between two epochs.  When attributes are defined which use
            this type, the description of the attribute identifies
            both of the reference epochs.

            For example, the TimeStamp64 type (defined in this
            module) is based on the TimeTicks64 type.";
    };

    typedef TimeStamp32 {
        type        TimeTicks32;
        description
           "The value of an associated TimeTicks32 attribute at
            which a specific occurrence happened.  The specific
            occurrence must be defined in the description of any
            attribute defined using this type.  When the specific
            occurrence occurred prior to the last time the
            associated TimeTicks32 attribute was zero, then the
            TimeStamp32 value is zero.  Note that this requires all
            TimeStamp32 values to be reset to zero when the value of
            the associated TimeTicks32 attribute reaches 497+ days
            and wraps around to zero.

            The associated TimeTicks32 attribute should be specified
            in the description of any attribute using this type.
            If no TimeTicks32 attribute has been specified, the
            default scalar attribute sysUpTime is used.";
        reference
           "RFC 2579, Section 2.";
    };

    typedef TimeStamp64 {
        type        TimeTicks64;
        description
           "The value of an associated TimeTicks64 attribute at which
            a specific occurrence happened.  The specific occurrence
            must be defined in the description of any attribute
            defined using this type.  When the specific occurrence
            occurred prior to the last time the associated TimeTicks64
            attribute was zero, then the TimeStamp64 value is zero.
            The associated TimeTicks64 attribute must be specified in
            the description of any attribute using this
            type. TimeTicks32 attributes must not be used as
            associated attributes.";
    };

    typedef TimeInterval32 {
        type        Integer32 (0..2147483647);
        description
           "A period of time, measured in units of 0.01 seconds.

            The TimeInterval32 type uses Integer32 rather than
            Unsigned32 for compatibility with RFC 2579.";
        reference
           "RFC 2579, Section 2.";
    };

    typedef TimeInterval64 {
        type        Integer64;
        description
           "A period of time, measured in units of 0.01 seconds.
            Note that negative values are allowed.";
    };
    typedef DateAndTime {
        type        OctetString (8 | 11);
        default     0x0000000000000000000000;
        format      "2d-1d-1d,1d:1d:1d.1d,1a1d:1d";
        description
           "A date-time specification.

            field  octets  contents                  range
            -----  ------  --------                  -----
             1      1-2   year*                     0..65535
             2       3    month                     1..12 | 0
             3       4    day                       1..31 | 0
             4       5    hour                      0..23
             5       6    minutes                   0..59
             6       7    seconds                   0..60
                          (use 60 for leap-second)
             7       8    deci-seconds              0..9
             8       9    direction from UTC        '+' / '-'
             9      10    hours from UTC*           0..13
            10      11    minutes from UTC          0..59

            * Notes:
            - the value of year is in big-endian encoding
            - daylight saving time in New Zealand is +13

            For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would
            be displayed as:

                         1992-5-26,13:30:15.0,-4:0

            Note that if only local time is known, then timezone
            information (fields 8-10) is not present.

            The two special values of 8 or 11 zero bytes denote an
            unknown date-time specification.";
        reference
           "RFC 2579, Section 2.";
    };

    typedef TruthValue {
        type        Enumeration (true(1), false(2));
        description
           "Represents a boolean value.";
        reference
           "RFC 2579, Section 2.";
    };

    typedef PhysAddress {

        type        OctetString;
        format      "1x:";
        description
           "Represents media- or physical-level addresses.";
        reference
           "RFC 2579, Section 2.";
    };

    typedef MacAddress {
        type        OctetString (6);
        format      "1x:";
        description
           "Represents an IEEE 802 MAC address represented in the
            `canonical' order defined by IEEE 802.1a, i.e., as if it
            were transmitted least significant bit first, even though
            802.5 (in contrast to other 802.x protocols) requires MAC
            addresses to be transmitted most significant bit first.";
        reference
           "RFC 2579, Section 2.";
    };

    // The DisplayString definition below does not impose a size
    // restriction and is thus not the same as the DisplayString
    // definition in RFC 2579. The DisplayString255 definition is
    // provided for mapping purposes.

    typedef DisplayString {
        type        OctetString;
        format      "1a";
        description
           "Represents textual information taken from the NVT ASCII
            character set, as defined in pages 4, 10-11 of RFC 854.

            To summarize RFC 854, the NVT ASCII repertoire specifies:

             - the use of character codes 0-127 (decimal)

             - the graphics characters (32-126) are interpreted as
               US ASCII

             - NUL, LF, CR, BEL, BS, HT, VT and FF have the special
               meanings specified in RFC 854

             - the other 25 codes have no standard interpretation

             - the sequence 'CR LF' means newline

             - the sequence 'CR NUL' means carriage-return
             - an 'LF' not preceded by a 'CR' means moving to the
               same column on the next line.

             - the sequence 'CR x' for any x other than LF or NUL is
               illegal.  (Note that this also means that a string may
               end with either 'CR LF' or 'CR NUL', but not with CR.)
        ";
    };

    typedef DisplayString255 {
        type        DisplayString (0..255);
        description
           "A DisplayString with a maximum length of 255 characters.
            Any attribute defined using this syntax may not exceed 255
            characters in length.
ok bestie, the vibe here is Gauge64 is giving "I can go up, I can go down, but I will NEVER leave my min and max zone" πŸŽ€βœ¨
            The DisplayString255 type has the same semantics as the
            DisplayString textual convention defined in RFC 2579.";
        reference
           "RFC 2579, Section 2.";
    };

    // The Utf8String and Utf8String255 definitions below facilitate
    // internationalization. The definition is consistent with the
    // definition of SnmpAdminString in RFC 2571.

    typedef Utf8String {
        type        OctetString;
        format      "65535t";      // is there a better way ?
        description
           "A human readable string represented using the ISO/IEC IS
            10646-1 character set, encoded as an octet string using
            the UTF-8 transformation format described in RFC 3629.

            Since additional code points are added by amendments to
            the 10646 standard from time to time, implementations must
            be prepared to encounter any code point from 0x00000000 to
            0x7fffffff.  Byte sequences that do not correspond to the
            valid UTF-8 encoding of a code point or are outside this
            range are prohibited.

            The use of control codes should be avoided. When it is
            necessary to represent a newline, the control code
            sequence CR LF should be used.

            The use of leading or trailing white space should be
            avoided.

            For code points not directly supported by user interface
            hardware or software, an alternative means of entry and
            display, such as hexadecimal, may be provided.

            For information encoded in 7-bit US-ASCII, the UTF-8
            encoding is identical to the US-ASCII encoding.

            UTF-8 may require multiple bytes to represent a single
            character / code point; thus the length of a Utf8String in
            octets may be different from the number of characters
            encoded.  Similarly, size constraints refer to the number
            of encoded octets, not the number of characters
            represented by an encoding.";
    };

    typedef Utf8String255 {
        type        Utf8String (0..255);
        format      "255t";
        description
           "A Utf8String with a maximum length of 255 octets.  Note
            that the size of an Utf8String is measured in octets, not
            characters.";
    };

    identity null {
        description
           "An identity used to represent null pointer values.";
    };

};

Appendix B. SMIng ABNF Grammar 🧚

Okay bestie, the SMIng grammar is written in Augmented Backus-Naur Form (ABNF) [RFC2234], so it's super neat and tidy. Here's the whole thing, copied exactly as it's supposed to be πŸ’…βœ¨

   The SMIng grammar conforms to the Augmented Backus-Naur Form (ABNF)
   [RFC2234].

;;
;; sming.abnf -- SMIng grammar in ABNF notation (RFC 2234).
;;
;; @(#) $Id: sming.abnf,v 1.33 2003/10/23 19:31:55 strauss Exp $
;;
;; Copyright (C) The Internet Society (2004). All Rights Reserved.
;;

smingFile               = optsep *(moduleStatement optsep)

;;
;; Statement rules.

;;

moduleStatement         = moduleKeyword sep ucIdentifier optsep
                              "{" stmtsep
                              *(importStatement stmtsep)
                              organizationStatement stmtsep
                              contactStatement stmtsep
                              descriptionStatement stmtsep
                              *1(referenceStatement stmtsep)
                              1*(revisionStatement stmtsep)
                              *(extensionStatement stmtsep)
                              *(typedefStatement stmtsep)
                              *(identityStatement stmtsep)
                              *(classStatement stmtsep)
                          "}" optsep ";"

extensionStatement      = extensionKeyword sep lcIdentifier optsep
                              "{" stmtsep
                              statusStatement stmtsep
                              descriptionStatement stmtsep
                              *1(referenceStatement stmtsep)
                              *1(abnfStatement stmtsep)
                          "}" optsep ";"

typedefStatement        = typedefKeyword sep ucIdentifier optsep
                              "{" stmtsep
                              typedefTypeStatement stmtsep
                              *1(defaultStatement stmtsep)
                              *1(formatStatement stmtsep)
                              *1(unitsStatement stmtsep)
                              statusStatement stmtsep
                              descriptionStatement stmtsep
                              *1(referenceStatement stmtsep)
                          "}" optsep ";"

identityStatement       = identityStmtKeyword sep lcIdentifier optsep
                              "{" stmtsep
                              *1(parentStatement stmtsep)
                              statusStatement stmtsep
                              descriptionStatement stmtsep
                              *1(referenceStatement stmtsep)
                          "}" optsep ";"

classStatement          = classKeyword sep ucIdentifier optsep
                              "{" stmtsep
                              *1(extendsStatement stmtsep)
                              *(attributeStatement stmtsep)
                              *1(uniqueStatement stmtsep)
                              *(eventStatement stmtsep)
                              statusStatement stmtsep
                              descriptionStatement stmtsep
                              *1(referenceStatement stmtsep)
                          "}" optsep ";"

attributeStatement      = attributeKeyword sep
                              lcIdentifier optsep
                              "{" stmtsep
                              typeStatement stmtsep
                              *1(accessStatement stmtsep)
                              *1(defaultStatement stmtsep)
                              *1(formatStatement stmtsep)
                              *1(unitsStatement stmtsep)
                              statusStatement stmtsep
                              descriptionStatement stmtsep
                              *1(referenceStatement stmtsep)
                          "}" optsep ";"

uniqueStatement         = uniqueKeyword optsep
                              "(" optsep qlcIdentifierList
                              optsep ")" optsep ";"

eventStatement          = eventKeyword sep lcIdentifier
                              optsep "{" stmtsep
                              statusStatement stmtsep
                              descriptionStatement stmtsep
                              *1(referenceStatement stmtsep)
                          "}" optsep ";"

importStatement         = importKeyword sep ucIdentifier optsep
                              "(" optsep
                              identifierList optsep
                          ")" optsep ";"

revisionStatement       = revisionKeyword optsep "{" stmtsep
                              dateStatement stmtsep
                              descriptionStatement stmtsep
                          "}" optsep ";"

typedefTypeStatement    = typeKeyword sep refinedBaseType optsep ";"

typeStatement           = typeKeyword sep
                          (refinedBaseType / refinedType) optsep ";"

parentStatement         = parentKeyword sep qlcIdentifier optsep ";"

extendsStatement        = extendsKeyword sep qucIdentifier optsep ";"
dateStatement           = dateKeyword sep date optsep ";"

organizationStatement   = organizationKeyword sep text optsep ";"

contactStatement        = contactKeyword sep text optsep ";"

formatStatement         = formatKeyword sep format optsep ";"

unitsStatement          = unitsKeyword sep units optsep ";"

statusStatement         = statusKeyword sep status optsep ";"

accessStatement         = accessKeyword sep access optsep ";"

defaultStatement        = defaultKeyword sep anyValue optsep ";"

descriptionStatement    = descriptionKeyword sep text optsep ";"

referenceStatement      = referenceKeyword sep text optsep ";"

abnfStatement           = abnfKeyword sep text optsep ";"

;;
;;
;;

refinedBaseType         = ObjectIdentifierKeyword /
                          OctetStringKeyword *1(optsep numberSpec) /
                          PointerKeyword *1(optsep pointerSpec) /
                          Integer32Keyword *1(optsep numberSpec) /
                          Unsigned32Keyword *1(optsep numberSpec) /
                          Integer64Keyword *1(optsep numberSpec) /
                          Unsigned64Keyword *1(optsep numberSpec) /
                          Float32Keyword *1(optsep floatSpec) /
                          Float64Keyword *1(optsep floatSpec) /
                          Float128Keyword *1(optsep floatSpec) /
                          EnumerationKeyword
                                      optsep namedSignedNumberSpec /
                          BitsKeyword optsep namedNumberSpec

refinedType             = qucIdentifier *1(optsep anySpec)

anySpec                 = pointerSpec / numberSpec / floatSpec

pointerSpec             = "(" optsep qlcIdentifier optsep ")"
numberSpec              = "(" optsep numberElement
                              *furtherNumberElement
                              optsep ")"

furtherNumberElement    = optsep "|" optsep numberElement

numberElement           = signedNumber *1numberUpperLimit

numberUpperLimit        = optsep ".." optsep signedNumber

floatSpec               = "(" optsep floatElement
                              *furtherFloatElement
                              optsep ")"

furtherFloatElement     = optsep "|" optsep floatElement

floatElement            = floatValue *1floatUpperLimit

floatUpperLimit         = optsep ".." optsep floatValue

namedNumberSpec         = "(" optsep namedNumberList optsep ")"

namedNumberList         = namedNumberItem
                              *(optsep "," optsep namedNumberItem)

namedNumberItem         = lcIdentifier optsep "(" optsep number
                              optsep ")"

namedSignedNumberSpec   = "(" optsep namedSignedNumberList optsep ")"

namedSignedNumberList   = namedSignedNumberItem
                              *(optsep "," optsep
                                           namedSignedNumberItem)

namedSignedNumberItem   = lcIdentifier optsep "(" optsep signedNumber
                              optsep ")"

identifierList          = identifier
                              *(optsep "," optsep identifier)

qIdentifierList         = qIdentifier
                              *(optsep "," optsep qIdentifier)

qlcIdentifierList       = qlcIdentifier
                              *(optsep "," optsep qlcIdentifier)

bitsValue               = "(" optsep bitsList optsep ")"
bitsList                = *1(lcIdentifier
                              *(optsep "," optsep lcIdentifier))

;;
;; Other basic rules.

;; Encoding specific rules. ;;

textVChar               = %x21 / %x23-7E
                          ; any VCHAR except DQUOTE

ucAlpha                 = %x41-5A

lcAlpha                 = %x61-7A

nonZeroDigit            = %x31-39

;;
;; RFC 2234 core rules.
;;

ALPHA          =  %x41-5A / %x61-7A
                       ; A-Z / a-z

CR             =  %x0D
                       ; carriage return

CRLF           =  CR LF
                       ; Internet standard newline

DIGIT          =  %x30-39
                       ; 0-9

DQUOTE         =  %x22
                       ; " (Double Quote)

HEXDIG         =  DIGIT /
                  %x61 / %x62 / %x63 / %x64 / %x65 / %x66

                       ; only lower-case a..f

HTAB           =  %x09
                       ; horizontal tab

LF             =  %x0A
                       ; linefeed

SP             =  %x20
                       ; space

VCHAR          =  %x21-7E
                       ; visible (printing) characters

WSP            =  SP / HTAB
                       ; white space

;; End of ABNF

Authors' Addresses πŸŽ€

Two brilliant people wrote this one, and here's how to find them if you ever need to ping them with a question πŸ’Œ

Frank Strauss πŸ¦‹ TU Braunschweig Muehlenpfordtstrasse 23 38106 Braunschweig Germany Phone: +49 531 391 3266 EMail: strauss@ibr.cs.tu-bs.de URI: http://www.ibr.cs.tu-bs.de/

Juergen Schoenwaelder 🌸 International University Bremen P.O. Box 750 561 28725 Bremen Germany Phone: +49 421 200 3587 EMail: j.schoenwaelder@iu-bremen.de URI: http://www.eecs.iu-bremen.de/

Okay, the legal bit, but make it cute πŸ’… The Internet Society owns the copyright here, from 2004. This document is covered by the rights, licenses and restrictions in BCP 78. Apart from what BCP 78 says, the authors keep all their own rights.

This document and the information in it come "AS IS". The contributor, the organization they represent or are sponsored by (if any), the Internet Society, and the Internet Engineering Task Force all DISCLAIM ALL WARRANTIES, express or implied. That includes, but isn't limited to, any warranty that using the information won't infringe anyone's rights, and any implied warranty of merchantability or fitness for a particular purpose. Nobody is promising it'll work for every use πŸ™ˆ

Intellectual Property πŸ”

The IETF doesn't take a side on whether any intellectual property rights that might be claimed over the technology in this document are valid or how far they reach. It also doesn't say whether licenses for those rights are available. And it hasn't independently gone hunting for any such rights either πŸ•΅οΈβ€β™€οΈ Info about how rights work for RFC documents lives in BCP 78 and BCP 79.

Copies of IPR disclosures sent to the IETF Secretariat, any promises of licenses, and the results of any attempt to get a general license or permission for implementers or users are all available from the IETF online IPR repository at http://www.ietf.org/ipr.

The IETF also invites anyone who knows of copyrights, patents, patent applications, or other proprietary rights that might cover technology needed to implement this standard to tell them. Send that info to the IETF at ietf-ipr@ietf.org πŸ’œ

Acknowledgement 🫢

Funding for the RFC Editor function is currently provided by the Internet Society. Thank you, Internet Society, you're literally keeping the lights on ✨

πŸ“¦ where this page comes from