Jan. 16, 2010, 7 a.m.
posted by vendetta
The SimpleSNMP Program
The system object identifiers are often used to track and identify devices on the network. They are also used to help network administrators track information from a central network management station. For your first simple SNMP program, you’ll start out by querying a network device for its system information from the MIB database. Figure shows the system object values that will be queried from the sample program.
|
Object |
Object Identifier |
Datatype |
Description |
|---|---|---|---|
|
sysUptime |
1.3.6.1.2.1.1.3.0 |
Integer |
How long a device has been powered on (in hundredths of seconds) |
|
sysContact |
1.3.6.1.2.1.1.4.0 |
String |
The person responsible for the device |
|
sysName |
1.3.6.1.2.1.1.5.0 |
String |
The network name of the device |
|
sysLocation |
1.3.6.1.2.1.1.6.0 |
String |
The location of the device |
| Note |
This program requires that you have an existing network device with SNMP capabilities and a valid community name to query data values in the device. This can include a Windows NT or 2000 server with the SNMP service running. |
The SimpleSNMP.cs program, Listing 12.2, is a simple network program used to retrieve the system objects from a remote network device using the SNMP class. To compile the SimpleSNMP.cs program, you must include the SNMP.cs class file on the compiler command line:
csc SimpleSNMP.cs SNMP.cs
using System;
using System.Text;
class SimpleSNMP
{
public static void Main(string[] argv)
{
int commlength, miblength, datatype, datalength, datastart;
int uptime = 0;
string output;
SNMP conn = new SNMP();
byte[] response = new byte[1024];
Console.WriteLine("Device SNMP information:");
// Send sysName SNMP request and get the response
response = conn.get("get", argv[0], argv[1], "1.3.6.1.2.1.1.5.0");
if (response[0] == 0xff)
{
Console.WriteLine("No response from {0}", argv[0]);
return;
}
// Get the community name and MIB lengths from the packet
commlength = Convert.ToInt16(response[6]);
miblength = Convert.ToInt16(response[23 + commlength]);
// Extract the MIB data from the SNMP response
datatype = Convert.ToInt16(response[24 + commlength + miblength]);
datalength = Convert.ToInt16(response[25 + commlength + miblength]);
datastart = 26 + commlength + miblength;
output = Encoding.ASCII.GetString(response, datastart, datalength);
Console.WriteLine(" sysName - Datatype: {0}, Value: {1}",
datatype, output);
// Send a sysLocation SNMP request
response = conn.get("get", argv[0], argv[1], "1.3.6.1.2.1.1.6.0");
if (response[0] == 0xff)
{
Console.WriteLine("No response from {0}", argv[0]);
return;
}
// Get the community name and MIB lengths from the response
commlength = Convert.ToInt16(response[6]);
miblength = Convert.ToInt16(response[23 + commlength]);
// Extract the MIB data from the SNMP response
datatype = Convert.ToInt16(response[24 + commlength + miblength]);
datalength = Convert.ToInt16(response[25 + commlength + miblength]);
datastart = 26 + commlength + miblength;
output = Encoding.ASCII.GetString(response, datastart, datalength);
Console.WriteLine(" sysLocation - Datatype: {0}, Value: {1}",
datatype, output);
// Send a sysContact SNMP request
response = conn.get("get", argv[0], argv[1], "1.3.6.1.2.1.1.4.0");
if (response[0] == 0xff)
{
Console.WriteLine("No response from {0}", argv[0]);
return;
}
// Get the community and MIB lengths
commlength = Convert.ToInt16(response[6]);
miblength = Convert.ToInt16(response[23 + commlength]);
// Extract the MIB data from the SNMP response
datatype = Convert.ToInt16(response[24 + commlength + miblength]);
datalength = Convert.ToInt16(response[25 + commlength + miblength]);
datastart = 26 + commlength + miblength;
output = Encoding.ASCII.GetString(response, datastart, datalength);
Console.WriteLine(" sysContact - Datatype: {0}, Value: {1}",
datatype, output);
// Send a SysUptime SNMP request
response = conn.get("get", argv[0], argv[1], "1.3.6.1.2.1.1.3.0");
if (response[0] == 0xff)
{
Console.WriteLine("No response from {0}", argv[0]);
return;
}
// Get the community and MIB lengths of the response
commlength = Convert.ToInt16(response[6]);
miblength = Convert.ToInt16(response[23 + commlength]);
// Extract the MIB data from the SNMp response
datatype = Convert.ToInt16(response[24 + commlength + miblength]);
datalength = Convert.ToInt16(response[25 + commlength + miblength]);
datastart = 26 + commlength + miblength;
// The sysUptime value may by a multi-byte integer
// Each byte read must be shifted to the higher byte order
while(datalength > 0)
{
uptime = (uptime << 8) + response[datastart++];
datalength—;
}
Console.WriteLine(" sysUptime - Datatype: {0}, Value: {1}",
datatype, uptime);
}
}
The SimpleSNMP program uses the get() method from the SNMP class to send each GetRequest packet to the remote network device. As each GetResponse packet is received, it is decoded from the SNMP packet.
| Note |
This is an excellent opportunity to use the WinDump or Analyzer programs. After sending a test SNMP request packet, you should see the response packet returned by the SNMP device. You must decode the SNMP response packet to determine where in the packet structure the variables you are interested in are located. The numbers used to reference variables in these examples are taken from counting byte locations in test SNMP response packets. |
First, the community name length and MIB length are computed from their locations in the returned packet:
int commlength = Convert.ToInt16(response[6]); int miblength = Convert.ToInt16(response[23 + commlength]);
The community length is easy, as it is always located in the same spot in the packet. The miblength byte location depends on the length of the community name.
After the community name and MIB lengths, you determine the datatype of the returned object value, its start and length. When you know the location and length of the byte string, you can extract it from the packet:
int datatype = Convert.ToInt16(response[24 + commlength + miblength]);
int datalength = Convert.ToInt16(response[25 + commlength + miblength]);
int datastart = 26 + commlength + miblength;
string output = Encoding.ASCII.GetString(response, datastart, datalength);
Console.WriteLine("response:");
Console.WriteLine(" Datatype: {0}, length: {1}", datatype, datalength);
Console.WriteLine(" data: {0}", output);
The sysName MIB object is represented as a string value, so it’s easy to find the location of the string within the SNMP packet (knowing how the packet is formatted and knowing the community name and MIB lengths).
The sysUptime value can be a little tricky to decode. There is no way of knowing ahead of time how many bytes it will be. You must program for each possibility, from 1 byte to 5 bytes. The datalength value indicates how many bytes are used in the value. As each byte is read, it is added to a total. When another byte is read, the preceding bytes are shifted 8 bits over to make the high byte value.
Testing the Program
You can test the SimpleSNMP program with any SNMP-capable device on your network, as long as you have access to at least a read-only community name. Many network hubs and switches, as well as Windows NT and 2000 servers, have the SNMP service installed and can be used for this test:
C:\>SimpleSNMP 192.168.1.100 test1 Device SNMP information: sysName - Datatype: 4, Value: Rich's workstation sysLocation - Datatype: 4, Value: The office sysContact - Datatype: 4, Value: Rich Blum sysUptime - Datatype: 67, Value: 4906397 C:\>
For this example, the remote device is located at IP address 192.168.1.100 and is using a read-only community name of test1. If you have created the SNMP packet properly and have used a valid community name, you should get an SNMP response back from the network device. If you do not get a response, you can use WinDump or Analyzer to watch the SNMP packet get sent and see if it is formatted properly.
| Note |
The Windows 2000 Server does not include the SNMP service by default. You must manually install the SNMP service and configure the system information. The default read community name is “public”. |
Watching the Packets
More interesting than watching the output of this program is watching the packets that are generated. The easiest way to understand the SNMP packet format is to watch a live SNMP transaction on the network. Listing 12.3 shows the SNMP GetRequest and GetResponse packets that resulted from the sysName SNMP query in the SimpleSNMP program.
C:\>windump -X udp port 161 Windump listening on\Device\Packet_El90x1 10:19:22.728534 192.168.1.6.1450 > 192.168.1.100.161: [12 extra after iSEQ]C=te st1 GetRequest(28) system.sysName.0 0x0000 4500 041c fb1f 0000 8011 0667 c0a8 0106 E..........g..}. 0x0010 c0a8 0164 05aa 00a1 0408 5f8b 3028 0201 ..|......._.0(.. 0x0020 0004 0574 6373 7431 a01c 0204 0000 0001 ...test1........ 0x0030 0201 0002 0100 300e 300c 0608 2b06 0102 ......0.0...+... 0x0040 0101 0500 0500 0000 0000 0000 0000 0000 ................ 0x0050 0000 .. 10:19:22.731609 192.168.1.100 > 192.168.1.6.1450: C=test1 GetResponse(40) system.sysName.0="Rich's workstat" 0x0000 4500 005b 755e 0000 1e11 f1e9 c0a8 0164 E..[u^........|. 0x0010 c0a8 0106 00a1 05aa 0047 f69e 303d 0201 ..}......G..0=.. 0x0020 0004 0574 6373 7431 a231 0201 0102 0100 ...test1.1...... 0x0030 0201 0030 2630 2406 082b 0601 0201 0105 ...0&0$..+...... 0x0040 0004 1252 6964 6827 7320 776F 726b 7374 ...Rich's workst 0x0050 6274 at 551 packets received by filter 0 packets dropped by kernel C:\>
The WinDump program recognized the SNMP packet and did some of the decoding work for us. The packet headers show the pertinent information for each SNMP packet:
10:19:22.728534 192.168.1.6.1450 > 192.168.1.100.161: [12 extra after iSEQ]C=te st1 GetRequest(28) system.sysName.0
This packet is a GetRequest packet, and it queries for the sysName object, located under the mib-2 system object. Note that the community name used also appears in the WinDump header (C=test1).
The raw packet dump contains both the IP and UDP packet headers, as well as the SNMP packet information. The SNMP packet starts at location 0x001c with the 0x30 sequence byte. You should recognize the bytes in the packet (you helped create them). The community name and MIB object identifier are contained within the packet as expected.
WinDump also decoded the important information in the SNMP GetResponse packet that was returned by the remote device:
10:19:22.731609 192.168.1.100 > 192.168.1.6.1450: C=test1 GetResponse(40) system.sysName.0="Rich's workstat"
Note that the object value was truncated because of the default capture size of the WinDump program. If you want to see the entire GetResponse packet, you can use the -s option to increase the capture buffer size to a larger value.