Ah Yes, the SIMPLE Network Management Protocol

Table of Contents

If that title isn’t a dead giveaway, I’m not happy. But yet I will somehow manage to vent my frustrations and explain something at the same time. Today: SNMP, or, “How to gather lots of stats on remote machines,” or, “Because you thought CVS was hard to wrap your head around.”

On that last point, remember where I talked about CVS revision numbers? Remember that for later.

Now, officially, SNMP is, per Wikipedia, “an Internet Standard protocol for collecting and organizing information about managed devices on IP networks and for modifying that information to change device behavior. Devices that typically support SNMP include cable modems, routers, switches, servers, workstations, printers, and more.”

Layman’s terms: SNMP allows you to manage, but actually more commonly, monitor devices on a network by requesting specific attributes from them, for example “How many bytes have been sent on interface eth0?” Some attributes can be written to, for example, the ability to write to one to administratively disable a certain network interface, but I pretty much set everything I have to read-only since I don’t need remote modification, so we’ll focus on the monitoring point.

Many monitoring systems (like the one I use, Zabbix) support checking devices via SNMP for some statistics.

SNMP Network Architecture

SNMP operates on what kinda looks like a client-server architecture. The ‘client,’ called the ‘manager’ can send GetRequest commands to the ‘server,’ called the ‘agent,’ and it will respond with a particular value (there’s other commands, that’s besides the point.) In this manner, SNMP is a request-only system, nothing is actually pushed out from the SNMP-enabled device (meaning a device running an SNMP agent, such as snmpd). However there is a provision for just that, called SNMP traps.

The actual messages being sent on the wire are ASN.1 structures, sent using BER. Or in other words, it’s a binary protocol.

And do note: SNMP uses UDP as its transport, not TCP. Port 161 for normal requests, and 162 for traps.

Traps

SNMP traps can be sent from the agent to a manager, as an unsolicited notification, for example, alerts if a power supply has failed, or disk S.M.A.R.T. variables indicate a potential failure, whatever. A common trap type is SNMP authentication failure, which, let’s look at that.

Versions

SNMP has three major versions, SNMPv1, SNMPv2c, and SNMPv3.

Version 1 is… really nothing special, it’s basic, it’s insecure, and there’s little to speak about.

SNMPv2c

Version 2 (not 2c) added some performance improvements, and the GetBulkRequest for accessing large numbers of attributes in a single request. Version 2 also changed the ‘counter’ type attributes from being 32 bits wide to 64 bits wide. Version 2 also added an “overly complex” security model that wasn’t ever really adopted.

Out of this, 2c was born, the c for “community.” SNMPv1 and SNMPv2c both base their entire security model on what are called community strings. How these work is that a particular device accepts requests from a certain community (the default one is public), and may even accept multiple communities, some being read only, some read-write, but this is at the discretion of the SNMP agent being used.

The community string is transmitted in the packet in plain text, which is compared with the community list on the agent, and a valid community gets a response, an invalid community gets a failure.

Here’s the problem: that’s effectively the password. That’s likely going to be repeated on every SNMP device, and is transmitted clear. Anyone who gets into your network will have no trouble figuring it out.

Note: user-based authentication, 2u, was also a thing that acted as a compromise between 2 and 2c, but it was never really used until v3.

SNMPv3

Think of this one as a major security update. Each device, client or server, is an SNMP entity. Every entity has an SNMPEngineID, and (with the exception of traps), an entity will not communicate with another entity presenting an unknown SNMPEngineID. SNMPv3 uses a user-based authentication model, and can protect against message modification in-flight, entity spoofing, and a few other things like eavesdropping on SNMP traffic. To accomplish this, there are three security levels that SNMPv3 can operate at: NoAuthNoPriv, AuthNoPriv, and AuthPriv. Looking at these tells you the two main services at work here: authentication and privacy. To this end, each of those requires a passkey. Authentication passes are a pre-shared hash between two devices, and privacy passes are symmetric encryption ciphers with a pre-shared initialization. For example, my system uses SHA for authentication and AES for privacy, though some devices may only support MD5 / DES. When sending an AuthPriv level request, both the hash value and cipher value have to match what’s on the other device or it won’t accept it. The actual data that you, the human in the loop, need to give it are a text phrase (the actual pass), and the algorithm in use. This means that the rest is just protocol work, and text passwords can still be brute-forced through generating, hashing/encrypting, and then sending them to a target device, assuming it hasn’t whitelisted known IPs and is allowing all addresses to send requests. Additionally, like stated before, the SNMPEngineID works for this too. The first time an ID is seen, it’s noted with the IP it came from. If that ID is ever presented from a different IP, or the original IP has a different ID, then the request is going to be ignored.

SNMP Structure

So the rest is really cool, and this is where things suddenly go from smooth sailing to please pass me the bottle of Jack Daniel’s. SNMP uses a hierarchical namespace for its attributes, think of it as a giant tree structure. This is represented textually through an Object Identifier or OID. OIDs are standardized methods of naming any, well, thing with an unambiguous name. OIDs are represented as a dotted-decimal structure, where each number is another level on the OID tree, and the actual number is what branch you pick. OIDs are found pretty commonly within ITU-T creations, as well as ISO/IEC work, including things like LDAP (and X.500 DAP), TLS certificates, and DHCP vendor suboptions.

For example, Intel has an ‘arc’ (branch of the OID tree) that everything for them is under, which is their assigned IANA enterprise number, 343. Their OID, therefore, is 1.3.6.1.4.1.343. Do… do you see the resemblance (and headache) to CVS yet? If we were to give those names it would look like this:

iso.identified-organization.dod.internet.private.enterprise.intel

Fun fact: the dod there is indeed Department of Defense, a very old hold-over from the early days of the internet which was pretty much developed by the DoD, for the DoD. And yes, the DoD does technically have authority over anything under 1.3.6, in the same sense that IANA has control over 1.3.6.1.4.1, in the same sense that Intel has free reign over their own subtree, 1.3.6.1.4.1.343, to do what they please. OIDs are meant to be pretty permanent and unambiguous too — once something is assigned an OID, ideally, that OID shouldn’t change.

With THAT explanation over, if you’re not seeing my main headache with SNMP, just wait, it gets better. SNMP attributes are exported as OIDs for their ’names.’ For example, the system hostname is available at 1.3.6.1.2.1.1.5.0, the MAC address of network interface 2 is at 1.3.6.1.2.1.2.2.1.6.2, and the number of bytes received by interface 1 is at 1.3.6.1.2.1.2.2.1.10.1.

Naturally we decided that this is all too complicated and started assigning real names. The hostname is at RFC1213-MIB::sysName.0, NIC 2 MAC address is RFC1213-MIB::ifPhysAddress.2, and NIC 1 received bytes is RFC1213-MIB::ifInOctets.1.

Where do we map these names to numbers? MIBs.

MIBs

A Management Information Base, or MIB, is a text file formatted in the Structure of Management Information Version 2 (SMIv2), syntax, which looks very much like Brackus-Narr or ASN.1 syntax.

Protocol-wise, everything in SNMP is raw OIDs. Anything that interfaces with a human uses its MIBs to translate textual names into OIDs.

If a hardware vendor (say, like, Netgear) wanted to add their own custom statistics into SNMP, the best thing to do is to construct and allow downloading of a MIB that defines all their attributes.

Speaking of, here’s one:

    READYNASOS-MIB DEFINITIONS ::= BEGIN

        IMPORTS
            OBJECT-TYPE
                FROM RFC-1212
            TRAP-TYPE
                FROM RFC-1215
            enterprises
                FROM RFC1155-SMI
            DisplayString
                FROM RFC1213-MIB;

        netgear     OBJECT IDENTIFIER ::= { enterprises 4526 }
                productID       OBJECT IDENTIFIER ::= { netgear 100 }

        ReadyNASOS  OBJECT IDENTIFIER ::= { productID 16 }

        ngNasManager    OBJECT IDENTIFIER ::= { netgear 22 }


        nasMgrSoftwareVersion OBJECT-TYPE
            SYNTAX DisplayString (SIZE (1..32))
            ACCESS read-only
            STATUS current
            DESCRIPTION
                "Version information for the ReadyNASOS
                 ngNasManager software."
            ::= { ngNasManager 1 }

                 nasMgrSerialNUM OBJECT-TYPE
                        SYNTAX DisplayString (SIZE (1..32))
                        ACCESS read-only
                        STATUS current
                        DESCRIPTION
                               "Serial number for the ReadyNASOS."
                        ::= { ngNasManager 2 }

And this file tells you, for example, that this textual name:

READYNASOS-MIB::netgear.ngNasManager.nasMgrSoftwareVersion

is the OID 1.3.6.1.4.1.4526.22.1, and it is the “Version information for the ReadyNASOS ngNasManager software.”

And thus, when I run this command:

snmpget READYNASOS-MIB::netgear.ngNasManager.nasMgrSoftwareVersion

It will send an SNMP GetRequest for 1.3.6.1.4.1.4526.22.1. Which this all leads me to one question: Why???

No, seriously. For the “simple” protocol, we’ve taken three distinct versions to get actual good security, security that requires you know two passwords and two algorithms, the actual representation of everything is just an explosion of numbers the likes of which only IPv6 and CVS have matched, and since like IPs, we decided that remembering numbers is dumb, so we invented DNS MIBs written in this weird semi-human-readable and machine-parsable Brackus-Narr markup that defines everything. And which every vendor likely has their own MIB, if not for every individual product they have, if not multiple for one product!! NICE!

I wasn’t even setting up proper SNMP when I got this ranty, I tried doing that a while back, and setting up SNMPv3 gets tiring quick. I was just trying to get it to run on that NAS that kinda died until I made it live again, which… really just set me off. Like just hear me out, if you’re going to present a MIB for all your details, SNMP traps, and the like, well, maybe have the device that presents it actually use them. The model I have pretty much only exports stuff from RFC1213-MIB and that’s it. Not to mention it only supports SNMPv2c.

See, the protocol itself in implementation is simple, I give it that. But in terms of usage and configuring, it’s anything but. For example, for Zabbix to recognize my human-readable names, it needs the MIB. These are kept in /usr/share/snmp/mibs. Except it keeps saying not found… and I know this is an issue with a software library, not SNMP, but WHY does the most popular implementation, Net-SNMP, have a critical bug in 5.7.x where the MIB compiler (official term) will read MIBs and then spit out a Module not found error for every file that it just read? And why does no package repository actually contain 5.8 where this is fixed? I know this is linux world but I would assume the Debian package repositories which are usually on top of things would have either a backport fix, or upgrade to a version without a crippling bug in it.

I spent an hour and a half dealing with all this, dealing with MIBs, dealing with a stupidly slow NAS CPU, dealing with Net-SNMP’s crap to be told “oh by the way, the thing you just tried putting together isn’t even supported in the slightest.”

Simple. Yes. Totally. Simple.

Wait hold up. That’s not true. None of that with my NAS is true. See, because the way I was attempting to look at its exported SNMP attribute was with the snmpwalk command, which sends a lot of GetNextRequests to “walk” the SNMP tree, one value at a time. Well those MIBs that they published are supported and do exist, but the won’t be walked when used like that! I had to manually specify the starting point for each MIB (I did the same process with my UPS, which had 7 MIBs to look through) and see which it actually returns results for. Isn’t the point of walking the tree to discover the entire tree?!?!?!


I can’t be bothered to deal with this anymore, I’m done here.