RFC 1448

Protocol Operations for version 2 of the Simple Network Management Protocol (SNMPv2)

๐Ÿ“… published April 1993, from ietf

RFC 1448: Protocol Operations for version 2 of the Simple Network Management Protocol (SNMPv2) ๐Ÿ’–

okay bestie, this one is from April 1993 and it's the Request for Comments: 1448 ๐Ÿฆ‹ It's written by four people: J. Case (SNMP Research, Inc.), K. McCloghrie (Hughes LAN Systems), M. Rose (Dover Beach Consulting, Inc.), and S. Waldbusser (Carnegie Mellon University) ๐Ÿซถ

The category is IAB standards track, so this is an official protocol spec, not just an opinion piece ๐Ÿ‘‘ The header doesn't list anything it obsoletes or updates, so there's nothing to chase there โœจ

Status of this Memo ๐Ÿ“œ

ngl this part is super important, so pay attention ๐Ÿ’… This memo specifies an IAB standards track protocol for the Internet community, and it asks for discussion and suggestions for improvements ๐Ÿ’œ For the standardization state and status of this protocol, check the current edition of the "IAB Official Protocol Standards" ๐ŸŒธ Distribution of this memo is unlimited ๐ŸŒˆ

Table of Contents ๐Ÿ“‘

1 Introduction ..........................................    2
1.1 A Note on Terminology ...............................    2
2 Overview ..............................................    3
2.1 Roles of Protocol Entities ..........................    3
2.2 Management Information ..............................    3
2.3 Access to Management Information ....................    4
2.4 Retransmission of Requests ..........................    4
2.5 Message Sizes .......................................    5
2.6 Transport Mappings ..................................    6
3 Definitions ...........................................    7
4 Protocol Specification ................................   12
4.1 Common Constructs ...................................   12
4.2 PDU Processing ......................................   12
4.2.1 The GetRequest-PDU ................................   13
4.2.2 The GetNextRequest-PDU ............................   15
4.2.2.1 Example of Table Traversal ......................   16
4.2.3 The GetBulkRequest-PDU ............................   18
4.2.3.1 Another Example of Table Traversal ..............   21
4.2.4 The Response-PDU ..................................   22
4.2.5 The SetRequest-PDU ................................   23
4.2.6 The SNMPv2-Trap-PDU ...............................   26
4.2.7 The InformRequest-PDU .............................   27
5 Acknowledgements ......................................   29
6 References ............................................   33
7 Security Considerations ...............................   35
8 Authors' Addresses ....................................   35

1. Introduction โœจ

okay so picture a network management system ๐Ÿงš It has a bunch of nodes (potentially many of them), and each one has a processing entity called an agent that can get at its management instrumentation ๐Ÿ“ It also needs at least one management station, plus a management protocol to carry management information between the agents and the stations ๐Ÿ’– All of this runs under an administrative framework that defines both authentication and authorization policies, which is giving very responsible vibes ๐Ÿ”

Management stations run management applications that monitor and control network elements. Network elements are things like hosts, routers, terminal servers, and so on. You monitor and control them by accessing their management information ๐Ÿ’…

The management information is seen as a collection of managed objects living 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 subset of OSI's Abstract Syntax Notation One (ASN.1) [1]. That subset is called the Structure of Management Information (SMI) [2] โœจ

Next, SNMPv2 (version 2 of the Simple Network Management Protocol) is the bit that lets agents and stations swap messages carrying management information ๐Ÿฆ‹ Each message is a message "wrapper" that encapsulates a Protocol Data Unit (PDU) ๐ŸŽ€ The form and meaning of that wrapper are decided by the administrative framework, which again defines authentication and authorization policies. Iconic structure, honestly ๐Ÿ‘‘

So what's this document for? It defines the operations of the protocol: how PDUs get sent and received ๐Ÿซถ

1.1. A Note on Terminology ๐Ÿ“–

For clarity, the original Internet-standard Network Management Framework is called the SNMP version 1 framework (SNMPv1) ๐Ÿ’œ It's described in RFCs 1155, 1157, and 1212 ๐ŸŒˆ The current framework is called the SNMP version 2 framework (SNMPv2) โญ

2. Overview ๐Ÿ’…

2.1. Roles of Protocol Entities ๐ŸŽ€

okay so every SNMPv2 entity can be a manager or an agent, and it's literally decided by the implementation and configuration ๐Ÿงš

An entity is acting as an agent when it does SNMPv2 management operations because it got an SNMPv2 protocol message (other than an inform notification), or when it sends trap notifications. โœจ

An entity is acting as a manager when it starts SNMPv2 management operations by generating SNMPv2 protocol messages, or when it does SNMPv2 management operations because it got a trap or inform notification. ๐Ÿ’–

A SNMPv2 entity may support either or both roles, as dictated by its implementation and configuration. Further, a SNMPv2 entity can also act as a proxy agent ๐Ÿฆ‹ It appears to be acting in an agent role, but it satisfies management requests by acting in a manager role with a remote entity. The use of proxy agents and the transparency principle that defines their behavior is described in [3]. ๐Ÿ”ฅ

2.2. Management Information ๐ŸŒธ

The term "variable" refers to an instance of a non-aggregate object type defined according to the conventions set forth in the SMI [2] or the textual conventions based on the SMI [4]. ๐Ÿซถ

The term "variable binding" normally refers to the pairing of the name of a variable and its associated value. BUT if certain kinds of exceptional conditions happen during processing of a retrieval request, a variable binding will pair a name and an indication of that exception. ๐Ÿ˜ญ

A variable-binding list is a simple list of variable bindings. ๐Ÿ“

The name of a variable is an OBJECT IDENTIFIER, and it's the concatenation of the OBJECT IDENTIFIER of the corresponding object-type together with an OBJECT IDENTIFIER fragment identifying the instance. The OBJECT IDENTIFIER of the corresponding object-type is called the OBJECT IDENTIFIER prefix of the variable. ๐Ÿ’œ

2.3. Access to Management Information ๐Ÿ‘‘

Three types of access to management information are provided by the protocol. โญ

The first type is a request-response interaction. A SNMPv2 entity acting in a manager role sends a request to a SNMPv2 entity acting in an agent role, and the latter SNMPv2 entity then responds to the request. This type is used to retrieve or modify management information associated with the managed device. ๐Ÿ’…

The second type is also a request-response interaction, but this time a SNMPv2 entity acting in a manager role sends a request to a SNMPv2 entity also acting in a manager role, and the latter SNMPv2 entity then responds to the request. This type is used to notify a SNMPv2 entity acting in a manager role of management information associated with another SNMPv2 entity, also acting in a manager role. ๐Ÿฆ‹

The third type is an unconfirmed interaction. A SNMPv2 entity acting in an agent role sends an unsolicited message, termed a trap, to a SNMPv2 entity acting in a manager role, and no response is returned. This type is used to notify a SNMPv2 entity acting in a manager role of an exceptional situation that has resulted in changes to management information associated with the managed device. ๐Ÿ™ˆ

2.4. Retransmission of Requests ๐Ÿ”

For all types of request in this protocol, the receiver is required under normal circumstances to generate and transmit a response to the originator of the request. ๐Ÿซถ

Whether or not a request should be retransmitted if no corresponding response is received in an appropriate time interval is at the discretion of the application originating the request. This will normally depend on the urgency of the request. ๐Ÿง  However, such an application needs to act responsibly in respect to the frequency and duration of re-transmissions. Not spamming, bestie ๐Ÿซ 

2.5. Message Sizes ๐Ÿ“

The maximum size of a SNMPv2 message is limited to the minimum of:

  1. the maximum message size which the destination SNMPv2 entity can accept; and,
  2. the maximum message size which the source SNMPv2 entity can generate.

The former is indicated by partyMaxMessageSize[5] of the destination party. The latter is imposed by implementation-specific local constraints. ๐ŸŽฏ

Each transport mapping for SNMPv2 indicates the minimum message size which a SNMPv2 implementation must be able to produce or consume. Although implementations are encouraged to support larger values whenever possible, a conformant implementation must never generate messages larger than allowed by the receiving SNMPv2 entity. ๐Ÿšซ

One of the aims of the GetBulkRequest-PDU, specified in this protocol, is to minimize the number of protocol exchanges required to retrieve a large amount of management information. So this PDU type allows a SNMPv2 entity acting in a manager role to request that the response be as large as possible given the constraints on message sizes. These constraints include the limits on the size of messages which the SNMPv2 entity acting in an agent role can generate, and the limits on the size of messages which the SNMPv2 entity acting in a manager role can receive. ๐Ÿš€

However, it is possible that such maximum sized messages may be larger than the Path MTU of the path across the network traversed by the messages. In this situation, such messages are subject to fragmentation. Fragmentation is generally considered to be harmful [6], since among other problems, it leads to a decrease in the reliability of the transfer of the messages. ๐Ÿ’€ Thus, a SNMPv2 entity which sends a GetBulkRequest-PDU must take care to set its parameters accordingly, so as to reduce the risk of fragmentation. In particular, under conditions of network stress, only small values should be used for max-repetitions. ๐Ÿฅบ

2.6. Transport Mappings ๐ŸšŒ

It is important to note that the exchange of SNMPv2 messages requires only an unreliable datagram service, with every message being entirely and independently contained in a single transport datagram. โš ๏ธ Specific transport mappings and encoding rules are specified elsewhere [7]. However, the preferred mapping is the use of the User Datagram Protocol [8]. ๐Ÿ’–


3. Definitions ๐Ÿ“œ

SNMPv2-PDU DEFINITIONS ::= BEGIN

IMPORTS
    ObjectName, ObjectSyntax, Integer32
        FROM SNMPv2-SMI;


-- protocol data units

PDUs ::=
    CHOICE {
        get-request
            GetRequest-PDU,

        get-next-request
            GetNextRequest-PDU,

        get-bulk-request
            GetBulkRequest-PDU,

        response
            Response-PDU,

        set-request
            SetRequest-PDU,

        inform-request
            InformRequest-PDU,

        snmpV2-trap
            SNMPv2-Trap-PDU
    }

-- PDUs

GetRequest-PDU ::=
    [0]
        IMPLICIT PDU

GetNextRequest-PDU ::=
    [1]
        IMPLICIT PDU

Response-PDU ::=
    [2]
        IMPLICIT PDU

SetRequest-PDU ::=
    [3]
        IMPLICIT PDU

-- [4] is obsolete

GetBulkRequest-PDU ::=
    [5]
        IMPLICIT BulkPDU

InformRequest-PDU ::=
    [6]
        IMPLICIT PDU

SNMPv2-Trap-PDU ::=
    [7]
        IMPLICIT PDU

max-bindings
    INTEGER ::= 2147483647

PDU ::=
    SEQUENCE {
        request-id
            Integer32,

        error-status            -- sometimes ignored
            INTEGER {
                noError(0),
                tooBig(1),
                noSuchName(2),   -- for proxy compatibility
                badValue(3),     -- for proxy compatibility
                readOnly(4),     -- for proxy compatibility
                genErr(5),
                noAccess(6),
                wrongType(7),
                wrongLength(8),
                wrongEncoding(9),
                wrongValue(10),
                noCreation(11),
                inconsistentValue(12),
                resourceUnavailable(13),
                commitFailed(14),
                undoFailed(15),
                authorizationError(16),
                notWritable(17),
                inconsistentName(18)
            },

        error-index            -- sometimes ignored
            INTEGER (0..max-bindings),

        variable-bindings   -- values are sometimes ignored
            VarBindList
    }

BulkPDU ::=                     -- MUST be identical in
    SEQUENCE {                  -- structure to PDU
        request-id
            Integer32,

        non-repeaters
            INTEGER (0..max-bindings),

        max-repetitions
            INTEGER (0..max-bindings),

        variable-bindings       -- values are ignored
            VarBindList
    }

-- variable binding

VarBind ::=
    SEQUENCE {
        name
            ObjectName,

        CHOICE {
            value
                ObjectSyntax,

            unSpecified         -- in retrieval requests
                    NULL,

                                -- exceptions in responses
            noSuchObject[0]
                    IMPLICIT NULL,

            noSuchInstance[1]
                    IMPLICIT NULL,

            endOfMibView[2]
                    IMPLICIT NULL
        }
    }


-- variable-binding list

VarBindList ::=
    SEQUENCE (SIZE (0..max-bindings)) OF
        VarBind


END

4. Protocol Specification ๐ŸŽฌ

4.1. Common Constructs โœจ

The request-id is basically the bestie tag on every message ๐Ÿซถ A Response-PDU copies the request-id from the request it's answering. That's how an SNMPv2 app keeps track of (potentially lots of) outstanding requests and matches each response to the right one ๐Ÿ’… On an unreliable datagram service, the request-id is also a lowkey way to spot messages the network duplicated ๐Ÿ‘€ If a request gets resent with the same request-id, the response to either the original or the resend can satisfy it. BUT if you want to measure the round trip time for a request-response transaction, the app needs a different request-id on the retransmitted request. That second strategy is the one recommended for most situations โœจ

A non-zero error-status in a Response-PDU means something went wrong and the request couldn't be processed ๐Ÿ˜ญ When that happens, a non-zero error-index tells you which variable binding caused the exception. Variable bindings are identified by their index: the first one is index one, the second is index two, and so on ๐Ÿ”ข

Also, SNMPv2 caps OBJECT IDENTIFIER values at 128 sub-identifiers, and each sub-identifier can go up to 2**32-1 ๐Ÿงš

---

4.2. PDU Processing โœจ

It's mandatory for every SNMPv2 entity acting in an agent role to be able to generate Response-PDU and SNMPv2-Trap-PDU. On top of that, all of these implementations have to be able to receive GetRequest-PDU, GetNextRequest-PDU, GetBulkRequest-PDU, and SetRequest-PDU ๐ŸŽ€

It's mandatory for every SNMPv2 entity acting in a manager role to be able to generate GetRequest-PDU, GetNextRequest-PDU, GetBulkRequest-PDU, SetRequest-PDU, InformRequest-PDU, and Response-PDU. Those implementations also have to be able to receive Response-PDU, SNMPv2-Trap-PDU, and InformRequest-PDU ๐Ÿ’–

In the elements of procedure below, any field of a PDU that the relevant procedure doesn't reference gets ignored by the receiving SNMPv2 entity. BUT every component of a PDU, including the ignored ones, still has to have valid ASN.1 syntax and encoding. Like, a GetRequest-PDU only cares about the name of a variable, not its value, so the value portion of the variable binding is ignored ๐Ÿ™ˆ The unSpecified value exists for exactly that job.

For all generated PDUs, the message "wrapper" that encapsulates the PDU is generated and sent as specified in [3]. A message's size is only limited by the maximum message size, either a local limit or the limit tied to the message's destination party. It is NOT limited by the number of variable bindings ๐ŸŒธ

When a management communication comes in, the procedures in Section 3.2 of [3] get followed. If those procedures say the PDU inside the message "wrapper" should be processed, then the SNMPv2 context linked to that PDU decides which object resources are visible to the operation ๐Ÿ‘‘

4.2.1. The GetRequest-PDU

A GetRequest-PDU gets generated and sent whenever a SNMPv2 application asks for it ๐Ÿฆ‹

When a GetRequest-PDU arrives, the receiving SNMPv2 entity processes each variable binding in the variable-binding list to produce a Response-PDU. All fields of the Response-PDU match the corresponding fields of the request, except for what's listed below. Each variable binding gets handled like this:

  1. If the variable binding's name doesn't have an OBJECT IDENTIFIER prefix that exactly matches the OBJECT IDENTIFIER prefix of any variable accessible by this request, then its value field is set to `noSuchObject'.
  2. Otherwise, if the variable binding's name doesn't exactly match the name of a variable accessible by this request, then its value field is set to `noSuchInstance'.
  3. Otherwise, the variable binding's value field is set to the value of the named variable.

If processing any variable binding fails for a reason that isn't listed above, the Response-PDU gets re-formatted. It keeps the same request-id and variable-bindings as the received GetRequest-PDU, its error-status is set to `genErr', and its error-index is set to the index of the failed variable binding ๐Ÿ˜ฌ

Otherwise, the Response-PDU's error-status is set to `noError', and its error-index is zero โœ…

The generated Response-PDU then gets encapsulated into a message. If that message's size is less than or equal to both a local constraint and the maximum message size of the request's source party, it's sent back to the originator of the GetRequest-PDU ๐Ÿ“ฌ

Otherwise, an alternate Response-PDU gets generated. It has the same request-id as the received GetRequest-PDU, its error-status is set to `tooBig', its error-index is set to zero, and its variable-bindings field is empty. This alternate Response-PDU gets encapsulated into a message too. If that message's size is less than or equal to both a local constraint and the maximum message size of the request's source party, it's sent to the originator of the GetRequest-PDU. Otherwise, the resulting message gets discarded ๐Ÿ—‘๏ธ

4.2.2. The GetNextRequest-PDU

A GetNextRequest-PDU gets generated and sent whenever a SNMPv2 application asks for it ๐Ÿง

When a GetNextRequest-PDU arrives, the receiving SNMPv2 entity processes each variable binding in the variable-binding list to produce a Response-PDU. All fields of the Response-PDU match the corresponding fields of the request, except for what's listed below. Each variable binding gets handled like this:

  1. The variable is located in the lexicographically ordered list of the names of all variables accessible by this request. It has to be the first lexicographic successor of the variable binding's name in the incoming GetNextRequest-PDU. The corresponding variable binding's name and value fields in the Response-PDU get set to the name and value of the located variable ๐Ÿ”
  2. If the requested variable binding's name does not lexicographically precede the name of any variable accessible by this request, i.e., there's no lexicographic successor, then the matching variable binding in the Response-PDU has its value field set to 'endOfMibView', and its name field set to the variable binding's name in the request ๐Ÿ”š

If processing any variable binding fails for a reason that isn't listed above, the Response-PDU gets re-formatted. It keeps the same request-id and variable-bindings as the received GetNextRequest-PDU, its error-status is set to `genErr', and its error-index is set to the index of the failed variable binding ๐Ÿ˜ฌ

Otherwise, the Response-PDU's error-status is set to `noError', and its error-index is zero โœ…

The generated Response-PDU then gets encapsulated into a message. If that message's size is less than or equal to both a local constraint and the maximum message size of the request's source party, it's sent to the originator of the GetNextRequest-PDU ๐Ÿ“ฌ

Otherwise, an alternate Response-PDU gets generated. It has the same request-id as the received GetNextRequest-PDU, its error-status is set to `tooBig', its error-index is set to zero, and its variable-bindings field is empty. This alternate Response-PDU gets encapsulated into a message. If that message's size is less than or equal to both a local constraint and the maximum message size of the request's source party, it's sent to the originator of the GetNextRequest-PDU. Otherwise, the resulting message gets discarded ๐Ÿ—‘๏ธ

4.2.2.1. Example of Table Traversal

GetNextRequest-PDUs are super important for traversing conceptual tables inside a MIB ๐Ÿ—‚๏ธ The semantics of this request, plus the way individual object instances are identified in the MIB, gives you access to related objects as if they were arranged in a table. It's giving spreadsheet energy ๐Ÿ“Š

In the exchange below, a SNMPv2 application grabs the media-dependent physical address and the address-mapping type for each entry in the IP net-to-media Address Translation Table [9] of a particular network element. It also grabs the value of sysUpTime [9], which is when the mappings existed. Say the agent's IP net-to-media table has three entries:

  Interface-Number  Network-Address  Physical-Address  Type

         1            10.0.0.51     00:00:10:01:23:45  static
         1             9.2.3.4      00:00:10:54:32:10  dynamic
         2            10.0.0.15     00:00:10:98:76:54  dynamic

The SNMPv2 entity acting in a manager role starts by sending a GetNextRequest-PDU that contains the indicated OBJECT IDENTIFIER values as the requested variable names ๐Ÿš€

    GetNextRequest ( sysUpTime,
                     ipNetToMediaPhysAddress,
                     ipNetToMediaType )

The SNMPv2 entity acting in an agent role replies with a Response-PDU ๐Ÿ’Œ

    Response (( sysUpTime.0 =  "123456" ),
              ( ipNetToMediaPhysAddress.1.9.2.3.4 =
                                         "000010543210" ),
              ( ipNetToMediaType.1.9.2.3.4 =  "dynamic" ))

I'm picking up mid-example, so here's the rest of the table walk-through. Those sysUpTime numbers are still ticking, so watch the clock. ๐Ÿ•ฐ๏ธ๐Ÿ’–

The manager keeps asking for the next row, one pair of columns at a time:

GetNextRequest ( sysUpTime,
                 ipNetToMediaPhysAddress.1.9.2.3.4,
                 ipNetToMediaType.1.9.2.3.4 )

The agent replies with the next row it finds:

Response (( sysUpTime.0 =  "123461" ),
          ( ipNetToMediaPhysAddress.1.10.0.0.51 =
                                      "000010012345" ),
          ( ipNetToMediaType.1.10.0.0.51 =  "static" ))

Then the manager keeps going:

GetNextRequest ( sysUpTime,
                 ipNetToMediaPhysAddress.1.10.0.0.51,
                 ipNetToMediaType.1.10.0.0.51 )

Another row, another reply:

Response (( sysUpTime.0 =  "123466" ),
          ( ipNetToMediaPhysAddress.2.10.0.0.15 =
                                       "000010987654" ),
          ( ipNetToMediaType.2.10.0.0.15 =  "dynamic" ))

The manager asks again:

GetNextRequest ( sysUpTime,
                 ipNetToMediaPhysAddress.2.10.0.0.15,
                 ipNetToMediaType.2.10.0.0.15 )

Now the fun part ๐ŸŽ€. There are no more entries in the table, so the agent answers with whatever variables come next in the lexicographical ordering of the accessible object names. Here's an example:

Response (( sysUpTime.0 =  "123471" ),
          ( ipNetToMediaNetAddress.1.9.2.3.4 =
                                           "9.2.3.4" ),
          ( ipRoutingDiscards.0 =  "2" ))

That's the signal ๐Ÿšฆ that the table is over, as far as the manager is concerned. Iconic closure, honestly. ๐Ÿ’…


4.2.3. The GetBulkRequest-PDU โœจ

A GetBulkRequest-PDU gets generated and sent whenever an SNMPv2 application asks for one ๐Ÿ“ฎ. Its whole purpose is to request a potentially huge amount of data, and that includes (but isn't limited to) grabbing big tables quickly and efficiently ๐Ÿš€.

When a receiving SNMPv2 entity gets a GetBulkRequest-PDU, it goes through each variable binding in the variable-binding list and builds a Response-PDU. The response's request-id field matches the one from the request. First, it checks the non-repeaters and max-repetitions fields. If non-repeaters is less than zero, it gets set to zero. Same deal for max-repetitions: a negative value becomes zero ๐Ÿงธ.

Here's the big difference from the other PDU types. For a GetBulkRequest-PDU, one successfully processed variable binding can produce zero or more variable bindings in the Response-PDU ๐ŸŒˆ. The one-to-one mapping from GetRequest-PDU, GetNextRequest-PDU, and SetRequest-PDU does not apply here.

The non-repeaters and max-repetitions values are what decide how much work the request asks for. The first N variable bindings in the request each get one response binding. The remaining R variable bindings each get M response bindings. So the total number of requested variable bindings is N + (M * R), where:

  • N is the minimum of: a) the value of the non-repeaters field in the request, and b) the number of variable bindings in the request.
  • M is the value of the max-repetitions field in the request.
  • R is the maximum of: a) the number of variable bindings in the request minus N, and b) zero.

The receiving entity makes a Response-PDU with up to that many requested variable bindings. The request-id shall have the same value as the received GetBulkRequest-PDU ๐ŸŽฏ.

If N is greater than zero, the first through N-th variable bindings in the Response-PDU are each built like this:

  1. Find the variable in the lexicographically ordered list of names of every variable accessible for this request. Its name has to be the first lexicographic successor of the variable binding's name in the incoming GetBulkRequest-PDU. Copy that variable's name and value into the corresponding Response-PDU binding.
  2. If the requested name doesn't lexicographically precede any accessible variable, meaning there's no lexicographic successor, the response binding's value field gets set to endOfMibView, and its name field gets set to the name from the request. That's the "we're out of stuff" signal ๐Ÿ˜ญ.

If M and R are both non-zero, the (N + 1)-th and later variable bindings in the Response-PDU are built in a similar way ๐Ÿ”. For each iteration i, where i is greater than zero and less than or equal to M, and for each repeated variable r, where r is greater than zero and less than or equal to R, the (N + ((i-1) * R) + r)-th variable binding of the Response-PDU is built like this:

  1. Find the variable in the lexicographically ordered list of names of every variable accessible for this request whose name is the i-th lexicographic successor of the (N + r)-th variable binding's name in the incoming GetBulkRequest-PDU. Then set the variable binding's name and value fields to the name and value of that variable.
  2. If there is no i-th lexicographic successor, the response binding's value field gets set to endOfMibView. Its name field gets set to either the last lexicographic successor, or, if there aren't any lexicographic successors, the (N + r)-th variable binding's name from the request.

The Response-PDU can have at most N + (M * R) bindings. But the response might end up with fewer bindings, possibly even zero, for either of two reasons ๐Ÿง:

  1. If the message wrapping the Response-PDU would be bigger than either a local constraint or the maximum message size of the request's source party, the response gets generated with fewer bindings. The bindings at the end of the ordered set get dropped, so the message stays roughly as big as possible without going over the smaller of the local constraint and the source party's maximum message size. How many get dropped has nothing to do with N, M, or R. ๐Ÿ“ฆ
  2. The response can also come out shorter if, for some iteration i, where i is greater than zero and less than or equal to M, every generated binding has its value field set to endOfMibView. In that case, the bindings can be cut off right after the (N + (i * R))-th variable binding โœ‚๏ธ.

If processing any variable binding fails for a reason that isn't listed above, the Response-PDU gets reformatted. It keeps the same request-id and variable-bindings values as the received GetBulkRequest-PDU. The error-status field gets set to genErr, and the error-index field gets set to the index of the failed variable binding ๐Ÿ’ฅ.

Otherwise, the Response-PDU's error-status field gets set to noError, and its error-index field gets set to zero ๐ŸŒธ.

Finally, the Response-PDU (even if its variable-bindings field is empty) gets wrapped in a message. If that message is less than or equal to both a local constraint and the maximum message size of the request's source party, it gets sent to whoever originated the GetBulkRequest-PDU. Otherwise, the message gets thrown away ๐Ÿ—‘๏ธ.


4.2.3.1. Another Example of Table Traversal ๐Ÿฆ‹

This example shows how the GetBulkRequest-PDU can work as an alternative to the GetNextRequest-PDU ๐Ÿ’…. It does the same traversal of the IP net-to-media table we saw in Section 4.2.2.1, but it takes fewer exchanges. Efficiency is everything, bestie ๐Ÿ‘‘.

The SNMPv2 entity acting in a manager role starts by sending a GetBulkRequest-PDU with a modest max-repetitions value of 2. It also includes the OBJECT IDENTIFIER values as the variable names it wants:

GetBulkRequest [ non-repeaters = 1, max-repetitions = 2 ]
                ( sysUpTime,
                  ipNetToMediaPhysAddress,
                  ipNetToMediaType )

The SNMPv2 entity acting in an agent role answers with a Response-PDU:

Response (( sysUpTime.0 =  "123456" ),
          ( ipNetToMediaPhysAddress.1.9.2.3.4 =
                                     "000010543210" ),
          ( ipNetToMediaType.1.9.2.3.4 =  "dynamic" ),
          ( ipNetToMediaPhysAddress.1.10.0.0.51 =
                                      "000010012345" ),
          ( ipNetToMediaType.1.10.0.0.51 =  "static" ))

Then the manager keeps going with another GetBulkRequest-PDU ๐Ÿ”ฅ:

GetBulkRequest [ non-repeaters = 1, max-repetitions = 2 ]
                ( sysUpTime,
                  ipNetToMediaPhysAddress.1.10.0.0.51,
                  ipNetToMediaType.1.10.0.0.51 )

The SNMPv2 entity acting in an agent role responds with:

              Response (( sysUpTime.0 =  "123466" ),
                        ( ipNetToMediaPhysAddress.2.10.0.0.15 =
                                                   "000010987654" ),
                        ( ipNetToMediaType.2.10.0.0.15 =
                                                        "dynamic" ),
                        ( ipNetToMediaNetAddress.1.9.2.3.4 =
                                                        "9.2.3.4" ),
                        ( ipRoutingDiscards.0 =  "2" ))

And that's the end of the table! โœจ This Response tells the SNMPv2 entity acting in a manager role that there's nothing else to read, so it's like the little "that's all, folks" ๐ŸŽ€ at the bottom of the list. Iconic.

4.2.4. The Response-PDU ๐Ÿฆ‹

Okay, bestie, the Response-PDU only shows up when an SNMPv2 entity has received one of these: a GetRequest-PDU, a GetNextRequest-PDU, a GetBulkRequest-PDU, a SetRequest-PDU, or an InformRequest-PDU. That's the whole vibe, it's a reply, it doesn't just show up uninvited ๐Ÿ’…

If the error-status field of the Response-PDU is non-zero, the value fields in the variable-binding list get ignored. So if something went wrong, don't bother reading the values, they're not the main character right now ๐Ÿ™ˆ

If both the error-status field and the error-index field are non-zero, then the error-index tells you which variable binding failed. The first variable binding in the request's list is index one, the second is index two, and so on. Super helpful when you're hunting for the one that's causing drama ๐Ÿ”

A compliant SNMPv2 entity acting in a manager role must be able to properly receive and handle a Response-PDU whose error-status is noSuchName, badValue, or readOnly. (See Section 3.1.2 of [10].) No ghosting these errors, they're part of the job ๐Ÿ’€

When a Response-PDU comes in, the receiving SNMPv2 entity hands its contents over to the SNMPv2 application that made the request, matched up by the same request-id value. Delivery, but make it organized ๐Ÿ“ฌ๐ŸŒธ

4.2.5. The SetRequest-PDU ๐Ÿ’–

A SetRequest-PDU gets created and sent whenever an SNMPv2 application asks for it. Literally the request starts with the app, not the protocol ๐Ÿงš

When a SetRequest-PDU arrives, the receiving SNMPv2 entity first checks how big a message would be if it wrapped up a Response-PDU with the same request-id, error-status, error-index, and variable-bindings values as the request. If that size is bigger than either a local constraint or the maximum message size of the request's source party, then an alternate Response-PDU gets made, sent back to whoever sent the SetRequest-PDU, and processing of that SetRequest-PDU stops right there. โœ‹

The alternate Response-PDU has the same request-id as the SetRequest-PDU, its error-status is set to tooBig, its error-index is set to zero, and its variable-bindings field is empty. It then gets encapsulated into a message. If that message is less than or equal to both a local constraint and the maximum message size of the request's source party, it's sent to the originator. Otherwise the message is discarded. Either way, processing of the SetRequest-PDU ends. Bye bye ๐Ÿ‘‹

Otherwise, the receiving SNMPv2 entity goes through each variable binding in the variable-binding list to build a Response-PDU. Every field of that Response-PDU matches the request's fields, except for the changes listed below. โœจ

The variable bindings are conceptually handled as a two-phase operation. In the first phase, each variable binding gets validated. If every validation passes, then each variable gets altered in the second phase. Implementors are totally free to do either phase, or both, as multiple implementation phases, and sometimes that's actually needed to keep things consistent. Flexibility is everything ๐ŸŒˆ

These validations happen in the first phase, one variable binding at a time, until they all succeed or one of them fails:

  1. If the variable binding's name refers to a variable that isn't accessible by this request, then the error-status field of the Response-PDU is set to noAccess, and the error-index is set to the index of the failed variable binding.
  2. Otherwise, if the variable binding's name refers to a variable that doesn't exist and could never be created, then the error-status is set to noCreation, and the error-index is set to the index of the failed variable binding.
  3. Otherwise, if the variable binding's name refers to a variable that exists but can't be modified no matter what new value is given, then the error-status is set to notWritable, and the error-index is set to the index of the failed variable binding.
  4. Otherwise, if the variable binding's value field, according to the ASN.1 language, has a type that doesn't match what the variable requires, then the error-status is set to wrongType, and the error-index is set to the index of the failed variable binding.
  5. Otherwise, if the variable binding's value field, according to the ASN.1 language, has a length that doesn't match what the variable requires, then the error-status is set to wrongLength, and the error-index is set to the index of the failed variable binding.
  6. Otherwise, if the variable binding's value field contains an ASN.1 encoding that doesn't match that field's ASN.1 tag, then the error-status is set to wrongEncoding, and the error-index is set to the index of the failed variable binding.
  7. Otherwise, if the variable binding's value field specifies a value that could never under any circumstances be assigned to the variable, then the error-status is set to wrongValue, and the error-index is set to the index of the failed variable binding.
  8. Otherwise, if the variable binding's name refers to a variable that doesn't exist and can't be created under the present circumstances (even though it could be created under other circumstances), then the error-status is set to inconsistentName, and the error-index is set to the index of the failed variable binding.
  9. Otherwise, if the variable binding's value field specifies a value that could be assigned to the variable under other circumstances but is presently inconsistent, then the error-status is set to inconsistentValue, and the error-index is set to the index of the failed variable binding.
  10. Otherwise, if assigning the value in the variable binding's value field to the specified variable requires allocating a resource that is presently unavailable, then the error-status is set to resourceUnavailable, and the error-index is set to the index of the failed variable binding.
  11. If processing the variable binding fails for a reason not listed above, then the error-status is set to genErr, and the error-index is set to the index of the failed variable binding.
  12. Otherwise, the validation of the variable binding succeeds.

At the end of the first phase, if every variable binding passed validation, then:

The error-status field of the Response-PDU is set to noError, and the error-index field is set to zero. ๐ŸŽ‰

For each variable binding in the request, the named variable gets created if it needs to be, and the specified value gets assigned to it. These assignments are meant to happen as if they all happened at the same time with respect to every other assignment in the same request. However, if the same variable is named more than once in a single request with different associated values, then which assignment actually sticks is implementation-specific. Messy, but that's on the request ๐Ÿซ 

If any of these assignments fail, even after all the earlier validations, then every other assignment gets undone, and the Response-PDU is changed so its error-status is commitFailed and its error-index is the index of the failed variable binding. ๐Ÿ˜ฌ

If and only if it's not possible to undo all of the assignments, then the Response-PDU gets changed so its error-status is undoFailed and its error-index is set to zero. Implementations are strongly encouraged to take every possible measure to avoid commitFailed or undoFailed. These two error-status codes are not a license to take the easy way out in an implementation. Do the work, bestie ๐Ÿ’ช

Finally, the generated Response-PDU is encapsulated into a message and sent to the originator of the SetRequest-PDU. ๐Ÿ“จ

4.2.6. The SNMPv2-Trap-PDU ๐Ÿšจ

A SNMPv2-Trap-PDU is generated and sent by an SNMPv2 entity acting in an agent role whenever an exceptional situation happens. It's the "hey, something's up!" message, and it's giving urgent ๐Ÿ”ฅ

To figure out where a SNMPv2-Trap-PDU goes, the sender checks the aclTable [5] for every entry that meets all of these conditions:

  1. The value of aclSubject refers to the SNMPv2 entity.
  2. The value of aclPrivileges allows for the SNMPv2-Trap-PDU.
  3. aclResources refers to a SNMPv2 context denoting local object resources, and the notification's administratively assigned name is present in the corresponding MIB view. ๐ŸŽ€

(That is, the set of entries in the viewTable [5] for which the instance of viewIndex has the same value as the aclResources's contextViewIndex, define a MIB view which contains the notification's administratively assigned name.) ๐ŸŽ€

Okay so the last check on the list is this one ๐Ÿ’…

  1. If the OBJECTS clause is present in the invocation of the corresponding NOTIFICATION-TYPE macro, then the correspondent variables are all present in the MIB view corresponding to aclResource.

Then, for each entry that passes these conditions, a SNMPv2-Trap-PDU is sent from aclSubject with context aclResources to aclTarget. โœจ The instance of snmpTrapNumbers [11] corresponding to aclTarget gets incremented, and that number is used as the request-id field of the SNMPv2-Trap-PDU. Then the variable-bindings field gets built like this:

  1. The first variable is sysUpTime.0 [9].
  2. The second variable is snmpTrapOID.0 [11], which contains the administratively assigned name of the notification.
  3. If the OBJECTS clause is present in the invocation of the corresponding NOTIFICATION-TYPE macro, then each corresponding variable is copied, in order, to the variable-bindings field.
  4. At the option of the SNMPv2 entity acting in an agent role, additional variables may follow in the variable-bindings field.

4.2.7. The InformRequest-PDU ๐Ÿฆ‹

An InformRequest-PDU is generated and transmitted at the request of an application in a SNMPv2 entity acting in a manager role, when that application wants to notify another application (in a SNMPv2 entity also acting in a manager role) about information in the MIB View of a party local to the sending application. ๐Ÿ’œ

The destination(s) that an InformRequest-PDU is sent to are determined by inspecting the snmpEventNotifyTable [12], or as specified by the requesting application. The first two variable bindings in the variable binding list of an InformRequest-PDU are sysUpTime.0 [9] and snmpEventID.i [12], in that order. If the OBJECTS clause is present in the invocation of the corresponding NOTIFICATION-TYPE macro, then each corresponding variable, as instantiated by this notification, is copied, in order, to the variable-bindings field.

Upon receipt of an InformRequest-PDU, the receiving SNMPv2 entity works out the size of a message that encapsulates a Response-PDU with the same values in its request-id, error-status, error-index and variable-bindings fields as the received InformRequest-PDU. ๐ŸŒธ If that message size is greater than either a local constraint or the maximum message size of the request's source party, then an alternate Response-PDU is generated and transmitted to the originator of the InformRequest-PDU, and processing of the InformRequest-PDU ends right after that. This alternate Response-PDU has the same values in its request-id field as the received InformRequest-PDU, its error-status field set to `tooBig', its error-index field set to zero, and an empty variable-bindings field. This alternate Response-PDU is then encapsulated into a message. If the size of the resulting message is less than or equal to both a local constraint and the maximum message size of the request's source party, it is transmitted to the originator of the InformRequest-PDU. Otherwise, the resulting message is discarded. Either way, processing of the InformRequest-PDU ends. ๐Ÿซถ

Otherwise, the receiving SNMPv2 entity does three things:

  1. presents its contents to the appropriate SNMPv2 application;
  2. generates a Response-PDU with the same values in its request-id and variable-bindings fields as the received InformRequest-PDU, with the value of its error-status field set to `noError' and the value of its error-index field set to zero; and
  3. transmits the generated Response-PDU to the originator of the InformRequest-PDU.

5. Acknowledgements โœจ

This document is based, in part, on RFC 1157. The mechanism for bulk retrieval is influenced by many experiments, including RFC1187 and also Greg Satz's work on SNMP over TCP. ๐Ÿ‘‘

Finally, the comments of the SNMP version 2 working group are gratefully acknowledged, so big thanks to these legends ๐ŸŒˆ

  • Beth Adams, Network Management Forum
  • Steve Alexander, INTERACTIVE Systems Corporation
  • David Arneson, Cabletron Systems
  • Toshiya Asaba
  • Fred Baker, ACC
  • Jim Barnes, Xylogics, Inc.
  • Brian Bataille
  • Andy Bierman, SynOptics Communications, Inc.
  • Uri Blumenthal, IBM Corporation
  • Fred Bohle, Interlink
  • Jack Brown
  • Theodore Brunner, Bellcore
  • Stephen F. Bush, GE Information Services
  • Jeffrey D. Case, University of Tennessee, Knoxville
  • John Chang, IBM Corporation
  • Szusin Chen, Sun Microsystems
  • Robert Ching
  • Chris Chiotasso, Ungermann-Bass
  • Bobby A. Clay, NASA/Boeing
  • John Cooke, Chipcom
  • Tracy Cox, Bellcore
  • Juan Cruz, Datability, Inc.
  • David Cullerot, Cabletron Systems
  • Cathy Cunningham, Microcom
  • James R. (Chuck) Davin, Bellcore
  • Michael Davis, Clearpoint
  • Mike Davison, FiberCom
  • Cynthia DellaTorre, MITRE
  • Taso N. Devetzis, Bellcore
  • Manual Diaz, DAVID Systems, Inc.
  • Jon Dreyer, Sun Microsystems
  • David Engel, Optical Data Systems
  • Mike Erlinger, Lexcel
  • Roger Fajman, NIH
  • Daniel Fauvarque, Sun Microsystems
  • Karen Frisa, CMU
  • Shari Galitzer, MITRE
  • Shawn Gallagher, Digital Equipment Corporation
  • Richard Graveman, Bellcore
  • Maria Greene, Xyplex, Inc.
  • Michel Guittet, Apple
  • Robert Gutierrez, NASA
  • Bill Hagerty, Cabletron Systems
  • Gary W. Haney, Martin Marietta Energy Systems
  • Patrick Hanil, Nokia Telecommunications
  • Matt Hecht, SNMP Research, Inc.
  • Edward A. Heiner, Jr., Synernetics Inc.
  • Susan E. Hicks, Martin Marietta Energy Systems
  • Geral Holzhauer, Apple
  • John Hopprich, DAVID Systems, Inc.
  • Jeff Hughes, Hewlett-Packard
  • Robin Iddon, Axon Networks, Inc.
  • David Itusak
  • Kevin M. Jackson, Concord Communications, Inc.
  • Ole J. Jacobsen, Interop Company
  • Ronald Jacoby, Silicon Graphics, Inc.
  • Satish Joshi, SynOptics Communications, Inc.
  • Frank Kastenholz, FTP Software
  • Mark Kepke, Hewlett-Packard
  • Ken Key, SNMP Research, Inc.
  • Zbiginew Kielczewski, Eicon
  • Jongyeoi Kim
  • Andrew Knutsen, The Santa Cruz Operation
  • Michael L. Kornegay, VisiSoft
  • Deirdre C. Kostik, Bellcore
  • Cheryl Krupczak, Georgia Tech
  • Mark S. Lewis, Telebit
  • David Lin
  • David Lindemulder, AT&T/NCR
  • Ben Lisowski, Sprint
  • David Liu, Bell-Northern Research
  • John Lunny, The Wollongong Group
  • Robert C. Lushbaugh Martin, Marietta Energy Systems
  • Michael Luufer, BBN
  • Carl Madison, Star-Tek, Inc.
  • Keith McCloghrie, Hughes LAN Systems
  • Evan McGinnis, 3Com Corporation
  • Bill McKenzie, IBM Corporation
  • Donna McMaster, SynOptics Communications, Inc.
  • John Medicke, IBM Corporation
  • Doug Miller, Telebit
  • Dave Minnich, FiberCom
  • Mohammad Mirhakkak, MITRE
  • Rohit Mital, Protools
  • George Mouradian, AT&T Bell Labs
  • Patrick Mullaney, Cabletron Systems
  • Dan Myers, 3Com Corporation
  • Rina Nathaniel, Rad Network Devices Ltd.
  • Hien V. Nguyen, Sprint
  • Mo Nikain
  • Tom Nisbet
  • William B. Norton, MERIT
  • Steve Onishi, Wellfleet Communications, Inc.
  • David T. Perkins, SynOptics Communications, Inc.
  • Carl Powell, BBN
  • Ilan Raab, SynOptics Communications, Inc.
  • Richard Ramons, AT&T
  • Venkat D. Rangan, Metric Network Systems, Inc.
  • Louise Reingold, Sprint
  • Sam Roberts, Farallon Computing, Inc.
  • Kary Robertson, Concord Communications, Inc.
  • Dan Romascanu, Lannet Data Communications Ltd.
  • Marshall T. Rose, Dover Beach Consulting, Inc.
  • Shawn A. Routhier, Epilogue Technology Corporation
  • Chris Rozman
  • Asaf Rubissa, Fibronics
  • Jon Saperia, Digital Equipment Corporation
  • Michael Sapich
  • Mike Scanlon, Interlan
  • Sam Schaen, MITRE
  • John Seligson, Ultra Network Technologies
  • Paul A. Serice, Corporation for Open Systems
  • Chris Shaw, Banyan Systems
  • Timon Sloane
  • Robert Snyder, Cisco Systems
  • Joo Young Song
  • Roy Spitier, Sprint
  • Einar Stefferud, Network Management Associates
  • John Stephens, Cayman Systems, Inc.
  • Robert L. Stewart, Xyplex, Inc. (chair)
  • Kaj Tesink, Bellcore
  • Dean Throop, Data General
  • Ahmet Tuncay, France Telecom-CNET
  • Maurice Turcotte, Racal Datacom
  • Warren Vik, INTERACTIVE Systems Corporation
  • Yannis Viniotis
  • Steven L. Waldbusser, Carnegie Mellon Universitty
  • Timothy M. Walden, ACC
  • Alice Wang, Sun Microsystems
  • James Watt, Newbridge
  • Luanne Waul, Timeplex
  • Donald E. Westlake III, Digital Equipment Corporation
  • Gerry White
  • Bert Wijnen, IBM Corporation
  • Peter Wilson, 3Com Corporation
  • Steven Wong, Digital Equipment Corporation
  • Randy Worzella, IBM Corporation
  • Daniel Woycke, MITRE
  • Honda Wu
  • Jeff Yarnell, Protools
  • Chris Young, Cabletron
  • Kiho Yum, 3Com Corporation

6. References ๐Ÿ“š

okay so this is the bibliography bestie ๐Ÿซถ every doc the spec leans on lives here, and I'm keeping every single entry exactly as written, no cap ๐Ÿ’…

  • [1] Information processing systems - Open Systems Interconnection - Specification of Abstract Syntax Notation One (ASN.1), International Organization for Standardization. International Standard 8824, (December, 1987). ๐Ÿงš
  • [2] Case, J., McCloghrie, K., Rose, M., and Waldbusser, S., "Structure of Management Information for version 2 of the Simple Network Management Protocol (SNMPv2)", RFC 1442, SNMP Research, Inc., Hughes LAN Systems, Dover Beach Consulting, Inc., Carnegie Mellon University, April 1993. ๐Ÿ’–
  • [3] Galvin, J., and McCloghrie, K., "Administrative Model for version 2 of the Simple Network Management Protocol (SNMPv2)", RFC 1445, Trusted Information Systems, Hughes LAN Systems, April 1993. โœจ
  • [4] Case, J., McCloghrie, K., Rose, M., and Waldbusser, S., "Textual Conventions for version 2 of the the Simple Network Management Protocol (SNMPv2)", RFC 1443, SNMP Research, Inc., Hughes LAN Systems, Dover Beach Consulting, Inc., Carnegie Mellon University, April 1993. ๐ŸŒธ
  • [5] McCloghrie, K., and Galvin, J., "Party MIB for version 2 of the Simple Network Management Protocol (SNMPv2)", RFC 1447, Hughes LAN Systems, Trusted Information Systems, April 1993. ๐ŸŽ€
  • [6] C. Kent, J. Mogul, Fragmentation Considered Harmful, Proceedings, ACM SIGCOMM '87, Stowe, VT, (August 1987). ๐Ÿ”ฅ
  • [7] Case, J., McCloghrie, K., Rose, M., and Waldbusser, S., "Transport Mappings for version 2 of the Simple Network Management Protocol (SNMPv2)", RFC 1449, SNMP Research, Inc., Hughes LAN Systems, Dover Beach Consulting, Inc., Carnegie Mellon University, April 1993. ๐Ÿฆ‹
  • [8] Postel, J., "User Datagram Protocol", STD 6, RFC 768, USC/Information Sciences Institute, August 1980. ๐Ÿ‘‘
  • [9] McCloghrie, K., and Rose, M., "Management Information Base for Network Management of TCP/IP-based internets: MIB-II", STD 17, RFC 1213, March 1991. ๐ŸŒˆ
  • [10] Case, J., McCloghrie, K., Rose, M., and Waldbusser, S., "Coexistence between version 1 and version 2 of the Internet-standard Network Management Framework", RFC 1452, SNMP Research, Inc., Hughes LAN Systems, Dover Beach Consulting, Inc., Carnegie Mellon University, April 1993. ๐Ÿ’œ
  • [11] Case, J., McCloghrie, K., Rose, M., and Waldbusser, S., "Management Information Base for version 2 of the Simple Network Management Protocol (SNMPv2)", RFC 1450, SNMP Research, Inc., Hughes LAN Systems, Dover Beach Consulting, Inc., Carnegie Mellon University, April 1993. โญ
  • [12] Case, J., McCloghrie, K., Rose, M., and Waldbusser, S., "Manager-to-Manager Management Information Base", RFC 1451, SNMP Research, Inc., Hughes LAN Systems, Dover Beach Consulting, Inc., Carnegie Mellon University, April 1993. ๐Ÿ“

7. Security Considerations ๐Ÿ’€

okay this one is short but it is SERIOUS bestie ๐Ÿ˜ญ the memo itself says security issues are not discussed in it. so no security analysis here, just the honest truth from the source ๐Ÿ™ˆ

Security issues are not discussed in this memo.


8. Authors' Addresses ๐Ÿ’Œ

these are the humans behind the spec, the ones who made it happen โœจ every name, office, phone and email is kept exactly as the original lists it ๐Ÿซถ

Jeffrey D. Case
SNMP Research, Inc.
3001 Kimberlin Heights Rd.
Knoxville, TN  37920-9716
US

Phone: +1 615 573 1434
Email: case@snmp.com


Keith McCloghrie
Hughes LAN Systems
1225 Charleston Road
Mountain View, CA  94043
US

Phone: +1 415 966 7934
Email: kzm@hls.com


Marshall T. Rose
Dover Beach Consulting, Inc.
420 Whisman Court
Mountain View, CA  94043-2186
US

Phone: +1 415 968 1052
Email: mrose@dbc.mtview.ca.us

Steven Waldbusser
Carnegie Mellon University
4910 Forbes Ave
Pittsburgh, PA  15213
US

Phone: +1 412 268 6628
Email: waldbusser@cmu.edu

and that's the whole spec bestie ๐ŸŒˆ๐Ÿ‘‘ we made it through all seven parts together ๐Ÿฅน๐Ÿ’–

๐Ÿ“ฆ where this page comes from