Working with Object Identifiers

Object Identifier is a list of numbers representing path through the MIB tree to an eventual sub-tree or value. It is represented, in human readable form, as a list of dot separated decimal numbers. For example, object identifier 1.3.6.1.2.1.1.1.0 represents sysDescr.0 MIB variable.

Before we look into the workings of object ids I need to mention one thing. Object id should never have a value of NULL (or empty value). According to the standards, NULL or empty object id value is represented as (decimal) 0.0. A lot of the implementations do not follow this rule and still allow empty object id values to be encoded. Snmp#Net library can handle object ids encoded as null (0 length) values but it will always encode null object ids as standard compliant 0.0 values.

In Snmp#Net object identifiers are manipulated using Oid class.

Oid class provides you with the methods to create, manipulate, access, encode and decode object identifiers.

Lets start with how you create an Oid class.

You can create an empty Oid class (NULL class as mentioned above) by using the following constructor:

Oid myoid = new Oid();

This constructor will create an empty Oid class. You can test it this way:

if( myoid.IsNull )
  Console.WriteLine("This class is null.");
else
  Console.WriteLine("This class is NOT null.");

This is not very helpful, at least not to start with. We will get to describing actions you can take with the class once constructed that will make it useful later.

Let’s see a way to create a class that would make it useful right from the beginning.

In this example, we will create an Oid class initialized with the sysDescr.0 object id in the constructor. As mentioned before, object identifier is a list of integers. As such, it makes sense to have a constructor that can take an array of Int32 values and initialize the newly created Oid class.

Here is how to do it:

UInt32[] oidval = new UInt32[] { 1, 3, 6, 1, 2, 1, 1, 1, 0 };
Oid myoid = new Oid(oidval);
if (myoid.IsNull)
  Console.WriteLine("OID is null.");
else {
  Console.WriteLine("OID length is: {0}", myoid.Length);
  Console.WriteLine("OID: {0}", myoid.ToString());
}

Output of the above will look something like this:

OID length is: 9
OID: 1.3.6.1.2.1.1.1.0

Now that the class has content you can add it to a request or compare it with another Oid class. Before going there, let’s have a look at how you can clone an existing Oid class.

Getting a duplicate of the existing class, meaning another distinct Oid class representing the same object identifier without any references to the original Oid class, is simple.

You can clone the existing class:

Oid myoid2 = (Oid)myoid.Clone();

or you can construct another Oid class using the copy constructor:

Oid myoid2 = new Oid(myoid);

Trick here is that in both cases, object identifier values are duplicated and not copied by reference. This way you will create a new class with its own data buffer without any reference to the class that was the source of information.

Same is true when you create the class with a numeric array passed to the constructor (as in the earlier example). Snmp#Net never uses references of the data passed as arguments to methods or constructors, except where clearly stated.

There is one final way you can create an Oid class. The most common way object identifiers are presented to users is in dotted decimal notation. Since this is the most common way to present it, there is also a way to pass the object identifier to the Oid class constructor as a string representing the class.

Here is the final constructor example:

Oid myoid = new Oid("1.3.6.1.2.1.1.1.0");
Console.WriteLine("OID: {0}", myoid.ToString());
# Returns:
OID: 1.3.6.1.2.1.1.1.0

This is the most inefficient way to construct an Oid class. Parsing of text and error testing takes much longer (longer is relative considering the speed of computers) then passing an array of integers to a constructor.

Every method of initializing the Oid class using the constructor is also possible with a pre-initialized class. You can use Oid.Set methods to set the class object identifier in the same way as with constructors:

UInt32[] oidval = new UInt32[] { 1, 3, 6, 1, 2, 1, 1, 1, 0 };
Oid myoid = new Oid();
myoid.Set(oidval);
// or
myoid.Set("1.3.6.1.2.1.1.1.0");

Each of the Oid.Set() operations deletes existing object identifier value stored in the class (if any) and sets the class value to the new object identifier.

One thing to keep in mind is that minimum length of an object identifier is 2 values. Single value object identifiers are encoded as null value (0.0).

You can access object identifier individual values as you would access values in any array:

UInt32[] oidval = new UInt32[] { 1, 3, 6, 1, 2, 1, 1, 1, 0 };
Oid myoid = new Oid(oidval);
for (int i = 0; i < myoid.Length; i++)
  Console.Write("{0} ", myoid[i]);
Console.WriteLine("");

To manipulate values, you can retrieve Oid class value as an array of integers using:

int[] oidval = myoid.ToArray();

As usual, Oid.ToArray() will create a duplicate of the internal class data and return that. Changing the returned array will not affect the original class contents.

There is a shortcut to this method, just cast an Oid class as int[]:

uint[] oidval = (uint[])myoid;

This does the same thing as Oid.ToArray() method.

Usually what you have to deal with when working with object identifier values is to add values to it. For example:

UInt32[] oidval = new UInt32[] { 1, 3, 6, 1, 2, 1, 1, 1 };
Oid myoid = new Oid(oidval);
Console.WriteLine("OID: {0}", myoid.ToString());
// OID: 1.3.6.1.2.1.1.1
myoid.Add(0);
Console.WriteLine("OID: {0}", myoid.ToString());
// OID: 1.3.6.1.2.1.1.1.0

You can add more then one value by supplying an array of integers to the Oid.Add() method or you can add another Oid class value or a string value.

Adding values to an Oid class can also be done using simple arithmetic:

UInt32[] oidval = new UInt32[] { 1, 3, 6, 1, 2, 1, 1, 1 };
Oid myoid = new Oid(oidval);
Console.WriteLine("OID: {0}", myoid.ToString());
// OID: 1.3.6.1.2.1.1.1
myoid += 0;
Console.WriteLine("OID: {0}", myoid.ToString());
// OID: 1.3.6.1.2.1.1.1.0

When working with object identifiers I tend to spend a lot of time comparing them. Oid class provides some helpful ways to do that.

First are Oid.Compare() methods. These methods will compare object identifiers (represented as another Oid class, an array of integers or a string value) up to the length of the shorter value. This means that if you are comparing 1.3.6.1 and 1.3.6.1.2.1.1, result will be 0 (values are the same). This behavior was created for compatibility with libraries I used a long time ago in c/c++ world.

If you wish to perform an exact comparison, then you should use Oid.CompareExact() methods. These methods will compare class values, including their length.

If you are looking for a quick and easy comparison, the Oid.Equals() and override operators == and != all use Oid.CompareExact() to check object identifier equality and are easy enough to use:

UInt32[] oidval = new UInt32[] { 1, 3, 6, 1, 2, 1, 1, 1 };
Oid myoid = new Oid(oidval);
Oid myoid2 = new Oid(oidval);
if (myoid == myoid2)
  Console.WriteLine("Equal: {0} == {1}", myoid.ToString(), myoid.ToString());
// Prints: Equal: 1.3.6.1.2.1.1.1 == 1.3.6.1.2.1.1.1
myoid2 += 0;
if (myoid != myoid2)
  Console.WriteLine("NotEqual: {0} != {1}", myoid.ToString(), myoid2.ToString());
// Prints: NotEqual: 1.3.6.1.2.1.1.1 != 1.3.6.1.2.1.1.1.0
int compRes = myoid.Compare(myoid2);
Console.WriteLine("Compare(\"{0}\" , \"{1}\") = {2}", myoid.ToString(), myoid2.ToString(), compRes);
// Prints: Compare("1.3.6.1.2.1.1.1" , "1.3.6.1.2.1.1.1.0") = 0

Further comparison is possible with greater-then “>” and less-then “<" operators.

Oid class method that has to be described because it is probably the most used is the Oid.IsRootOf() method. When performing SNMP walk operation (multiple Get-Next or Get-Bulk) methods to retrieve an entire MIB sub-tree, you will need to make sure that object identifiers contained in the reply of your request are sub-identifiers of the original request. This is where this method shines.

Oid myoid1 = new Oid(new UInt32[] { 1, 3, 6, 1, 2, 1, 1, 1 });
Oid myoid2 = new Oid(new UInt32[] { 1, 3, 6, 1, 2, 1, 1, 1, 0 });
if (myoid1.IsRootOf(myoid2))
  Console.WriteLine("Oid {0} is root of {1}", myoid1.ToString(), myoid2.ToString());
// Prints: Oid 1.3.6.1.2.1.1.1 is root of 1.3.6.1.2.1.1.1.0
myoid2.Set(new Int32[] { 1, 3, 6, 1, 2, 1, 1, 2, 0 });
if(! myoid1.IsRootOf(myoid2))
  Console.WriteLine("Oid {0} is NOT root of {1}", myoid1.ToString(), myoid2.ToString());
// Prints: Oid 1.3.6.1.2.1.1.1 is NOT root of 1.3.6.1.2.1.1.2.0

In this example you can see both positive and negative root object identifier check.

Final method to review here is the Oid.GetChildIdentifiers(Oid, Oid) static method. This method can be very handy when you have retrieved a value with a specific Oid and wish to use the instance of that value to retrieve additional values.

Child identifiers are identifiers that are child values not present in the root value (first Oid argument).

Child identifiers are returned as an array of unsigned integers. If first argument is not a root of the second or if they are the same, this method will return null as there are no child identifiers in that scenario.

During normal use of the Snmp#Net library you do not have to worry about encoding and decoding individual variables. This process is taken care of by the Pdu and SNMP packet classes described elsewhere. For this reason I have excluded those methods from this description of the class. If you are interested in how encode/decode is done, look at the API documentation and library source code.