In an SNMP MIB, why does a scalar's instance end in .0 while a table column's ends in index values, and how are they encoded?
answer
- object name plus instance suffix
- scalar versus columnar object
- the row's INDEX clause
- length prefix unless IMPLIED
- IpAddress takes four sub-identifiers
basics
~20 sSMIv2 names a scalar's single instance by appending .0; a table column has one instance per row, so its suffix is the row's INDEX values: an integer as one sub-identifier, a variable-length string as its length then one sub-identifier per octet.
solid answer
~40 sIn SMIv2 (RFC 2578) an instance OID is the **object's name plus an instance suffix**. A scalar has exactly one instance, so the suffix is a single `0`. A columnar object has one instance per conceptual row, so the suffix comes from the row's `INDEX` clause, encoded by syntax: an integer is one sub-identifier; a fixed-length string, or a variable-length one marked `IMPLIED`, is one sub-identifier per octet; a variable-length string without `IMPLIED` is its length followed by its octets; an `OBJECT IDENTIFIER` index is likewise length-prefixed unless `IMPLIED`; an `IpAddress` is four sub-identifiers. Several index objects are concatenated in order. So `vacmGroupName` for security model 3 and name `ops` is `1.3.6.1.6.3.16.1.2.1.3.3.3.111.112.115`.
code
pseudocode · 17 linesfunction encodeIndex(values, indexSyntaxes):
suffix = []
for i, (value, syntax) in enumerate(zip(values, indexSyntaxes)):
last = (i == len(values) - 1)
if syntax.isInteger:
suffix.append(value) # one sub-identifier
else if syntax.isIpAddress:
suffix.extend(value.octets) # four sub-identifiers
else if syntax.isFixedLength or (syntax.implied and last):
suffix.extend(value.parts) # n sub-identifiers
else:
suffix.append(len(value.parts)) # length first
suffix.extend(value.parts) # then n sub-identifiers
return suffix
# vacmGroupName, model 3, name "ops"
# encodeIndex([3, "ops"], [Integer, VarString]) -> [3, 3, 111, 112, 115]go deeper
Remember that .0 marks a scalar and that table columns carry index values after the column number instead.
Be ready to encode an integer plus a variable-length string index by hand, including the length sub-identifier, and to explain what IMPLIED removes.
Use the encoding to debug: decode an index from a raw walk, explain odd sort order from length prefixes, and recognise auxiliary index columns you cannot fetch.
When designing a table, weigh IMPLIED's shorter suffixes and natural ordering against its restrictions, and choose stable index values that survive reboots.
## Object names versus instance names Every OID an SNMP agent answers for is two things glued together: the **name of the object type**, registered in a MIB module, and an **instance identifier** saying which occurrence of that object you mean. RFC 2578 (SMIv2) section 7 defines both cases: - A **scalar** (a leaf object not inside a conceptual table) has exactly one instance, identified by appending a sub-identifier of **zero**. `sysUpTime` is `1.3.6.1.2.1.1.3`; its value lives at `1.3.6.1.2.1.1.3.0`. - A **columnar object** sits inside a conceptual table and has one instance per **conceptual row**. The `INDEX` clause on the row object (the table's entry) says which values identify a row, and those values become the instance suffix. The zero is therefore not a row number. It is the fixed marker for "the one instance" of a non-tabular object. ## How a table is laid out A conceptual table has a fixed shape: the table object, then exactly one row object registered as sub-identifier `1` beneath it, then the columns numbered beneath the row. A column instance is `table.1.column.<index>`. In SNMPv2-MIB (RFC 3418), `sysORTable` is `system 9`; its row is `sysOREntry` (`.1`) with `INDEX { sysORIndex }`; `sysORDescr` is column 3. The description of row 2 is therefore `1.3.6.1.2.1.1.9.1.3.2`. ## The encoding rules RFC 2578 section 7.7 gives the rule for each index syntax: | Index syntax | Sub-identifiers produced | |---|---| | integer-valued (non-negative) | one, holding the value | | fixed-length string | `n`, one per octet | | variable-length string, not `IMPLIED` | `n+1`: the length, then one per octet | | variable-length string, `IMPLIED` | `n`, one per octet | | `OBJECT IDENTIFIER`, not `IMPLIED` | `n+1`: the count, then each sub-identifier | | `OBJECT IDENTIFIER`, `IMPLIED` | `n`, each sub-identifier copied | | `IpAddress` | four, in `a.b.c.d` order | When an `INDEX` clause names several objects, their encodings are concatenated in the order listed. ## Two worked examples 1. **Two-part index with a length prefix.** In the VACM MIB (RFC 3415), `vacmSecurityToGroupEntry` has `INDEX { vacmSecurityModel, vacmSecurityName }` and `vacmGroupName` is column 3, under `1.3.6.1.6.3.16.1.2.1`. For security model `3` (the User-based Security Model) and security name `ops`, the suffix is `3` (the integer), then `3` (the string length), then `111.112.115` (the octets of `o`, `p`, `s`). The full instance is `1.3.6.1.6.3.16.1.2.1.3.3.3.111.112.115`. 2. **An IMPLIED string.** In SNMP-TARGET-MIB (RFC 3413), `snmpTargetAddrEntry` has `INDEX { IMPLIED snmpTargetAddrName }`, and `snmpTargetAddrTAddress` is column 3 under `1.3.6.1.6.3.12.1.2.1`. For the name `nms` there is no length: `1.3.6.1.6.3.12.1.2.1.3.110.109.115`. ## Why the length prefix exists, and why IMPLIED drops it The length prefix makes a multi-part suffix unambiguous to parse: a reader knows exactly where one string ends and the next index begins. It also shapes ordering. SNMP orders instances **lexicographically** by sub-identifier, so with a length prefix all two-octet names sort before all three-octet names. `IMPLIED` removes the prefix, which saves a sub-identifier and makes rows sort by string content. The price is that nothing can follow it, so RFC 2578 allows `IMPLIED` only on the **last** object in an `INDEX` clause, only on variable-length syntaxes, and never on a string that might be zero-length. ## Rules a module author must follow - A `Counter32` or `Counter64` object **must not** be an index: a single counter value carries no information. - Integer indexes should start at **one**, not zero, except in special cases. - An index object that is also a column of the same row is an **auxiliary object** and normally has `MAX-ACCESS not-accessible`; you read its value out of the instance suffix, not by fetching it. - A row may use `AUGMENTS` instead of `INDEX` to extend another table; its columns then use the base row's index. ## Why an operator needs this Knowing the rules turns raw output into answers without any tool: - When a walk prints `...1.3.3.3.111.112.115`, you can split it into column 3, security model 3 and the name `ops`. - You can spot an off-by-one where an implementation numbers integer rows from zero, which RFC 2578 asks authors to avoid. - You can explain why a row created with a three-octet name appears after every two-octet name in a walk. - You can build the exact instance OID for a single Get instead of walking a whole table to find one row.
- Why must a Counter32 never appear in a table's INDEX clause?RFC 2578 says a counter has no defined initial value, so a single counter reading has no information content; only the difference between two readings means anything. An index must unambiguously and stably identify a row, which a value that keeps increasing and wraps cannot do. SMIv2 therefore forbids `Counter32` and `Counter64` in an `INDEX` clause.
- How do you read the index values of a row whose index columns are not-accessible?You decode them from the instance suffix of any accessible column in that row. Index objects that are also columns of the same row are auxiliary objects with `MAX-ACCESS not-accessible`, so fetching them directly is not possible; the values are already present in every column's OID, encoded by the section 7.7 rules.
saying these in an interview costs you the question
- The trailing .0 on a scalar is row zero of a one-row table
- A string index is always encoded without a length prefix
- IMPLIED may be used on any index object in the clause
- Table rows are numbered 0, 1, 2 by the agent regardless of INDEX
- A counter object makes a good table index because it is unique