snmpnet-snmp

How is a RowPointer formatted with multiple Indices in SNMP?


In RFC 2579 section 2, a RowPointer is defined as:

RowPointer ::= TEXTUAL-CONVENTION
    STATUS       current
    DESCRIPTION

            "Represents a pointer to a conceptual row.  The value is the
            name of the instance of the first accessible columnar object
            in the conceptual row.

            For example, ifIndex.3 would point to the 3rd row in the
            ifTable (note that if ifIndex were not-accessible, then
            ifDescr.3 would be used instead)."
    SYNTAX       OBJECT IDENTIFIER

The RFC gives a good example for a row pointer with a single index (I assume the full OID for ifIndex.3 is .1.3.6.1.2.1.2.2.1.1.3 (.1.3.6.1.2.1.2.2.1.1 for ifIndex, the index of the ifTable, and the .3 for the value of the index)). So it having the format:

OID-to-table-entry.OID-of-index.value-of-index

I was wondering how a multiple index RowPointer would work then as there would be multiple OID's, one to each index. I can think of the following two formats

OID-to-table-entry.OID-of-index-1.value-of-index-1.OID-of-index-2.value-of-index-2...

and

OID-to-table-entry.OID-of-index-1.value-of-index-1.value-of-index-2...

where the second omits any additional OID to reference the other indices

I also know that for an OID to an object in a row that is not an index, the OID is of the format:

OID-to-table-entry.OID-of-non-index-object.value-of-index1.value-of-index-2...

However, this OID points to a particular object in a row, not the entire row (as a RowPointer should do) (which is why I am confused as a RowPointer shouldn't have the OID to a particular object in the row like the example given in the RFC has)

I can't seem to find any examples online or details in the RFC about multiple index row pointers. Any clarification would be appreciated.


Solution

  • I'll admit, I'm confused about what exactly you are asking, and I think it's because you are using the word "index" wrong. I'll start by explaining SNMP tables, what exactly an INDEX is, and how a RowPointer works, and hopefully some part of that will answer your question.

    Object Instances

    The first thing to understand is that an OID like ifIndex (1.3.6.1.2.1.2.2.1.1), refers to an OBJECT-TYPE, as defined in some MIB definition file. You can't directly query the value of ifIndex, instead you have to query the value of some instance of ifIndex.

    There are some variables that can only have a single instance per engine. A common example is sysDescr, which gives a textual description of the system. To refer to the concrete instance of this variable, you add the .0 sub-identifier to the OID. A response PDU should never provide a value forsysDescr, only for sysDescr.0. For more on this, see RFC 1157, section 3.2.6.3.

    Tables

    Variables that may have multiple instances per engine are organized into tables. Like any table, SNMP tables contain rows and columns. Most often, the rows are referred to as "entries". The SYNTAX clause of the *Entry OBJECT-TYPE gives the name of a SEQUENCE of OBJECT-TYPES, each of which defines a column in the table. The numbering of the columns is actually determined by the OBJECT-TYPE definition for each column, though there's probably a requirement somewhere that says that the OID numbers have to match the ordering in the *Entry SEQUENCE.

    If all that jargon was confusing, here's the definition of ifTable:

    ifTable OBJECT-TYPE
        SYNTAX      SEQUENCE OF IfEntry
        MAX-ACCESS  not-accessible
        STATUS      current
        DESCRIPTION
                "A list of interface entries.  The number of entries is
                given by the value of ifNumber."
        ::= { interfaces 2 }
    
    ifEntry OBJECT-TYPE
        SYNTAX      IfEntry
        MAX-ACCESS  not-accessible
        STATUS      current
        DESCRIPTION
                "An entry containing management information applicable to a
                particular interface."
        INDEX   { ifIndex }
        ::= { ifTable 1 }
    
    IfEntry ::=
        SEQUENCE {
            ifIndex                 InterfaceIndex,
            ifDescr                 DisplayString,
            ifType                  IANAifType,
            ifMtu                   Integer32,
            ifSpeed                 Gauge32,
            ifPhysAddress           PhysAddress,
            ifAdminStatus           INTEGER,
            ifOperStatus            INTEGER,
            ifLastChange            TimeTicks,
            ifInOctets              Counter32,
            ifInUcastPkts           Counter32,
            ifInNUcastPkts          Counter32,  -- deprecated
            ifInDiscards            Counter32,
            ifInErrors              Counter32,
            ifInUnknownProtos       Counter32,
            ifOutOctets             Counter32,
            ifOutUcastPkts          Counter32,
            ifOutNUcastPkts         Counter32,  -- deprecated
            ifOutDiscards           Counter32,
            ifOutErrors             Counter32,
            ifOutQLen               Gauge32,    -- deprecated
            ifSpecific              OBJECT IDENTIFIER -- deprecated
        }
    
    ifIndex OBJECT-TYPE
        SYNTAX      InterfaceIndex
        MAX-ACCESS  read-only
        STATUS      current
        DESCRIPTION
                "A unique value, greater than zero, for each interface.  It
                is recommended that values are assigned contiguously
                starting from 1.  The value for each interface sub-layer
                must remain constant at least from one re-initialization of
                the entity's network management system to the next re-
                initialization."
        ::= { ifEntry 1 }
    
    ifDescr OBJECT-TYPE
        SYNTAX      DisplayString (SIZE (0..255))
        MAX-ACCESS  read-only
        STATUS      current
        DESCRIPTION
                "A textual string containing information about the
                interface.  This string should include the name of the
                manufacturer, the product name and the version of the
                interface hardware/software."
        ::= { ifEntry 2 }
    

    You can see that ifTable is a SEQUENCE OF IfEntry. Each entry (row) is accessible through ifEntry, which is an IfEntry whose OID is ifTable.1. Then, you can see that ifIndex is defined as { ifEntry 1 }, meaning its OID is ifEntry.1, and similarly, ifDescr is ifEntry.2. This may be what you meant by "index" in your question, but it's important to understand that it's the column index, which I would prefer to call the "column number", because the term "index" usually refers to the "instance identifier", or the portion of the OID that identifies a concrete instance of an object. Remember that it's not valid for a response PDU to provide a value for ifIndex, because that's the OID of the OBJECT-TYPE, not of any single instance.

    Row Index

    So, to finally get to the point, the INDEX clause of the *Entry OBJECT-TYPE definition tells you which columns are used to uniquely identify a row. This is perfectly analagous to the PRIMARY KEY in an SQL database table. In most cases, such as the ifTable, the INDEX simply uses a single INTEGER. You can see from the MIB excerpt above, that ifEntry uses ifIndex as the INDEX, meaning that ifIndex.3, ifDescr.3, and ifType.3 refer to three columns in the same row. The .3 would generally be called the "index".

    It is absolutely possible for a row to have a composite INDEX, just as a database table can have a composite PRIMARY KEY. One example is IP-MIB::ipAddressEntry:

    ipAddressEntry OBJECT-TYPE
        SYNTAX     IpAddressEntry
        MAX-ACCESS not-accessible
        STATUS     current
        DESCRIPTION
               "An address mapping for a particular interface."
        INDEX { ipAddressAddrType, ipAddressAddr }
        ::= { ipAddressTable 1 }
    
    IpAddressEntry ::= SEQUENCE {
            ipAddressAddrType     InetAddressType,
            ipAddressAddr         InetAddress,
            ipAddressIfIndex      InterfaceIndex,
            ipAddressType         INTEGER,
            ipAddressPrefix       RowPointer,
            ipAddressOrigin       IpAddressOriginTC,
            ipAddressStatus       IpAddressStatusTC,
            ipAddressCreated      TimeStamp,
            ipAddressLastChanged  TimeStamp,
            ipAddressRowStatus    RowStatus,
            ipAddressStorageType  StorageType
        }
    

    Note that the INDEX contains two column names. The first is an INTEGER, indicating the IP address type (ipv4 or ipv6), and the second is an OCTET STRING containing the address itself. Both ipAddressAddrType and ipAddressAddr are marked as not-accessible, meaning that it's not valid to query/return those columns directly. Their values can only be determined by querying the value of another column in the table.

    RowPointer

    Having explained all this, hopefully the definition of a RowPointer will make more sense. Copying from the text in your question:

    The value is the name of the instance of the first accessible columnar object in the conceptual row.

    The example given refers to ifIndex.3. This is because ifIndex is the first OBJECT-TYPE in IfEntry, and it's MAX-ACCESS is read-only. The value of any other column in that row can be determined by extracting the .3 index, and tacking it onto to the OID of the desired column.

    For a row of ipAddressTable, the RowPointer might be something like ipAddressIfIndex.1.4.192.168.0.1. Unlike in IfEntry, the first two OBJECT-TYPEs in IpAddressEntry are marked not-accessible, so the "first accessible columnar object" is ifAddressIfIndex. The index contains an ipAddressAddrType of .1 (ipv4), and an ipAddressAddr of .4.192.168.0.1, which encodes the IP address "192.168.0.1" (the .4 indicates the length -- that's a different discussion). Together these two fields constitute the index of the row. To query, say, ipAddressCreated for the same row, you would use ipAddressCreated.1.4.192.168.0.1.