// Pure C NIF for net-snmp MIB resolution // Directly links to libnetsnmp without Rust layer #include #include #include #include #include #include // 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)