Using GetNextRequest Queries



Using GetNextRequest Queries

As can be seen from the SimpleSNMP and CiscoRouter programs, writing GetRequest programs using the SNMP class is a snap. The next step is to experiment with the SNMP GetNextRequest queries. These queries behave a little bit differently from the GetRequest queries we’ve looked at so far.

Extracting the Next MIB

A GetNextResponse PDU is used when you need to retrieve a series of data from the network device. The data in the series is usually related in some way, such as a series of network interfaces installed on the device, or a series of statistics taken of a period of time.

The data series is stored in the MIB using separate objects, each one located beneath the original object. For example, the interface object may have several child nodes below it, one for each interface on the device. Figure shows what this would look like.

Click To expand
Figure: The interface objects define individual interfaces in the MIB database

The MIB database contains an object for the interface table (ifTable) at object identifier 1.3.6.1.2.1.2.2. In order for a device to query each of the interface objects in the series, it must start at the ifTable object and "walk" through the related child nodes in the database. This is done with the help of the SNMP device.

When a GetNextRequest packet is sent to a network device, the device responds with a standard GetResponse packet. The difference is that it changes the value of the MIB entry in the packet. The new MIB entry is the next MIB that should be queried to continue in the series of information.

To follow the series, you must extract the new MIB value from the GetResponse packet and use it as the MIB value in the next GetNextRequest query. This continues until the remote SNMP device returns an error status, or you determine you have finished collecting all of the data in the series.

The getnextMIB() Method

Because extracting the next MIB in the GetResponse packet is a common function, it would be best to create a new method in the SNMP class. Any SNMP application program that needs to walk through a data series can call the new method. Listing 12.6 shows the getnextMIB() method that should be added to the SNMP.cs class file, after the get() method.

Listing 12.6: The getnextMIB() method of the SNMP class
Start example
public string getnextMIB(byte[] mibin)
{
  string output = "1.3";
  int commlength = mibin[6];
  int mibstart = 6 + commlength + 17; //find the start of the mib section
  //The MIB length is the length defined in the SNMP packet
  // minus 1 to remove the ending .0, which is not used
  int miblength = mibin[mibstart] - 1;
  mibstart += 2; //skip over the length and 0x2b values
  int mibvalue;
  for(int i = mibstart; i < mibstart + miblength; i++)
  {
   mibvalue = Convert.ToInt16(mibin[i]);
   if (mibvalue > 128)
   {
     mibvalue = (mibvalue/128)*128 + Convert.ToInt16(mibin[i+1]);
     i++;
   }
   output += "." + mibvalue;
  }
  return output;
}
End example

The getnextMIB() method is basically the opposite operation to what was done by the get() method. This method takes a byte array representing the MIB and converts it to a string value. The only tricky part is remembering to watch for object node values that are larger than 128 and thus occupy 2 bytes instead of the usual 1 byte.

After the MIB is converted to a string, it is returned to the calling application. The application can then display the new MIB and use it in another get() method call to determine the object value for the MIB.

The MAC Address Program

There are quite a few applications that need to step through a series of data on a network device. One application that I find myself using quite often is determining the active MAC addresses on Cisco switches. The Cisco switch keeps track of all active MAC addresses for each configured VLAN on the switch in a table accessible from a common MIB under the interfaces object. Using the SNMP class, it is easy to write an application that walks through the table, retrieving all of the active MAC addresses on the VLAN.

Listing 12.7 shows the MacAddress.cs program, which queries a Cisco switch for all the active MAC addresses.

Listing 12.7: The MacAddress.cs program
Start example
using System;
class MacAddress
{
  public static void Main(string[] argv)
  {
   int commlength, miblength, datastart, datalength;
   string nextmib, value;
   SNMP conn = new SNMP();
   string mib = "1.3.6.1.2.1.17.4.3.1.1";
   int orgmiblength = mib.Length;
   byte[] response = new byte[1024];
   nextmib = mib;
   while (true)
   {
     response = conn.get("getnext", argv[0], argv[1], nextmib);
     commlength = Convert.ToInt16(response[6]);
     miblength = Convert.ToInt16(response[23 + commlength]);
     datalength = Convert.ToInt16(response[25 + commlength + miblength]);
     datastart = 26 + commlength + miblength;
     value = BitConverter.ToString(response, datastart, datalength);
     nextmib = conn.getnextMIB(response);
     if (!(nextmib.Substring(0, orgmiblength) == mib))
      break;
     Console.WriteLine("{0} = {1}", nextmib, value);
   }
  }
}
End example

With the help of the SNMP class file, it is amazing how small the application program can be. The only tricky part to the application is knowing when to stop walking the MIB objects.

The Cisco switch uses the dot1netTable (1.3.6.1.2.1.17.4.3.1.1) object, located in the mib-2 database (1.3.6.1.2.1) to access the MAC address table. Once you reference the base object of the table, the Cisco switch will direct you to the individual values with the returned MIBs.

The switch will continually walk down the child nodes from the MIB you specify, but when it runs out, it attempts to go to the next MIB object on the same level. To prevent this, you must keep a copy of the original MIB object identifier and compare it to the new MIB returned in the GetResponse packets. Each child node will have more node values in the object identifier, so all you need to compare is the length of the original MIB to the new MIB. When the new MIB changes within the length of the original MIB, you know that a new object on the same level has been returned, and you can stop walking.

For example, the original MIB value is as follows:

1.3.6.1.2.1.17.4.3.1.1

The first data object in this table has the following MIB value:

1.3.6.1.2.1.17.4.3.1.1.0.0.12.7.172.124

The subsequent values use a depth-first approach within the table: the lower MIB places are expanded until they are exhausted, then the next higher MIB place is expanded, and so on up until the base MIB value changes to the following:

1.3.6.1.2.1.17.4.3.1.2

When the base MIB value changes, the program detects this and halts the search.

Because MAC addresses are usually presented in their hexadecimal values, you can use the BitConverter class’s ToString() method to produce a string version of the hexadecimal values of the MAC address.

Testing the Program

After compiling the MacAddress program (with the new version of the SNMP class file), you can run it with a Cisco switch. One oddity of Cisco equipment is the way it handles community names.

Cisco uses a system called indexed community names. A general community name, such as the word test, provides access to only the general switch information. To access information for a particular VLAN, you must add the VLAN number to the community name. For example, to access the MAC address table for VLAN 100, you would use the community name test@100.

Listing 12.8 shows a partial output from running the MacAddress program on a busy Cisco switch.

Listing 12.8: Sample MacAddress program output
Start example
C:\>MacAddress 192.168.1.5 horse@100
1.3.6.1.2.1.17.4.3.1.1.0.0.12.7.172.124 = 00-00-0C-07-AC-7C
1.3.6.1.2.1.17.4.3.1.1.0.0.170.91.20.67 = 00-00-AA-5B-14-43
1.3.6.1.2.1.17.4.3.1.1.0.0.170.91.23.14 = 00-00-AA-5B-17-0E
1.3.6.1.2.1.17.4.3.1.1.0.0.170.91.24.202 = 00-00-AA-5B-18-CA
1.3.6.1.2.1.17.4.3.1.1.0.1.3.190.158.77 = 00-01-03-BE-9E-4D
1.3.6.1.2.1.17.4.3.1.1.0.1.230.77.209.187 = 00-01-E6-4D-D1-BB
1.3.6.1.2.1.17.4.3.1.1.0.1.230.84.119.202 = 00-01-E6-54-77-CA
1.3.6.1.2.1.17.4.3.1.1.0.4.0.248.44.12 = 00-04-00-F8-2C-0C
1.3.6.1.2.1.17.4.3.1.1.0.6.91.52.17.87 = 00-06-5B-34-11-57
1.3.6.1.2.1.17.4.3.1.1.0.6.91.52.108.108 = 00-06-5B-34-6C-6C
1.3.6.1.2.1.17.4.3.1.1.0.6.91.129.249.64 = 00-06-5B-81-F9-40
1.3.6.1.2.1.17.4.3.1.1.0.6.91.130.3.112 = 00-06-5B-82-03-70
1.3.6.1.2.1.17.4.3.1.1.0.6.91.166.250.231 = 00-06-5B-A6-FA-E7
1.3.6.1.2.1.17.4.3.1.1.0.16.131.13.143.121 = 00-10-83-0D-8F-79
1.3.6.1.2.1.17.4.3.1.1.0.80.218.16.120.6 = 00-50-DA-10-78-06
.
.
.
1.3.6.1.2.1.17.4.3.1.1.0.224.41.146.58.140 = 00-E0-29-92-3A-8C
1.3.6.1.2.1.17.4.3.1.1.8.0.9.216.30.170 = 08-00-09-D8-1E-AA
1.3.6.1.2.1.17.4.3.1.1.8.0.32.117.204.112 = 08-00-20-75-CC-70
C:\>
End example

The returned MIBs from the Cisco switch show some interesting values. Notice how there is no particular order to the returned MIB values. Only Cisco knows what these individual node values mean—all we poor programmers know is that each one represents a MAC address value.

The other feature to notice is that the program stopped when the returned MIB’s object identifier differed from the original MIB object identifier sent to the device.

 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows