Network Management Week 6

Fall 2009; LT-412, Wed 4:15-6:45 pm

mibs and "mib deltas"
private mibs
SNMPv2

Notes on the midterm, tentatively set for October 14:
It will be open book, but not open-note.
If I ask a question about a mib file, you will have access to the file.
You should know something about the creation and layout of mib files
You should know about how SNMP stores and manages tables, including all the ways of fetching tables.



mib-2 extensions

We looked last time at IP-Forward-mib, TCP-mib, and IF-mib. These had different approaches to how they extended the ip, tcp, and interface groups of the original rfc1213.
Note that there is no real reason for including the index to a table as one of the columns; the index is embedded in every OID key. While it is conventional to include the index, it is often not so convenient when a table is being extended somewhere else.

SNMPv2 defined a formal framework for table augmentation. Note, however, that SNMPv1 can do this, just on an ad hoc basis.
 

Some notes on ETHERLIKE-MIB:
Standard statistics for Ethernet: collision counts and other errors. Note the SingleCollisonFrames counter and the MultipleCollisionFrames counter.

The etherlike-mib adds to the transmission group mib-2.10 (which is only a stub in mib2)

The StatsTable is indexed by ifNumber, and as such represents an extension of the interfaces table, at least for those rows that represent Ethernet-like interfaces. For such rows, the StatsTable simply adds some additional columns. Two of those columns are the count of packets that have collided once (dot3StatsSingleCollisionFrames) and more than once (dot3StatsMultipleCollisionFrames).

The CollTable counts, on a per-interface basis, how many frames were involved for every possible CollisionCount, eg
123456 frames had no collisions
 87654 frames had 1 collision
 13579 frames had 2 collisions
   4128 frames had 3 collisions
     987 frames had 4 collisions
...

That is, it is as if the StatsTable had arbitrarily many columns Collision1, Collision2, Collision3, etc, for each number of collisions.

However, the CollTable is in fact indexed by both the interface number and the CollisionCount. Thus, it might appear as
interface
collCount
frequency
1
1
2345
1
2
159
1
3
21
1
4
3
2
1
358
2
2
47
2
3
19
3
1
5418
3
2
29

The CollTable thus, in effect, extends selected rows of the interfaces table with multiple values.



SNMPv2


Some of the changes:

New MIBs

Note that an SNMPv1 manager can request any of these OIDs; they are not restricted to requests using SNMPv2.

Row Creation

So far we've been looking at tables that are either static, or which are expanded only by the agent. What happens when the manager starts creating new table rows in the agent? What happens if two managers simultaneously try to create rows?



Preliminary example: RMON (Remote MONitoring; RMON-MIB.MIB): the manager will create a row to specify a certain kind of data to be collected, and the agent will then create one or more rows in a supplemental table to store that data as it is collected.

RMON uses rowStatus values valid, createRequest, underCreation, and invalid. Once a manager sets a row to invalid, the agent may or may not get around promptly to removing it. Quoting from the MIB (line 95):
Entries shall exist in the underCreation(3) state until the management station is finished configuring the entry and sets this object to valid(1)

RMON generally uses a control table set by the manager to control the kinds of data being collected; the actual data then goes into the data table. For an example, we'll briefly consider the Host Group (starting at line 1324), which is indended to monitor all physical addresses (host addresses) seen on particular interfaces. The HostControlTable (1392) has entries
              hostControlIndex            INTEGER (1..65535),
              hostControlDataSource       OBJECT IDENTIFIER,
              hostControlTableSize        INTEGER,
              hostControlLastDeleteTime   TimeTicks,
              hostControlOwner            OwnerString,
              hostControlStatus           EntryStatus
The index here is a pseudorandom number (actually, how it is chosen is not clear; it could be the same as the DataSource); the DataSource is an interface number from the mib-2.2 interfaces table. The TableSize is a bound on the size of the host table to be created. The last field is the valid | createRequest | underCreation | invalid field, though createRequest will never appear here.

Once the row is created, the corresponding hostTable (hostDataTable) is created (line 1509), indexed by the DataSource and the hostAddress. Other columns in the hostTable are InPkts, OutPkts, InOctets, OutOctets, etc, all specific for that host.



SNMPv2 defined six possible status values. The row itself will have one column rowStatus, which will have as its value one of the first three status values active, notInService or notReady. The other three status values never appear in the row, but are used by the manager to make update requests.

Note that if the manager is creating rows, then usually the agent will do something (as in the ping mib, or RMON) once the row is created. SNMPv3 uses row-creation to create new users and views. The intent here is for the MANAGER to control the insertion of new rows in the  agent device table, although the agent can do this too.

active
the row is operational.
RMON valid
notInService
the row is complete, but not yet activated
RMON underCreation
notReady
the row is missing one or more columns
RMON underCreation
createAndGo
the manager included in one SET request all the column values, including this one for the rowStatus column.
RMON createRequest, full row
createAndWait
the manager is asking that the row not yet be marked active, either because it hasn't yet set all the rows, or because it is not yet time.
RMON createRequest, partial row
destroy
the manager wishes that the row be deleted.
RMON invalid, but invalid is also used to mark "dead" rows

If a row is created with createAndWait, with incomplete columns, then the agent will set the status to notReady. When the remaining column values are set, the agent will set the status to notInService.

Note that every request must include an index value implicitly as part of the OID, even if the index column is not formally set. (In fact, we don't need to have an index column at all; the index is always included implicitly in the OID key for every table value.) Choosing the new index value is the first step the manager must make. One possibility is that the index is already in use, either because the manager didn't check, or because two managers are trying to add a row concurrently. What happens depends somewhat on the agent's rules on the use of SET, but in most cases the second SET will fail. It will fail, in particular, in the SNMPv2 scenario we are considering here: if the agent receives a createAndGo or createAndWait request that refers to a pre-existing index value (because someone else's create-request got there first); the SET will fail.

Consider the following example, from Subramanian: the table has three columns, (rowStatus, index, data). We are contemplating adding a row 3.

create-and-go
    manager sends SET (rowStatus.3 = createAndGo, index.3 = 3, data.3=whatever)
    agent creates the row, and sets rowStatus.3 = active (note that this differs from the usual semantics of SET)
    agent sends back RESPONSE(rowStatus.3 = active, index.3 = 3, data.3 = whatever)

create-and-wait
    manager sends SET(rowStatus.3 = createAndWait, index.3 = 3)
    agent creates partial row, sends back RESPONSE(rowStatus.3=notReady, index.3 = 3)
   
At this point the partial row values should show up in a table-traversal, but whatever action the row's creation was to precipitate has not yet begun.

    manager sends SET(data.3=whatever)
    agent sends back RESPONSE(rowStatus.3=notInService, data.3=whatever)  note the rowStatus response
    ...
    manager sends SET(rowStatus.3=active)
    agent begins using the newly created row
    agent sends RESPONSE(rowStatus.3=active)
 

Example: cisco ping mib. The idea here is for the manager to create a row that tells the agent what neighbors to ping. Once the row is ready, the agent goes ahead and does the pinging, and leaves the results in that row.

In the ping mib, the manager chooses a pseudorandom value to use as the row index (ciscoPingSerialNumber). Also, the row is large enough that it might be reasonable to be built piecemeal, although createAndGo is explicitly mentioned.

    ciscoPingSerialNumber INTEGER,    (mandatory)
    ciscoPingProtocol CiscoNetworkProtocol,    (mandatory)
    ciscoPingAddress CiscoNetworkAddress,    (mandatory)
    ciscoPingPacketCount INTEGER,    (default 5)
    ciscoPingPacketSize INTEGER,    (default 100)
    ciscoPingPacketTimeout INTEGER,  (default 2000ms)
    ciscoPingDelay INTEGER,    (default 0)
    ciscoPingTrapOnCompletion TruthValue,  (default false)
    ciscoPingSentPackets Counter,    (set by agent)
    ciscoPingReceivedPackets Counter,    (set by agent)
    ciscoPingMinRtt INTEGER,    (set by agent)
    ciscoPingAvgRtt INTEGER,    (set by agent)
    ciscoPingMaxRtt INTEGER,    (set by agent)
    ciscoPingCompleted TruthValue,    (set by agent)
    ciscoPingEntryOwner OwnerString,    (an identifier, eg hostname, for the manager)
    ciscoPingEntryStatus RowStatus,
    ciscoPingVrfName OCTET STRING    (default ""; used for VPNs)

The manager sets the lines in bold in the usual way, after picking a tentative serial number (and having it succeed!). The manager then sets the ciscoPingEntryStatus column of the new row to active, which enables the whole thing and causes the agent to:

TestAndIncr versus table creation

createAndGo and createAndWait act as semaphores: if another create has been executed on that index, they fail. The TestAndIncr is another, more explicit, semaphore: a SET request includes a value for the TestAndIncr. The agent response is to verify that the value set matches the TestAndIncr's current value; if so, the value is incremented. Like all SETs, if this SET command fails (because someone else TestAndIncred the object just before), then all the other SET commands in the SET REQUEST also fail. Note, however, that the semantics for updating the particular TestAndIncr column in question are not standard.


Table Expansion

SNMPv2 allows a new MIB to expand an existing table, without simply replacing the definition. Table expansion can take one of three forms. Augmentation means the new table simply adds more columns to the base table , as ifXTable does. The new table explicitly uses the same INDEX as the base table, meaning that it does not contain its own INDEX. Other options are "dense tables", which expand the base table but which may add rows as well as columns, and "sparse tables", which expand the base table but only in selected rows. In the ETHERLIKE-MIB example above, the StatsTable is an example of a sparse table: we extend only those rows of the interfaces table that represent Ethernet-like devices. The CollisionCount table, on the other hand, is an example of a dense table: we extend selected rows of the interfaces table with multiple values (one corresponding to each CollisionCount recorded).

Table expansion does not correspond to the database join operation, except in limited cases (eg augmentation can be thought of as the join of two tables that have the same index).

Augmented tables

The keyword AUGMENTS often appears in MIB files, but usually as a comment; that is, the actual definition doesn't use the SNMPv2 extension. However, a genuine use appears in HOST-RESOURCES-MIB.txt. First is the definition of hrSWRunTable,
with the following Entry definition. The table is indexed by hrSWRunIndex, its first column.
hrSWRunEntry OBJECT-TYPE
    SYNTAX     HrSWRunEntry
    MAX-ACCESS not-accessible
    STATUS     current
    ....
    INDEX { hrSWRunIndex }
    ::= { hrSWRunTable 1 }

Then the hrSWRunPerfTable table is defined to augment the above through the addition of additional columns (hrSWRunPerfCPU and hrSWRunPerfMem), indexed the same way:
    hrSWRunPerfEntry OBJECT-TYPE
        SYNTAX HrSWRunPerfEntry
           -- SEQUENCE { hrSWRunPerfCPU, hrSWRunPerfMem }
        ...
        AUGMENTS { hrSWRunEntry }
        ::= {hrSWRunPerfTable 1}
The AUGMENTS keyword means that the index of this new hrSWRunPerfTable is the same as that of hrSWRunTable, that is, hrSWRunIndex. The AUGMENTS { hrSWRunEntry } could be thought of as a standin for INDEX { hrSWRunIndex }, except that the AUGMENTS keyword is intended to suggest that every row of the base table is extended by the new table, and the latter (INDEX) form does not. Note, however, that in the (SNMPv1) extended interfaces table ifXTable in IF-MIB-V1SMI.MIB, the definition is:
ifXEntry OBJECT-TYPE
    SYNTAX IfXEntry
    ...
    INDEX { ifIndex }
    ::= { ifXTable 1 }
(in the SNMPv2 form of the ifXEntry table, the AUGMENTS keyword is used.) Note that the INDEX clause directly refers to the ifIndex column of the original ifTable (a field not present in ifXTable).

Recall that identifiers such as hrSWRunEntry and hrSWRunIndex represent OIDs:
    hrSWRunIndex = hrSWRunEntry.1
The OID in the AUGMENTS clause is that of the other table's Entry; the OID in the INDEX clause is that of the actual column.

Note that hrSWRunEntry is also defined in this same MIB file (though perhaps hrSWRunPerfEntry was added later in the version history).

dense tables

It is also possible to add a "dense" table, where more than one row of the new table may correspond to one row of the base table. One possible example: the original table is a table of cards (hardware modules) in a switch, and the new table is a table of ports on cards. Some cards may contain multiple ports. Another example is the CollisionCount table of ETHERLIKE-MIB; each interface of the right kind is extended in multiple ways. In both of these examples, it is possible that, while some rows of the base table might be extended with multiplicity, other rows might not be extended at all.

The bottom line is that the new table is viewed as being "tacked on" to the base table, but is indexed by a combination of the base-table index and at least one new column of its own.

By way of example, suppose the base table is cardTable, for expansion cards added to a switch, with an Entry as follows:
    cardEntry OBJECT-TYPE
       SYNTAX   CardEntry      -- SEQUENCE { cardNumber, cardWhatever, ...}
       ...
       INDEX   {cardNumber}
      ::= {cardTable 1}

Then the new table might be portTable, to add information about all the ports on each with an Entry as follows:
    portEntry OBJECT-TYPE
       SYNTAX PortEntry       -- SEQUENCE {portNumber, portStats, ...}
       ...
       INDEX {cardNumber, portNumber }
       ::= {portTable 1}
Note that we use INDEX, rather than AUGMENTS, and we simply list the two fields used to index the additional data, even though the first field is part of the base table but not part of the second table. Rows on the first table would be slot1, slot2, slot3, etc; rows in the augmenting table might be
     (slot1, port1) -- single-port card
     (slot2, port1), (slot2, port2), (slot2, port3)   -- three-port card
     (slot3, port1), (slot3, port2)      -- two-port card
      ...

sparse tables

Adding a "sparse" table to a base table is like augmentation except that some added rows are empty. In keeping with the slot example above, suppose we have a new table cardExtraTable, with information only for certain cards. We might then define
    cardExtraTable OBJECT-TYPE
          SYNTAX CardExtraEntry      -- SEQUENCE {cardExtraInfo1, cardExtraInfo2, ...}
          ...
          INDEX {cardEntry }
          ::= {cardExtraTable 1}
Note the INDEX claue, not AUGMENTS, and the fact that the index is defined to be the entire cardEntry, not just the cardNumber (though it is hard to be sure, since this SNMPv2 option was seldom used). This is actually very similar to the SNMPv1 formulation of an AUGMENTed table.

The ETHERLIKE-MIB StatsTable is an example of a sparse table extension: we extend those rows in the interfaces table that correspond to Ethernet interfaces, and not the other rows. In the actual ETHERLIKE-MIB file, the index for the StatsTable is specified as dot3StatsIndex, and it is only later, in the definition of this field, that it is made clear that this is supposed to be the same as the ifIndex of the interfaces table; this slightly muddies the exposition.
 

GET-BULK

 
A GET-BULK request can include requests for a list of (non-repeating) scalars, followed by a list of OIDs to be repeatedly requested via agent-side GET-NEXT. The request has fields NON-REPEATERS and MAX_REPETITIONS. The former counts how many contiguous scalar values should be sent, the latter in effect how many ROWS of a table should be sent.

In other words, non-repeaters have to be listed before repeaters (they cannot be intermixed), and all the repeaters get the same repetition count.
 
Responses can be spread over multiple packets, If a response cannot fit into one packet, it is truncated at the max size that does fit. This is different from SNMPv1, where all the responses had to go in one packet, but that packet could be a very large (and thus fragmented) IP/UDP packet.
 
The semantics are supposed to be the same as the traditional GetNext approach, but with the GetNexts executed on the agent.
 
getBulk. Note parameters 2,3 and 0,3. Note similarity to GetNext.
 
 
                                    |
       +-----+-------------+--------+--------+ 
       |     |             |                 |
       |     |        +---------+       +----------+
       A     B        |    T    |       |    U     |
                      +---------+       +----------+
                           |
                      +---------+
                      |    E    |
                      +---------+  
                           |
         +-----------------+-----------------+
         |                 |                 |
    +---------+       +---------+       +---------+
    | T.E.1.1 |       | T.E.2.1 |       | T.E.3.1 |
    +---------+       +---------+       +---------+
         |                 |                 |
    +---------+       +---------+       +---------+
    | T.E.1.2 |       | T.E.2.2 |       | T.E.3.2 |
    +---------+       +---------+       +---------+
         |                 |                 |
    +---------+       +---------+       +---------+
    | T.E.1.3 |       | T.E.2.3 |       | T.E.3.3 |
    +---------+       +---------+       +---------+
         |                 |                 |
    +---------+       +---------+       +---------+
    | T.E.1.4 |       | T.E.2.4 |       | T.E.3.4 |
    +---------+       +---------+       +---------+
 
Suppose we issue
 
    GetBulk(2,3,A,B, T.E.1, T.E.2, T.E.3)
 
Then the agent returns
    A, B, T.E.1.1, T.E.2.1, T.E.3.1, 
        T.E.1.2, T.E.2.2, T.E.3.2,    ;; row 2
        T.E.1.3, T.E.2.3, T.E.3.3,    ;; row 3
         
Now suppose we do this again, but for 0 nonrepeaters and with a repeat count of 2: 
    GetBulk(0,3, T.E.1.3, T.E.2.3, T.E.3.3)
The agent returns two "rows":
    T.E.1.4, T.E.2.4, T.E.3.4,      ;; row 4
    T.E.2.1, T.E.3.1, U               ;; "wrap-around"
 
 
One last note: if you ask to retrieve a bunch of values in SNMPv1, and one does not exist, nothing is returned. In SNMPv2, values that exist are returned and nonexistent values generate an error response. However, SET requests still either succeed for all OIDs, fail for all.
 
SNMPv1--SNMPv2 compatibility
 
bilingual manager: sends either v1 or v2 queries, v2 if possible else v1.
bilingual agent: get v1 queries, process using v2 engine but send back v1-protocol message
 
proxy agent server: an SNMPv1 proxy is implemented as front-end agent to an SNMP v2 manager. Allows examination of v1 objects. get-bulk is translated to get-next in the obvious way; of course, only one row at a time will be fetched.



Enterprise (private) mibs


We just looked at this table in passing:
 
2
ibm
9
cisco
11
hp
16
hpnr
23
novell
36
dec
77
lanmanager
119
nec
224
lanOptics
232
compaq
311
microsoft
353
atmForum
494
madge
711
lightstream
795
adaptec
1123
symbios
1608
mylex
2021
ucd-snmp
2636
juniper
8072
net-snmp
 



Network Management Systems

discovery
capability detection

The first step is to discover nodes. This can be done via manual configuration, but is more often done with ping, or possibly with some other form of TCP scan (eg connection requests to  port 139, the netbios port), or possibly probes to UDP port 161, the SNMP port. The range of desired hosts is almost always specified manually.

The next phase is to probe the detected host to figure out "what it is" and "what it is doing". This, especially for the latter sense, is also called capability detection. One common way to proceed is with a TCP port scan.

However, we can also get a lot of information from SNMP. (Actually, we can get the effect of a TCP port scan by retrieving the SNMP TCP Group, in particular the Connections Table).  Some particular things to look at include the SysOID value, which the NMS can use as a lookup key to figure out what kinds of "private" SNMP queries to make. For example, a cisco switch would send back a SysOID that began 1.3.6.1.4.1.9, and a net-snmp agent returns 1.3.6.1.4.1.8072.3.2.10.   
From NET-SNMP-MIB.txt:
     
1.3.6.1.4.1 private
private.8072 net-snmp
net-snmp.1
netSnmpObjects
net-snmp.3 netSnmpEnumerations
netSnmpEnumerations.2 netSNMPAgentOIDs
netSNMPAgentOIDs.10 not specifically mentioned