# Network Management and SNMP

Network management, broadly construed, consists of all the administrative actions taken to keep a network running efficiently. This may include a number of non-technical considerations, eg staffing the help desk and negotiating contracts with vendors, but we will restrict attention exclusively to the technical aspects of network management.

The ISO and the International Telecommunications Union have defined a formal model for telecommunications and network management. The original model defined five areas of concern, and was sometimes known as FCAPS after the first letter of each area:

- fault
- configuration
- accounting
- performance
- security

Most non-ISP organizations have little interest in network accounting (the A in FCAPS is often replaced with “administration” for that reason, but that is a rather vague category). Network security is arguably its own subject entirely. As for the others, we can identify some important subcategories:

fault:

- device management: monitoring of all switches, routers, servers and other network hardware to make sure they are running properly.
- server management: monitoring of the network’s application layer, that is, all network-based software services; these include login authentication, email, web servers, business applications and file servers.
- link management: monitoring of long-haul links to ensure they are working.

configuration:

- network architecture: the overall design, including topology, switching vs routing and subnet layout.
- configuration management: arranging for the consistent configuration of large numbers of network devices.
- change management: how does a site roll out new changes, from new IP addresses to software updates and patches?

performance:

- traffic management: using the techniques of 23 Queuing and Scheduling to allocate bandwidth shares (and perhaps bucket sizes) among varying internal or external clients or customers.
- service-level management: making sure that agreed-upon service targets – eg bandwidth – are met (depending on the focus, this could also be placed in the fault category).

While all these aspects are important, technical network management narrowly construed often devolves to an emphasis on fault management and its companion, reliability management: making sure nothing goes wrong, and, when it does, fixing it promptly. It is through fault management that some network providers achieve the elusive availability goal of 99.999% uptime.

SNMP versus Management

While SNMP is a very important tool for network management, it is just a tool. Network management is the process of making decisions to achieve the goals outlined above, subject to resource constraints. SNMP simply provides some input for those decisions.

By far the most common device-monitoring protocol, and the primary focus for this chapter, is the Simple Network Management Protocol or SNMP (26.2 SNMP Basics). This protocol allows a device to report information about its current operational state; for example, a switch or router may report the configuration of each interface and the total numbers of bytes and packets sent via each interface.

In this chapter we describe the basics of SNMP, and, in particular, SNMP version 1. In the following chapter we describe version 2 and version 3; the latter is where reasonable security is finally implemented.

Implicit in any device-monitoring strategy is initial device discovery: the process by which the monitor learns of new devices. The ping protocol (10.4 Internet Control Message Protocol) is common here, though there are other options as well; for example, it is possible to probe sequential IPv4 addresses on the UDP port used for SNMP – usually 161. As was the case with router configuration (13 Routing-Update Algorithms), manual entry is simply not a realistic alternative.

SNMP and the Application Layer

SNMP can be studied entirely from a network-management perspective, but it also makes an excellent self-contained case study of the application layer. Like essentially all applications, SNMP defines rules for client and server roles and for the format of requests and responses. SNMP also contains its own authentication mechanisms (26.11 SNMPv1 communities and security and 27.3 SNMPv3), generally unrelated to any operating-system-based login authentication.

It is a practical necessity, for networks of even modest size, to automate the job of checking whether everything is working properly. Waiting for complaints is not an option. Such a monitoring system is known as a network management system or NMS; there are a wide range of both proprietary and open-source NMS’s available. At its most basic, an NMS consists of a library of scripts to discover new network devices and then to poll each device (possibly but not necessarily using SNMP) at regular intervals. Generally the data received is recorded and analyzed, and alarms are sounded if a failure is detected.

When SNMP was first established, there was a common belief that it would soon be replaced by the OSI’s Common Management Interface Protocol. CMIP is defined in the International Telecommunication Union’s X.711 protocol and companion protocols. CMIP uses the same ASN.1 syntax as SNMP, but has a richer operations set. It remains the network management protocol of choice for OSI networks, and was once upon a time believed to be destined for adoption in the TCP/IP world as well.

But it was not to be. TCP/IP-based network-equipment vendors never supported CMIP widely, and so any network manager had to support SNMP as well. With SNMP support essentially universal, there was never a need for a site to bother with CMIP.

CMIP, unlike SNMP, is supported over TCP, using the CMIP Over Tcp, or CMOT, protocol. One advantage of using TCP is the elimination of uncertainty as to whether a request or a reply was received.

## 1 Network Architecture

Before turning to SNMP in depth, we offer a few references to other parts of this book relating to network architecture. At the LAN and Internetwork layers local to a site, perhaps the main issues are redundancy, bandwidth and cost. Cabling between buildings, in particular, needs to provide redundancy. See 3.1 Spanning Tree Algorithm and Redundancy and 3.2 Virtual LAN (VLAN) for some considerations at the Ethernet level, and 9.6.3 Subnets versus Switching.

Next, a site must determine what sort of connection to the Internet it will have. ISP contracts vary greatly in terms of bandwidth, burst bandwidth, and agreed-upon responses in the event of an outage. Some aspects of service-level specification appear in 24 Token Bucket Rate Limiting and 25.7.2 Assured Forwarding.

Organizations with geographically dispersed internal networks – ISPs and larger corporations – must decide how their internal sites should be connected. Should they communicate over the public Internet? Should a VPN be used (5.1 Virtual Private Networks)? Or should private lines (such as SONET, 6.2.2 SONET, or carrier Ethernet, 5.2 Carrier Ethernet) be leased between sites? If private lines are used, link monitoring becomes essential.

One increasingly important architectural decision at the application layer is the extent to which network software services are outsourced to the cloud, and run on remote servers managed by third parties.

## 2 SNMP Basics

SNMP is far and away the most popular protocol for supporting network device monitoring. At its most basic level, SNMP allows polling of individual designated device attributes, such as the system name or the number of packets received via interface `eth0`. Attributes may, however, be organized into records, sets and tables. Tables may be indexed contiguously, like an array – eg interface[1], interface[2], interface[3], etc – or sparsely – eg interface[1], interface[32767].

While the simplest routers and switches may have quite limited provisions for SNMP, virtually all “serious” networking hardware provides extensive support, for both standard sets of “basic” device attributes and for proprietary attributes as well. Devices with significant SNMP support are sometimes referred to as managed devices.

An SNMP node that replies to requests for information is known as an SNMP agent. The network node doing the SNMP querying is known as the manager; it may be part of an NMS or – more simply – be a standalone tool known as an SNMP browser or MIB browser (where MIB stands for Management Information Base, below). While most MIB browsers are understood to have a graphical user interface, there are also command-line tools to make SNMP queries, such as the `snmpget` and `snmpwalk` commands of the Net-SNMP project at net-snmp.org. These can be invoked by scripting languages to build a simple if rudimentary NMS.

SNMP runs exclusively over UDP. The choice of UDP was made to avoid the connection overhead of what was envisioned to be a simple request-reply protocol; if a manager polls 1,000 devices once a minute, that is 2,000 packets in all over UDP but might easily be 8,000 packets with TCP and the necessary SYN/FIN packets. This may be especially significant when the network is congested to near the point of failure.

The use of UDP does raise two problems: lost packets and having more data than will fit in one packet. For the simplest case of manager-initiated data requests, a manager can handle packet loss by polling a device until a response is received. If a response (or even a request) is too big, the usual strategy is to use IP-layer fragmentation (9.4 Fragmentation and 11.5.4 IPv6 Fragment Header). This is not ideal, but as most SNMP data stays within local and organizational networks it is at least workable.

Another consequence of the use of UDP is that every SNMP device needs an IP address. For devices acting at the IP layer and above, this is not an issue, but an Ethernet switch or hub has no need of an IP address for its basic function. Devices like switches and hubs that are to use SNMP must be provided with a suitable IP interface and address. Sometimes, for security, these IP addresses for SNMP management are placed on a private, more-or-less hidden subnet.

SNMP also supports the writing of attributes to devices, thus implementing a remote-configuration mechanism. As writing to devices has rather more security implications than reading from them, this mechanism must be used with care. Even for read-only SNMP, however, security is an important issue; see 26.11 SNMPv1 communities and security and 27.3 SNMPv3.

Writing to agents may be done either to configure the network behavior of the device – eg to bring an interface up or down, or to set an IP address – or specifically to configure the SNMP behavior of the agent.

Finally, SNMP supports asynchronous notification through its traps mechanism: a device can be configured to report an error immediately rather than wait until asked. While traps are quite important at many sites, we will largely ignore them here.

SNMP is sometimes used for server monitoring as well as device monitoring; alternatively, simple scripts can initiate connections to services and discern at least something about their readiness. It is one thing, however, to verify that an email server is listening on TCP port 25 and responds with the correct RFC 5321 (originally RFC 821) EHLO message; it is another to verify that messages are accepted and then actually delivered.

### 2.1 SNMP versions

SNMP has three official versions, SNMPv1, SNMPv2 and SNMPv3. SNMPv1 made its first appearance in 1988 in a collection of RFCs starting with RFC 1065 (updated in RFC 1155); the current definition for “core” attribute reporting was released as RFC 1213 in 1991. We will return to this below in 26.10 MIB-2.

SNMPv2 was introduced in 1993 with RFC 1441. Loosely speaking, SNMPv2 expanded on the basic information, starting with RFC 1442 (currently RFC 2578), and also introduced improved techniques for managing tables.

SNMPv2 also included a proposed security mechanism, but it was largely rejected by the marketplace. Ultimately, a version of SNMPv2 that used the SNMPv1 “community” security mechanism (26.11 SNMPv1 communities and security) was introduced; see RFC 1901. This “community”-security version became known as SNMPv2c.

SNMPv3 then finally delivered a model for reasonably effective security. The “User-based Security Model” or USM was first proposed in 1998 in RFC 2264; the final 2002 version is in RFC 3414.

## 3 SNMP Naming and OIDs

A central part of the SNMP protocol is how to name each device attribute in a consistent manner. The naming scheme chosen was the Object Identifier, or OID, hierarchy.

Starting in 1985, the International Standards Organization (ISO) and the standardization sector of the International Telecommunications Union (ITU-T, then known as CCITT) developed the Object Identifier scheme for naming anything in the world that needed naming. Names, or OIDs, consist of strings of non-negative integers. In textual representation these component integers are often written separated by periods, eg 1.3.6.1; the notation { 1 3 6 1 } is also used. The scheme is standardized in ITU-T X.660.

All OIDs used by SNMP begin with the prefix 1.3.6.1. For example, the `sysName` attribute corresponds to OID 1.3.6.1.2.1.1.5. The prefix 1.3.6.1.2.1 is known as `mib-2`, and the one-step-longer prefix 1.3.6.1.2.1.1 is `system`. Occasionally we will abuse notation and act as if names referred to single additional levels rather than full prefixes, eg the latter two names in `mib-2.system.sysName`.

The basic SNMP read operation is `Get()` (sent via the `GetRequest` protocol message), which takes an OID as parameter. The agent receiving the `Get` request will, if authentication checks out and if the OID corresponds to a valid attribute, return a pair consisting of the OID and the attribute value. We will return to this in 26.8 SNMP Operations, and see how multiple attribute values can be requested in a single operation.

OIDs form a tree or hierarchy; the immediate child nodes of, say, 1.3.6.1 of length 4 are all nodes 1.3.6.1.N of length 5. The root node, with this understanding, is anonymous; OIDs are sometimes rendered with a leading “.” to emphasize this: .1.3.6.1. Alternatively, the numbers can be thought of as labels on the arcs of the tree rather than the nodes.

There is no intrinsic way to distinguish internal OID nodes – prefixes – from leaf OID nodes that correspond to actual named objects. Context is essential here.

It is common to give the numeric labels at any specific level human-readable string equivalents. The three nodes immediately below the root are, with their standard string equivalents

- itu-t(0)
- iso(1)
- joint-iso-itu-t(2)

The string equivalents can be thought of as external data; when OIDs are encoded only the numeric values are included.

OID naming has been adopted in several non-SNMP contexts as well. ISO and ITU-T both use OIDs to identify official standards documents. The X.509 standard uses OID naming in encryption certificates; see RFC 5280. The X.500 directory standard also uses OIDs; the related RFC 4524 defines several OID classes prefixed by 0.9.2342.19200300.100.1. As a non-computing example, Health Level Seven names US healthcare information beginning with the OID prefix 2.16.840.1.113883.

As mentioned above, SNMP uses OIDs beginning with 1.3.6.1; in the OID tree these levels correspond to

- iso(1)
- org(3): organizations
- dod(6): the US Department of Defense, the original sponsor of the Internet
- internet(1), apparently the only sublevel of DOD

Here is a portion of the OID tree showing a few other assignments in the iso subtree, and highlighting the 1.3.6.1 prefix above. More entries are available from oid-info.com/get.

> itu-t(0)
> iso(1)
> standard(0)
> registration-authority(1)
> member-body(2)
> identified-organization(3)
> nist(5)
> dod(6)
> icd-ecma(12) (European Computer Manufacturers Association)
> oiw(14) (OSE Implementers Workshop)
> edifactboard(15)
> ewos(16) (European Workshop on Open Systems)
> osf(22) (Open Software Foundation)
> nordunet(23)
> nato(26)
> icao(27) (International Civil Aviation Organization)
> …
> amazon(187) (the author has no idea if Amazon currently uses this)
> joint-iso-itu-t(2)

This SNMP 1.3.6.1 prefix is spelled out in RFC 1155 via the syntax

    internet      OBJECT IDENTIFIER ::= { iso org(3) dod(6) 1 }

which means that the string `internet` has the type `OBJECT IDENTIFIER` and its actual value is the list to the right of `::=`: 1.3.6.1. The use of `iso` here represents the OID prefix .1, conceptually different from just the level iso(1). The formal syntax here is ASN.1, as defined by the ITU-T standard X.680. We will expand on this below in 26.6 ASN.1 Syntax and SNMP, though the presentation will mostly be informal; further details can be found at http://www.itu.int/en/ITU-T/asn1/Pages/introduction.aspx.

After the above, RFC 1155 then defines the OID prefixes for management information, `mgmt`, and for private-vendor use, `private`, as

    mgmt          OBJECT IDENTIFIER ::= { internet 2 }
    private       OBJECT IDENTIFIER ::= { internet 4 }

Again, `internet` represents the newly defined prefix 1.3.6.1. Most general-purpose SNMP OIDs begin with the `mgmt` prefix. The `private` prefix is for OIDs representing vendor-specific information; for example, the prefix 1.3.6.1.4.1.9 has been delegated to Cisco Systems, Inc. RFC 1155 states that both the mgmt and private prefixes are delegated by the IAB to the IANA.

Responsibility for assigning names that begin with a given OID prefix can easily be delegated. The Internet Activities Board, for example, is in charge of 1.3.6.1. (According to RFC 1155, this delegation by the Department of Defense to the IAB was never made officially; the IAB just began using it.)

## 4 MIBs

A MIB, or Management Information Base, is a set of definitions that associate OIDs with specific attributes, and, along the way, associates each numeric level of the OIDs with a symbolic name. For example, below is the set of so-called `System` definitions in the core RFC 1213 MIB known as MIB-2 (26.10 MIB-2). The `mib-2` and `system` prefixes are first defined as

    mib-2          OBJECT IDENTIFIER ::= { mgmt 1 }
    system         OBJECT IDENTIFIER ::= { mib-2 1 }

that is, 1.3.6.1.2.1 and 1.3.6.1.2.1.1 respectively. The `system` definitions, which represent actual attributes that can be retrieved, are as follows

    sysDescr       { system 1 }
    sysObjectID    { system 2 }
    sysUpTime      { system 3 }
    sysContact     { system 4 }
    sysName        { system 5 }
    sysLocation    { system 6 }
    sysServices    { system 7 }

Most of these attributes represent string values that need to be administratively defined; we will return to these in 26.10 MIB-2.

The MIB is not the actual attributes themselves; the set of all ⟨OID,value⟩ pairs stored by an SNMP manager – perhaps the result of a single set of queries, perhaps the result of a sequence of queries over time – is sometimes known as the management database, or MDB, though that term is not as universal.

Colloquially, a MIB can be either the abstract set of OID definitions or a particular ASCII file that defines the set. The latter must be run through a MIB compiler to be used by other software; users of a MIB browser generally find the information much more useful if the appropriate MIB file is loaded before making SNMP queries. Some RFCs can be read directly by the MIB compiler, which knows to edit out the non-MIB discussion.

The Net-SNMP package contains SNMP agents for Linux and Macintosh computers (Microsoft has their own SNMP-agent software), and command-line tools for making SNMP queries; these latter tools are also available for Windows. The sysName value, for example, can be retrieved with the `snmpget` command

    snmpget -v1 -c public localhost 1.3.6.1.2.1.1.5.0

The OID here corresponds to { system 5 0 }; the final 0 represents a SNMP convention that indicates this is a scalar value and is not part of a table. See 26.9 MIB Browsing for further examples.

MIBs serve as both documentation and data. As documentation, they tell the implementer of a given SNMP agent what information is to be returned for each OID request. As data, they serve to translate numeric OIDs into human-readable data. For example, if the above `snmpget` command understands the appropriate MIB (eg because the MIB file is in the right place), we can type

    snmpget -v1 -c public localhost sysName.0

It is not uncommon for an installation to involve several hundred different (possibly overlapping) MIBs, each defining a different portion of the OID tree. This is particularly true of the `private` subtree 1.3.6.1.4.1, for which one might have a separate MIB file for each manufacturer and device.

## 5 SNMPv1 Data Types

SNMP uses OIDs as the indexes for retrieval of attributes. The attributes themselves can have the following types, as of RFC 1155:

Datatype | Description
--- | ---
INTEGER | A 32-bit integer
OCTET STRING | Either a text string or a network address
Counter | A non-negative INTEGER that increases from 0, and can wrap around.
Gauge | An INTEGER that can rise and fall but that never wraps around
TimeTicks | An INTEGER used to measure time in 1/100ths of a second
OBJECT IDENTIFIER | An OID appearing as data
IpAddress | An IPv4 address, as an OCTET STRING of fixed length 4
NetworkAddress | An IpAddress, in theory allowing for other kinds of addresses later
Opaque | Any other data, ultimately an OCTET STRING

The type of the `sysName` attribute, for example, is OCTET STRING.

When SNMP data values are returned by an agent, they are tagged with their type. Thus, the receiver can See 26.12 SNMP and ASN.1 Encoding for further details.

When the INTEGER type is used to represent an enumerated type, the value of zero must not be used; an example is the status type `up(1)`, `down(2)`, `testing(3)`.

## 6 ASN.1 Syntax and SNMP

We have already seen the ASN.1 definition of some OBJECT IDENTIFIERs in 26.3 SNMP Naming and OIDs.

The attribute corresponding to any single OID is always a scalar type, eg one of the above. However, SNMP also uses some composite type definitions:

> SEQUENCE { fieldname1 type1, fieldname2 type2, … }: This defines a record type.
> SEQUENCE OF type: This defines a homogeneous list – an array or table – of objects of type type. In most cases, type represents a SEQUENCE type, and so the list is a list of records, or table in the database sense.

Note the importance of the keyword OF here to distinguish records from lists.

The OBJECT-TYPE macro is heavily used in MIB definitions. It has the format

> objname `OBJECT-TYPE`
> `SYNTAX` type
> `ACCESS` read-write or read-only or write-only or not-accessible
> `STATUS` mandatory or optional or obsolete
> `DESCRIPTION` descriptive string
> `INDEX` OID used for table indexing
> `::=` OID assigned to objname

The objname, sometimes called the OBJECT DESCRIPTOR, is intended to be globally unique. The value for `SYNTAX` must be a valid ASN.1 syntax specification; see 26.10 MIB-2 for examples. The values for `ACCESS` and `STATUS` are, in each case, one of the literal strings shown above. The value for `DESCRIPTION` is optional.

Many of the objects so defined will represent tables or other higher-level structures; in this case the Object ID of the last line will represent an internal node of the OID tree rather than a leaf node, and the ACCESS will be `not-accessible`. It is not that the table is actually inaccessible, but rather that the attributes must be retrieved one by one rather than collectively.

Here is a concrete simple example; other examples appear in the following section. The OID value of `ifEntry` here is 1.3.6.1.2.1.2.2.1.

    ifInOctets OBJECT-TYPE
        SYNTAX  Counter
        ACCESS  read-only
        STATUS  mandatory
        DESCRIPTION
                "The total number of octets received on the
                interface, including framing characters."
        ::= { ifEntry 10 }

The data type definitions and the above ASN.1 syntax used for are collectively known as the structure of management information, or SMI. The SMI in effect defines all the types and structures needed by the various MIBs. For SNMPv1, the SMI is defined in RFC 1155.

## 7 SNMP Tables

We have seen in 26.4 MIBs how SNMP organizes the OID names for the `system` group, which consists of scalar values. Each agent attribute is assigned a permanent OID name; eg { system 5 0 } is the full OID for the `sysName` attribute. OIDs for scalar (non-table) attributes always include a final “.0”.

Most agent data, however, comes in the form of tables, that is, lists of records. In SNMP, they are sometimes referred to as conceptual tables, as the SNMP agent offering the table is often not responsible for its storage or physical organization. In this sense, SNMP tables resemble database “views”.

These tables can be accessed by linear search or by primary key lookup; the key can be a single attribute or a set of attributes and is known as the index. SNMP does not support secondary keys on tables, though it would in principle be possible for an agent to present the same data in two tables, each with a different index (a related example of this trick appears in 27.2.1.5 Matrix).

SNMP tables are almost always small enough that the agent keeps all the records in main memory; database-style concerns about disk-storage organization and indexing are not an issue.

As in databases, an SNMP table index acts as a constraint, disallowing two distinct rows with identical index values. When an SNMP table mirrors a “real” table (eg the IP forwarding table), problems sometimes occur when the SNMP index is too “small” – has too few attributes – and so the SNMP table may not be able to accurately represent the original table. See 27.1.12.2 IP-Forward MIB for an example.

In the world of databases, a key with too many attributes may fail to capture an important constraint; it may also be less efficient. Neither is a concern with SNMP, with the exception of tables that support row creation, which we may take to be a special case. SNMP table indexes may thus sometimes include attributes that from a database perspective would likely be omitted from the key; again, see 27.1.12.2 IP-Forward MIB for examples.

We will look here at three examples of SNMPv1 tables, each part of MIB-2. The goal right now is to present how the tabular structure is mapped onto the underlying OID-tree structure; we will return to the syntactic definition of tables in 26.10.2 Table definitions and the interfaces Group and to the semantics of each table in 26.10 MIB-2. SNMPv2 has made some modest improvements to how tables are defined.

The first example will be the SNMP interface table, `ifTable`. For each network interface (eg loopback, Ethernet 0, Ethernet 1, Wireless 0, Point-to-Point 0, etc) a collection of attributes including the device name, MTU, bitrate, physical address, and the number of bytes and packets received and sent. The index here (as we shall see later) is an abstractly assigned interface number, known as `ifIndex`. Here is some sample data, not showing all columns that the actual MIB-2 table includes, and not (yet) showing any OIDs. The header of the index column, ifIndex, is in italic; the value is a single integer. The `eth0` interface has a higher number for packets than bytes (octets) because the 32-bit `inOctets` value has wrapped around.

ifIndex | name | MTU | bitrate | inOctets | inPackets
--- | --- | --- | --- | --- | ---
1 | lo | 16436 | 10,000,000 | 171 | 3
2 | eth0 | 1500 | 100,000,000 | 37155014 | 1833455677
3 | eth1 | 1500 | 100,000,000 | 0 | 0
4 | ppp0 | 1420 | 0 | 2906687015 | 2821825

Routes

Although the data here is mostly a mash-up of records from different sources, the 10.38.0.0 route below was a VPN route using the `ppp0` tunnel above. Otherwise, workstation forwarding tables often have just two entries, for the local subnet and the default route.

The SNMP routing table, `ipRouteTable`, based on the IP forwarding table of 1.10 IP - Internet Protocol, has a column representing the destination network, a variety of status and information columns (eg for RouteAge), and a NextHop column; the index is the destination-network attribute. Again, here is some sample data with the index column again in italic. The index here – an IP address – is a compound object: a list of four integers. We return to this below.

dest | mask | metric | next_hop | type
--- | --- | --- | --- | ---
0.0.0.0 | 0.0.0.0 | 1 | 192.168.1.1 | indirect(4)
10.0.0.0 | 255.255.255.0 | 0 | 0.0.0.0 | direct(3)
10.38.0.0 | 255.255.0.0 | 1 | 192.168.4.1 | indirect(4)

(The `type` column indicates whether the destination is local or nonlocal; the host is on subnet 10.0.0.0/24 and so the middle entry involves local delivery. The use of `indirect(4)` and `direct(3)` is an example of an SNMP enumerated type.)

The indexing of the interfaces table is (usually) dense: the values for `ifIndex` are typically consecutive numbers. The routing table, on the other hand, has a sparse index: the index values are likely nowhere near one another.

The TCP Connections table `tcpConnTable` lists every TCP connection together with its connection state as in 17.7 TCP state diagram; one can obtain this on most Linux, Windows or Macintosh systems with the command `netstat -a`. The index in this case consists of the four attributes of the connection-defining socketpair: the local address, the local port, the remote address and the remote port. In this table, the only attribute not part of the index is an integer representing the connection state (again represented by an SNMP enumerated type).

localAddr | localPort | remoteAddr | remotePort | state
--- | --- | --- | --- | ---
10.0.0.3 | 31895 | 147.126.1.209 | 993 | established(5)
10.0.0.3 | 40113 | 74.125.225.98 | 80 | timeWait(11)
10.0.0.3 | 20459 | 10.38.2.42 | 22 | established(5)

SNMP has adopted conventions for how tabular data such as the above is to be encoded in the OID tree. The first step in this strategy is to define, statically, an OID prefix for the subtree representing the table; in this section we will denote this by T (later, in 26.10.2 Table definitions and the interfaces Group, we will see that T often represents two OID levels of the form Table.Entry, or T.E). Each attribute of the table (that is, each column) is then assigned a non-leaf OID by appending successive integers to the table prefix, starting at 1. For example, the root prefix for the interfaces table is T = 1.3.6.1.2.1.2.2.1, known as `ifEntry`. The columns shown in the fraction of the interfaces table above have the following OIDs:

OID | table column
--- | ---
`{ ifEntry 1 }` or T.1 | The interface number, `ifIndex`
`{ ifEntry 2 }` or T.2 | The interface name or description
`{ ifEntry 4 }` or T.4 | The interface MTU
`{ ifEntry 5 }` or T.5 | The interface bitrate
`{ ifEntry 10 }` or T.10 | The number of inbound octets (bytes)
`{ ifEntry 11 }` or T.11 | The number of inbound unicast packets

The second step is to establish a convention for converting the index attributes to a list of integers that can be used as an OID suffix identifying a particular row. This index OID suffix is then appended to the attribute OID prefix (identifying the column) to obtain the full OID (with no trailing .0 this time) that represents the name of the table data value.

For the case of the interfaces table, the interface number is used as a single-level OID suffix. The `eth1` value for `inOctets` thus has OID name T.10.3: 10 is the number assigned to the `inOctets` column and 3 is the `ifIndex` value for the `eth1` row. Written in full, this is 1.3.6.1.2.1.2.2.1.10.3. Note this numbering has the form T.col.row, the transposition of the more common programming-language row-first convention T[row][col].

For the routing and TCP tables presented here, IPv4 addresses are written in dotted-decimal notation, eg 10.38.0.0, and then converted to four levels of OID suffix, eg .10.38.0.0. (Note that 10.38.0.0 has the same textual representation as an IPv4 address and as a four-level OID suffix (though a leading ‘.’ has been added here to the latter), but logically they are entirely different things).

This OID-suffix encoding may seem obvious, but this is deceptive, and in 27.1.13 TCP-MIB we will see a newer convention in which the IPv4 address 10.38.0.0 would be encoded as the OID suffix .1.4.10.38.0.0, where the 1 denotes an IPv4 address (2 for IPv6) and the 4 denotes the address length as an OCTET STRING. The present rule, for the .10.38.0.0 four-level encoding, comes from RFC 2578, §7.7; the `IpAddress` type (for IPv4 only) is defined as `IMPLICIT OCTET STRING (SIZE (4))` (in RFC 1155), of predetermined length. An IP address might have been encoded as a single OID level using the address as a single unsigned 32-bit integer, but this option was not chosen.

In the routing table above, the nextHop column is assigned the number 7, so the nextHop for 10.38.0.0 thus has the OID T.7.10.38.0.0.

The OID suffix encoding used for indexing has nothing to do with the ASN.1 BER encoding used for data values, 26.12 SNMP and ASN.1 Encoding.

For the TCP connections table, the two IP addresses involved each translate to four-level OID chunks, as in the routing table, and the two port numbers each translate to one-level chunks; the resultant OID suffix for the state of the first connection in the table above would be

> .10.0.0.3.31895.147.126.1.209.993

where, for clarity, the port-number levels are shown in italic. The state column is assigned the identifier 1, so this would all be appended to T.1 (it is common, but, as we see here, not universal, for column-number assignment to begin with the index columns).

Here is a column-oriented diagram of the interfaces-table fragment from above. As we have been doing, the table prefix is denoted T and nodes are labeled T.col.row. The topmost T and the first row (with the T.col OIDs) are internal nodes; these are drawn with the heavier, blue boxes. The lower four rows, with black boxes, are leaf nodes.

The diagram above emphasizes the arrangement into columns. The actual OID tree structure, however, is as in the diagram below; all the leaf nodes in one column above are actually sibling nodes.

In all three tables here, the index columns are part of the table data. This is unnecessary; all the index columns are of necessity encoded in the full OID of any table entry. In SNMPv2 it is required (though not necessarily enforced) to in effect omit the index columns from the table as presented by the agent (though they are still declared); see 27.1.4 SNMPv2 Indexes.

We also note that, in all three tables here (and in most SNMP tables that serve purely as sources of information), new rows cannot be added via the SNMP manager. Some SNMP tables, however, do support row creation; see 27.2 Table Row Creation.

## 8 SNMP Operations

As mentioned earlier, the fundamental read operation is `Get()`; the protocol message itself is known as `GetRequest`, and also contains the authentication information (that is, the community string for SNMPv1 and SNMPv2c). A request-id is included so multiple outstanding `GetRequest`s aren’t mixed up.

The `GetRequest` message can contain a list of OIDs (actually encoded as a list of ⟨OID,null⟩ pairs called a `VarBind` list). A single-OID get simply entails a `VarBind` list of length 1.

The agent receiving the `GetRequest` validates the authentication and then, if successful, looks up each OID in the received list to find the corresponding value. The filled-in `VarBind` list of all ⟨OID,value⟩ pairs is returned.

If any value cannot be found, eg because the OID represented an interior node of the OID tree instead of an actual value, or because the OID lay outside the portion of the OID tree that the request was authorized to access, then in SNMPv1 no values are returned. Only if every OID in the list corresponds to a valid value is a list of all the ⟨OID,value⟩ pairs returned. SNMPv2 later relaxed this rule to allow the return of whatever individual requests were successful.

If the exact OID is not known – which is often the case for tabular data – the `GetNext()` operation can be used. It works like `Get()`, except that for each OID oid in the request list the agent finds the next leaf OID strictly following oid in lexicographical order, which we will call oidʹ. The agent then includes the pair ⟨oidʹ,value⟩ in its response, where value is the value corresponding to oidʹ. Note that in the `GetNext()` case oidʹ is not previously known to the manager. Note also that SNMP always returns, with each value, the corresponding OID, which for tabular data encodes the full index for the value.

The `GetNext()` operation allows a manager to walk through any subtree of the OID tree. If the root of the subtree is prefix T, then the first call is to GetNext(T). This returns ⟨oid1,value1⟩. The next call to GetNext has parameter oid1; the agent returns ⟨oid2,value2⟩. The manager continues with the series of GetNext calls until, finally, the subtree is exhausted and the agent returns either an error or else (more likely) an ⟨oidN,valueN⟩ for which oidN is no longer an extension of the original prefix p.

As an example, let us start with the prefix 1.3.6.1.2.1.1, the start of the `system` group. The first call to `GetNext()` will return the pair

> ⟨1.3.6.1.2.1.1.1.0, sysDescr_value⟩

The OID 1.3.6.1.2.1.1.1.0 is the first leaf node below the interior node 1.3.6.1.2.1.1.

The second call to GetNext will take OID 1.3.6.1.2.1.1.1.0 as parameter and `GetNext()` will return

> ⟨1.3.6.1.2.1.1.2.0, sysObjectID_value⟩

Here, the OID returned is the next leaf node strictly following 1.3.6.1.2.1.1.1.0. The process will continue on through {system 3 0}, {system 4 0}, etc, until an OID is found that is not part of the system group. As we have seen, this will likely be the first entry of the interfaces group, `ifNumber.0`, with OID 1.3.6.1.2.1.2.1.0.

The action of `GetNext()` is particularly useful when retrieving a table, such as the interfaces table presented in the preceding section. In that example the index values are consecutive integers, and so an SNMP manager could likely guess them. However, guessing the index values for the other two tables – the IP forwarding table and the TCP connections table – would be well nigh impossible. And without the index values, we do not have a complete OID.

Consider again the partial-column version of the interfaces table as diagrammed in this format:

Let us initially call GetNext(T). The next leaf node is the upper left black box, with OID T.1.1. The call to GetNext(T) returns the pair ⟨T.1.1,1⟩. We now continue with a call to GetNext(T.1.1); this returns ⟨T.1.2,2⟩ and represents the black box immediately below the first one. The next two calls to `GetNext()` return, successively, ⟨T.1.3,3⟩ and ⟨T.1.4,4⟩.

Now we call GetNext(T.1.4). The next leaf node following is the first leaf node in the second column, T.2.1; the value returned is ⟨T.2.1,”lo”⟩. The next three calls to GetNext, each time supplied with parameter the value returned by the previous `GetNext()`, return the next three values making up the second, `ifDesc` column of the table.

At the bottom of the second column, the call to GetNext(T.2.4) returns the ⟨OID,value⟩ pair that is the first leaf node of the third column, ⟨T.3.1,16436⟩. At the bottom of the third column, `GetNext()` jumps to the top of the fourth column, and so on. In this manner `GetNext()` iterates through the entire table, one column at a time.

When we finally get to the last leaf node of the table, shown here as the lower-right T.11.4 (though the actual `ifTable` has additional columns). The call to GetNext(T.11.4) returns something outside the table. It will either return an error – in the event that there is no next OID that the manager is authorized to receive – or the next leaf node up and to the right of T. (In the normal MIB-2 collection, this is would be the first entry of the `atTable` table.)

Either way, the manager receiving the data can tell that its request for GetNext(T.11.4) has gone past the end of table T, and so T has been completely traversed.

Here is a diagram of the above sequence of `GetNext()` operations involved in traversing our partial interfaces table; it shows the GetNext(T.11.4) at the table’s lower right returning the beyond-the-table ⟨OID,value⟩ pair ⟨A,avalue⟩:

Some manager utilities performing this kind of table retrieval – often called a “walk” – will present the data in the order retrieved, one column at a time, and some will (perhaps as an option) format the data visually to look more like a table. See Net-SNMP’s `snmpwalk` and `snmptable` in 26.9 MIB Browsing.

### 8.1 Multi-attribute `Get()`

A single `Get()/GetNext()` request can in fact include a list of attributes to be retrieved. This provides an efficient way to request entire rows of a table.

A manager in general does not know all the index values of a table, but likely does know all the column OIDs (as in the blue full-length row above). The manager can speed up the table-retrieval process by asking for entire rows at a time. The first such `GetNext()` might be

> GetNext(T.1, T.2, T.3, T.4, T.10, T.11)

These are the OIDs of the blue full-length row; these are all non-leaf OIDs. The result is the list of pairs

> ⟨T.1.1,1⟩, ⟨T.2.1,”lo”⟩, ⟨T.3.1,16436⟩, ⟨T.4.1,10000000⟩, ⟨T.10.1,171⟩, ⟨T.11.1,3⟩

That is, the table’s entire first row is returned (where we are assuming again that the only columns are 1, 2, 3, 4, 10 and 11). The next call to `GetNext()` would use the OIDs returned above:

> GetNext(T.1.1, T.2.1, T.3.1, T.4.1, T.10.1, T.11.1)

This would then return the table’s entire second row, and so on. When we got to the very last row, the call

> GetNext(T.1.4, T.2.4, T.3.4, T.4.4, T.10.4, T.11.4)

would return (assuming now that the columns shown are the only columns that exist)

> ⟨T.2.1,”lo”⟩, ⟨T.3.1,16436⟩, ⟨T.4.1,10000000⟩, ⟨T.10.1,171⟩, ⟨T.11.1,3⟩, ⟨A,avalue⟩

where A is the next leaf OID above and to the right of T (assuming no error). At this point the manager knows, as in the single-attribute-get case, that the entire table has been retrieved.

It does not matter whether the manager uses this multi-attribute form of `GetNext()` to return all the columns of the relevant table, or only the subset of columns in which the manager is interested.

We will see in 27.1.3 SNMPv2 GetBulk() that SNMPv2 introduced an operation `GetBulk()` that can return multiple rows of multiple columns in a single operation.

Tables are not necessarily static. New dynamic interfaces may be added, and new routes and TCP connections are added all the time. A manager reading a table using the single-attribute form of `GetNext()` may find that only the latter part of a new row is retrieved, or, alternatively, that the first part of a deleted row is retrieved. The multi-attribute `GetNext()` offers a little more protection from this.

### 8.2 `Set()`

SNMP also allows a `Set()` operation. Not all attributes are writable, of course, and a manager must have write authority for those that are writable. And there is no `SetNext()`; the exact OID of each value must be supplied.

In the system group, the `sysName` attribute is writable. It has OID 1.3.6.1.2.1.1.5.0; to update this we would invoke

> Set(1.3.6.1.2.1.1.5.0, “newsysname”)

Like `Get()`, `Set()` can be invoked on multiple attributes. Suppose table T has three columns T.1, T.2 and T.3, and two rows:

T.1 | T.2 | T.3
--- | --- | ---
10 | eth0 | 3.14159
11 | eth1 | 2.71828

We can then change the second `eth1` row to ⟨11,ppp1,0.577216⟩ with

> Set( (T.2.11,ppp1), (T.3.11,0.577216) )

We cannot change the indexing this way, but see 27.2 Table Row Creation.

The semantics for multi-attribute `Set()` explicitly require – and still require – that either all the assignments succeed, or all fail; that is, multi-attribute `Set()` operations are atomic. (The usual reason for a failure is that the manager issuing the `Set()` lacked write permission for some item.) SNMPv1 had the same all-or-nothing rule for multi-attribute `Get()`, but that requirement was relaxed in SNMPv2. The all-or-nothing `Set()` semantics mean that certain updates cannot end up completed only half-way.

The multi-attribute `Set()` semantics do not mean, however, that near-simultaneous assignments by different managers are serialized. Suppose, for example, two separate managers both attempt to update attributes specified by OIDs X and Y, one to 10 and 100 and the other to 20 and 200. If the updates are scheduled at about the same time,

> Set((X,10), (Y,100))
> Set((X,20), (Y,200))

then it is possible for the Set()s to be performed in the order (X,20), (X,10), (Y,100), (Y,200), leaving X = 10 and Y = 200. See 27.1.5 TestAndIncr for a workaround.

## 9 MIB Browsing

Tools for individual SNMP reading are widely available, and are very helpful for viewing what is going on.

We have already mentioned the Net-SNMP package. The command `snmpget` issues a single (perhaps multi-attribute) `Get()` request; the very similar `snmpgetnext` issues a GetNext request. The command `snmpwalk` takes an OID representing the root of a subtree, and returns everything in that subtree. Here is an example that assumes the snmp agent on localhost is configured to use community string “tengwar” (see 26.11 SNMPv1 communities and security):

    snmpwalk -v 1 -c tengwar localhost 1.3.6.1.2.1.2.2

The OID here is that of `ifTable`. Multiple OIDs are permitted for `snmpget` and `snmpgetnext` but not for `snmpwalk`.

If the appropriate MIB files are loaded, the above command can also be entered as

    snmpwalk -v 1 -c tengwar localhost ifTable

This entails putting the RFC1213-MIB file into a special directory (eg $HOME/.snmp/mibs), and then either adding a line like

    mibs +RFC1213-MIB

to the snmp.conf file, or by including the MIB name on the command line:

    snmpwalk -v 1 -c tengwar -m RFC1213-MIB localhost ifTable

Note that `RFC1213-MIB` is the identifier assigned to the MIB file in its first line, as below, and not necessarily the name of the file containing the MIB.

    RFC1213-MIB DEFINITIONS ::= BEGIN

If the appropriate MIB file is loaded, the OIDs may be displayed symbolically rather than numerically, but the data presentation will not change:

       iso.3.6.1.2.1.1.5.0 = STRING: "valhal"       ;; without MIB
    RFC1213-MIB::sysName.0 = STRING: "valhal"       ;; with MIB

(Actually, if the data is of type Object ID, then without the MIB the OID will be displayed numerically, and with the appropriate MIB all or part of it may be displayed symbolically.)

Finally, the Net-SNMP manager package includes `snmptable`, which is like `snmpwalk` except that the data is displayed as a table rather than one column at a time. For the `snmptable` command, the appropriate MIB file must be installed. Net-SNMP comes with a mib browser with a graphical interface, `tkmib`.

The personal edition of the iReasoning MIB browser is not open-source, but it is free, and works well on Windows, Macs and Linux; SNMPv3 is not supported. The license does prohibit the publication of benchmark tests without consent.

## 10 MIB-2

We can now turn to the MIB that represents the core of SNMPv1 data, known as MIB-2, and defined in RFC 1213. The “2” here – often represented with a Roman numeral: MIB-II – represents the second iteration of the definition, but it is still part of SNMPv1. The predecessor MIB-1 was first documented in RFC 1066, 1988.

As we saw in 26.4 MIBs, the MIB-2 OID prefix is 1.3.6.1.2.1.

MIB-2 has since been extended. We look at a few extensions below in 27.1.7 SNMPv2 MIB Changes, 27.1.9 IF-MIB and ifXTable, 27.1.13 TCP-MIB and 27.1.12.2 IP-Forward MIB. In general, serious network management should make use of these newer versions. However, MIB-2 is still an excellent place to get started, even if partly obsolete.

The original MIB-2 is divided into ten groups, not all of which are in current use:

- `system(1)`: above
- `interfaces(2)`: above, in brief
- `at(3)`: the ARP cache, 10.2 Address Resolution Protocol: ARP
- `ip(4)`: including the IP forwarding table used as an example above
- `icmp(5)`: information about ICMP, 10.4 Internet Control Message Protocol
- `tcp(6)`: including the TCP connections table used as an example above
- `udp(7)`: information about UDP activity
- `egp(8)`: information about the now-obsolete EGP protocol, replaced by BGP
- `transmission(10)`: never used
- `snmp(11)`: about the SNMP agent itself

Originally group 9 was for CMOT, above, but this entry is commented out in RFC 1213 (though it does not appear at all in MIB-1).

The `at` group is officially deprecated; the same ARP-cache information is available within the `ip` group. The EGP protocol has been entirely replaced by BGP, and management of BGP routers is highly specialized; RFC 4273 contains a BGP MIB but private MIBs are almost universally used here as well.

The transmission group was included anticipating the day when “definitions for managed transmission media are defined”. This day has not come to pass.

The Net-SNMP agent implementation returns nothing for the egp and transmission groups.

### 10.1 The system Group

We have already looked at this in 26.4 MIBs and will not say much more, except to note that several of the entries in this group must be manually configured. This can be done either through editing of the configuration file, eg `snmpd.conf`, or else (in the case of `read-write` attributes) by using SNMP itself.

The `sysObjectID` value represents a vendor-specific OID for this particular system’s SNMP agent. For example, the Net-SNMP package installed on my system returns 1.3.6.1.4.1.8072.3.2.10. The 1.3.6.1.4.1 is the root of the `private`, vendor-specific, OID tree, and 8072 has been assigned to the Net-SNMP project. This often represents, in practice, the way an SNMP manager can figure out the vendor of an agent, and thus determine what vendor-specific information to query. One can use the 8072 discovered here to ask for the subtree 1.3.6.1.4.1.8072, and find all sorts of Net-SNMP-specific information. Similarly, on one particular Windows XP installation with Microsoft Network Monitoring installed, the sysObjectID value is 1.3.6.1.4.1.311.1.1.3.1.1; 311 is the OID level assigned to Microsoft in the private subtree 1.3.6.1.4.1.

The system group has been expanded in SNMPv2 with an Object Resource table, `sysORTable`; see 27.1.7 SNMPv2 MIB Changes.

### 10.2 Table definitions and the interfaces Group

The interfaces group consists of a single INTEGER value `ifNumber` representing the number of current network interfaces (including “down” interfaces), and the interfaces table partially introduced earlier. We take the opportunity here to outline exactly how MIB table definitions – and enumerated types – are structured using ASN.1.

Some of the interfaces-group definitions have later been updated. See, for example, RFC 2863, which also redefines the group to use the additional features of the SNMPv2 SMI. We will return to an extension of the interfaces group in 27.1.9 IF-MIB and ifXTable.

MIB table definitions almost always involve a two-level process: an OID is defined for the table, and then a second OID is defined for a table entry, that is, for one row of the table. This second OID is usually generated from the first by appending “.1”, and it is this second OID that represents the table prefix in the sense of 26.7 SNMP Tables, denoted there by T.

The actual ASN.1, slightly annotated, is as follows:

    ifTable OBJECT-TYPE
        SYNTAX  SEQUENCE OF IfEntry       -- note UPPER-CASE-I IfEntry
        ACCESS  not-accessible
        STATUS  mandatory
        DESCRIPTION
                "A list of interface entries.  The number of
                entries is given by the value of ifNumber."
        ::= { interfaces 2 }              -- that is, 1.3.6.1.2.1.2.2

    ifEntry OBJECT-TYPE                   -- note lower-case-i ifEntry
        SYNTAX  IfEntry                   -- note UPPER-CASE-I IfEntry
        ACCESS  not-accessible
        STATUS  mandatory
        DESCRIPTION
                "An interface entry containing objects at the
                subnetwork layer and below for a particular interface."
        INDEX   { ifIndex }
        ::= { ifTable 1 }                 -- that is, 1.3.6.1.2.1.2.2.1

Both these entries are `not-accessible` as they do not represent leaf nodes. The second declaration above is for the lower-case-i `ifEntry`; the next definition in RFC 1213 is for the UPPER-CASE-I version. The latter represents the complete list of all columns of an ifEntry/IfEntry object, together with their types from (in most cases) 26.5 SNMPv1 Data Types. The `PhysAddress` type is defined in RFC 1213 as a synonym for `OCTET STRING`. Definitions for the OID associated with each column comes later.

It is `ifEntry` that represents an actual row, and thus includes an `INDEX` entry to specify the attribute or attributes that make up the primary key for that row.

We now define IfEntry:

    IfEntry ::=
        SEQUENCE {
            ifIndex            INTEGER,
            ifDescr            DisplayString,
            ifType             INTEGER,
            ifMtu              INTEGER,
            ifSpeed            Gauge,
            ifPhysAddress      PhysAddress,
            ifAdminStatus      INTEGER,
            ifOperStatus       INTEGER,
            ifLastChange       TimeTicks,
            ifInOctets         Counter,
            ifInUcastPkts      Counter,
            ifInNUcastPkts     Counter,
            ifInDiscards       Counter,
            ifInErrors         Counter,
            ifInUnknownProtos  Counter,
            ifOutOctets        Counter,
            ifOutUcastPkts     Counter,
            ifOutNUcastPkts    Counter,
            ifOutDiscards      Counter,
            ifOutErrors        Counter,
            ifOutQLen          Gauge,
            ifSpecific         OBJECT IDENTIFIER
        }

Attributes

There can be a certain eye-glazing tedium in some of SNMP’s lengthy attribute lists, such as the one above. This can be replaced by panic in a hurry, though, when a problem has arisen and the proper attribute to diagnose it doesn’t seem to be included anywhere.

It would have been possible to plug in the above definition of IfEntry into the `SYNTAX` specification of the previous ifEntry, but that is cumbersome. The IfEntry definition is not a stand-alone `OBJECT-TYPE` and does not have its own OID.

If all we wanted to do was to implement the interfaces table using the shortest possible OIDs, we would not have created separate ifTable and ifEntry OIDs. This would mean, however, that we could not use the `OBJECT-TYPE` macro to define `ifTable`, which would have been less consistent (especially as the ASN.1 syntax also determines the packet encoding, as in 26.12 SNMP and ASN.1 Encoding). Essentially every table prefix in SNMP is defined using two additional OID levels, as here, rather than one.

We now turn to the 22 specific interface attributes. Here is the definition for the first, `ifIndex`; it defines column 1 of the interfaces table to be, in effect, ifEntry.1. As we are now talking about a leaf node, once the OID suffix is appended to represent the index, the `ACCESS` is no longer `not-accessible`.

    ifIndex OBJECT-TYPE
        SYNTAX  INTEGER
        ACCESS  read-only
        STATUS  mandatory
        DESCRIPTION
                "A unique value for each interface.  Its value ranges
                between 1 and the value of ifNumber.  The value for each
                interface must remain constant at least from one
                reinitialization of the entity's network management system
                to the next reinitialization."
        ::= { ifEntry 1 }

The `DESCRIPTION` clearly indicates that the values for `ifIndex`, used to specify interfaces, are to be consecutive integers. Compliance with this rule has been an early casualty, for various reasons, and is formally withdrawn in RFC 2863. Some vendors simply number physical interfaces non-consecutively. In other cases, there is some underlying issue with consecutive numbering. For example, one of the author’s systems running Net-SNMP returns `ifNumber` = 3, and then the following table values:

`ifIndex` | `ifName`
--- | ---
1 | lo
2 | eth0
549 | ppp0

It turns out that ppp0 is a virtual interface corresponding to a VPN tunnel, 5.1 Virtual Private Networks, and the underlying tunnel regularly fails and is then automatically re-instantiated. Each time it does so, the `ifIndex` is incremented by 1.

The rule that interfaces be numbered consecutively was formally deprecated in RFC 2863, an SNMPv2 update of the interface group. This, in turn, makes the `ifNumber` value rather less useful than it might be; most SNMP tables are not associated with a count attribute and seem to do just fine.

Most SNMP data values correspond straightforwardly with attributes defined by the hardware and the underlying operating system. The `ifIndex` value does not, at least not necessarily. Generally the agent must maintain at least some state to keep the `ifIndex` value consistent. In some cases, the `ifIndex` value may be taken from the relative position of the interface in some internal operating-system table. This is not, however, universally the case, as with the `ifIndex` value of 549 for ppp0 in the table above.

The `ifIndex` value is widely used throughout SNMP, and is often referenced in other tables. SNMPv2 even defines a special type (a TEXTUAL CONVENTION) for it, named `InterfaceIndex` (27.1.1 SNMPv2 SMI and Data Types).

As we mentioned earlier, there is no reason to include `ifIndex` as an actual column in the table; the value of `ifIndex` can always be calculated from the OID of any component of the row. The SNMPv2 approach here – basically to define `ifIndex` as `not-accessible` – is described below in 27.1.4 SNMPv2 Indexes.

The `ifDescr` is a textual description of the interface; it is usually the device name associated with the interface. RFC 1213 states “this string should include the name of the manufacturer, the product name and the version of the hardware interface”, but this is inconsistent with Linux device-naming conventions. The current rule is that it is merely unique.

The `ifType` attribute is our first example of how ASN.1 handles enumerated types. The value is a small integer and the hardware type associated with each integer is spelled out as follows. The majority of the networking technologies in this 1991 list have pretty much vanished from the face of the earth.

    ifType OBJECT-TYPE
        SYNTAX  INTEGER {
                    other(1),          -- none of the following
                    regular1822(2),
                    hdh1822(3),
                    ddn-x25(4),
                    rfc877-x25(5),
                    ethernet-csmacd(6),
                    iso88023-csmacd(7),
                    iso88024-tokenBus(8),
                    iso88025-tokenRing(9),
                    iso88026-man(10),
                    starLan(11),
                    proteon-10Mbit(12),
                    proteon-80Mbit(13),
                    hyperchannel(14),
                    fddi(15),
                    lapb(16),
                    sdlc(17),
                    ds1(18),           -- T-1
                    e1(19),            -- European equiv. of T-1
                    basicISDN(20),
                    primaryISDN(21),   -- proprietary serial
                    propPointToPointSerial(22),
                    ppp(23),
                    softwareLoopback(24),
                    eon(25),            -- CLNP over IP [11]
                    ethernet-3Mbit(26),
                    nsip(27),           -- XNS over IP
                    slip(28),           -- generic SLIP
                    ultra(29),          -- ULTRA technologies
                    ds3(30),            -- T-3
                    sip(31),            -- SMDS
                    frame-relay(32)
                }
        ACCESS  read-only
        STATUS  mandatory
        DESCRIPTION
                "The type of interface, distinguished according to
                the physical/link protocol(s) immediately 'below'
                the network layer in the protocol stack."
        ::= { ifEntry 3 }

A more serious problem is that over two hundred new technologies are unlisted here. To address this, the values for `ifType` have been placed under control of the IANA, as defined in `IANAifType`; see RFC 2863 and https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib. The IANA can then add new types without formally updating any RFC.

For the meaning of `ifMtu`, the interface MTU, see 9.4 Fragmentation. The 32-bit `ifSpeed` value will be unusable once speeds exceed 2 Gbps; RFC 2863 defines an `ifHighSpeed` object with speed measured in units of Mbps. This entry is part of the `ifXTable` table, 27.1.9 IF-MIB and ifXTable. RFC 2863 also clarifies that, for virtual interfaces that do not really have a bandwidth, the value to be reported is zero (though in the example earlier the loopback interface `lo` was reported to have a bandwidth of 10 Mbps). The `ifPhysAddress` is, on Ethernets, the Ethernet address of the interface.

The `ifAdminStatus` and `ifOperStatus` attributes are enumerated types: up(1), down(2), testing(3). If the `ifAdminStatus` is up(1), and the `ifOperStatus` disagrees, then there is a likely hardware malfunction. The `ifLastChange` attribute reflects the last time, in TimeTicks, there was a change in `ifOperStatus`.

The next eleven entries count bytes, packets and errors. The `ifInOctets` and `ifOutOctets` count bytes received and sent (including framing bytes, 6.1.5 Framing, if applicable). The problem with these is that they may wrap around too fast: in 34 seconds at 1 Gbps. The MIB-2 values are still used, but are generally supplemented with 64-bit counters defined in `ifXTable`, 27.1.9 IF-MIB and ifXTable.

The packet counters are for unicast packets, non-unicast packets, discarded packets, errors, and, for inbound packets only, packets with unknown protocols. The count of non-unicast packets is separated in `ifXTable` into separate counts of broadcast and multicast packets.

The interface queue length is available in `ifOutQLen`. It takes a considerable amount of traffic to make this anything other than 0. RFC 1213 says nothing about the timescale for averaging the queue length.

The last member of the classic MIB-2 interfaces group is `ifSpecific`, which has type ObjectID. It was to return an OID that may be queried for additional ifType-specific information about the interface. It was formally deprecated in RFC 2233.

### 10.3 The ip Group

The ip group contains several scalar attributes and three tables.

The first two attributes are writable:

- `ipForwarding`: a Boolean attribute to enable or disable forwarding
- `ipDefaultTTL`: the default TTL in outgoing packets (9.1 The IPv4 Header)

The next scalar attributes are as follows. Here and later, we summarize these informally.

- `ipInReceives`: the number of received IP packets
- `ipInHdrErrors`: the number of apparent IP packets received with header errors
- `ipInAddrErrors`: the number of IP packets received with bad destination addresses
- `ipForwDatagrams`: the number of forwarded IP packets
- `ipInUnknownProtos`: the number of received IP packets with unknown higher-level protocol
- `ipInDiscards`: the number of received IP packets that had no errors, but which were still discarded due to resource limits
- `ipInDelivers`: the number of received IP packets delivered locally
- `ipOutRequests`: the number of IP packets originated by this node for delivery elsewhere
- `ipOutDiscards`: the number of outbound IP packets discarded due to resource limits
- `ipOutNoRoutes`: the number of outbound IP packets for which there was no route in the IP forwarding table
- `ipReasmTimeout`: the value, in seconds, of the IP fragment-reassembly timer
- `ipReasmReqds`: the number of IP proper fragments received that needed reassembly at this node (forwarded fragments are reassembled at their destination)
- `ipReasmOKs`: the number of fragmented IP packets successfully reassembled
- `ipReasmFails`: the number of fragmented IP packets not successfully reassembled
- `ipFragFails`: primarily, packets that needed fragmentation but their Dont Fragment bit (9.4 Fragmentation) was set
- `ipFragCreates`: the number of IP fragments created by this node
- `ipRoutingDiscards`: the number of packets that should have been forwarded, but were discarded due to resource limits

The first table is the `ipAddrTable`, a list of all IP addresses assigned to this node together with interfaces and netmasks.

The second table is the `ipRouteTable`, that is, the forwarding table. We looked briefly at this in 26.7 SNMP Tables. Note that the index consists solely of the destination IP address `ipRouteDest`, which is not sufficient for CIDR-based forwarding in which the forwarding-table key is the ⟨dest,netmask⟩ pair. Traffic to 10.38.0.0/16 might be routed differently than traffic to 10.38.0.0/24!

This table contains four attributes `ipRouteMetric1` through `ipRouteMetric4`. RFC 1213 states that unused metrics should be set to -1, but many agents simply omit such columns entirely. Agents may similarly omit `ipRouteAge`.

The third table is `ipNetToMediaTable`, which is the ARP-cache table and which replaces a similar now-deprecated table in the at group. The `ipNetToMedia` table adds one column, `ipNetToMediaType`, not present in the at-group table; this column indicates whether an physical-to-IP-address mapping is invalid(2), dynamic(3) or static(4). Most ARP entries are dynamic.

For an updated version of the ip group, see 27.1.12.1 IP-MIB and 27.1.12.2 IP-Forward MIB.

### 10.4 The icmp Group

The icmp group consists of 26 counters for various ICMP events. ICMP Echo Request (ping request) packets have their own counter.

### 10.5 The tcp Group

The tcp group includes the following scalars:

- `tcpRtoAlgorithm`: the method for calculating the retransmission timeout, normally vanj(4) for the Jacobson-Karels algorithm in 18.12 TCP Timeout and Retransmission.
- `tcpRtoMin`: the minimum TCP retransmission timeout, in milliseconds
- `tcpRtoMax`: the maximum TCP retransmission timeout, again in ms
- `tcpMaxConn`: if the system imposes a static cap on the number of allowed TCP connections, that is returned. Most systems have no static cap, though, and return -1.
- `tcpActiveOpens`: the number of TCP connections opened from this node; specifically, the number of TCP state transitions CLOSED → SYN_SENT
- `tcpPassiveOpens`: literally, the number of TCP state transitions LISTEN → SYN_RECD
- `tcpAttemptFails`: the number of TCP connections that went to CLOSED without ever being ESTABLISHED
- `tcpEstabResets`: the number of TCP connections that went from ESTABLISHED or CLOSE_WAIT directly to CLOSED, via RST packets
- `tcpCurrEstab`: the number of TCP connections currently in either state ESTABLISHED or state CLOSE_WAIT
- `tcpInSegs`: the count of received TCP packets, including errors and duplicates (though duplicate reception is relatively rare)
- `tcpOutSegs`: the count of sent TCP packets, not including retransmissions
- `tcpRetransSegs`: the count of TCP packets with at least one retransmitted byte; the packet may also contain new data
- `tcpInErrs`: the number of TCP packets received with errors, including checksum errors
- `tcpOutRsts`: the number of RST packets sent

Perhaps surprisingly, there is no counter provided for total number of TCP bytes sent or received. There is also no entry for the congestion-management strategy, eg Reno v NewReno v TCP Cubic (22 Newer TCP Implementations).

The tcp group also includes the `tcpConnTable` table, which lists, for each connection (identified by ⟨localAddress,localPort,remoteAddress,remotePort⟩) the connection state. We looked at this earlier in 26.7 SNMP Tables. The table is noteworthy in that four of its five columns are part of the INDEX. A consequence is that to extract all the information from the table, a manager need only retrieve the `tcpConnState` column: the other four attributes will all be encoded in the OID index that is returned with each `tcpConnState` value.

We will look at the newer SNMPv2 replacement in 27.1.13 TCP-MIB.

### 10.6 The udp Group

The udp group contains the following scalars:

- `udpInDatagrams`: a count of the number of UDP packets received and deliverable to a socket
- `udpNoPorts`: a count of UDP packets received but undeliverable because the port was not open
- `udpInErrors`: a count of the number of UDP packets undeliverable due to any other errors
- `udpOutDatagrams`: a count of the number of UDP packets sent

The udp group also contains the table `udpTable`. This table lists those UDP ports that the node has open. The table rows have the form ⟨localAddr,localPort⟩, with both columns included in the index. If a UDP socket accepts connections from anywhere, the localAddr will appear as 0.0.0.0.

### 10.7 The snmp Group

The SNMP group consists of 28 SNMP status and error attributes, numbered from 1 to 30 with 7 and 23 not used.

## 11 SNMPv1 communities and security

An SNMPv1 manager authenticates itself to an agent by providing a string known as a community string that is a combination of both userid and password. That is, the single string identifies the manager (or manager group) and at the same time authenticates the manager.

The community string identifies a manager as a member of a designated “community”, in the conventional use of the word, of managers. Of course, at many sites there will be only one manager that interacts with any given agent.

Community strings are sent unencrypted, and so are vulnerable to sniffing. Even sniffing is not always necessary; far and away the most popular value – the default for many agents – is the string “public”. While the community string can be made obscure, and changed frequently, these are not enough; the only appropriate security practice today is to use SNMPv3 authentication (27.3 SNMPv3).

If a manager sends an incorrect community string, then in SNMPv1 and SNMPv2c there is no reply. SNMPv3 relaxed this rule somewhat, 27.3.3 SNMPv3 Engines.

A single agent can support multiple community strings. Each community has an associated subset of the MIB (a view) that it allows. For example, an agent can be configured so that the community string “system” allows the manager access to the `system` group, the community string “tengwar” allows access to the entire MIB-2 group, and the community string “galadriel” allows access to the preceding plus the `private` group(s). Using Net-SNMP (the most common agent on Linux and Macintosh machines) this would be achieved by the following entries in the `snmpd.conf` file:

    rocommunity  system    default   1.3.6.1.2.1.1
    rocommunity  tengwar   default   1.3.6.1.2.1
    rocommunity  galadriel default   -V mib2+private

    view   mib2+private included  1.3.6.1.2.1
    view   mib2+private included  1.3.6.1.4.1

In order that the galadriel community could contain two disconnected OID subtrees, it was necessary to make use of the View-based Access Control Model (VACM). Community galadriel has access to the OID-tree view named “mib2+private”; this view is in turn defined in the last two lines above.

For our purposes here, VACM can be seen as an implementation mechanism for specifying what portion of the OID tree is accessible to a given community. VACM allows read and write permissions to be granted to specific OID trees, and also to be excluded from specific subtrees (eg table columns). Using the “mask” mechanism, access can even be granted to specific rows of a table. We return to VACM for SNMPv3 very briefly in 27.3.9 VACM for SNMPv3.

On Microsoft operating systems, an SNMP agent is generally included but must be enabled, eg from “Windows components” or “Programs and Features”. After that, the agent must still be configured. This is done by accessing “Services” (eg through Control Panel → Administrative Tools or by launching services.msc), selecting “SNMP service”, and clicking on “Properties”. This applet only allows setting the community name and read vs write permissions; specifying collections of visible OID subtrees (views) is not supported here (though it may be via SNMP itself).

The community mechanism can offer a reasonable degree of security, if community names are changed frequently and if eavesdropping is not a concern. Perhaps the real problem with community-based security is that just how much information can leak out if an attacker knows the community string is not always well understood. SNMP access can reveal a site’s complete network and host structure, including VPNs, subnets, TCP connections, host-to-host trust relationships, and much more. Most sites block the SNMP port 161 at their firewall; some even go so far as to run SNMP only on a “hidden” network largely invisible even within the organization.

The view model for OID-tree access is formalized in RFC 3415 as part of SNMPv3; it is called the View-based Access Control Model or VACM. It allows the creation of named views. The `vacmAccessTable` spells out the viewing rights assigned to a given VACM group, which, in a rough sense, corresponds to an SNMPv1/SNMPv2c community. VACM supports, in addition to views consisting of disjoint unions of OID subtrees, table views that limit access to a specific set of columns.

## 12 SNMP and ASN.1 Encoding

When SNMP data is entered into a packet, it is encoded according to the ASN.1 Basic Encoding Rules, or BER. This is a hierarchical syntax-driven binary encoding strategy in which each atomic value (INTEGER, OCTET STRING, OBJECT IDENTIFIER, etc) is tagged with its type, and each compound structure is also tagged. This means that the receiver can understand a complex packet format without prior knowledge of its structure. The BER rules are part of the ITU standard X.690. (ASN.1 also supports several other encoding-rule formats, including the human-readable XML Encoding Rules, but SNMP uses BER.)

The general representation of an object is a “type-length-value” triple of the following form; the `type` tag makes the data self-identifying.

The first two bits of the `type` field, the `class` bits, identify the context. Universal types such as INTEGER and OBJECT IDENTIFIER have class bits 00; application-specific types such as Counter32 and TimeTicks have class bits 01.

The third bit is 0 for primitive types and 1 for constructed types such as STRUCTURE.

The rest of the first byte is the type tag. If a second byte is needed, the tag bits of the first byte are 11111.

### 12.1 Primitive Types

The standard ASN.1 universal-type primitive tag values used by SNMP are as follows:

00000 | NULL
00010 | INTEGER
00100 | OCTET STRING
00110 | OBJECT IDENTIFIER

SNMP also defines several application-specific tags (the following are from RFC 2578):

00000 | IpAddress
00001 | Counter32
00010 | Gauge32
00011 | TimeTicks
00100 | Opaque
00110 | Counter64

The second field of the type-length-value structure is the length of the `value` portion, in bytes. Most lengths of primitive types will fit into a single byte. If a data item is longer than 127 bytes (true for many composite types), the multi-byte integer encoding below is used.

The actual data is then encoded into the `value` field. For nonnegative integers, the integer is converted to a twos-complement bitstring and then encoded in as few bytes as possible, provided there is at least one leading 0-bit to represent the sign. Similarly, negative numbers must have at least one leading 1-bit to represent the sign.

For example, 127 can be encoded as a length=1 INTEGER with `value` byte 0111 1111, while 128 must be encoded as a length=2 integer with `value` bytes 0000 0000 and 1000 0000. The second `value` byte represents 128, but the first byte represents the sign; without the first byte the INTEGER represented by a length of 1 and a `value` byte of 1000 0000 is −128. Similarly, to encode decimal 10,000,000 (0x989680), four `value` bytes are needed as otherwise the leading bit would be 1 and the number would be interpreted as negative.

For OCTET STRINGs, the string bytes are placed in the `value` portion and the number of bytes is placed in the `length` portion.

Object IDs are generally encoded using one byte per level; there are two exceptions. First, the initial two levels x.y of the OID are encoded using a single level with value 40×x + y; all SNMP OIDs begin with 1.3 and so the first byte is 43 (0x2b). Second, if a level is greater than 127, it is encoded as multiple bytes. The first bit of the last byte is 0; the first bit of each of the preceding bytes is 1. The seven remaining bits of each byte contain the bits of the OID level. For example, 1.3.6.1.2.1.128.9 would have a `value` encoding of the following bytes in hexadecimal:

> 2b 06 01 02 01 81 00 09

where 128 is represented as the two seven-bit blocks 0000001 0000000 and the first block is prefixed by 1 and the second block by 0. Note that the encoding of 128 in an OID is quite different from the encoding of 128 as an INTEGER.

### 12.2 Composite Types

Now that we have the encodings of the primitive types we can build structures. For composite types the P/C bit will be set to 1. The only universal composite type we will consider is

10000 | SEQUENCE and SEQUENCE OF

There are also several application-specific composite types; the first three bits of the tag field for these will be 101.

00000 | Get-Request
00001 | Get-Next-Request
00010 | Get-Response
00011 | Set-Request
00100 | Trap

A `VarBind` pair is encoded in SNMPv1 as

    SEQUENCE { name OBJECT IDENTIFIER, value ObjectSyntax }

`ObjectSyntax` is specified as a `CHOICE` that can contain any of the tagged SNMP primitive types above; the `CHOICE` syntax adds no bytes so the `value` is simply encoded as above. The `VarBind` pair ⟨1.3.6.1.2.1.1.3.0, 8650000⟩ – the OID is `sysUpTime` and the value is 24 hours – would then be represented in hexadecimal bytes as below; the hexadecimal representation of 8650000 is 0x83d600.

The first byte of 0x30 marks this as a SEQUENCE; recall that the `type` byte for a SEQUENCE has a P/C bit of 1 and five low-order bits of 10000, for a numeric value of 48 decimal or 0x30.

The `VarBindList` is defined to be a `SEQUENCE OF VarBind`. If we have only the single `VarBind` above, the resultant enclosing `VarBindList` would be as follows; the `length` field is 0x12 = 18.

> 30 12 30 10 06 08 2b 06 01 02 01 01 03 00 02 04 00 83 d6 00

The BER encoding rules do not stop with the `VarBindList`. A slightly simplified ASN.1 specification for an entire SNMPv1 Get-Request packet portion (or “protocol data unit”) is

    SEQUENCE {
           request-id   INTEGER,
           error-status INTEGER,
           error-index  INTEGER,
           variable-bindings VarBindList
    }

The encoding of the whole packet would also be by the above BER rules.

The BER encoding mechanism represents a very different approach from the fixed-field layout of, say, the IP and TCP headers (9.1 The IPv4 Header and 17.2 TCP Header). The latter approach is generally quite a bit more compact, as only four bytes are needed for a larger integer versus six under BER, and no bytes are used for SEQUENCE specifications. The biggest advantage of the SNMP BER approach, however, is that all objects, from entire packets down to individual values, are self-describing. Given the variety of types used by SNMP, the fact that many are of variable length, and the fact that value “readers” such as MIB browsers often operate without having all the type-specifying MIBs loaded, this self-describing feature is quite useful.