RFC 1228

SNMP-DPI: Simple Network Management Protocol Distributed Program Interface

๐Ÿ“… published May 1991, from ietf

RFC 1228: SNMP-DPI โœจ

Simple Network Management Protocol Distributed Program Interface ๐Ÿฆ‹

Okay bestie, the title block first ๐Ÿ’… This is from the Network Working Group and it's Request for Comments 1228. The authors are G. Carpenter and B. Wijnen from the T.J. Watson Research Center, IBM Corp. ๐Ÿข It came out in May 1991 ๐Ÿ“… and its category is Experimental. The title block doesn't list anything it obsoletes or updates, so there's nothing to chase there ๐ŸŒธ


Status of this Memo ๐Ÿ“œ

So this RFC describes a protocol that International Business Machines Corporation (IBM) has been putting into most of its SNMP agents ๐Ÿ–ฅ๏ธ The point is to let MIBs get extended on the fly. It's an Experimental Protocol for the Internet community, and they're literally asking for discussion and suggestions for improvement ๐Ÿ—ฃ๏ธโœจ For the actual standardization state and status, check the current edition of the "IAB Official Protocol Standards" ๐Ÿ“š And distribution of this memo is unlimited, so share away ๐Ÿ’–

ABSTRACT ๐Ÿฆ‹

The Simple Network Management Protocol (SNMP) [1] Distributed Program Interface (DPI) is an extension to SNMP agents that lets end-users dynamically add, delete or replace management variables in the local Management Information Base ๐ŸŒˆ and the best part is the agent doesn't need to be recompiled ๐Ÿ™ˆ You get this by writing a so-called sub-agent that talks to the agent over the SNMP-DPI ๐Ÿงš

For the author of a sub-agent, SNMP-DPI means you don't need to know the nitty-gritty of ASN.1 [2] or SNMP PDU (Protocol Data Unit) encoding and decoding [1, 3] ๐Ÿซถ iconic, honestly ๐Ÿ‘‘

This protocol has been in use within IBM since 1989 and it's included in the SNMP agents for VM, MVS and OS/2 ๐Ÿ’œ

There's also sample sub-agent code and implementation examples you can grab with anonymous FTP from the University of Toronto ๐Ÿ“

MOTIVATION โœจ

The Simple Network Management Protocol [1] is a protocol for doing operations on a collection of variables ๐Ÿ’… That collection is the Management Information Base (MIB), and a core set of variables was already defined in [4, 5]. The thing is, the MIB design makes room for extension of that core set ๐ŸŒธ So an enterprise or an individual can define their own variables that represent info they actually care about ๐ŸŽ€

An example of a potentially interesting variable that is NOT in the core MIB would be CPU utilization (percent busy) ๐Ÿ’ป Unfortunately, conventional SNMP agent implementations give an end-user no way to make new variables available ๐Ÿ˜ญ

That's where the SNMP DPI comes in ๐Ÿฆ‹ It's a light-weight mechanism that lets a process register the existence of a MIB variable with the SNMP agent. When requests for that variable come in, the agent passes the query along to the process acting as a sub-agent. The sub-agent sends back an appropriate answer to the SNMP agent, and then the SNMP agent packages up an SNMP response packet and sends it back to the remote network management station that started the request ๐Ÿงšโ€โ™€๏ธ

The wild part is that none of the remote network management stations know the SNMP agent is calling on other processes to get an answer. As far as they can tell, there's only one network management application running on the host ๐Ÿ™ˆ Main character energy for the agent, no cap ๐Ÿ‘‘

THEORY OF OPERATION ๐Ÿ”ฎ

CONNECTION ESTABLISHMENT ๐Ÿ”Œ

So communication between the SNMP Agent and its clients (sub-agents) happens over a stream connection ๐ŸŒŠ That's typically a TCP connection, but other stream-oriented transport mechanisms can work too. For example, the VM SNMP agent allows DPI connections over IUCV (Inter-User Communications Vehicle) [6, 7] ๐Ÿ’– Apart from the connection establishment procedure, the protocol is identical in these environments, which is lowkey amazing ๐Ÿซถ

REGISTRATION ๐Ÿ“

No matter which connection-oriented transport you use, once a sub-agent has connected to the SNMP agent, it registers the set of variables it supports ๐ŸŽ€ Finally, after all the variable classes are registered, the sub-agent waits for requests from the SNMP agent or generates traps as required ๐Ÿฆ‹

DPI ARCHITECTURE ๐Ÿ—๏ธ

There are three requests the SNMP agent can start: GET, GET-NEXT and SET. These line up directly with the three SNMP requests a network management station can make ๐Ÿ’… The sub-agent answers any request with a RESPONSE packet โœจ

There are currently two requests a sub-agent can start: REGISTER and TRAP ๐Ÿ”ฅ


Here's the overview in picture form ๐Ÿ–ผ๏ธ Figure 1 is a big diagram, so it's shown exactly as written:

*---------------------------------*
|  SNMP Network                   |
|  Management Station             |
|                                 |
|---------------------------------|
|  SNMP Protocol                  |
*---------------------------------*
      A      | Get         A
      |      | GetNext     |  GetResponse
 Trap |      | Set         |
      |      V             |
*---------------------------------*            *----------------------*
|     SNMP Protocol               |            |   DPI Interface      |
|---------------------------------|  Reply     |    *-----------------|
|                        |        |<-----------|    |                 |
|     SNMP Agent         |        |            |    |   Client        |
|     A      *-----------+->      | MIB query  |    |                 |
|     |      | Get/Set   |        |----------->|    |     or          |
| Trap|      | info      |  SNMP  |            |    |                 |
|-----+------+-------*   |        |  trap      |    |   SNMP          |
|     |      V       |   |  DPI   |<-----------|    |   Sub-Agent     |
| TCP/IP layers,     |   |        |            |    |                 |
| Kernel             |   |        |<-----------|    |                 |
*---------------------------------*  Register  *----------------------*

Figure 1. SNMP DPI overview ๐ŸŒˆ

Remarks for Figure 1 (the important bits, babe) ๐Ÿ’œ

  • The SNMP agent talks to the SNMP manager using the standard SNMP protocol ๐Ÿ’Œ
  • The SNMP agent talks to the TCP/IP layers and the kernel (operating system) in an implementation-dependent way. It can potentially implement the standard MIB view this way ๐Ÿ–ฅ๏ธ
  • An SNMP sub-agent runs as its own separate process, and it could even be on another machine ๐Ÿงš It can register objects with the SNMP agent โœจ
  • The SNMP agent decodes SNMP packets. If a packet holds a Get/GetNext or Set request for an object that a sub-agent registered, the agent sends the request to the sub-agent in the matching query packet ๐Ÿ“ฆ
  • The SNMP sub-agent sends its responses back in a RESPONSE packet ๐Ÿ’–
  • The SNMP agent then encodes the reply into an SNMP packet and sends it back to the requesting SNMP manager ๐Ÿฆ‹
  • If the sub-agent wants to report an important state change, it sends a TRAP packet to the SNMP agent. The agent encodes that into an SNMP trap packet and sends it to the manager(s) ๐Ÿšจ

SNMP DPI PROTOCOL ๐Ÿ’…

This section is the actual protocol used between the SNMP agent and the sub-agents ๐Ÿ” This info has never been published before, so this is the exclusive tea ๐Ÿซ–๐Ÿ”ฅ

CONNECTION ESTABLISHMENT ๐Ÿ”Œ

In a TCP/IP environment, the SNMP agent listens on an arbitrary TCP port for a connection request from a sub-agent. This is important to get: a well-known port is NOT used. Every time the SNMP agent starts up, it could end up using a different TCP port ๐ŸŽฒ Plot twist, I know ๐Ÿ™ˆ

So a sub-agent has to figure out that port number before it can connect ๐Ÿ—๏ธ It learns the port from the agent by sending it one conventional SNMP get-request PDU ๐Ÿ“จ The port number is kept by the SNMP agent as the object whose identifier is 1.3.6.1.4.1.2.2.1.1.0. This variable is registered under the IBM enterprise-specific tree ๐Ÿข The SNMP agent replies with a conventional SNMP response PDU that contains the port number to use. The sub-agent examines that response, pulls out the port number, and then connects to the specified port โœจ

Now, it might look like the sub-agent has to be able to create and parse SNMP packets, but nope, it doesn't ๐Ÿ™…โ€โ™€๏ธ The DPI Application Program Interface (API) has a library routine called query_DPI_port() that generates and parses the required SNMP packets for you ๐Ÿงš It's very small (under 100 lines of C), so it doesn't really bloat any sub-agent ๐Ÿ’–

For completeness, byte-by-byte descriptions of the packets generated by the SNMP DPI API routine query_DPI_port() are given below. Honestly, this is probably of little interest to most readers, and reading the source to query_DPI_port() gives you much of the same info ๐Ÿ™ˆ Still, here it is, bestie ๐Ÿ’…

SNMP PDU TO GET THE AGENT'S DPI PORT ๐Ÿ“ฆ

As noted, before a TCP connection to the SNMP agent can be made, the sub-agent must learn which TCP port the agent is listening on ๐ŸŽ€ To do that, it can issue an SNMP GET for the IBM enterprise-specific variable 1.3.6.1.4.1.2.2.1.1.0 ๐Ÿ—๏ธ

NOTE: the object instance of ".0" is included for clarity in this document ๐Ÿ’œ

The SNMP PDU can be built the way shown below. This PDU must be sent to UDP port 161 on the host where the agent runs (probably the same host where the sub-agent runs) ๐ŸŒธ

+----------------------------------------------------------------------+
| Table 1. SNMP PDU for GET DPI_port.  This is the layout of an SNMP   |
|          PDU for GET DPI_port                                        |
+-----------------+-----------------+----------------------------------+
| OFFSET          | VALUE           | FIELD                            |
+-----------------+-----------------+----------------------------------+
| 0               | 0x30            | ASN.1 header                     |
+-----------------+-----------------+----------------------------------+
| 1               | 34 + len        | pdu_length, see formula below    |
+-----------------+-----------------+----------------------------------+
| 2               | 0x02 0x01 0x00  | version (integer, length=1,      |
|                 | 0x04            | value=0), community name         |
|                 |                 | (string)                         |
+-----------------+-----------------+----------------------------------+
| 6               | len             | length of community name         |
+-----------------+-----------------+----------------------------------+
| 7               | community name  |                                  |
+-----------------+-----------------+----------------------------------+
| 7 + len         | 0xa0 0x1b       | SNMP GET request:                |
|                 |                 | request_type=0xa0, length=0x1b   |
+-----------------+-----------------+----------------------------------+
| 7 + len + 2     | 0x02 0x01 0x01  | SNMP request ID: integer,        |
|                 |                 | length=1, ID=1                   |
+-----------------+-----------------+----------------------------------+
| 7 + len + 5     | 0x02 0x01 0x00  | SNMP error status: integer,      |
|                 |                 | length=1, error=0                |
+-----------------+-----------------+----------------------------------+
| 7 + len + 8     | 0x02 0x01 0x00  | SNMP index: integer, length=1,   |
|                 |                 | index=0                          |
+-----------------+-----------------+----------------------------------+
| 7 + len + 11    | 0x30 0x10       | Varbind list, length=0x10        |
+-----------------+-----------------+----------------------------------+
| 7 + len + 13    | 0x30 0x0e       | Varbind, length=0x0e             |
+-----------------+-----------------+----------------------------------+
| 7 + len + 15    | 0x06 0x0a       | Object ID, length=0x0a           |
+-----------------+-----------------+----------------------------------+
| 7 + len + 17    | 0x2b 0x06 0x01  | Object instance:                 |
|                 | 0x04 0x01 0x02  | 1.3.6.1.4.1.2.2.1.1.0            |
|                 | 0x02 0x01 0x01  |                                  |
|                 | 0x00            |                                  |
+-----------------+-----------------+----------------------------------+
| 7 + len + 27    | 0x05 0x00       | null value, length=0             |
+-----------------+-----------------+----------------------------------+
+----------------------------------------------------------------------+

Okay so the length formula for the pdu_length field goes like this ๐Ÿ’– โœจ

  pdu_length =   length of version field and string tag (4 bytes)

             +   length of community length field (1 byte)
             +   length of community name (depends...)
             +   length of SNMP GET request (29 bytes)

             =   34 + length of community name

SNMP PDU CONTAINING THE RESPONSE TO THE GET ๐ŸŽ€

Okay bestie, so if no errors occurred ๐Ÿ™ˆ, the port comes back in the last 2 octets of the received packet. Here's the layout, and it's giving very organized โœจ

+----------------------------------------------------------------------+
| Table 2. SNMP RESPONSE PDU for GET of Agent's DPI port.  This is the |
|          layout of an SNMP RESPONSE PDU for GET DPI_port             |
+-----------------+-----------------+----------------------------------+
| OFFSET          | VALUE           | FIELD                            |
+-----------------+-----------------+----------------------------------+
| 0               | 0x30            | ASN.1 header                     |
+-----------------+-----------------+----------------------------------+
| 1               | 36 + len        | length, see formula below        |
+-----------------+-----------------+----------------------------------+
| 2               | 0x02 0x01 0x00  | version (integer, length=1,      |
|                 | 0x04            | value=0), community name         |
|                 |                 | (string)                         |
+-----------------+-----------------+----------------------------------+
| 6               | len             | length of community name         |
+-----------------+-----------------+----------------------------------+
| 7               | community name  |                                  |
+-----------------+-----------------+----------------------------------+
| 7 + len         | 0xa2 0x1d       | SNMP RESPONSE:                   |
|                 |                 | request_type=0xa2, length=0x1d   |
+-----------------+-----------------+----------------------------------+
| 7 + len + 2     | 0x02 0x01 0x01  | SNMP request ID: integer,        |
|                 |                 | length=1, ID=1                   |
+-----------------+-----------------+----------------------------------+
| 7 + len + 5     | 0x02 0x01 0x00  | SNMP error status: integer,      |
|                 |                 | length=1, error=0                |
+-----------------+-----------------+----------------------------------+
| 7 + len + 8     | 0x02 0x01 0x00  | SNMP index: integer, length=1,   |
|                 |                 | index=0                          |
+-----------------+-----------------+----------------------------------+
| 7 + len + 11    | 0x30 0x12       | Varbind list, length=0x12        |
+-----------------+-----------------+----------------------------------+
| 7 + len + 13    | 0x30 0x10       | Varbind, length=0x10             |
+-----------------+-----------------+----------------------------------+
| 7 + len + 15    | 0x06 0x0a       | Object ID, length=0x0a           |
+-----------------+-----------------+----------------------------------+
+----------------------------------------------------------------------+
+----------------------------------------------------------------------+
| Table 2. SNMP RESPONSE PDU for GET of Agent's DPI port.  This is the |
|          layout of an SNMP RESPONSE PDU for GET DPI_port             |
+-----------------+-----------------+----------------------------------+
| OFFSET          | VALUE           | FIELD                            |
+-----------------+-----------------+----------------------------------+
| 7 + len + 17    | 0x2b 0x06 0x01  | Object instance:                 |
|                 | 0x04 0x01 0x02  | 1.3.6.1.4.1.2.2.1.1.0            |
|                 | 0x02 0x01 0x01  |                                  |
|                 | 0x00            |                                  |
+-----------------+-----------------+----------------------------------+
| 7 + len + 27    | 0x02 0x02       | integer, length=2                |
+-----------------+-----------------+----------------------------------+
| 7 + len + 29    | msb lsb         | port number (msb, lsb)           |
+-----------------+-----------------+----------------------------------+
+----------------------------------------------------------------------+

And the pdu_length formula for the response is a bit different, so pay attention ๐Ÿ‘€

  pdu_length =   length of version field and string tag (4 bytes)
             +   length of community length field (1 byte)
             +   length of community name (depends...)
             +   length of SNMP RESPONSE (31 bytes)

             =   36 + length of community name

SNMP DPI PACKET FORMATS ๐Ÿ“ฆ

Every request to, or response from, the agent gets built as a "packet" and then written to the stream. Each packet starts with the length of the data still left in it ๐Ÿง The length is stored in network byte order, so most significant byte first and least significant last. The receiver reads the packet with something kinda like this:

      unsigned char len_bfr[2];
      char *bfr;
      int len;

      read(fd,len_bfr,2);
      len = len_bfr[0] * 256 + len_bfr[1];
      bfr = malloc(len);
      read(fd,bfr,len);
NOTE: the example above makes no provisions for error handling ๐Ÿ’€ and it doesn't handle a read returning less data than requested. This is not a suggested coding style, so please don't copy it into your code, bestie ๐Ÿ™…โ€โ™€๏ธ

The first part of every packet tells you which application protocol is in use, plus some version info. The protocol major version is meant to say, in broad strokes, what version of the protocol this is. The protocol minor version identifies major incompatible versions. The protocol release covers incremental changes. The constants that are valid for these fields are defined in Table 10 on page 18 ๐ŸŒธ

The next field that every packet has is the packet type. It tells you what kind of packet you're dealing with, like SNMP DPI GET, GET-NEXT, SET, TRAP, RESPONSE or REGISTER. The allowed values for it are defined in Table 11 on page 18 ๐Ÿฆ‹

+----------------------------------------------------------------------+
| Table 3. SNMP DPI packet header.  This header is present in all      |
|          packets.                                                    |
+-------------+--------------------------------------------------------+
| OFFSET      | FIELD                                                  |
+-------------+--------------------------------------------------------+
| 0           | packet length to follow (MSB)                          |
+-------------+--------------------------------------------------------+
| 1           | packet length to follow (LSB)                          |
+-------------+--------------------------------------------------------+
| 2           | protocol major version                                 |
+-------------+--------------------------------------------------------+
| 3           | protocol minor version                                 |
+-------------+--------------------------------------------------------+
| 4           | protocol release                                       |
+-------------+--------------------------------------------------------+
| 5           | packet type                                            |
+-------------+--------------------------------------------------------+
+----------------------------------------------------------------------+

From here on, the contents of the packet are defined by whichever protocol is in use. This section covers two things ๐Ÿ’…

  • the structure of packets for the SNMP DPI protocol, version 1.0.
  • the constants defined for this version of the protocol.

REGISTER ๐Ÿงš

So an SNMP sub-agent wants to register a branch in the MIB tree? Then it sends an SNMP DPI REGISTER packet to the agent ๐ŸŒˆ

That packet has the standard SNMP DPI header plus some REGISTER-specific data. That data is basically a null terminated string holding the object ID in dotted ASN.1 notation, with a trailing dot! โœจ

+----------------------------------------------------------------------+
| Table 4. SNMP DPI REGISTER packet.  This is the layout of an SNMP    |
|          DPI REGISTER packet                                         |
+-------------+--------------------------------------------------------+
| OFFSET      | FIELD                                                  |
+-------------+--------------------------------------------------------+
| 0           | packet length to follow (MSB)                          |
+-------------+--------------------------------------------------------+
| 1           | packet length to follow (LSB)                          |
+-------------+--------------------------------------------------------+
| 2           | protocol major version                                 |
+-------------+--------------------------------------------------------+
| 3           | protocol minor version                                 |
+-------------+--------------------------------------------------------+
| 4           | protocol release                                       |
+-------------+--------------------------------------------------------+
| 5           | packet type = SNMP_DPI_REGISTER                        |
+-------------+--------------------------------------------------------+
| 6           | null terminated object ID                              |
+-------------+--------------------------------------------------------+
+----------------------------------------------------------------------+

GET ๐Ÿ”

When the SNMP agent gets a PDU with an SNMP GET request for a variable that a sub-agent registered with it, the agent passes an SNMP DPI GET packet over to that sub-agent ๐Ÿ’œ

That packet has the standard SNMP DPI header plus GET-specific data. That data is basically a null terminated string holding the object ID in dotted ASN.1 notation.

+----------------------------------------------------------------------+
| Table 5. SNMP DPI GET packet.  This is the layout of an SNMP DPI GET |
|          packet                                                      |
+-------------+--------------------------------------------------------+
| OFFSET      | FIELD                                                  |
+-------------+--------------------------------------------------------+
| 0           | packet length to follow (MSB)                          |
+-------------+--------------------------------------------------------+
| 1           | packet length to follow (LSB)                          |
+-------------+--------------------------------------------------------+
| 2           | protocol major version                                 |
+-------------+--------------------------------------------------------+
| 3           | protocol minor version                                 |

GET-NEXT ๐Ÿงš

okay so when the SNMP agent gets a PDU with a GET-NEXT request for a variable a sub-agent might be in charge of, it passes an SNMP DPI GET-NEXT packet down to that sub-agent ๐Ÿ’…

this packet has the usual DPI header plus some GET-NEXT extras. the extras are two null terminated strings. the first is the object ID in dotted ASN.1 notation, and the second is the group ID, also in dotted ASN.1 notation ๐Ÿซถ

+----------------------------------------------------------------------+
| Table 6. SNMP DPI GET NEXT packet.  This is the layout of an SNMP    |
|          DPI GET NEXT packet                                         |
+-------------+--------------------------------------------------------+
| OFFSET      | FIELD                                                  |
+-------------+--------------------------------------------------------+
| 0           | packet length to follow (MSB)                          |
+-------------+--------------------------------------------------------+
| 1           | packet length to follow (LSB)                          |
+-------------+--------------------------------------------------------+
| 2           | protocol major version                                 |
+-------------+--------------------------------------------------------+
| 3           | protocol minor version                                 |
+-------------+--------------------------------------------------------+
| 4           | protocol release                                       |
+-------------+--------------------------------------------------------+
| 5           | packet type = SNMP_DPI_GET_NEXT                        |
+-------------+--------------------------------------------------------+
| 6           | null terminated object ID                              |
+-------------+--------------------------------------------------------+
| 6 + len     | null terminated group ID                               |
+-------------+--------------------------------------------------------+
| NOTE:  len=strlen(object ID)+1                                       |
+----------------------------------------------------------------------+

SET ๐Ÿ’Œ

when the agent gets a SET request for a variable that a sub-agent has registered with it, the agent sends an SNMP DPI SET packet over to that sub-agent โœจ

the packet has the standard DPI header plus SET-specific data. basically that's a null terminated string holding the object ID in ASN.1 notation, followed by the type, the value length, and the value to be set. the permitted types for the type field live in Table 12 on page 19. integers get sent as 4-byte elements in network byte order, so most significant byte first and least significant byte last ๐Ÿ”ข

+----------------------------------------------------------------------+
| Table 7. SNMP DPI SET packet.  This is the layout of an SNMP DPI SET |
|          packet                                                      |
+-------------+--------------------------------------------------------+
| OFFSET      | FIELD                                                  |
+-------------+--------------------------------------------------------+
| 0           | packet length to follow (MSB)                          |
+-------------+--------------------------------------------------------+
| 1           | packet length to follow (LSB)                          |
+-------------+--------------------------------------------------------+
| 2           | protocol major version                                 |
+-------------+--------------------------------------------------------+
| 3           | protocol minor version                                 |
+-------------+--------------------------------------------------------+
| 4           | protocol release                                       |
+-------------+--------------------------------------------------------+
| 5           | packet type = SNMP_DPI_SET                             |
+-------------+--------------------------------------------------------+
| 6           | null terminated object ID                              |
+-------------+--------------------------------------------------------+
| 6 + len     | SNMP Variable Type Value                               |
+-------------+--------------------------------------------------------+
| 6 + len + 1 | Length of value (MSB)                                  |
+-------------+--------------------------------------------------------+
| 6 + len + 2 | Length of value (LSB)                                  |
+-------------+--------------------------------------------------------+
| 6 + len + 3 | Value                                                  |
+-------------+--------------------------------------------------------+
| NOTE:  len=strlen(object ID)+1                                       |
+----------------------------------------------------------------------+

RESPONSE ๐Ÿ’ฌ

an SNMP sub-agent must respond to a GET, GET_NEXT or SET request it got from the agent (unless it fails or has a bug). so it sends an SNMP DPI RESPONSE packet back to the agent ๐Ÿซก

this packet has the standard DPI header plus RESPONSE-specific data. that's basically an error_code and, if there was no error, the name/type/value tuple for the returned object. the tuple is described by a string holding the object ID in ASN.1 notation, plus the type, value length and value of the object that got manipulated. the permitted types for the type field are defined in Table 12 on page 19. integers are sent as 4-byte elements in network byte order (most significant byte first, least significant byte last) ๐Ÿ’œ

+----------------------------------------------------------------------+
| Table 8. SNMP DPI RESPONSE packet.  This is the layout of an SNMP    |
|          DPI RESPONSE packet                                         |
+-------------+--------------------------------------------------------+
| OFFSET      | FIELD                                                  |
+-------------+--------------------------------------------------------+
| 0           | packet length to follow (MSB)                          |
+-------------+--------------------------------------------------------+
| 1           | packet length to follow (LSB)                          |
+-------------+--------------------------------------------------------+
| 2           | protocol major version                                 |
+-------------+--------------------------------------------------------+
| 3           | protocol minor version                                 |
+-------------+--------------------------------------------------------+
| 4           | protocol release                                       |
+-------------+--------------------------------------------------------+
| 5           | packet type = SNMP_DPI_RESPONSE                        |
+-------------+--------------------------------------------------------+
| 6           | SNMP error code                                        |
+-------------+--------------------------------------------------------+
| 7           | null terminated object ID                              |
+-------------+--------------------------------------------------------+
| 7 + len     | SNMP Variable Type Value                               |
+-------------+--------------------------------------------------------+
| 7 + len + 1 | Length of value (MSB)                                  |
+-------------+--------------------------------------------------------+
| 7 + len + 2 | Length of value (LSB)                                  |
+-------------+--------------------------------------------------------+
| 7 + len + 3 | Value                                                  |
+-------------+--------------------------------------------------------+
| NOTE:  len=strlen(object ID)+1                                       |
+----------------------------------------------------------------------+

TRAP ๐Ÿšจ

a sub-agent can ask the agent to generate a TRAP by sending an SNMP DPI TRAP packet to the agent ๐Ÿ“ฃ

this packet has the standard DPI header plus TRAP-specific data. that's basically the generic and specific trap codes, plus a name/type/value tuple. the tuple is described by a string holding the object ID in ASN.1 notation, plus the type, value length and value of the object being sent in the trap. the permitted types for the type field are defined in Table 12 on page 19. integers are sent as 4-byte elements in network byte order (most significant byte first, least significant byte last) ๐ŸŒธ

+----------------------------------------------------------------------+
| Table 9. SNMP DPI TRAP packet.  This is the layout of an SNMP DPI    |
|          TRAP packet                                                 |
+-------------+--------------------------------------------------------+
| OFFSET      | FIELD                                                  |
+-------------+--------------------------------------------------------+
| 0           | packet length to follow (MSB)                          |
+-------------+--------------------------------------------------------+
| 1           | packet length to follow (LSB)                          |
+-------------+--------------------------------------------------------+
| 2           | protocol major version                                 |
+-------------+--------------------------------------------------------+
| 3           | protocol minor version                                 |
+-------------+--------------------------------------------------------+
| 4           | protocol release                                       |
+-------------+--------------------------------------------------------+
| 5           | packet type - SNMP_DPI_TRAP                            |
+-------------+--------------------------------------------------------+
| 6           | SNMP generic trap code                                 |
+-------------+--------------------------------------------------------+
| 7           | SNMP specific trap code                                |
+-------------+--------------------------------------------------------+
| 8           | null terminated object ID                              |
+-------------+--------------------------------------------------------+
| 8 + len     | SNMP Variable Type Value                               |
+-------------+--------------------------------------------------------+
| 8 + len + 1 | Length of value (MSB)                                  |
+-------------+--------------------------------------------------------+
| 8 + len + 2 | Length of value (LSB)                                  |
+-------------+--------------------------------------------------------+
| 8 + len + 3 | Value                                                  |

+-------------+--------------------------------------------------------+

NOTE: len=strlen(object ID)+1

+----------------------------------------------------------------------+ +----------------------------------------------------------------------+

CONSTANTS AND VALUES โœจ

okay so this section lists all the constants that got defined for this version of the SNMP DPI Protocol ๐ŸŽ€ bookmark it, bestie ๐Ÿ’…

PROTOCOL VERSION AND RELEASE VALUES

+----------------------------------------------------------------------+
| Table 10. Protocol version and release values                        |
+-----------------------------------+----------------------------------+
| FIELD                             | VALUE                            |
+-----------------------------------+----------------------------------+
| protocol major version            | 2 (SNMP DPI protocol)            |
+-----------------------------------+----------------------------------+
| protocol minor version            | 1 (version 1)                    |
+-----------------------------------+----------------------------------+
| protocol release                  | 0 (release 0)                    |
+-----------------------------------+----------------------------------+
+----------------------------------------------------------------------+

any other values are currently undefined ๐Ÿ™ˆ so if you see something not on the table, it's not official yet, no cap

PACKET TYPE VALUES

the packet type field can have the following values ๐Ÿฆ‹

+----------------------------------------------------------------------+
| Table 11. Valid values for the packet type field                     |
+-------+--------------------------------------------------------------+
| VALUE | PACKET TYPE                                                  |
+-------+--------------------------------------------------------------+
| 1     | SNMP_DPI_GET                                                 |
+-------+--------------------------------------------------------------+
| 2     | SNMP_DPI_GET_NEXT                                            |
+-------+--------------------------------------------------------------+
| 3     | SNMP_DPI_SET                                                 |
+-------+--------------------------------------------------------------+
| 4     | SNMP_DPI_TRAP                                                |
+-------+--------------------------------------------------------------+
| 5     | SNMP_DPI_RESPONSE                                            |
+-------+--------------------------------------------------------------+
| 6     | SNMP_DPI_REGISTER                                            |
+-------+--------------------------------------------------------------+
+----------------------------------------------------------------------+

VARIABLE TYPE VALUES

the variable type field can have these values, and the vibes are immaculate ๐ŸŒˆ

+----------------------------------------------------------------------+
| Table 12. Valid values for the Value Type field                      |
+-------+--------------------------------------------------------------+
| VALUE | VALUE TYPE                                                   |
+-------+--------------------------------------------------------------+
| 0     | text representation                                          |
+-------+--------------------------------------------------------------+
| 129   | number (integer)                                             |
+-------+--------------------------------------------------------------+
| 2     | octet string                                                 |
+-------+--------------------------------------------------------------+
| 3     | object identifier                                            |
+-------+--------------------------------------------------------------+
| 4     | empty (no value)                                             |
+-------+--------------------------------------------------------------+
| 133   | internet address                                             |
+-------+--------------------------------------------------------------+
| 134   | counter (unsigned)                                           |
+-------+--------------------------------------------------------------+
| 135   | gauge (unsigned)                                             |
+-------+--------------------------------------------------------------+
| 136   | time ticks (1/100ths seconds)                                |
+-------+--------------------------------------------------------------+
| 9     | display string                                               |
+-------+--------------------------------------------------------------+
+----------------------------------------------------------------------+
NOTE: here's the key bit ๐Ÿ”‘ any field holding a value stored as a 4-byte integer gets its base type value ORed with 128. so 129 is just number (integer) with that 128 bit flipped on, and 133, 134, 135 and 136 work the same way ๐Ÿ’œ

Error Code Values for SNMP Agent Detected Errors

the error code can be one of these ๐Ÿ’€

+----------------------------------------------------------------------+
| Table 13. Valid values for the SNMP Agent Minor Error Code field     |
+-------+--------------------------------------------------------------+
| VALUE | SNMP AGENT ERROR CODE                                        |
+-------+--------------------------------------------------------------+
| 0     | no error                                                     |
+-------+--------------------------------------------------------------+
| 1     | too big                                                      |
+-------+--------------------------------------------------------------+
| 2     | no such name                                                 |
+-------+--------------------------------------------------------------+
| 3     | bad value                                                    |
+-------+--------------------------------------------------------------+
| 4     | read only                                                    |
+-------+--------------------------------------------------------------+
| 5     | general error                                                |
+-------+--------------------------------------------------------------+
+----------------------------------------------------------------------+

SNMP DPI APPLICATION PROGRAM INTERFACE ๐Ÿ‘‘

this part documents an API that implements the SNMP DPI ๐Ÿงš it's been published before, [6, 8], but this info is more current as of May 14, 1991, so this is the one you want ๐Ÿ’–

OVERVIEW OF REQUEST PROCESSING

GET PROCESSING

okay so a GET request is the easiest one to process ๐Ÿ“ when the DPI packet gets parsed, the parse tree holds the object ID of the variable being requested.

if the sub-agent doesn't support that object, it returns the error indication "no such name". no name/type/value info comes back at all ๐Ÿ˜ญ

unsigned char *cp;

cp = mkDPIresponse(SNMP_NO_SUCH_NAME,0);

if the object IS recognized, the sub-agent builds a parse tree with the name/type/value of the object (using the DPI API routine mkDPIset()) and returns no error indication. here's how that looks, with a string being returned โœจ

char *obj_id;

unsigned char *cp;
struct dpi_set_packet *ret_value;
char *data;

/* obj_id = object ID of variable, like 1.3.6.1.2.1.1.1 */
/* should be identical to object ID sent in get request */
data = "a string to be returned";
ret_value = mkDPIset(obj_id,SNMP_TYPE_STRING,
                strlen(data)+1,data);
cp = mkDPIresponse(0,ret_value);

SET PROCESSING

SET processing is only slightly harder than GET ๐Ÿ’… this time the parse tree has extra info in it, namely the type, length and value to be set.

the sub-agent can return "no such name" if the variable isn't recognized, same as GET. if the variable IS recognized but can't be set, "no such name" should also be returned, even though it's super tempting to return "read only" instead ๐Ÿ™ˆ the vibes say otherwise, but stick with the rule.

GET NEXT PROCESSING

GET-NEXT requests are the most complicated ones to process ๐ŸŒธ after parsing one, the parse tree has two parameters. the first is the object ID the GET-NEXT operation is being run on. the semantics are that the sub-agent has to return the name/type/value of the next variable it supports whose name lexicographically follows the passed object ID.

it's really important to realize that one sub-agent might support several discontiguous sections of the MIB tree ๐Ÿฆ‹ jumping from one section to another in that case would be incorrect. the second parameter handles this. it's the "reason" the sub-agent got called, and it holds the prefix of the tree the sub-agent said it supported.

if the next variable the sub-agent supports doesn't start with that prefix, it must return the error indication "no such name". if needed, the SNMP agent calls the sub-agent again but passes a different group prefix. here's the example:

Assume there are two sub-agents. The first sub-agent registers two distinct sections of the tree, A and C. In reality, the sub-agent supports variables A.1 and A.2, but it correctly registers the minimal prefix required to uniquely identify the variable class it supports.

The second sub-agent registers a different section, B, which sits between the two sections registered by the first agent.

If a remote management station starts dumping the MIB from A, this is the sequence of queries that happens ๐Ÿงš

  Sub-agent 1 gets called:
        get-next(A,A) == A.1
        get-next(A.1,A) = A.2
        get-next(A.2,A) = error(no such name)

  Sub-agent 2 is then called:
        get-next(A.2,B) = B.1
        get-next(B.1,B) = error(no such name)

  Sub-agent 1 gets called again:
        get-next(B.1,C) = C.1

REGISTER REQUESTS

a sub-agent must register the variables it supports with the SNMP agent ๐Ÿซถ you make the packets with the DPI API library routine mkDPIregister().

     unsigned char *cp;

     cp = mkDPIregister("1.3.6.1.2.1.1.2.");

NOTE: object IDs are registered with a trailing dot (".").

TRAP REQUESTS

a sub-agent can ask the SNMP agent to generate a trap for it ๐Ÿ”ฅ it has to supply the values it wants for the generic and specific parameters of the trap. it may optionally include a name/type/value parameter, which goes into the trap packet too. the DPI API library routine mkDPItrap() can generate the required packet โœจ

DPI API LIBRARY ROUTINES ๐Ÿงฐโœจ

okay bestie, this is the library toolkit ๐Ÿ’… these are the API functions a DPI client can call so it doesn't have to hand-build packets like a menace. this section documents the Application Program Interfaces to the DPI.

QUERY_DPI_PORT() ๐Ÿ”Œ

int port;
char *hostname, *community_name;

port = query_DPI_port(hostname, community_name);

the query_DPI_port() function is how a DPI client figures out which TCP port number belongs to the DPI ๐Ÿฆ‹ you need that port to connect() to the SNMP agent. if the port can't be determined, it returns -1.

it takes two arguments: a string with the host's name or IP address, and the community name to use when making the request.

this is the bootstrap moment โœจ the port number comes from an SNMP GET request, but the client doesn't have to create or parse SNMP packets. query_DPI_port() handles all of that for you, periodt.

NOTE: query_DPI_port() assumes the community name doesn't contain any null characters. if it does, use _query_DPI_port() instead, which takes a third parameter: the length of the community name ๐Ÿ’€

MKDPIREGISTER ๐Ÿ“

#include "snmp_dpi.h"

unsigned char *packet;
int len;

/* register sysDescr variable */
packet = mkDPIregister("1.3.6.1.2.1.1.1.");

len = *packet * 256 + *(packet + 1);
len += 2;  /* include length bytes */

mkDPIregister() makes the register-request packet and hands you back a pointer to a static buffer holding the packet contents ๐ŸŒธ if something goes wrong while the packet is being made, you get the null pointer (0) back.

the length of the remainder of the packet is stored in the first two bytes, as the example above shows ๐Ÿ“

NOTE: object identifiers are registered with a trailing dot ("."). no skipping that dot, bestie ๐Ÿ™ˆ

MKDPISET ๐ŸŽ€

#include "snmp_dpi.h"

struct dpi_set_packet *set_value;

char *obj_id;
int type, length;
char *value;

set_value = mkDPIset(obj_id, type, length, value);

mkDPIset() builds the chunk of a parse tree that holds one name/value pair, the kind you'd normally find in a response packet ๐Ÿงš it returns a pointer to a dynamically allocated parse tree with the name/type/value info. if an error shows up while the tree is being created, you get the null pointer (0).

the value of type can be one of these (defined in the include file "snmp_dpi.h"):

  • SNMP_TYPE_NUMBER
  • SNMP_TYPE_STRING
  • SNMP_TYPE_OBJECT
  • SNMP_TYPE_INTERNET
  • SNMP_TYPE_COUNTER
  • SNMP_TYPE_GAUGE
  • SNMP_TYPE_TICKS

the value parameter is always a pointer to the first byte of the object's value.

NOTE: the parse tree is dynamically allocated and copies are made of the passed parameters. after a successful call to mkDPIset(), the app can dispose of them however it likes without affecting the parse tree contents ๐Ÿซถ

MKDPIRESPONSE ๐Ÿ’Œ

#include "snmp_dpi.h"

unsigned char *packet;
int error_code;
struct dpi_set_packet *ret_value;

packet = mkDPIresponse(error_code, ret_value);

len = *packet * 256 + *(packet + 1);
len += 2;  /* include length bytes */

mkDPIresponse() builds an appropriate response packet ๐Ÿ‘‘ it takes two parameters. the first is the error code to return. it may be 0 (no error) or one of these (defined in the include file "snmp_dpi.h"):

  • SNMP_NO_ERROR
  • SNMP_TOO_BIG
  • SNMP_NO_SUCH_NAME
  • SNMP_BAD_VALUE
  • SNMP_READ_ONLY
  • SNMP_GEN_ERR

if the error code means no error, the second parameter is a pointer to a parse tree (created by mkDPIset()) holding the name/type/value info being returned. if there's an error, the second parameter gets passed as a null pointer (0).

if the packet gets created, you get a pointer to a static buffer with the packet contents. it's the same buffer mkDPIregister() uses. if an error pops up while making the packet, you get the null pointer (0) back.

the length of the remainder of the packet is stored in the first two bytes, as the example above shows ๐Ÿ“

NOTE: mkDPIresponse() always frees the passed parse tree.

MKDPITRAP ๐Ÿšจ

#include "snmp_dpi.h"

unsigned char *packet;

int generic, specific;
struct dpi_set_packet *ret_value;

packet = mkDPItrap(generic, specific, ret_value);

len = *packet * 256 + *(packet + 1);
len += 2;  /* include length bytes */

mkDPItrap() creates an appropriate trap request packet ๐Ÿฆ‹ the first two parameters match the generic and specific fields in the SNMP trap packet. the third one can carry a name/value pair to be included in the SNMP trap packet. that info gets passed as the set-packet portion of the parse tree.

for example, a linkDown trap for interface 3 might be generated like this:

struct dpi_set_packet *if_index_value;
unsigned long data;
unsigned char *packet;
int len;

data = 3;  /* interface number = 3 */
if_index_value = mkDPIset("1.3.6.1.2.1.2.2.1.1", SNMP_TYPE_NUMBER,
        sizeof(unsigned long), &data);
packet = mkDPItrap(2, 0, if_index_value);
len = *packet * 256 + *(packet + 1);
len += 2;  /* include length bytes */
write(fd,packet,len);

if the packet gets created, you get a pointer to a static buffer with the packet contents, the same buffer mkDPIregister() uses. if an error shows up while making the packet, you get the null pointer (0) back.

the length of the remainder of the packet is stored in the first two bytes, as the example above shows ๐Ÿ“

NOTE: mkDPItrap() always frees the passed parse tree.

PDPIPACKET ๐Ÿ“ฆ

#include "snmp_dpi.h"

unsigned char *packet;

struct snmp_dpi_hdr *hdr;

hdr = pDPIpacket(packet)

pDPIpacket() parses a DPI packet and returns a parse tree representing what's inside ๐Ÿ” the parse tree is dynamically allocated and holds copies of the info in the DPI packet. after a successful call, the packet can be disposed of however the app likes without affecting the parse tree contents. if an error comes up during the parse, you get the null pointer (0).

NOTE: the relevant parse tree structures are defined in the include file "snmp_dpi.h", and that file remains the definitive reference ๐Ÿ‘‘

the root of the parse tree is a snmp_dpi_hdr structure:

struct snmp_dpi_hdr {
    unsigned char  proto_major;
    unsigned char  proto_minor;
    unsigned char  proto_release;

    unsigned char  packet_type;
    union {
         struct dpi_get_packet    *dpi_get;
         struct dpi_next_packet   *dpi_next;
         struct dpi_set_packet    *dpi_set;
         struct dpi_resp_packet   *dpi_response;
         struct dpi_trap_packet   *dpi_trap;
    } packet_body;
};

the field you care about is packet_type. it can have one of these values (defined in the include file "snmp_dpi.h"):

  • SNMP_DPI_GET
  • SNMP_DPI_GET_NEXT
  • SNMP_DPI_SET

the packet_type field tells you what request is being made of the DPI client. the rest of packet_body looks different depending on which of these requests it is.

if it's a get request, the object ID of the variable you want comes in a dpi_get_packet structure:

struct dpi_get_packet {
    char *object_id;
};

a get-next request is similar, but the dpi_next_packet structure also holds the object ID prefix of the group currently being traversed:

struct dpi_next_packet {
    char *object_id;
    char *group_id;
};

if the next object whose object ID lexicographically follows the object ID in object_id doesn't begin with the suffix in group_id, the DPI client must return an error indication of SNMP_NO_SUCH_NAME. ๐Ÿ˜ฌ

a set request has the most data attached, and it lives in a dpi_set_packet structure:

struct dpi_set_packet {
    char      *object_id;
    unsigned char  type;
    unsigned short value_len;
    char      *value;
};

object_id is the object ID of the variable being modified. the variable's type goes in type, which can be one of these:

  • SNMP_TYPE_NUMBER
  • SNMP_TYPE_STRING
  • SNMP_TYPE_OBJECT
  • SNMP_TYPE_EMPTY
  • SNMP_TYPE_INTERNET
  • SNMP_TYPE_COUNTER
  • SNMP_TYPE_GAUGE
  • SNMP_TYPE_TICKS

the length of the value to be set is stored in value_len, and value holds a pointer to the value.

NOTE: the storage pointed to by value gets reclaimed when the parse tree is freed. the DPI client must make provision for copying the value contents. ๐Ÿง 

FDPIPARSE ๐Ÿงน

#include "snmp_dpi.h"

struct snmp_dpi_hdr *hdr;

fDPIparse(hdr);

fDPIparse() frees a parse tree that was previously created by a call to pDPIpacket(). it's declared as void, so it has no return value, just vibes ๐ŸŒˆ

NOTE: after calling fDPIparse(), no further references to the parse tree can be made. the tree is gone, bestie ๐Ÿ’€

AGENT IMPLEMENTATION ISSUES ๐Ÿงฉ

okay so the SNMP DPI protocol is completely documented in this paper ๐Ÿ“„ but the document is kinda biased toward clearly defining the interface for sub-agents. basically it's a specification of a C language API ๐Ÿ’– this detailed coverage works because the client side of the interface is completely self-contained.

the agent side is a different story ๐Ÿ˜ค it has to be integrated into individual vendor implementations, and lots of those have their own unique organizational structures to deal with performance and storage constraints. so it's infeasible to offer much more than suggestions for SNMP agent implementers. that leaves a big gap for interpretation, and that can lead to implementations that don't quite work the way they should. too much ambiguity is a bad thing, fr fr ๐Ÿ™ƒ

the following characteristics of an agent implementation are to be considered mandatory:

DUPLICATE REGISTRATIONS ๐Ÿ”

with this release of the protocol, order of registration is significant ๐Ÿซ  the last sub-agent to register a variable is the one deemed authoritative. variables implemented by the base SNMP agent are considered to have been registered before any sub-agent registrations. so sub-agents can re-implement support for variables a vendor implemented incorrectly. main character energy for the sub-agent, no cap โœจ

AUTOMATIC DEREGISTRATION ON CLOSE ๐Ÿšช

all SNMP DPI connections are carried over a stream connection. when the client closes the connection (no matter what the cause), the agent must automatically unregister all of the variables that were registered by the sub-agent. no leftovers, no drama ๐Ÿงน๐Ÿ’…

TIMELY RESPONSE CONSTRAINTS โฐ

okay so, a sub-agent has to answer a request quickly, no lag allowed ๐Ÿ’… In this version of the protocol, the sub-agent must respond to a request from the SNMP agent within 5 seconds. If it misses that window, the SNMP agent should terminate the connection and unregister all of the variables that the sub-agent had registered before. bye bye, variables ๐Ÿ‘‹

NOTE: agent implementations that don't have access to a timer may not be able to do this at all. If that's the case, they're left open to getting stuck forever if the sub-agent malfunctions. yikes ๐Ÿ’€

SUPPORT FOR MULTIPLE MIB VIEWS ๐Ÿฆ‹

Some agents let you pick a different MIB view depending on which community name was used. This document isn't here to judge the different approaches people have proposed or built. It just acknowledges that these implementations exist, and that's it ๐Ÿซถ

The main point is this: objects supported by an SNMP DPI sub-agent should be registered under the MIB view that was selected by the community name used in the SNMP GET request that got the DPI_port value. Super specific, very important โœจ

The SNMP DPI doesn't reserve a port. Instead, sub-agents bootstrap themselves by sending an SNMP GET request for the DPI_port variable. That variable gives the TCP port the sub-agent should connect to. And there's no reason the SNMP agent can't have several listens (passive opens) active at once, each one tied to a different MIB view. So the port number returned can change depending on the community name used in the SNMP GET request for DPI_port. main character energy ๐Ÿ‘‘

CONSIDERATIONS FOR THE NEXT RELEASE ๐ŸŒธ

The SNMP DPI protocol makes room for extension and for parallel use of releases that might not be compatible with each other. Everything above describes the protocol as it's currently being used. It doesn't dig into the features that might be worth thinking about for a future revision, so that's a whole other conversation ๐Ÿ“

UNREGISTER ๐Ÿงš

For closure, an UNREGISTER request could be useful. โœจ

SUPPORT FOR ATOMIC SETS ๐Ÿ’œ

The SNMP protocol [1] says this:

Each variable assignment specified by the SetRequest-PDU should be effected as if simultaneously set with respect to all other assignments specified in the same message.

The SNMP DPI has no way to back out a SET request that was processed successfully if one of the later variable assignments fails. ngl this is a choice, and it comes from a few biases:

  • the SNMP DPI was meant to be light-weight ๐Ÿชถ
  • a belief that the SNMP RFC describes semantics that are infeasible to implement unless the range of applications is restricted ๐Ÿ™ˆ

Someone suggested adding a new request, TEST_SET, to the DPI protocol. Processing a SET request would then go like this:

  • all variables would be processed using TEST_SET unless an error occurred. The sub-agents would check that they could process the request.
  • if no error occurred, each of the variables would be processed again, this time with a SET request.

The catch is that this approach depends on TEST_SET to promise that the request can be carried out successfully. If that promise isn't possible, then you can't claim the prescribed semantics will be provided. And those situations really do exist. For example, a SET request that causes the far-end channel service unit to be looped up. You don't know if the operation will work until you actually perform it. ๐Ÿ˜ญ

SAMPLE SNMP DPI API IMPLEMENTATION ๐ŸŽ€

The following C language sources show an example implementation of the SNMP DPI Application Programming Interface, as it would be exposed to the sub-agents. โœจ

SAMPLE SNMP DPI INCLUDE FILE ๐Ÿ“ฆ

  /* SNMP distributed program interface */

  #define SNMP_DPI_GET            1
  #define SNMP_DPI_GET_NEXT       2
  #define SNMP_DPI_SET            3
  #define SNMP_DPI_TRAP           4
  #define SNMP_DPI_RESPONSE       5
  #define SNMP_DPI_REGISTER       6

  #define SNMP_DPI_PROTOCOL       2
  #define SNMP_DPI_VERSION        1
  #define SNMP_DPI_RELEASE        0

  /* SNMP error codes from RFC 1098 (1067) */
  #define SNMP_NO_ERROR           0
  #define SNMP_TOO_BIG            1
  #define SNMP_NO_SUCH_NAME       2
  #define SNMP_BAD_VALUE          3
  #define SNMP_READ_ONLY          4
  #define SNMP_GEN_ERR            5

  /* variable types */
  #define SNMP_TYPE_TEXT          0       /* textual representation */
  #define SNMP_TYPE_NUMBER        (128|1) /* number */
  #define SNMP_TYPE_STRING        2       /* text string */
  #define SNMP_TYPE_OBJECT        3       /* object identifier */
  #define SNMP_TYPE_EMPTY         4       /* no value */
  #define SNMP_TYPE_INTERNET      (128|5) /* internet address */
  #define SNMP_TYPE_COUNTER       (128|6) /* counter */
  #define SNMP_TYPE_GAUGE         (128|7) /* gauge */
  #define SNMP_TYPE_TICKS         (128|8) /* time ticks (1/100th sec) */
  #define SNMP_TYPE_MASK          0x7f    /* mask for type */


  struct dpi_get_packet {
          char    *object_id;
  };

  struct dpi_next_packet {
          char    *object_id;
          char    *group_id;
  };

  struct dpi_set_packet {
          char            *object_id;
          unsigned char   type;
          unsigned short  value_len;
          char            *value;
  };

  struct dpi_resp_packet {
          unsigned char   ret_code;
          struct dpi_set_packet   *ret_data;
  };

  struct dpi_trap_packet {
          unsigned char   generic;
          unsigned char   specific;
          struct dpi_set_packet   *info;
  };

  struct snmp_dpi_hdr {

          unsigned char   proto_major;
          unsigned char   proto_minor;
          unsigned char   proto_release;

          unsigned char   packet_type;
          union {
                  struct dpi_get_packet   *dpi_get;
                  struct dpi_next_packet  *dpi_next;
                  struct dpi_set_packet   *dpi_set;
                  struct dpi_resp_packet  *dpi_response;
                  struct dpi_trap_packet  *dpi_trap;
          } packet_body;
  };

  extern struct snmp_dpi_hdr *pDPIpacket();
  extern void fDPIparse();
  extern unsigned char *mkMIBquery();
  extern unsigned char *mkDPIregister();
  extern unsigned char *mkDPIresponse();
  extern unsigned char *mkDPItrap();
  extern struct dpi_set_packet *mkDPIset();

SAMPLE QUERY_DPI_PORT() FUNCTION ๐Ÿง

  #ifdef VM

  #include <manifest.h>
  #include <snmp_vm.h>
  #include <bsdtime.h>
  #include <bsdtypes.h>
  #include <socket.h>
  #include <in.h>
  #include <netdb.h>
  #include <inet.h>

  #else

  #include <sys/time.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <netdb.h>
  #include <arpa/inet.h>

  #endif

  static unsigned char asn1_hdr[] = {0x30};
  /* insert length of remaining packet, not including this */
  static unsigned char version[] = {0x02, 0x01, 0x00, 0x04};

  /* integer, len=1, value=0, string */
  /* insert community name length and community name */
  static unsigned char request[] = {
      0xa0, 0x1b,         /* get request, len=0x1b */
      0x02, 0x01, 0x01,   /* integer, len=1,request_id = 1 */
      0x02, 0x01, 0x00,   /* integer, len=1, error_status = 0 */
      0x02, 0x01, 0x00,   /* integer, len=1, error_index = 0 */
      0x30, 0x10,         /* varbind list, len=0x10 */
      0x30, 0x0e,         /* varbind , len=0x0e */
      0x06, 0x0a,         /* object ID, len=0x0a */
      0x2b, 0x06, 0x01, 0x04, 0x01, 0x02, 0x02, 0x01, 0x01, 0x00,
      0x05, 0x00          /* value, len = 0 */
  };

  static          extract_DPI_port();

  query_DPI_port(hostname, community_name)
  char           *hostname;
  char           *community_name;
  {
      int             community_len;
      int             rc;

      community_len = strlen(community_name);

      rc = _query_DPI_port(hostname, community_name, community_len);
      return (rc);
  }

  /* use if community_name has embedded nulls */

  _query_DPI_port(hostname, community_name, community_len)
  char           *hostname;
  char           *community_name;
  int             community_len;
  {
      unsigned char   packet[1024];
      int             packet_len;
      int             remaining_len;
      int             fd, rc, sock_len;
      struct sockaddr_in sock, dest_sock;
      struct timeval  timeout;
      unsigned long   host_addr, read_mask;
      int             tries;
      host_addr = lookup_host(hostname);
      packet_len = 0;
      bcopy(asn1_hdr, packet, sizeof(asn1_hdr));
      packet_len += sizeof(asn1_hdr);

      remaining_len = sizeof(version) + 1 +
        community_len + sizeof(request);

      packet[packet_len++] = remaining_len & 0xff;
      bcopy(version, packet + packet_len, sizeof(version));
      packet_len += sizeof(version);
      packet[packet_len++] = community_len & 0xff;
      bcopy(community_name, packet + packet_len, community_len);
      packet_len += community_len;
      bcopy(request, packet + packet_len, sizeof(request));
      packet_len += sizeof(request);

      fd = socket(AF_INET, SOCK_DGRAM, 0);
      if (fd < 0) {
      return (-1);
      }
      bzero(&sock, sizeof(sock));
      sock.sin_family = AF_INET;
      sock.sin_port = 0;
      sock.sin_addr.s_addr = 0;
      rc = bind(fd, &sock, sizeof(sock));
      if (rc < 0)
      return (-1);
      timeout.tv_sec = 3;
      timeout.tv_usec = 0;
      bzero(&dest_sock, sizeof(dest_sock));
      dest_sock.sin_family = AF_INET;
      dest_sock.sin_port = htons(161);
      dest_sock.sin_addr.s_addr = host_addr;

      tries = 0;
      while (++tries < 4) {
      rc = sendto(fd, packet, packet_len, 0, &dest_sock,
               sizeof(dest_sock));
      read_mask = 1 << fd;
      rc = select(read_mask + 1, &read_mask, 0, 0, &timeout);
      if (rc <= 0)
          continue;
      sock_len = sizeof(dest_sock);
      packet_len = recvfrom(fd, packet, sizeof(packet), 0,
                      &dest_sock, &sock_len);
      if (packet_len <= 0) {
          return (-1);

      }
      rc = extract_DPI_port(packet, packet_len);
      return (rc);
      }
      return (-1);
  }

  static          extract_DPI_port(packet, len)
  unsigned char   packet[];
  int             len;

  {
      int             offset;
      int             port;

      /* should do error checking (like for noSuchName) */
      offset = len - 2;
      port = (packet[offset] << 8) + packet[offset + 1];
      return (port);
  }

SAMPLE DPI FUNCTIONS ๐Ÿฆ‹

  /* DPI parser */

  #ifdef VM
  #include "manifest.h"
  #endif

  #include "snmp_dpi.h"

  static struct dpi_get_packet *pDPIget();
  static struct dpi_next_packet *pDPInext();
  static struct dpi_set_packet *pDPIset();
  static struct dpi_trap_packet *pDPItrap();
  static struct dpi_resp_packet *pDPIresponse();

  static void     fDPIget();
  static void     fDPInext();
  static void     fDPIset();
  static void     fDPItrap();
  static void     fDPIresponse();

  static int      cDPIget();
  static int      cDPInext();
  static int      cDPIset();
  static int      cDPItrap();
  static int      cDPIresponse();
  static struct snmp_dpi_hdr *mkDPIhdr();
  static struct dpi_get_packet *mkDPIget();
  static struct dpi_next_packet *mkDPInext();
  struct dpi_set_packet *mkDPIset();


  extern char    *malloc();

  static unsigned char new_packet[1024];
  static int      packet_len;

  struct snmp_dpi_hdr *pDPIpacket(packet)
  unsigned char  *packet;
  {
      struct snmp_dpi_hdr *hdr;
      int             len, offset;


      hdr = (struct snmp_dpi_hdr *) malloc(sizeof(struct snmp_dpi_hdr));
      if (hdr == 0)
          return (0);

len = (packet[0] << 8) + packet[1]; len += 2; offset = 2; hdr->proto_major = packet[offset++]; hdr->proto_minor = packet[offset++]; hdr->proto_release = packet[offset++]; hdr->packet_type = packet[offset++]; switch (hdr->packet_type) { case SNMP_DPI_GET: case SNMP_DPI_REGISTER: hdr->packet_body.dpi_get = pDPIget(packet + offset, len - offset); break; case SNMP_DPI_GET_NEXT: hdr->packet_body.dpi_next = pDPInext(packet + offset, len - offset); break; case SNMP_DPI_SET: hdr->packet_body.dpi_set = pDPIset(packet + offset, len - offset); break; case SNMP_DPI_TRAP: hdr->packet_body.dpi_trap = pDPItrap(packet + offset, len - offset); break; case SNMP_DPI_RESPONSE:

hdr->packet_body.dpi_response = pDPIresponse(packet + offset, len - offset); break; } return (hdr); }

static struct dpi_get_packet *pDPIget(packet, len) unsigned char *packet; int len; { struct dpi_get_packet *get; int l;

get = (struct dpi_get_packet *) malloc(sizeof(struct dpi_get_packet)); if (get == 0) return (0); l = strlen(packet) + 1; get->object_id = malloc(l); strcpy(get->object_id, packet); return (get); }

static struct dpi_next_packet *pDPInext(packet, len) unsigned char *packet; int len; { struct dpi_next_packet *next; int l; unsigned char *cp;

next = (struct dpi_next_packet *) malloc(sizeof(struct dpi_next_packet)); if (next == 0) return (0); cp = packet; l = strlen(cp) + 1; next->object_id = malloc(l); strcpy(next->object_id, cp); cp += l; l = strlen(cp) + 1; next->group_id = malloc(l); strcpy(next->group_id, cp); return (next); }

static struct dpi_set_packet *pDPIset(packet, len) unsigned char *packet; int len; { struct dpi_set_packet *set; int l; unsigned char *cp;

if (len == 0) return (0); /* nothing to parse */ set = (struct dpi_set_packet *) malloc(sizeof(struct dpi_set_packet)); if (set == 0) return (0);

cp = packet; l = strlen(cp) + 1; set->object_id = malloc(l); strcpy(set->object_id, cp); cp += l; set->type = (cp++); l = ((cp++) << 8); l += *(cp++); set->value_len = l; set->value = malloc(l); bcopy(cp, set->value, l); return (set); }

static struct dpi_trap_packet *pDPItrap(packet, len) unsigned char *packet; int len; { struct dpi_trap_packet *trap;

trap = (struct dpi_trap_packet *) malloc(sizeof(struct dpi_trap_packet)); if (trap == 0) return (0);

trap->generic = *packet; trap->specific = *(packet + 1); trap->info = pDPIset(packet + 2, len - 2); return (trap); }

static struct dpi_resp_packet *pDPIresponse(packet, len) unsigned char *packet; int len; { struct dpi_resp_packet *resp;

resp = (struct dpi_resp_packet *) malloc(sizeof(struct dpi_resp_packet)); if (resp == 0) return (0);

resp->ret_code = *packet; resp->ret_data = pDPIset(packet + 1, len - 1); return (resp); }

void fDPIparse(hdr) struct snmp_dpi_hdr *hdr; { if (hdr == 0) return; switch (hdr->packet_type) { case SNMP_DPI_GET: case SNMP_DPI_REGISTER: fDPIget(hdr); break; case SNMP_DPI_GET_NEXT: fDPInext(hdr); break; case SNMP_DPI_SET: fDPIset(hdr); break; case SNMP_DPI_TRAP: fDPItrap(hdr); break; case SNMP_DPI_RESPONSE: fDPIresponse(hdr); break; } free(hdr); }

static void fDPIget(hdr) struct snmp_dpi_hdr *hdr; { struct dpi_get_packet *get;

get = hdr->packet_body.dpi_get; if (get == 0) return; if (get->object_id)

free(get->object_id); free(get); }

static void fDPInext(hdr) struct snmp_dpi_hdr *hdr; { struct dpi_next_packet *next;

next = hdr->packet_body.dpi_next; if (next == 0) return; if (next->object_id) free(next->object_id); if (next->group_id) free(next->group_id); free(next); }

static void fDPIset(hdr) struct snmp_dpi_hdr *hdr; { struct dpi_set_packet *set;

set = hdr->packet_body.dpi_set; if (set == 0) return; if (set->object_id) free(set->object_id); if (set->value) free(set->value); free(set); }

static void fDPItrap(hdr) struct snmp_dpi_hdr *hdr; { struct dpi_trap_packet *trap; struct dpi_set_packet *set;

trap = hdr->packet_body.dpi_trap; if (trap == 0) return;

set = trap->info; if (set != 0) { if (set->object_id) free(set->object_id);

if (set->value) free(set->value); free(set); } free(trap); }

static void fDPIresponse(hdr) struct snmp_dpi_hdr *hdr; { struct dpi_resp_packet *resp; struct dpi_set_packet *set;

resp = hdr->packet_body.dpi_response; if (resp == 0) return;

set = resp->ret_data; if (set != 0) { if (set->object_id) free(set->object_id); if (set->value) free(set->value); free(set); } free(resp); }

unsigned char *cDPIpacket(hdr) struct snmp_dpi_hdr *hdr; { int rc, len; if (hdr == 0) { return (0); } packet_len = 2; new_packet[packet_len++] = hdr->proto_major; new_packet[packet_len++] = hdr->proto_minor; new_packet[packet_len++] = hdr->proto_release; new_packet[packet_len++] = hdr->packet_type; switch (hdr->packet_type) { case SNMP_DPI_GET: case SNMP_DPI_REGISTER: rc = cDPIget(hdr->packet_body.dpi_get); break; case SNMP_DPI_GET_NEXT: rc = cDPInext(hdr->packet_body.dpi_next); break;

case SNMP_DPI_SET: rc = cDPIset(hdr->packet_body.dpi_set); break; case SNMP_DPI_TRAP: rc = cDPItrap(hdr->packet_body.dpi_trap); break; case SNMP_DPI_RESPONSE: rc = cDPIresponse(hdr->packet_body.dpi_response); break; } if (rc == -1) return (0); len = packet_len - 2; new_packet[1] = len & 0xff; len >>= 8; new_packet[0] = len & 0xff; return (new_packet); }

static int cDPIget(get) struct dpi_get_packet *get; { if (get->object_id == 0) return (-1);

strcpy(&new_packet[packet_len], get->object_id); packet_len += strlen(get->object_id) + 1; return (0); }

static int cDPInext(next) struct dpi_next_packet *next; { if (next->object_id == 0) return (-1); if (next->group_id == 0) return (-1);

strcpy(&new_packet[packet_len], next->object_id); packet_len += strlen(next->object_id) + 1; strcpy(&new_packet[packet_len], next->group_id); packet_len += strlen(next->group_id) + 1; return (0); }

static int cDPIset(set) struct dpi_set_packet *set; {

int len;

if (set->object_id == 0) return (-1); if ((set->value == 0) && (set->value_len != 0)) return (-1);

strcpy(&new_packet[packet_len], set->object_id); packet_len += strlen(set->object_id) + 1; new_packet[packet_len++] = set->type; len = set->value_len >> 8; new_packet[packet_len++] = len & 0xff; new_packet[packet_len++] = set->value_len & 0xff; bcopy(set->value, &new_packet[packet_len], set->value_len); packet_len += set->value_len; return (0); }

static int cDPIresponse(resp) struct dpi_resp_packet *resp; { int rc;

if (resp == 0) return (-1);

new_packet[packet_len++] = resp->ret_code; if (resp->ret_data != 0) { rc = cDPIset(resp->ret_data); } else rc = 0; return (rc); }

static int cDPItrap(trap) struct dpi_trap_packet *trap; { int rc;

new_packet[packet_len++] = trap->generic; new_packet[packet_len++] = trap->specific; if (trap->info != 0) rc = cDPIset(trap->info); else rc = 0; return (rc); } unsigned char *mkMIBquery(cmd, oid_name, group_oid, type, len, value) int cmd; char *oid_name, *group_oid; int type, len; char *value; { struct snmp_dpi_hdr *hdr; unsigned char *cp;

hdr = mkDPIhdr(cmd); if (hdr == 0) return (0); switch (hdr->packet_type) { case SNMP_DPI_GET: case SNMP_DPI_REGISTER: hdr->packet_body.dpi_get = mkDPIget(oid_name); break; case SNMP_DPI_GET_NEXT: hdr->packet_body.dpi_next = mkDPInext(oid_name, group_oid); break; case SNMP_DPI_SET: hdr->packet_body.dpi_set = mkDPIset(oid_name, type, len, value); break; } cp = cDPIpacket(hdr); fDPIparse(hdr); return (cp); }

unsigned char *mkDPIregister(oid_name) char *oid_name; { return (mkMIBquery(SNMP_DPI_REGISTER, oid_name)); }

unsigned char *mkDPIresponse(ret_code, value_list) int ret_code; struct dpi_set_packet *value_list; { struct snmp_dpi_hdr *hdr; struct dpi_resp_packet *resp; unsigned char *cp;

hdr = mkDPIhdr(SNMP_DPI_RESPONSE); resp = (struct dpi_resp_packet *) malloc(sizeof(struct dpi_resp_packet)); if (resp == 0) {

free(hdr); return (0); } hdr->packet_body.dpi_response = resp; resp->ret_code = ret_code; resp->ret_data = value_list; cp = cDPIpacket(hdr); fDPIparse(hdr); return (cp); }

unsigned char *mkDPItrap(generic, specific, value_list) int generic, specific; struct dpi_set_packet *value_list; { struct snmp_dpi_hdr *hdr; struct dpi_trap_packet *trap; unsigned char *cp;

hdr = mkDPIhdr(SNMP_DPI_TRAP); trap = (struct dpi_trap_packet *) malloc(sizeof(struct dpi_trap_packet)); if (trap == 0) { free(hdr); return (0); } hdr->packet_body.dpi_trap = trap; trap->generic = generic; trap->specific = specific; trap->info = value_list; cp = cDPIpacket(hdr); fDPIparse(hdr); return (cp); }

static struct snmp_dpi_hdr *mkDPIhdr(type) int type; {

struct snmp_dpi_hdr *hdr;

hdr = (struct snmp_dpi_hdr *) malloc(sizeof(struct snmp_dpi_hdr)); if (hdr == 0) return (0); hdr->proto_major = SNMP_DPI_PROTOCOL; hdr->proto_minor = SNMP_DPI_VERSION; hdr->proto_release = SNMP_DPI_RELEASE; hdr->packet_type = type; return (hdr); }

static struct dpi_get_packet *mkDPIget(oid_name) char *oid_name; { struct dpi_get_packet *get; int l;

get = (struct dpi_get_packet *) malloc(sizeof(struct dpi_get_packet)); if (get == 0) return (0);

l = strlen(oid_name) + 1; get->object_id = malloc(l); strcpy(get->object_id, oid_name); return (get); }

  static struct dpi_next_packet *mkDPInext(oid_name, group_oid)
  char           *oid_name;
  char           *group_oid;
  {
      struct dpi_next_packet *next;
      int             l;

      next = (struct dpi_next_packet *)
        malloc(sizeof(struct dpi_next_packet));
      if (next == 0)
          return (0);
      l = strlen(oid_name) + 1;
      next->object_id = malloc(l);
      strcpy(next->object_id, oid_name);
      l = strlen(group_oid) + 1;
      next->group_id = malloc(l);
      strcpy(next->group_id, group_oid);
      return (next);
  }

  struct dpi_set_packet *mkDPIset(oid_name, type, len, value)
  char           *oid_name;
  int             type;
  int             len;
  char           *value;
  {
      struct dpi_set_packet *set;
      int             l;
      set = (struct dpi_set_packet *)
        malloc(sizeof(struct dpi_set_packet));
      if (set == 0)
          return (0);

      l = strlen(oid_name) + 1;
      set->object_id = malloc(l);
      strcpy(set->object_id, oid_name);
      set->type = type;
      set->value_len = len;
      set->value = malloc(len);
      bcopy(value, set->value, len);
      return (set);
  }

SAMPLE SOURCES FOR ANONYMOUS FTP โœจ

okay so if you want the real code, it's all up for grabs ๐ŸŽ€ The complete source to two SNMP DPI-related programs is available for anonymous ftp from the University of Toronto. The host name to use is vm.utcs.utoronto.ca (128.100.100.2). The files are in the anonymou.204 minidisk, so you've gotta issue a cd anonymou.204 after logging in. Don't forget to use the binary transmission mode ๐Ÿ’พ๐Ÿซถ

The Ping Engine ๐Ÿ“

this one is giving main character energy ๐Ÿ’… This program is an SNMP DPI sub-agent which allows network management stations to perform remote PINGs. The source to this application is in the file ping_eng.tarbin. The source to the SNMP DPI API is also contained within the archive ๐Ÿฆ‹

The DPI->SMUX daemon ๐Ÿงš

this one is the blueprint for how you'd actually include the SNMP DPI in an SNMP agent ๐Ÿ˜ญ This is a SMUX-based agent that works with the ISODE SNMP agent and provides an interface for SNMP DPI sub-agents. The source to this program is in the file dpid.tarbin. ISODE 6.7, or later, is a prerequisite ๐ŸŒธ


References ๐Ÿ“š

[1] Case, J., Fedor, M., Schoffstall, M., and J. Davin, "Simple Network Management Protocol", RFC 1157, SNMP Research, Performance Systems International, Performance Systems International, MIT Laboratory for Computer Science, May 1990.

[2] Information processing systems - Open Systems Interconnection, "Specification of Abstract Syntax Notation One (ASN.1)", International Organization for Standardization, International Standard 8824, December 1987.

[3] Information processing systems - Open Systems Interconnection, "Specification of Basic Encoding Rules for Abstract Syntax Notation One (ASN.1)", International Organization for Standardization, International Standard 8825, December 1987.

[4] McCloghrie K., and M. Rose, "Management Information Base for Network Management of TCP/IP-based internets", RFC 1156, Performance Systems International and Hughes LAN Systems, May 1990.

[5] Rose, M., and K. McCloghrie, "Structure and Identification of Management Information for TCP/IP-based internets", RFC 1155, Performance Systems International and Hughes LAN Systems, May 1990.

[6] International Business Machines, Inc., "TCP/IP for VM: Programmer's Reference", SC31-6084-0, 1990.

[7] International Business Machines, Inc., "Virtual Machine System Facilities for Programming, Release 6", SC24-5288-01, 1988.

[8] International Business Machines, Inc., "TCP/IP Version 1.1 for OS/2 EE: Programmer's Reference", SC31-6077-1, 1990.


Security Considerations ๐Ÿ”

Security issues are not discussed in this memo. ๐Ÿ˜ณ Ngl this is the one part of the doc where it's just, like, a blank space, so keep that in mind ๐Ÿ’€


Authors' Addresses ๐Ÿ’Œ

Geoffrey C. Carpenter IBM T. J. Watson Research Center P. O. Box 218 Yorktown Heights, NY 10598

Phone: (914) 945-1970

Email: gcc@watson.ibm.com

Bert Wijnen IBM International Operations Watsonweg 2 1423 ND Uithoorn The Netherlands

Phone: +31-2975-53316

Email: wijnen@uitvm2.iinus1.ibm.com

and that's the whole doc, bestie ๐ŸŒˆ๐Ÿ‘‘โœจ

๐Ÿ“ฆ where this page comes from