On newer glibc versions, BSD types like u_char are only available when _DEFAULT_SOURCE or _BSD_SOURCE feature test macros are defined. Define these macros before including any headers to ensure BSD types are available for net-snmp headers.
150 lines
4.7 KiB
C
150 lines
4.7 KiB
C
// Pure C NIF for net-snmp MIB resolution
|
|
// Directly links to libnetsnmp without Rust layer
|
|
|
|
// Enable BSD types (u_char, u_short, etc.) on newer glibc
|
|
#define _DEFAULT_SOURCE
|
|
#define _BSD_SOURCE
|
|
|
|
#include <erl_nif.h>
|
|
#include <sys/types.h>
|
|
#include <net-snmp/net-snmp-config.h>
|
|
#include <net-snmp/net-snmp-includes.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
// Initialization state
|
|
static int snmp_initialized = 0;
|
|
|
|
// Helper function to create Elixir string (binary) from C string
|
|
static ERL_NIF_TERM make_binary_string(ErlNifEnv* env, const char* str) {
|
|
size_t len = strlen(str);
|
|
ErlNifBinary bin;
|
|
if (!enif_alloc_binary(len, &bin)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
memcpy(bin.data, str, len);
|
|
return enif_make_binary(env, &bin);
|
|
}
|
|
|
|
// Initialize net-snmp library
|
|
static ERL_NIF_TERM init_mib_library(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
|
|
if (snmp_initialized) {
|
|
return make_binary_string(env, "already_initialized");
|
|
}
|
|
|
|
// Initialize SNMP library
|
|
// net-snmp reads MIB directories from MIBDIRS and MIBS environment variables
|
|
// These must be set BEFORE calling this function
|
|
init_snmp("towerops");
|
|
|
|
// Initialize MIB subsystem
|
|
netsnmp_init_mib();
|
|
|
|
// Load all MIBs into memory
|
|
// This takes ~1-2 seconds but only happens once
|
|
read_all_mibs();
|
|
|
|
snmp_initialized = 1;
|
|
|
|
return make_binary_string(env, "initialized");
|
|
}
|
|
|
|
// Set MIB directory - should be called BEFORE init_mib_library
|
|
static ERL_NIF_TERM load_mib_directory(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
|
|
ErlNifBinary bin;
|
|
char mib_dir[2048];
|
|
|
|
// Get binary (Elixir strings are binaries)
|
|
if (!enif_inspect_binary(env, argv[0], &bin)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
// Copy to null-terminated C string
|
|
if (bin.size >= sizeof(mib_dir)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
memcpy(mib_dir, bin.data, bin.size);
|
|
mib_dir[bin.size] = '\0';
|
|
|
|
// Add MIB directory to search path (can be called before or after init)
|
|
add_mibdir(mib_dir);
|
|
|
|
return enif_make_atom(env, "ok");
|
|
}
|
|
|
|
// Resolve MIB name to numeric OID
|
|
static ERL_NIF_TERM resolve_oid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
|
|
ErlNifBinary bin;
|
|
char mib_name[256];
|
|
oid objid[MAX_OID_LEN];
|
|
size_t objid_len = MAX_OID_LEN;
|
|
char oid_str[512];
|
|
|
|
// Get binary (Elixir strings are binaries)
|
|
if (!enif_inspect_binary(env, argv[0], &bin)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
// Copy to null-terminated C string
|
|
if (bin.size >= sizeof(mib_name)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
memcpy(mib_name, bin.data, bin.size);
|
|
mib_name[bin.size] = '\0';
|
|
|
|
// Ensure library is initialized
|
|
if (!snmp_initialized) {
|
|
// Auto-initialize on first call
|
|
// net-snmp reads MIB directories from environment variables during init
|
|
// MIBDIRS and MIBS must be set BEFORE this code runs
|
|
init_snmp("towerops");
|
|
netsnmp_init_mib();
|
|
read_all_mibs();
|
|
snmp_initialized = 1;
|
|
}
|
|
|
|
// Use snmp_parse_oid which automatically searches all loaded MIBs
|
|
// and handles both "sysDescr" and "SNMPv2-MIB::sysDescr" formats
|
|
if (!snmp_parse_oid(mib_name, objid, &objid_len)) {
|
|
return enif_make_tuple2(env,
|
|
enif_make_atom(env, "error"),
|
|
make_binary_string(env, "Failed to resolve MIB name"));
|
|
}
|
|
|
|
// Convert OID array to dotted decimal string (numeric format)
|
|
// Use snprint_objid with -On flag equivalent (numeric only)
|
|
int save_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT);
|
|
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC);
|
|
|
|
if (snprint_objid(oid_str, sizeof(oid_str), objid, objid_len) < 0) {
|
|
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, save_format);
|
|
return enif_make_tuple2(env,
|
|
enif_make_atom(env, "error"),
|
|
make_binary_string(env, "Failed to format OID"));
|
|
}
|
|
|
|
// Restore original format setting
|
|
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, save_format);
|
|
|
|
// Strip leading dot if present
|
|
char* result = oid_str;
|
|
if (result[0] == '.') {
|
|
result++;
|
|
}
|
|
|
|
// Return OID string as binary (Elixir string)
|
|
return make_binary_string(env, result);
|
|
}
|
|
|
|
// NIF function table
|
|
static ErlNifFunc nif_funcs[] = {
|
|
{"load_mib_directory", 1, load_mib_directory, 0},
|
|
{"init_mib_library", 0, init_mib_library, ERL_NIF_DIRTY_JOB_CPU_BOUND},
|
|
{"resolve_oid", 1, resolve_oid, ERL_NIF_DIRTY_JOB_CPU_BOUND}
|
|
};
|
|
|
|
// NIF initialization
|
|
ERL_NIF_INIT(Elixir.ToweropsNative, nif_funcs, NULL, NULL, NULL, NULL)
|