5.3 KiB
5.3 KiB
MIB Resolution with net-snmp C Library
Implementation Summary
Successfully integrated the net-snmp C library for ultra-fast MIB name resolution, achieving LibreNMS-style performance.
Performance Comparison
| Method | First Resolution | Cached Resolution |
|---|---|---|
| Subprocess (old) | ~300-400ms | <1µs (ETS cache) |
| net-snmp C library (new) | ~1-20µs | ~1-20µs (in-memory) |
Benchmark Results
sysDescr avg: 1.6µs min: 1µs max: 6µs
IF-MIB::ifDescr avg: 17.4µs min: 9µs max: 23µs
SNMPv2-MIB::sysUpTime avg: 12.3µs min: 7µs max: 19µs
IF-MIB::ifInOctets avg: 3.5µs min: 2µs max: 6µs
IP-MIB::ipAdEntAddr avg: 4.8µs min: 1µs max: 14µs
Result: 17-300x faster than subprocess approach!
Architecture
Stack
Elixir (MibCache GenServer)
↓
Rustler NIF (towerops_native)
↓
Rust FFI bindings (netsnmp_ffi.rs)
↓
net-snmp C library (libnetsnmp)
Key Components
-
netsnmp_ffi.rs - Rust FFI bindings to net-snmp C functions
initialize_snmp_library()- One-time MIB loading (~1-2s startup)resolve_mib_name()- Fast OID resolution (<20µs)
-
mib.rs - Rust NIF implementation
load_mib_directory_impl()- Configure MIB search pathsinit_mib_library_impl()- Initialize net-snmp (DirtyCpu scheduler)resolve_oid_impl()- Resolve MIB names with Rust HashMap cache
-
lib.rs - Rustler NIF exports
- Exports functions to Elixir as
ToweropsNativemodule
- Exports functions to Elixir as
-
lib/towerops_native.ex - Elixir NIF wrapper
load_mib_directory/1- Set MIB search pathsinit_mib_library/0- Initialize net-snmp libraryresolve_oid/1- Resolve MIB name to numeric OID
-
lib/towerops/profiles/mib_cache.ex - GenServer cache layer
- Async pre-resolution of common MIBs at startup
- ETS cache for ultra-fast lookups
- Runtime fallback for cache misses
Initialization Flow
- Application starts →
load_mib_directory/1called with MIB paths - First
resolve_oid/1call triggers lazy initialization:- Sets
MIBDIRSandMIBS=ALLenvironment variables - Calls
init_snmp()andnetsnmp_init_mib() - Calls
read_all_mibs()to load all MIBs into memory (~1-2s)
- Sets
- Subsequent calls use in-memory MIB tree (fast!)
Resolution Flow
- Elixir calls
ToweropsNative.resolve_oid("sysDescr") - Rust checks Rust HashMap cache (miss on first call)
- Calls C
read_objid()to resolve MIB name → OID array - Calls C
snprint_objid()to format OID as string - Caches result in Rust HashMap
- Returns to Elixir
- MibCache GenServer caches in ETS
Files Created/Modified
New Files
native/towerops_native/src/netsnmp_ffi.rs- C library FFI bindingsnative/towerops_native/build.rs- Cargo build script for linking
Modified Files
native/towerops_native/Cargo.toml- Addedlibcandccdependenciesnative/towerops_native/src/lib.rs- NIF function exportsnative/towerops_native/src/mib.rs- Refactored for C library integrationlib/towerops_native.ex- Addedinit_mib_library/0function
Existing Files (unchanged)
lib/towerops/profiles/mib_cache.ex- Still provides caching layerlib/towerops/profiles/yaml_profiles.ex- Uses MibCache as beforelib/towerops/snmp/client.ex- Uses MibCache as before
Build Requirements
Development (macOS)
- Homebrew net-snmp:
brew install net-snmp - Headers:
/usr/include/net-snmp/*or/opt/homebrew/include/net-snmp/* - Library:
/usr/lib/libnetsnmp.dylibor/opt/homebrew/lib/libnetsnmp.dylib
Production (Docker/Linux)
- Package:
net-snmpandnet-snmp-devel(CentOS) orlibsnmp-dev(Debian/Ubuntu) - Headers:
/usr/include/net-snmp/* - Library:
/usr/lib/x86_64-linux-gnu/libnetsnmp.so
Build Process
build.rsusesnet-snmp-config --libsto get library paths- Links against
libnetsnmpdynamically - Works in both dev (macOS) and prod (Linux Docker)
Advantages
- Performance: 17-300x faster than subprocess calls
- LibreNMS-aligned: Same C library as LibreNMS PHP extension
- Memory-efficient: MIBs loaded once, shared across all resolutions
- Thread-safe: C library handles concurrent access
- Works in Docker: Dynamic linking finds system libnetsnmp
- No MIB pre-compilation: MIBs loaded as text files (easy updates)
Trade-offs
-
Startup time: ~1-2 seconds to load all MIBs (one-time cost)
- Happens lazily on first resolution call
- Runs on DirtyCpu scheduler (doesn't block main BEAM schedulers)
-
C dependency: Requires net-snmp library installed
- Already required for
snmptranslatecommand - Standard package on all Linux distros
- Already required for
-
Complexity: Added Rust FFI layer
- Well-documented and type-safe
- Similar to PHP's SNMP extension internals
Testing
All existing tests pass:
mix test test/towerops/profiles/yaml_profiles_test.exs # 12 tests, 0 failures
mix test test/towerops/snmp/discovery_test.exs # 13 tests, 0 failures
Future Enhancements
- Lazy MIB loading: Only load MIBs for matched vendors (faster startup)
- MIB dependency tracking: Load only required MIB dependencies
- Shared library caching: Share MIB tree across multiple NIF instances
- Telemetry: Track resolution times and cache hit rates