How We Wrote Our Own SNMP MIB for BeezKeeper

Writing a Custom SNMP MIB

SNMP stands for Simple Network Management Protocol, which is an Internet-standard protocol for collecting and organizing information about managed devices on IP networks and for modifying that information to change device behavior. If you use SNMP in your organization, you probably have an SNMP collector to gather information on the status of devices in your network.

Since the NetBeez BeezKeeper is the central server for our network monitoring solution designed to provide an end-user experience view of your network, it would only make sense to be able to integrate it with your organization’s SNMP collector and receive any critical and performance alerts from your remote locations immediately, and be able to correlate them with other SNMP measurements/alerts.

Since NetBeez provides a new and unique view of your network, we had to create our own set of SNMP traps to accommodate our data model and the types of alerts generated by our product.

Writing Your Own SNMP MIB: Procedure

The first step in writing our own SNMP MIB was to get our Private Enterprise Number (PEN). This number represents the root OID of an organization. For NetBeez, this is 1.3.6.1.4.1.44523, which translates to:

iso.org.dod.internet.private.enterprises.netbeez

And it should be the first declaration in our MIB:

netbeez MODULE-IDENTITY
     LAST-UPDATED "201704200000Z"
     ORGANIZATION "NetBeez, Inc."
     CONTACT-INFO
        "NetBeez, Inc.
        netbeez.net

        Postal: NetBeez, Inc.
        5124B Penn Avenue
        Pittsburgh, PA 15224
        USA

        Phone: +1 844-NET-BEEZ
        EMail: support@netbeez.net"

     DESCRIPTION
        "This MIB contains definition of the SNMP Traps
        associated to alerts sent by the NetBeez BeezKeeper"

     REVISION
        "201704200000Z"

     DESCRIPTION
        "First revision that includes only the alerts subtree"

  ::= { enterprises 44523 }


The next step was to create a hierarchy of the objects that made sense to include in our MIB. First, we decided to create a child object right under the enterprise OID for the specific product: BeezKeeper.

beezKeeperMIB   OBJECT IDENTIFIER ::= { netbeez 1 }


Everything else is rooted under this OID as shown in the diagram below 
(generated using MIB Browser):

OID diagram
Since we wanted to include all the alerts generated by our system, we split them in two categories. The first one is agent device alerts to represent alerts such as reachability and wireless interface up/down alerts. The second one is test alerts, which are generated when a test on an agent is experiencing degraded performance or a failure. Both categories are grouped under the nbAlerts group:

nbAlerts       OBJECT IDENTIFIER ::= { beezKeeperMIB 1 }

beezAlerts  OBJECT IDENTIFIER ::= { nbAlerts 1 }
testAlerts OBJECT IDENTIFIER ::= { nbAlerts 2 }

We then decided to include two table objects that will hold a SEQUENCE of the alert records for the two alert types: beezStatusAlerts and the testStatusAlerts. Doing it at this layer of the hierarchy gives us the ability to add more alert types if we need them in the future.

Here is the beezAlertsTable object:

beezStatusAlertTable    OBJECT-TYPE
  SYNTAX    SEQUENCE OF BeezStatusAlertEntry
  MAX-ACCESS not-accessible
  STATUS    current
  DESCRIPTION
    "This is the data structure associated to
    alerts triggered by the NetBeez BeezKeeper."
  ::= { beezAlerts 1 }

Then for each of the tables, you have to define the entries that go in that table. Here is the beezStatusAlertEntry which is of type BeezStatusAlertEntry:

beezStatusAlertEntry    OBJECT-TYPE
  SYNTAX    BeezStatusAlertEntry
  MAX-ACCESS not-accessible
  STATUS    current
  DESCRIPTION
    "This is the data structure associated to
    alerts triggered by the NetBeez BeezKeeper."
  INDEX  { beezAlertID }
  ::= { beezStatusAlertTable 1 }

BeezStatusAlertEntry   ::= SEQUENCE   {
  beezAlertID       Integer32,
  beezAlertType     INTEGER,
  beezAlertSeverity INTEGER,
  beezSourceAlertID  INTEGER,
  beezAlertTimestamp OCTET STRING,
  beezName      OCTET STRING,
  beezID       INTEGER,
  beezAlertMessage   OCTET STRING
  }

Then for each sequence, we had to define each of the objects that comprise it. Here’s just the first two for the BeezStatusAlertEntry:

beezAlertID OBJECT-TYPE
  SYNTAX    Integer32(0..127)
  MAX-ACCESS read-only
  STATUS    current
  DESCRIPTION
    "The alert ID of the alert being sent; this
    number can be used to correlate cleared alerts
    with raised ones."
  ::= { beezStatusAlertEntry 1 }

beezAlertType  OBJECT-TYPE
  SYNTAX    INTEGER {
     beezReachabilityAlert (1),
     beez80211 (2)
  }
  MAX-ACCESS read-only
  STATUS    current
  DESCRIPTION
    "The alert ID of the alert being sent; this
    number can be used to correlate cleared alerts
    with raised ones."
  ::= { beezStatusAlertEntry 2 }

Once all the entries and the sequence of entries (tables) are defined, we defined the notifications, which represent the messages sent from the NetBeez server to your trap collector. You have to group them under a separate notifications group:

nbAlertNotifications    OBJECT IDENTIFIER ::= { beezKeeperMIB 0 }

nbAlertNotificationsGroup NOTIFICATION-GROUP
   NOTIFICATIONS { beezAlertNotification, testAlertNotification }
   STATUS        current
   DESCRIPTION
      "The basic notifications implemented by an SNMP entity
       supporting command responder applications."
   ::= { nbGroups 3 }

Inside this group, you can describe the NOTIFICATION-TYPE messages. These messages are also sequences of attribute objects, referencing the attributes we defined earlier:

-- Notification types:

beezAlertNotification NOTIFICATION-TYPE
     OBJECTS { beezAlertID, beezAlertType, beezSourceAlertID, beezAlertTimestamp, beezName, beezID, beezAlertMessage, beezAlertSeverity }
     STATUS       current
     DESCRIPTION
         "A notification about a change in the alert status of a beez agent."
     ::= { nbAlertNotifications 1 }

testAlertNotification NOTIFICATION-TYPE
     OBJECTS { testAlertID, testType, testAlertSeverity, testSourceAlertID, testAlertTimestamp, testBeezName, testBeezID, testTargetName, testAlertMessage, testTargetDisplayName }
     STATUS       current
     DESCRIPTION
         "A notification about a change in the alert status of a monitoring test."
     ::= { nbAlertNotifications 2 }

This is the design we iteratively came up with. There may be some redundancies, or non-standard things in there, but this was all done by trial and error, and any feedback or discussion on this is always welcome.

Of course, SNMP works great if your organization is using SNMP as their primary way of looking into the status of network devices. Otherwise, you could also integrate the NetBeez BeezKeeper alerts using our Syslog integration.