Checking the MIB parser against libsmi and net-snmp

SnmpKit.MIB.Parser is a port of OTP's snmpc tokenizer and grammar. To keep it honest against reference implementations, test/mib/smilint_oracle_test.exs runs libsmi's smilint and net-snmp's snmptranslate over every fixture MIB and asserts that SnmpKit never rejects a file either tool parses. We may be more lenient than a reference, never stricter.

libsmi is the strict reference (a bison grammar with a catalogue of numbered errors); net-snmp is the permissive one that real deployments rely on. Only their syntax-level findings count: import resolution and semantic checks (missing modules, range limits, SMI version consistency) are ignored because the parser does no semantic validation.

Running it

The tests are tagged :mib_oracle and excluded by default. Each looks for its tool on PATH or in an environment variable (SMILINT, SNMPTRANSLATE) and passes with a notice when the tool is absent.

SMILINT=/path/to/libsmi/bin/smilint \
SNMPTRANSLATE=/path/to/net-snmp/bin/snmptranslate \
  mix test --include mib_oracle test/mib/smilint_oracle_test.exs

Status at the time of writing: all 185 fixture MIBs parse natively. About a dozen carry warnings for constructs net-snmp tolerates and libsmi rejects; see "Constructs accepted with a warning" below.

Building libsmi

libsmi 0.5.0 is not packaged for every distribution. It builds from source in a minute, but its 2014 code uses empty prototypes that GCC 15's C23 default rejects, so pin the C standard:

curl -sSLO https://www.ibr.cs.tu-bs.de/projects/libsmi/download/libsmi-0.5.0.tar.gz
tar xzf libsmi-0.5.0.tar.gz && cd libsmi-0.5.0
./configure --prefix="$HOME/.local/libsmi" --disable-shared
make CFLAGS="-std=gnu11 -O2 -Wno-implicit-function-declaration -Wno-incompatible-pointer-types"
make install

The install also ships the IETF, IANA and IRTF MIB trees under share/mibs; the test adds them to smilint's search path so IMPORTS resolve and only real syntax problems are reported.

Building net-snmp

Only the applications are needed (snmptranslate loads a MIB given as a path with -m):

curl -sSLO https://github.com/net-snmp/net-snmp/archive/refs/tags/v5.9.4.tar.gz
tar xzf v5.9.4.tar.gz && cd net-snmp-5.9.4
./configure --prefix="$HOME/.local/net-snmp" --with-defaults --disable-agent \
  --disable-manuals --disable-scripts --without-perl-modules \
  --disable-embedded-perl --disable-shared --with-mib-modules="" --with-transports=UDP
make && make install

Parse errors surface as <message>: At line N in <file>; the test treats only those as rejections, not the Undefined identifier / Unlinked OID linkage messages.

Constructs accepted with a warning

These are invalid SMIv2 but appear in shipped vendor MIBs and are loaded by net-snmp, so the grammar accepts them and the parser records a warning:

ConstructTreated as
SYNTAX Integer32 { a(1) } / Unsigned32 { ... }INTEGER enumeration
enumeration label starting with an uppercase letterkept as written
MAX-ACCESS write-onlywrite-only
UNITS on an SMIv1 (ACCESS/mandatory) OBJECT-TYPEkept as written
LAST-UPDATED / REVISION stamp with an impossible datekept as written

net-snmp rejects identifiers containing underscores outright; SnmpKit accepts them with a warning, as it always has.

Lexer checks adopted from libsmi

The tokenizer (SnmpKit.MIB.SnmpTokenizer.scan/1) returns {line, message} warnings, exposed as warnings on the parsed MIB map and turned into errors by SnmpKit.MIB.Compiler when warnings_as_errors: true:

Checklibsmi error
identifier contains an underscoreERR_UNDERSCORE_IN_IDENTIFIER
identifier ends in a hyphenERR_ID_ENDS_IN_HYPHEN
hex string with an odd number of digitsERR_HEX_STRING_MUL2
binary string not a multiple of 8 bitsERR_BIN_STRING_MUL8
non-radix character in a hex/binary stringlexer rejects the token
input is not valid UTF-8 (decoded as Latin-1)ERR_ILLEGAL_CHAR_IN_STRING

Two libsmi behaviours were adopted outright: a -- inside an identifier starts a comment rather than being swallowed into the name, and backslashes in quoted strings are literal characters (SMI has no escape sequences).

Deliberately different from libsmi

  • Comments run to end of line. libsmi and ASN.1 also end a comment at the next -- on the same line; net-snmp and OTP use end of line, which is what vendor MIBs in the wild assume.
  • No semantic checks (base types, ranges, MODULE-IDENTITY presence, SMI version consistency). Those belong to a validator, not the parser.