MIB catalogue and OID resolution

An OID like .1.3.6.1.4.1.6574.2.1.1.5 means nothing on its own. Turning it into diskTemperature requires a MIB, and on an air-gapped probe you cannot look one up.

Axiom Border therefore ships the catalogue with the product: over 12,500 MIB modules in JSON format covering the vendors you are likely to meet in the field. Symbolic names work anywhere numeric OIDs work, and returned values come back with readable names attached.

Directory Contents Used by
MIBS/JSON-FORMAT/ ~12,500 .json modules Yes — this is the catalogue the probe reads
MIBS/ASN1-FORMAT/ ~15,900 .mib modules No — provided for the host’s own SNMP tooling

The JSON tree is what the probe uses, and its location is configurable through securityProbes.snmp.mibDir (default MIBS/JSON-FORMAT). The ASN.1 tree is a convenience: copy it to /usr/share/snmp/mibs if you want snmpwalk and friends on the host to resolve names too.

One operation is slower than the rest

Listing vendors, inspecting a module and resolving a symbol are all fast, whatever the size of the catalogue. The one operation that is noticeably slower is a symbol search without a vendor, because it has to cover every module in the catalogue. Pass vendor whenever you know it.

Vendors

Vendor is derived from the module name: everything before the first hyphen, lowercased. So SYNOLOGY-DISK-MIB belongs to vendor synology, and A3COM-HUAWEI-DEVICE-MIB to a3com. A module name with no hyphen becomes its own vendor. This is a heuristic, not metadata from the file — it works because MIB naming conventions are near-universal, but do not expect it to be perfect on unusual modules.

Browsing the catalogue

List vendors and modules

curl -s http://192.168.1.10:8083/security/mibs \
  -H "Authorization: Bearer <jwt-token>"
{
  "root": "MIBS/JSON-FORMAT",
  "vendors": [
    { "vendor": "synology", "modules": ["SYNOLOGY-DISK-MIB", "SYNOLOGY-SYSTEM-MIB"], "moduleCount": 7 }
  ]
}

This is inexpensive regardless of catalogue size.

Search for a symbol

curl -s 'http://192.168.1.10:8083/security/mibs/search?q=diskTemp&vendor=synology&limit=50' \
  -H "Authorization: Bearer <jwt-token>"
{
  "query": "diskTemp",
  "vendor": "synology",
  "limit": 50,
  "matches": [
    {
      "vendor": "synology",
      "module": "SYNOLOGY-DISK-MIB",
      "name": "diskTemperature",
      "oid": "1.3.6.1.4.1.6574.2.1.1.6",
      "class": "objecttype",
      "description": "Temperature of the disk"
    }
  ]
}

q is mandatory and matched as a case-insensitive substring against object names. Always pass vendor when you can — with it the search is limited to that vendor’s modules; without it the search covers the whole catalogue and takes considerably longer.

limit of zero or less means unlimited. Results are ordered by vendor, then module, then name.

Inspect a module

curl -s http://192.168.1.10:8083/security/mibs/SYNOLOGY-DISK-MIB \
  -H "Authorization: Bearer <jwt-token>"
{
  "module": "SYNOLOGY-DISK-MIB",
  "vendor": "synology",
  "objects": [
    { "name": "diskID", "oid": "1.3.6.1.4.1.6574.2.1.1.2", "class": "objecttype", "description": "..." }
  ]
}

Module names are exact and case-sensitive — they are the filename without the .json extension. An unknown module returns 404.

How resolution works

Two directions matter, and they behave differently: symbols become OIDs before the request goes out, and OIDs get names again on the way back.

Inbound: symbols become OIDs before the request

When a scan request contains symbolic names, they are resolved before anything is sent on the wire:

  1. A small built-in table of the universal system OIDs — sysDescr, sysObjectID, sysName, sysLocation, sysContact.
  2. Anything already numeric passes through untouched.
  3. Everything else is looked up in the vendor’s modules.
flowchart TB
    S["Requested name<br>sysDescr"] --> C1{"Built-in<br>system OID?"}
    C1 -->|"no"| C2{"Already<br>numeric?"}
    C2 -->|"no"| C3{"In the vendor's<br>modules?"}
    C3 -->|"no"| FAIL["Execution fails<br>unresolved oids"]:::danger
    C1 -->|"yes"| OK["Numeric OID<br>sent on the wire"]
    C2 -->|"yes"| OK
    C3 -->|"yes"| OK
    classDef danger fill:#fff0ed,stroke:#ff664e,color:#101010
An unresolved symbol fails the whole execution

If any requested symbol cannot be resolved, the execution ends as failed with snmp: unresolved oids for vendor=<vendor>: <list> — it does not silently skip the unknown ones and query the rest. Verify symbols with /security/mibs/search before relying on them, or pass numeric OIDs.

The vendor hint

Symbol resolution needs to know which vendor’s modules to search. Axiom Border works it out in order:

  1. The mib field of the scan request.
  2. securityProbes.snmp.mib from configuration.
  3. Inferred from the host’s manufacturer as recorded by a previous nmap scan.

The third is the useful one: run a network scan first, and the manufacturer discovered from the MAC address becomes the vendor hint automatically. Explicit rules exist for common vendors — Synology, Cisco, HP, Huawei, Juniper, D-Link — with a fallback to the first token of the manufacturer name.

Note that the hint is only consulted when a request actually contains non-numeric symbols. All-numeric requests need no vendor at all.

Outbound: OIDs become names on the response

Every OID that comes back is put through the same four steps, in order, and the first match wins:

  1. The built-in table of universal system OIDs.
  2. The vendor’s modules, trimming up to two trailing segments — which is how indexed OIDs such as ifDescr.3 resolve to ifDescr.
  3. The SNMPv2 module.
  4. A set of generic modules — if, ip, tcp, udp, host, entity, sysappl, ucd, net — by exact match.

If nothing matches, the name field of the result is literally unknown. The value is still returned — only the label is missing.

Ambiguity yields no name rather than a wrong one

When trimming produces more than one candidate match, resolution stops and returns no name. This is deliberate: an OID labelled with a plausible-but-wrong symbol is worse than an unlabelled one, because it silently misleads whoever reads the report.

Using symbols in scans

Symbolic names work in both SNMP operations. A GET with symbols:

curl -s -X POST http://192.168.1.10:8083/security/scan/snmp \
  -H "Authorization: Bearer <jwt-token>" \
  -H 'Content-Type: application/json' \
  -d '{
        "target": "192.168.1.50",
        "mib": "synology",
        "oids": ["sysDescr", "sysName", "diskTemperature"]
      }'

And a walk from a symbolic root:

curl -s -X POST http://192.168.1.10:8083/security/scan/snmp/walk \
  -H "Authorization: Bearer <jwt-token>" \
  -H 'Content-Type: application/json' \
  -d '{"target": "192.168.1.50", "mib": "synology", "walkRoot": "diskTable"}'

A walkRoot symbol that does not resolve fails that target with snmp: walkRoot symbol not found vendor=<vendor> walkRoot=<root>.

oids always wins over walkRoot

If a request contains both oids and walkRoot, the walkRoot is silently ignored and only the listed OIDs are fetched. A request must contain at least one of the two, or it fails with snmp: no oids or walkRoot provided. If you meant to walk, do not send oids.

Values in results

Each SNMP result entry carries the numeric oid, the resolved name (or unknown), the formatted value and a status.

Values are formatted by type: numbers as numbers, IP addresses in dotted form, and byte strings as text when they are printable UTF-8, otherwise as 0x<hex> — so binary values are legible rather than mangled. The status is up, or error carrying the SNMP condition name for NoSuchObject, NoSuchInstance and EndOfMibView.

Walks mark disappearances, GETs do not

A walk covers a whole subtree, so an OID that was previously known and is now absent is genuinely gone, and gets marked as down. A GET only asks about specific OIDs, so absence proves nothing and nothing is marked. This is why change detection over SNMP inventory should use walks.

Extending the catalogue

To add a vendor’s MIB, drop its JSON module into the configured mibDir. New modules are picked up automatically, and changing mibDir reloads the catalogue from the new location.

If the catalogue fails to load — a bad path, unreadable files — the probe logs a warning and continues with an empty catalogue. It does not refuse to start. Symbol resolution will then fail for everything except the built-in system OIDs, which is a good signature for this particular misconfiguration: numeric OIDs work, sysDescr works, everything else does not.