RFC 1228
SNMP-DPI: Simple Network Management Protocol Distributed Program Interface
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_NUMBERSNMP_TYPE_STRINGSNMP_TYPE_OBJECTSNMP_TYPE_INTERNETSNMP_TYPE_COUNTERSNMP_TYPE_GAUGESNMP_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_ERRORSNMP_TOO_BIGSNMP_NO_SUCH_NAMESNMP_BAD_VALUESNMP_READ_ONLYSNMP_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_GETSNMP_DPI_GET_NEXTSNMP_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_NUMBERSNMP_TYPE_STRINGSNMP_TYPE_OBJECTSNMP_TYPE_EMPTYSNMP_TYPE_INTERNETSNMP_TYPE_COUNTERSNMP_TYPE_GAUGESNMP_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
- ietf: rfc1228 1991-05
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms