Category Archives: Uncategorized

Major bug discovered in version 0.7.4 and earlier

I would like to thank Jozsef Horvath for discovering, reporting and providing a fix for an issue related to encoding of specific unsigned integer values used by Gauge32, Counter32, etc. variable types derived from UInteger32 base type.

Fix for the issue will be included in the version 0.7.5 scheduled for release tomorrow.

Changes in preparation for version 0.7.5 release

Based on the feedback so far from users and the fixes needed in the released version of the library, following changes have so far been planned for the new release:

  • fixed: socket error 10054 now throws SnmpNetworkException in UdpTransport.Request method
  • added ICloneable to all AsnType child classes that didn’t have it
  • added IComparable interface implementation to OctetString class
  • implemented error handling in UdpTransport async methods. Now legitimate errors will result in request being aborted (same as blocking requests) with
    appropriate error code returned in the callback
  • added VbCollection.ContainsOid(Oid) method
  • added VbCollection index access to variable bindings using Oid class and string representation of oid (this(string oid))
  • removed unnecessary byte array allocation in MutableByte.Append(byte) method
  • removed unused MutableByte class allocation from Pdu.encode() method
  • added a check for a correct sequence type at the start of an SNMP packet in SnmpPacket.GetProtocolVersion() and SnmpPacket.decode() methods (SnmpDecodingException thrown)
  • added sequence type check when parsing global message data sequence in SnmpV3Packet.decode() method
  • removed unused versionHeaderLength variable in SnmpV3Packet.encode method
  • removed unused variable byteCount from Privacy3DES.Encrypt method
  • removed unused asnBuf variable from Counter64.decode method
  • removed unused oldOffset variable from Oid.decode method
  • removed unused oldOffset variable from Sequence.decode method
  • removed unused AgentParameter assignments from UdpTarget.AsyncResponse method
  • added sequence type check in SnmpV3Packet.GetUSM method
  • added exception XML documentation to SnmpV3Packet.GetUSM method
  • removed unused preVersionLength variable in SnmpV3Packet.encode method
  • removed unused asnBuf variable from Counter64.decode() method
  • implemented IDisposeable interface in UdpTransport class and changed UdpTransport.Close() into UdpTransport.Dispose() method
  • added IDisposeable interface to UdpTarget class. Implementation Dispose() method is inherited from the UdpTransport class
  • fixed Pdu.Reset() method request id value increment to handle value rollover to avoid integer overflow

More updates will be made based on any new feedback received.

To keep the library evolving to provide as complete as possible implementation of SNMP, send me feedback or comments you have through the SourceForge project page and I’ll do my best to implement it.

SnmpSharpNet in IronPython

I had a little bit of time today and figured I’ll try to use the library in IronPython.

I have used Python a lot a few years back for an SNMP based project and did like it a lot. It has a clean syntax and is very easy to learn.

It has taken about an hour of googling to find what I needed and in the end I had a fully working Get example:

import clr
clr.AddReference("System.Net")
clr.AddReferenceToFile("SnmpSharpNet.dll")
from SnmpSharpNet import *
from System.Net import *
 
param = AgentParameters()
param.Community.Set("public")
agentIP = IpAddress("127.0.0.1")
agent = UdpTarget(IpAddress.op_Explicit(agentIP), 161, 2000, 1)
pdu = Pdu(PduType.Get)
pdu.VbList.Add("1.3.6.1.2.1.1.1.0") # sysDescr
pdu.VbList.Add("1.3.6.1.2.1.1.2.0") # sysObjectID
pdu.VbList.Add("1.3.6.1.2.1.1.3.0") # sysUpTime
pdu.VbList.Add("1.3.6.1.2.1.1.4.0") # sysContact
pdu.VbList.Add("1.3.6.1.2.1.1.5.0") # sysName
 
result = None
 
try:
  result = agent.Request(pdu, param)
except:
  pass
 
# If result is null then agent didn't reply or we couldn't parse the reply.
if (result != None):
  # ErrorStatus other then 0 is an error returned by
  # the Agent - see SnmpConstants for error definitions
  if (result.Pdu.ErrorStatus != 0):
    # agent reported an error with the request
    print "Error in SNMP reply. Error ", result.Pdu.ErrorStatus, \
      "index ", result.Pdu.ErrorIndex
  else:
    # Reply variables are returned in the same order as they were added
    #  to the VbList
    print "sysDescr(", result.Pdu.VbList[0].Oid.ToString(), ") (", \
      SnmpConstants.GetTypeName(result.Pdu.VbList[0].Value.Type), "): ", \
      result.Pdu.VbList[0].Value.ToString()
    print "sysObjectID(", result.Pdu.VbList[1].Oid.ToString(), ") (", \
      SnmpConstants.GetTypeName(result.Pdu.VbList[1].Value.Type), "): ", \
      result.Pdu.VbList[1].Value.ToString()
    print "sysUpTime(", result.Pdu.VbList[2].Oid.ToString(), ") (", \
      SnmpConstants.GetTypeName(result.Pdu.VbList[2].Value.Type) ,"): ", \
      result.Pdu.VbList[2].Value.ToString()
    print "sysContact(", result.Pdu.VbList[3].Oid.ToString(), ") (", \
      SnmpConstants.GetTypeName(result.Pdu.VbList[3].Value.Type) ,"): ", \
      result.Pdu.VbList[3].Value.ToString()
    print "sysName(", result.Pdu.VbList[4].Oid.ToString(), ") (", \
      SnmpConstants.GetTypeName(result.Pdu.VbList[4].Value.Type) ,"): ", \
      result.Pdu.VbList[4].Value.ToString()
else:
  print("No response received from SNMP agent.")
 
agent.Close()

Output is as expected:

c:\>IPythonSnmp.exe
sysDescr( 1.3.6.1.2.1.1.1.0 ) ( OctetString ):  "Dual core Intel notebook"
sysObjectID( 1.3.6.1.2.1.1.2.0 ) ( ObjectId ):  1.3.6.1.9.233233.1.1
sysUpTime( 1.3.6.1.2.1.1.3.0 ) ( TimeTicks ):  0d 7h 54m 1s 460ms
sysContact( 1.3.6.1.2.1.1.4.0 ) ( OctetString ):  "msinadinovic@users.sourceforge.net"
sysName( 1.3.6.1.2.1.1.5.0 ) ( OctetString ):  "milans-nbook"

Progress report

Today I have posted version 0.5.1 to the SourceForge site with 3 critical bug fixes included.

I’m not too happy about releasing too many versions but if I find a bug with a serious impact on library usability, I’ll release a new versions in rapid succession.

I’m already working on the next version. One of the changes that I have already put in place for the next version is to change Pdu.Type (and associated ScopedPdu.Type) data type from byte and reliance on the SnmpConstants byte constants, that can cause mistakes, to a new enumeration PduType.

I’m looking through the code looking for other places where enumerations make more sense then using constants.

I have decided that enumerations are a better way to go about this. Constants mean that you can set a wrong value in a wrong place. It also means that a lot of code needs to be put in place to verify input. Extra code means more potential bugs, not checking every parameter means wrong values can be set by the library users and result in unexpected consequences. So, enumerations are the way forward…

Current project status

Just a quick note.

Version 0.5.0 is out the door … finally.

There is plenty to do about the project but I have decided to start working on actually delivering some applications instead of just plugging away at SNMP.

Before shifting focus, I’ll work on a series of documentation pages to be posted on the project web site. Usually people get the coding to a certain level and take vacation forgetting the documentation. I am trying not to do that.

My intention from the beginning was to depend on the people using the library to tell me what bugs or issues they had using it.

I would like to ask you all that if you are using the library and find an issue, let me know.

You don’t have to have a solution for the problem, just a description of what you were trying to do, what happened and possibly dump of information you were working on at the time so I can dig into it deeper.

By letting me know what issues or limitation you’ve found, you will help improve this project for everybody (yes, yourself included).

SNMPv3 INFORM and TRAP handling

Just had an interesting time testing TRAP and INFORM handling with SNMP version 3 using net-snmp snmptrap utility.

Here is the summary of what I have learned over the last week and what the testing has confirmed.

SNMP Traps are handled like a response to a query. You get a packet, parse it using USM information for the specific agent and presto, you’ve got the notification information.

In the Trap packet, engineId, engineBoots, engineTime and securityName are set by the agent sending the trap so handling is relatively easy with the only difference being that you have to locally store engineId and securityName mappings to the correct authentication digest, privacy protocol and related secrets. Then, when the packet arrives, parse the USM header information, find the appropriate information for the authoritative engine that sent you the trap, apply the authentication and privacy parameters to the SnmpV3Packet class and call SnmpV3Packet.decode method.

While this is not as easy as request/response operation, it is not too hard.

SNMP version 3 Inform is a whole different game. In the Inform packet, engineId, engineBoots and engineTime are set to the manager values. This means that your application has to be able to respond to discovery packets.

This took a little while to figure out. With the use of SnmpV3Packet.GetUSM (and the unintended consequence of how I implemented this method) you can get all the values needed to generate a Discovery response. New SnmpV3Packet methods like IsDiscoveryPacket and DiscoveryResponse should help with implementing this functionality.

Only thing remaining to finish before version 0.5 is released is SNMP version 2 inform handling and testing. I’m planning to have it done and tested enough for release by the end of next week.

Keep checking the project page for news…

SNMP v2c and v3 EndOfMibView, NoSuchInstance and NoSuchObject types

SNMP v2 introduced a concept of MIB status notification encoded in the VbList as a value that notifies the caller when there is an issue with the request or if request has reached the end of available data.

This is where EndOfMibView, NoSuchInstance and NoSuchObject come in. EndOfMibView tells caller that there is no more data to “walk” (using either GET-NEXT or GET-BULK packets), NoSuchInstance tells the caller that requested instance of the object does not exist (for example, asking for interface speed – 1.3.6.1.2.1.2.2.1.5 for interface instance 200 on an agent that only has interfaces up to instance 100 will result in this error), and EndOfMibView signifying the end of the data in the requested view.

To check for these notifications, you should evaluate each value returned by the agent, first for the notification and second for the value it should contain.

For example, after a request completed using SNMP v2c or 3, you could do the following:

SnmpV2Packet pkt = GetData();
foreach( Vb v in pkt.Pdu.VbList ) {
  if( v.Value.Type == SnmpConstants.SMI_ENDOFMIBVIEW ) {
    // end of mib reached
    return;
  } else {
   // process Vb value
  }
}

OctetString.ToString() method

Unlike the name of the class, OctetString doesn’t really store text strings as would be expected, but any type of byte based data.

Because data in the OctetString is just a series of bytes, there is no guarantee that it is printable so some fancy footwork is required to perform the ToString() method operation.

In the current implementation, OctetString.ToString() tries to determine if the value contained within the class is binary and therefore unreadable, or ASCII and therefore printable. This is done by scanning the class data for values that are less then 32 (excluding the new line characters which are considered printable) or greater then 127. Values outside of this range are not considered ASCII text so assumption is that class value is binary and special output format is required.

Now, if the value is determined to be all text then there is no problem, convert the byte array to a System.String class and return the result.

Problem is that if the value is binary, then converting the class data to a System.String would result in a bunch of junk that is not printable. When this happens, OctetString.ToString() method calls OctetString.ToHexString() which will print out each byte value as a 2 character hex value representing the byte value.

As you can imagine, this method of trying to “guess” what type of value is contained within the class is not very exact. One example is that you have binary data that just happens to fall within the ASCII character range and shouldn’t be represented as a printable string.

In cases where you know you will be receiving binary data that is not in the text format, you should use OctetString.ToHexString() to get predictable results.

There is an additional ToString??? method provided for MAC addresses. I have a need for specific vendor format MAC address that groups MAC address bytes into groups of 2 delimited by “.”. For example: 0008.0102.0304. To get this kind of output, you can use OctetString.ToMACAddressString(). Please note that this method will only work if the OctetString class data is exactly 6 bytes long. Any other data length will result in a zero length string being returned.

Progress report

Progress has been slow and painful mostly because of my own stupidity. I have been going nutz with authentication error while parsing INFORM packets from SNMPv3 agents and couldn’t find the fault only to find out that I was passing receive buffer length to the decode function and not received length resulting in a longer buffer padded with zeros and messing up with the authentication hash generation. Such is life…

That is fixed now and SNMPv3 INFORM and TRAP handling is moving into alpha (from pre-release) with a couple of hours of testing planned for next week against actual agents in the real world. I expect to have this nailed down soon and ready for release.

One thing I have discovered from e-mails I’ve been receiving is that high level of control while coding SNMP is not always desirable. To make it easier for people who just want to get to the data, I have created a SimpleSnmp class that will allow SNMPv1 and SNMPv2 request generation and reply processing with no or very little knowledge of how SNMP works.

I have replaced a lot of code based on other libraries implementations from the encode/decode methods in base type classes. This has taken some doing and a lot of debugging. To make sure this all works, I have written a testing server using a proven and stable C++ SNMP library and then written a whole lot of c# apps to send encoded data to it for verification. Just so you know, this is a bad idea and just the testing of Integer32 class has taken a better part of 3 days on a quad-core system I use for development.

Besides the “big” changes, I have been working on introducing higher level of consistency to class methods and properties. During development you tend to focus so much on delivering functionality that you forget that classes performing similar functions should have similar methods and properties. That work continues and I don’t think it will be fully done in the next release but at least I don’t plan to remove anything but just keep adding what’s missing.

Exception generation is another area that I have tried to put a little time into making it more sensible. In the earlier releases I have been creating exception classes as needed and ended up with a lot of them being used only once or twice. Again, work is far from over but reduction in the number of exception classes thrown and better notification of what went wrong is the goal. Introduction of error codes in each exception class so that caller can determine if it is fatal or recoverable (as most SNMP exceptions on a network manager are) should help.

At this point, I expect I’ll take TripleDES, AES-192 and AES-256 privacy classes completely out of the next release and target to re-introduce them later down the road when I have the time to finish implementing them. I work on the library away from agents that support these protocols so I am finding it very hard to find the time to spend in a lab testing code with all the other obligations I have. If you have a project that absolutely depends on these privacy protocols being implemented, let me know and I’ll see what I can do to speed things out but for now this functionality is out.

Check back often and look out for library release 0.5 no later then a month from now. This release will be the first “real” beta with all functionality included and tested as much as possible in the lab environment. This is where I’ll start doing the real world testing with the applications that are in the pipeline.

SNMP Version 3 TRAPs and INFORMs

I finally managed to figure out proper handling of TRAP and INFORM packets with SNMP version 3.

Turns out the SnmpV3Packet.decode function will not be sufficient because it attempts to authorize and un-encrypt the packet. With notification packet types we need to look-ahead into the packet to determine the values required to perform these operations. Values needed are part of the USM header, EngineId, SecurityName (or user name), EngineBoots and EngineTime.

To be able to do a lookup of the required values prior to full packet decode, authentication and privacy processing, I have added SnmpV3Packet.GetUSM method that performs a partial packet decode and returns UserSecurityModel class containing values in the header that we need to proceed. As part of the GetUSM parsing, required values are set in the SnmpV3Packet class so the only additional step needed to finalize the packet decoding is lookup of the security parameters for the EngineId/SecurityName combination (that should be stored by the application) and setting the appropriate AuthenticationProtocol, PrivacyProtocol and secrets for both so that packet can be decoded.

In other words, once you receive a packet you will call SnmpV3Packet.GetUSM and retrieve UserSecurityModel class. Using EngineId and SecurityName and match them with the authentication and privacy settings you have stored for each SNMP agent, set the appropriate Authentication, AuthenticationSecret, Privacy and PrivacySecret values and then call SnmpV3Packet.decode to process the packet.