SNMP v1 and v2c Protocol Data Unit

SNMP v1 and v2c Protocol Data Unit, represented by the Pdu class in the SnmpSharpNet library, contains request and reply values relevant to the data exchange manager initiated.

Protocol Data Unit is a sequence of values. Following values are encoded in the PDU sequence:

  • Sequence type – Sequence type is the code value that describes the data type. For requests it is: Get, GetNext, GetBulk or Set, for replies code represents Response.
  • Sequence length – length of the data encoded in the Pdu sequence
  • Request id – unique request id used to match requests and responses as Integer32 TLV
  • Error status – value representing the error status encoded as Integer32 TLV
  • Error index – pointer to the entry in the variable binding list that caused the error described by Error status value. It is encoded as Integer32 TLV.
  • Varible binding list – an array of variable bindings containing Oid/Value pairs that are part of the request/response. Variable binding list is a sequence of variable bindings, each of which is a sequence itself of Oid/Value pairs.

Two additional values are available for GetBulk PDUs:

  • MaxRepetitions
  • NonRepeaters

Pdu type is set using PduType enumeration. Following values are defined in the enumeration:

PduType.Get; // Value: 0xa0
PduType.GetNext; // Value: 0xa1
PduType.Response; // Value: 0xa2
PduType.Set; // Value: 0xa3
PduType.GetBulk; // Value: 0xa5

Using the PduType enumeration you can set the Pdu type this way:

Pdu pdu = new Pdu();
pdu.Type = PduType.GetBulk;

There are convenience static methods available to help with the creation of different Pdu types:

Pdu getPdu = Pdu.GetPdu(vbs);
Pdu getNextPdu = Pdu.GetNextPdu(vbs);
Pdu getBulkPdu = Pdu.GetBulkPdu(vbs);
Pdu setPdu = Pdu.setPdu(vbs);

I’ll expain what the vbs argument in the above examples is later on…

Request id is a unique identifier used to match requests and responses. Pdu class by default sets the request id to a random value using the standard Random

class that is part of the .NET framework.

If you wish to reset the request id to a new random value, just set the request id value to 0 (zero). This action will cause the encoding routine to retrieve a new random request id:

Pdu pdu = new Pdu();
pdu.RequestId = 0;

Above example will result in the Pdu being encoded with a new random value. This is mostly useful when you are reusing a Pdu for multiple requests. By setting the RequestId value to 0 you can ensure that each request is sent with a different request id.

Another way to reuse a Pdu class instance is to call the Pdu.Reset() method. When called, Reset method sets the RequestId value to the previous value incremented by 1. Request id value will rollover to 1 when maximum value of integer variable is reached to avoid overflow exceptions.

Error status is a value used to represent error that was encountered by the agent when processing the request. Error status values can be tested using PduErrorStatus

enumeration. Error status value of PduErrorStatus.NoError

(or value of 0 - zero) signifies that there was no error. When there is no error in the request, Error index value will be set to 0.

For different error status codes and their meaning, have a look at PduErrorStatus

code documentation.

Error index is used when error reported in Error status is related to a specific variable in the Pdu. In all other cases (like if there is no error or if error is not related to a variable) Error index value is set to zero. If Error index value is set to value 1+ then that value points to a variable binding in the variable binding list.

When processing replies it is critically important to verify that ErrorStatus value does not contain an error. If error is returned and you continue to process the response there is a good chance further processing will fail.

Here is a full example of how to handle a response:

VbCollection vbCol = new VbCollection();
vbCol.Add("1.3.6.1.2.1.1.1.0");
vbCol.Add("1.3.6.1.2.1.1.2.0");
// vbCol.Add("1.3.6.1.2.1.1.1.1.0");
Pdu getPdu = Pdu.GetPdu(vbCol);
 
UdpTarget target = new UdpTarget(IPAddress.Loopback);
AgentParameters agentParams = 
	new AgentParameters(SnmpVersion.Ver2, new OctetString("public"));
 
SnmpV2Packet response = (SnmpV2Packet)target.Request(getPdu, agentParams);
 
if (response != null)
{
	if( response.Pdu.ErrorStatus == (int)PduErrorStatus.noError )
	{
		Console.WriteLine("Response id {0}\n1: {1}\n2: {2}", 
			response.Pdu.RequestId, 
			response.Pdu.VbList[0].Value.ToString(), 
			response.Pdu.VbList[1].Value.ToString());
		Console.WriteLine("Error status: {0}", 
			((PduErrorStatus)response.Pdu.ErrorStatus).ToString());
	} else {
		Console.WriteLine("Response id {0}", response.Pdu.RequestId);
		Console.WriteLine("Error code: {0} index: {1} name: {2}", 
			response.Pdu.ErrorStatus, response.Pdu.ErrorIndex, 
			((PduErrorStatus)response.Pdu.ErrorStatus).ToString());
	}
}
target.Dispose();

Important part to focus on is the processing of response.Pdu.ErrorStatus

. Before focusing attempting to process data in the packet you will want to check if ErrorStatus value is equal to noError. Any other value represents an error and data in the packet is suspect.

Pdu class provides helper methods to make access to the variable binding collection easier. You can access VarBind collection directly through an indexer in the Pdu class itself (without using Pdu.VbList collection) or by accessing Pdu class enumerator:

// SnmpV2Packet response = (SnmpV2Packet)target.Request(pdu, param);
foreach (Vb v in response.Pdu)
{
  Console.WriteLine("{0}: {1}", v.Oid.ToString(), v.Value.ToString());
}
// or
for (int i = 0; i < response.Pdu.VbCount; i++)
{
  Console.WriteLine("{0} - {1}: {2}", i, 
    response.Pdu[i].Oid.ToString(), response.Pdu[i].Value.ToString());
}

VarBind collection access helpers were introduced in library version 0.7.8