Monday, September 24, 2012

Net-SNMP and read_all_mibs

For better or for worse, Net-SNMP is the SNMP implementation for use with C/C++ (and Linux).  My feelings on Net-SNMP are a topic for another time, however.

Here, we'll be focused on using the "read_all_mibs" function to load the MIBs that have been deployed and how to interpret the result of that function.

Our major use cases revolve around:

  • Finding information about an OID (known by its numeric form); and
  • Showing the contents of the OID tree (from some arbitrary location).

Initializing Net-SNMP

If your MIBs are in a non-standard directory, or if you just want to take charge and tell Net-SNMP what to do, you'll need to specify the MIB directory before calling "init_snmp".  For example:
netsnmp_set_mib_directory( "/usr/local/snmp/mibs" );

Once you've done that, your next step is to initialize Net-SNMP.  You'll need to give it some arbitrary text (which is used for logging purposes, I believe).  This should normally be the name of your application or something otherwise unique.
init_snmp( "sense-codons" );

"init_snmp" will then load all of the MIBs in the directory that you specified, and it will spew errors all over the screen if your MIBs are in any way not perfect.  And trust me, if you put some proprietary MIBs in that directory, you'll see what I'm talking about.  The point is that the "init_snmp" function might take a minute or two to run; once that function returns, you can rest assured that the MIBs have been loaded and, as best as it could, Net-SNMP has organized them into an OID tree for you to use.

Root nodes

The top-level structure of the OID tree looks like this:
  • ccitt(0)
  • iso(1)
  • joint-iso-ccitt(2)
This much will be understood by Net-SNMP even if there are no MIBs present.

Since this is understood to be a tree, I would have expected there to be an uppermost, root node in the tree, but that's not the case with Net-SNMP.  Rather, all of the top-level items are the roots of their own trees, and there is no root node.

Getting a root node

To obtain the first (that is, lowest-numbered) root node (remember, there are multiple root nodes), you call "read_all_mibs".  This returns a "struct tree*", which has the following useful fields:
  • label, a "char*" that is the name of the node.
  • subid, an unsigned integer that is the numeric ID of the node.
  • next_peer, a "struct tree*" pointer to the next sibling of the node.  This will be NULL if there is no sibling node.
  • child_list, a "struct tree*" pointer to the first child of the node.  This will be NULL if there is no child node.
With a basic set of MIBs, you should get back a node with subid=0, label=ccitt and a sibling of subid=1, label=iso.

Trusting node IDs

This whole setup seems pretty straightforward; you'd think that the usual tree-navigation strategies would work.  However, there is just one more item that I need to mention: node IDs are not technically unique.

Due to the magic and wonder of SNMP, it often is the case that two manufacturers (or even one manufacturer at two times) will redefine a node.  That is, the same node ID will have different names ("label" in the structure).

As far as I can tell, Net-SNMP will always store both names; it will choose one of them to be authoritative, and the others will be more or less shortcuts to the "real" name.  These are stored in the tree as siblings.

So, if you are looking for node ".0.0.8.341.1", under node ".0.0.8.341" there might be multiple "1" nodes.  Once you find the first subid=1 node, you should keep iterating through all of the "next_peer" nodes that have subid=1.  The last such node is the node that you actually one.

For example, assume that you've found the first such node; we'll call it "node".  This little one-line for-loop will update "node" to the last node with that node ID.  This will work even if it is the only such node.
int currentSubid = node->subid;
for( ; node && node->next_peer && node->next_peer->subid == currentSubid; node = node->next_peer );