Implement your own MIBs with Python
DIY MIBs
If you think of Wikipedia first when considering a freely expandable, hierarchical knowledge database, you are not totally wrong, but you have overlooked one classic example: The Simple Network Management Protocol (SNMP). This time-honored protocol can also conjure up a very wide range of very different information through a formalized protocol and Management Information Base (MIBs) modules.
MIBs are readily available from device and software manufacturers, but application developers looking to expose the internal metrics of their software can create them, as well. They can be equally useful for sys admins who want to monitor complex infrastructure setups such as DNSSEC. At first glance, implementing your own MIB might seem complex, but with the right tools, it's not that difficult. This article shows how to create a MIB for monitoring the system-on-a-chip (SoC) temperature of a Raspberry Pi with an easy-to-use Python module.
Management Information Base
SNMP can be compared in some ways to the Lightweight Directory Access Protocol (LDAP), another Internet classic. Both are network protocols that describe how to access information. In the case of LDAP, you consult an LDAP directory, which can take very different forms. In the case of SNMP, the Management Information Base that is an aggregation of implemented MIB modules can contain a wide range of information, from routing tables to the utilization level of filesystems, and from fan speeds to configuration states.
It's probably best to imagine the MIB as a collection of carefully defined objects and messages – in SNMP-speak this is Managed Objects and Notifications – that form a tree-like structure below a root node. Both are identified by their respective position in this tree through an object identifier (OID), which describes the path from the root node along the branches to the managed object or notification.
Each OID starts with a dot, representing the root node, followed by numbers, again separated by dots, that represent the branches and – at the end – the leaf of the tree. A rather short OID could be .1.3.6.1.2.1.31. There is an analogy to IP addresses here: Because it's difficult to remember an IP address as a series of numbers, the Domain Name System (DNS) was invented. In the case of OIDs, so-called MIB modules – text files also often called MIBs – likewise assign a name to each numeric component of an OID. For example, the above OID is easier to read as:
.iso.org.dod.internet.mgmt.mib-2.ifMIB
This example OID also demonstrates that the MIB tree (Figure 1) has a defining order. The described object is located, starting from the root node:
- in the branch of the International Organization for Standardization (ISO), iso (.1),
- in the branch for designated organizations, org (.3),
- in the branch of the US Department of Defense, dod (.6),
- in the branch for TCP/IP-based Internet, internet (.1),
- in the branch for network management, mgmt (.2),
- in the branch mib-2 (.1), and, finally,
- under the name ifMIB (.31).
MIBs can do much more, however. They can define entire sub-trees of the MIB, dock on top of existing trees, and often be extended at the bottom by other MIBs. At the top of the tree is SNMPv2-SMI (Structure of Management Information): Compliant to the specifications in RFC 2578 [1], it defines the .iso.org.dod.internet (.1.3.6.1) sub-tree, under which, the sub-trees directory (.1), mgmt (.2), experimental (.3), and private (.4) reside.
In practice, everything of interest is located within these sub-trees. Whereas the first three are characterized by vendor-independent standards, if you look at private (4) its enterprises (1) subtree is host to vendor-specific sub-trees with numbers assigned by the Internet Assigned Numbers Authority (IANA) [2]. MIBs also define syntax and semantics, assigning data types to the objects of their sub-tree and defining their own data types, if necessary – either scalar types (virtually anything that is not a table) or tables that represent a collection of scalar objects in rows. For example, the Interfaces Group (IF) MIB defined in RFC 2863 [3] describes the ifTable
, a table with all interfaces of a monitored system, and its statistics, such as the number of sent and received packets.
Embrace and Extend
A MIB thus ultimately simply represents a collection of definitions. However, a program known as an SNMP agent is needed to collect and provide this information. With network devices such as switches, the agent usually remains invisible because it is part of the firmware, and the question of extending it with your own MIBs simply does not arise.
In the case of a Linux system, like the Raspberry Pi in this example, things look different. Here, the snmpd
SNMP agent implements a fixed selection of MIBs (Table 1), but it can be extended in various ways.
Table 1
MIBs Managed by snmpd
DISMAN-EVENT-MIB
|
EtherLike-MIB
|
HOST-RESOURCES-MIB
|
IF-MIB
|
IP-FORWARD-MIB
|
IP-MIB
|
IPV6-MIB
|
NOTIFICATION-LOG-MIB
|
RMON-MIB
|
SNMPv2-MIB
|
TCP-MIB
|
UDP-MIB
|
At the very beginning, you have to install the packages needed for this project on the Raspberry Pi:
$ apt-get install snmp snmpd snmp-mibs-downloader smitools
The easiest way to extend it is with a minimal snmpd
configuration file:
rocommunity rpitesting extend rpitemp /bin/cat /sys/class/thermal/thermal_zone0/temp
For this first test, it makes sense to back up any existing /etc/snmp/snmpd.conf
file.
If you now start snmpd
, a temperature value of the Raspberry Pi Broadcom SoC can be obtained with the command:
$ snmpget -v2c -c rpitesting -Ovq localhost NET-SNMP-EXTEND-MIB::nsExtendOutputFull.\"rpitemp\"
The result is the current temperature, multiplied by 1,000 so it can be processed as an integer.
This works because the extend
module of snmpd
uses NET-SNMP-EXTEND-MIB
to create matching MIB entries dynamically below the .iso.org.dod.internet.private.enterprises.netSnmp.netSnmpObjects.nsExtensions
sub-tree for each configured command (Listing 1).
Listing 1
snmpwalk
$ snmpwalk -Of -v2c -c rpitesting localhost NET-SNMP-AGENT-MIB::nsExtensions .iso.org.dod.internet.private.enterprises.netSNmp.netSnmpObjects.nsExtensions.2.1.0 = INTEGER: 1 .iso.org.dod.internet.private.enterprises.netSNmp.netSnmpObjects.nsExtensions.2.2.1.2.7.114.112.105.116.101.109.112 = STRING: "/bin/cat" .iso.org.dod.internet.private.enterprises.netSNmp.netSnmpObjects.nsExtensions.2.2.1.3.7.114.112.105.116.101.109.112 = STRING: "/sys/class/thermal/thermal_zone0/temp" [...]
By the way, the double-colon notation shown here is a short form commonly used by all Net-SNMP tools. The MIB name comes first; the name of the managed object or notification for which you searched and which is defined in this MIB is at the end. Both MIB names and object and notification names must be unique within a MIB; therefore, the combination of the two is unique, as well. The extend
mechanism built into snmpd
is easy and quick to implement, but it also has some disadvantages. It is best suited for simple, unstructured information. You could easily call another script with another extend
line in snmpd.conf
that returns, for example, the value of a sensor accessible via I2C. However, with no caching, each query of an object implemented through extend
triggers a separate execution of the configured command, which might take a bit of time under certain circumstances. Also, you might want to present information in a more structured form than the inevitably generic NET-SNMP-EXTEND-MIB
allows.
To avoid these disadvantages, you need to do two things: Design your own MIB for the desired purpose and implement it in a more flexible way than with extend
.
Basics for Custom MIBs
MIBs are written as ASCII text files, and with a standard Net-SNMP installation they can usually be found in /usr/share/snmp/mibs/
. The files follow a language called Structure of Management Information (SMI). SMI – currently at version 2 – comprises an adapted part of the Abstract Syntax Notation (ASN.1) that is a shared standard of the ISO and the International Telecommunication Union (ITU) Telecommunication Standardization Sector (ITU-T) used primarily for communication technologies (e.g., for GSM or X.509 certificates), but also for LDAP.
In principle, a MIB comprises a series of definitions in the format:
Name Keyword [Parameters] ::= Value
The different parts can extend over several lines depending on the keyword. Keywords can have additional mandatory or optional parameters. Listing 2 shows an initial basic structure of the RASPI-MIB.txt
MIB.
Listing 2
Basic MIB Structure
RASPI-MIB DEFINITIONS ::= BEGIN ----------------------------------------------------------- -- RASPI-MIB for monitoring the temperature of the RasPi-SoC ----------------------------------------------------------- END
The DEFINITIONS
keyword assigns the name RASPI-MIB
a specific value: everything that follows between the keywords BEGIN
and END
. In this listing this value is not much, because everything between two single pairs of hyphens or between a pair of hyphens and the end of a line is taken as a comment and ignored. DEFINITIONS
also diverges from other keywords, in that you can use a leading capital letter and hyphens for the associated name. According to SMIv2, both are prohibited for all other names.
However, another exception needs to be investigated first: The IMPORTS
keyword. It does not follow the described format at all and, much like similar keywords in C and Python programs, is dedicated to the task of importing existing definitions from other MIBs. A typical IMPORTS
block that loads needed definitions might look like Listing 3.
Listing 3
Imports
-- Imports IMPORTS MODULE-IDENTITY, OBJECT-TYPE, Integer32 FROM SNMPv2-SMI netsnmpPlaypen FROM NET-SNMP-MIB;
The first part of a MIB is always a module definition, which contains a large amount of information about the MIB. In the C language, you would define a structure to convey the information, whereas in ASN.1, you use a macro. Macros are used to construct data types and values beyond the ASN.1 standard repertoire. The MODULE-IDENTITY
macro defined in SNMPv2-SMI is used as shown in Listing 4.
Listing 4
MODULE-IDENTITY Macro
raspiMIB MODULE-IDENTITY LAST-UPDATED "202005060200Z" ORGANIZATION "None" CONTACT-INFO "Editor: Careful Reader Raspberry-Pi-Way 42 10487 Berlin" DESCRIPTION "A MIB to monitor Raspberry PIs" REVISION "202005060200Z" DESCRIPTION "Version 1." ::= { netSnmpPlaypen 42 }
The name raspiMIB
complies with the above-mentioned rule that names start with a lowercase letter and must not contain hyphens. The additional parameters are all necessary and have the following meanings:
LAST-UPDATED
indicates when the MIB was last updated.ORGANIZATION
indicates the organization or company to which the authors belong.CONTACT-INFO
provides contact information for the authors of the MIB.DESCRIPTION
is a textual description of the MIB (e.g., its purpose).REVISION
andDESCRIPTION
always occur in pairs and describe a specific revision of the MIB. Changes to the MIB should always be explained briefly inDESCRIPTION
and accompanied by a newREVISION
, which is also reflected inLAST-UPDATED
.
The example assigns the value 42
to the name netSnmpPlaypen
. If you think this looks like another OID, you are right. This name is defined in NET-SNMP MIB
, again with reference to an existing name. If this chain is resolved recursively (e.g., using
$ snmptranslate -On NET-SNMP-MIB::netSnmpPlaypen
the OID .1.3.6.1.4.1.8072.9999.9999 is returned. The definition in Listing 4 appends the number 42, so in the end, the MIB defines raspiMIB
as .1.3.6.1.4.1.8072.9999.9999.42.
Maybe you're now assuming that, thanks to this definition, all further definitions of the MIB are automatically created below this OID, but this is by no means the case. On the one hand, the definitions must explicitly refer to raspiMIB
or other definitions. On the other hand, it is usually the case, but by no means the law, that they must refer to the names defined within the sub tree defined through the MODULE-IDENTITY
macro.
The question remains why raspiMIB was defined below netSnmpPlaypen
. Life is easy if you are developing a MIB in an organization for which IANA has assigned a separate sub-tree below enterprises
and where you can apply for a corresponding attachment point internally. Even in this case, however, it might be useful initially to use the netSnmpPlaypen
sub-tree, which is explicitly provided by the Net-SNMP project for your own experiments.
If your MIB is to be used within your organization only, you might be inclined to use any OID you like; after all, nobody outside the organization would be concerned. However, this hack can turn around and bite you if OID collisions occur because you have other MIBs in use. Such a choice should therefore be made only after careful consideration.
Buy this article as PDF
(incl. VAT)