next up previous contents
Next: A simple example Up: The Net-SNMP C API Previous: SNMP Internals   Contents

Watching SNMP on the wire

To illustrate more accurately how SNMP actually works on the wire, lets look at sniffed packets captured during an SNMP GET. Only 2 packets are exchanged, a request by the manager and a response by the agent. The following was gathered using Ethereal:

No.     Time        Source      Destination Protocol Info
    112 1.936155    10.10.1.110 10.10.1.224 SNMP     GET SNMP...
Simple Network Management Protocol
    Version: 1 (0)
    Community: public
    PDU type: GET (0)
    Request Id: 0x2a7ee1af
    Error Status: NO ERROR (0)
    Error Index: 0
    Object identifier 1: 1.3.6.1.2.1.1.4.0 (SNMPv2-MIB::sysContact.0)
    Value: NULL

No.     Time        Source      Destination Protocol Info
    113 1.943425    10.10.1.224 10.10.1.110 SNMP     RESPONSE SN...
Simple Network Management Protocol
    Version: 1 (0)
    Community: public
    PDU type: RESPONSE (2)
    Request Id: 0x2a7ee1af
    Error Status: NO ERROR (0)
    Error Index: 0
    Object identifier 1: 1.3.6.1.2.1.1.4.0 (SNMPv2-MIB::sysContact.0)
    Value: STRING: Ben Rockwood

Each packet is referred to as a PDU in SNMP speak. The first packet (with the Ethernet, IP, and UDP headers removed) is a version 1 GET request with an OID to get and a community name of "public". Notice that the GET PDU has a Value field, but its NULL. The second packet is the response which you'll notice is identical to the GET PDU with 2 exceptions: the PDU type is RESPONSE instead of GET and the Value field is populated.

Given this example you can see that when you make a request you hand a PDU to an agent and say "Please fill this in".

Putting this into the context of the Net-SNMP C API, look at the snmp_PDU structure.

typedef struct snmp_pdu {

    long            version;
    int             command;
    long            reqid;
    ...
    long            errstat;
    long            errindex;
    ...
    netsnmp_variable_list *variables;
    ...
    u_char         *community;
    ...

The version value maps directly the value seen in the packet above. The command value is the PDU type as seen in the packet above. And you can also see the reqid in the packets above as the Request ID. The important point here is that when a PDU structure is manipulated using the API your actually preparing an SNMP packet for transmission.

Forming a request, such as a GET, can be done in several ways. One method is to create a single PDU per OID you wish to request. As we saw above this would mean sending one packet per OID and thus receiving one packet for each request. Another method, which is completely valid, is to pack a single GET PDU with multiple OIDs for retrieval. The following is a break capture of such an exchange as seen on the wire:

No.Time        Source      Destination Protocol Info
28 2.240789    10.10.1.110 10.10.1.224 SNMP     GET SNMPv...
Simple Network Management Protocol
    Version: 1 (0)
    Community: public 
    PDU type: GET (0)
    Request Id: 0x30549f5c
    Error Status: NO ERROR (0)
    Error Index: 0
    Object identifier 1: 1.3.6.1.4.1.318.1.1.1.1.1.1.0 (SNMP...
    Value: NULL
    Object identifier 2: 1.3.6.1.4.1.318.1.1.1.1.2.3.0 (SNMP...
    Value: NULL

No.Time        Source      Destination Protocol Info
41 2.328751    10.10.1.224 10.10.1.110 SNMP     RESPONSE SNM...
Simple Network Management Protocol
    Version: 1 (0)
    Community: public
    PDU type: RESPONSE (2)
    Request Id: 0x30549f5c
    Error Status: NO ERROR (0)
    Error Index: 0
    Object identifier 1: 1.3.6.1.4.1.318.1.1.1.1.1.1.0 (SNMP...
    Value: STRING: "Silcon DP340E"
    Object identifier 2: 1.3.6.1.4.1.318.1.1.1.1.2.3.0 (SNMP...
    Value: STRING: "SE0XXXXXXX   "

This exchange looks identical to the previous exchange but this time we've packed multiple (8 total, but I only show 2 for brevity) OIDs into the PDU. Hence, we send one packet and we are returned one packet.

Looking back at the PDU structure, it should not be obvious why there is no fixed value for the OID but instead an included netsnmp_variable_list pointer. This variable list is otherwise referred to as a VARLIST or VARBIND.

typedef struct variable_list netsnmp_variable_list;

struct variable_list {
   struct variable_list *next_variable;
   oid            *name;
   size_t          name_length;
   u_char          type;
   netsnmp_vardata val;
   size_t          val_len;
   oid             name_loc[MAX_OID_LEN];
   u_char          buf[40];
   void           *data;
   void            (*dataFreeHook)(void *);
   int             index;
};

So looking at the variable list we immediately notice that its a linked list, hence the ability to pack multiple values into a single OID.

We can pull all this information together to get a much clearer idea of how the API really works. As seen in the captures what we really need to do is create, send, receive and then process one or more PDUs; thats the basics. In order to do this we need to initialize a PDU structure, append one or more variables into the variable list and then send that PDU out. What we'll get back is a fully populated (we hope) PDU, but this time each OID in the variable list will have a value referenced by the data pointer.

Next lets look at the actual code.


next up previous contents
Next: A simple example Up: The Net-SNMP C API Previous: SNMP Internals   Contents
2004-11-23