/*
+-------------------------------------------------------------------------+
| Copyright (C) 2002-2006 The Cacti Group |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with this library; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301, USA |
| |
+-------------------------------------------------------------------------+
| cactid: a backend data gatherer for cacti |
+-------------------------------------------------------------------------+
| This poller would not have been possible without: |
| - Larry Adams (current development and enhancements) |
| - Rivo Nurges (rrd support, mysql poller cache, misc functions) |
| - RTG (core poller code, pthreads, snmp, autoconf examples) |
| - Brady Alleman/Doug Warner (threading ideas, implimentation details) |
+-------------------------------------------------------------------------+
| - Cacti - http://www.cacti.net/ |
+-------------------------------------------------------------------------+
*/
#include "common.h"
#include "cactid.h"
#ifdef USE_NET_SNMP
#undef PACKAGE_NAME
#undef PACKAGE_VERSION
#undef PACKAGE_BUGREPORT
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/utilities.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/config_api.h>
#include <net-snmp/mib_api.h>
#else
#include <ucd-snmp/ucd-snmp-config.h>
#include <ucd-snmp/ucd-snmp-includes.h>
#include <ucd-snmp/system.h>
#include <mib.h>
#endif
/* resolve problems in debian */
#ifndef NETSNMP_DS_LIB_DONT_PERSIST_STATE
#define NETSNMP_DS_LIB_DONT_PERSIST_STATE 32
#endif
#define OIDSIZE(p) (sizeof(p)/sizeof(oid))
/*! \fn void snmp_cactid_init()
* \brief wrapper function for init_snmp
*
* Initializes snmp for the given application ID
*
*/
void snmp_cactid_init(void) {
#ifdef USE_NET_SNMP
#ifdef PACKAGE_VERSION
/* check that the headers we compiled with match the library we linked with -
apparently not defined in UCD-SNMP...
*/
CACTID_LOG_DEBUG(("DEBUG: SNMP Header Version is %s\n", PACKAGE_VERSION));
CACTID_LOG_DEBUG(("DEBUG: SNMP Library Version is %s\n", netsnmp_get_version()));
if(STRIMATCH(PACKAGE_VERSION,netsnmp_get_version())) {
init_snmp("cactid");
}else{
/* report the error and quit cactid */
die("ERROR: SNMP Library Version Mismatch (%s vs %s)\n",PACKAGE_VERSION,netsnmp_get_version());
}
#else
CACTID_LOG_DEBUG(("DEBUG: Issues with SNMP Header Version information, assuming old version of Net-SNMP.\n"));
init_snmp("cactid");
#endif
/* Prevent update of the snmpapp.conf file */
#ifdef NETSNMP_DS_LIB_DONT_PERSIST_STATE
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, 1);
#endif
/* Prevent update of the snmpapp.conf file */
#ifdef NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD, 1);
#endif
#ifdef NETSNMP_DS_LIB_DONT_PRINT_UNITS
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PRINT_UNITS, 1);
#endif
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_QUICK_PRINT, 1);
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_BARE_VALUE, 1);
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NUMERIC_TIMETICKS, 1);
#else
init_snmp("cactid");
ds_set_boolean(DS_LIBRARY_ID, DS_LIB_QUICK_PRINT, 1);
ds_set_boolean(DS_LIBRARY_ID, DS_LIB_PRINT_BARE_VALUE, 1);
ds_set_boolean(DS_LIBRARY_ID, DS_LIB_NUMERIC_TIMETICKS, 1);
#endif
}
/*! \fn void snmp_cactid_close()
* \brief wrapper function for the snmp_shutdown function
*
* Closes the snmp api for the given application ID
*
*/
void snmp_cactid_close(void) {
snmp_shutdown("cactid");
}
/*! \fn void *snmp_host_init(int host_id, char *hostname, int snmp_version,
* char *snmp_community, char *snmp_username, char *snmp_password, int snmp_port,
* int snmp_timeout)
* \brief initializes an snmp_session object for a Cactid host
*
* This function will initialize NET-SNMP or UCD-SNMP for the Cactid host
* in question.
*
*/
void *snmp_host_init(int host_id, char *hostname, int snmp_version, char *snmp_community,
char *snmp_username, char *snmp_password, int snmp_port, int snmp_timeout) {
void *sessp = NULL;
struct snmp_session session;
char hostnameport[BUFSIZE];
/* initialize SNMP */
snmp_sess_init(&session);
#ifdef USE_NET_SNMP
/* Prevent update of the snmpapp.conf file */
#ifdef NETSNMP_DS_LIB_DONT_PERSIST_STATE
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, 1);
#endif
/* Prevent update of the snmpapp.conf file */
#ifdef NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD, 1);
#endif
#ifdef NETSNMP_DS_LIB_DONT_PRINT_UNITS
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PRINT_UNITS, 1);
#endif
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_QUICK_PRINT, 1);
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_BARE_VALUE, 1);
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NUMERIC_TIMETICKS, 1);
#else
ds_set_boolean(DS_LIBRARY_ID, DS_LIB_QUICK_PRINT, 1);
ds_set_boolean(DS_LIBRARY_ID, DS_LIB_PRINT_BARE_VALUE, 1);
ds_set_boolean(DS_LIBRARY_ID, DS_LIB_NUMERIC_TIMETICKS, 1);
#endif
/* verify snmp version is accurate */
if (snmp_version == 2) {
session.version = SNMP_VERSION_2c;
}else if (snmp_version == 1) {
session.version = SNMP_VERSION_1;
}else if (snmp_version == 3) {
session.version = SNMP_VERSION_3;
}else {
CACTID_LOG(("Host[%i] ERROR: SNMP Version Error for Host '%s'\n", host_id, hostname));
return 0;
}
snprintf(hostnameport, sizeof(hostnameport), "%s:%i", hostname, snmp_port);
session.peername = hostnameport;
session.retries = 3;
session.remote_port = snmp_port;
session.timeout = (snmp_timeout * 1000); /* net-snmp likes microseconds */
if ((snmp_version == 2) || (snmp_version == 1)) {
session.community = snmp_community;
session.community_len = strlen(snmp_community);
}else {
/* set the SNMPv3 user name */
session.securityName = snmp_username;
session.securityNameLen = strlen(session.securityName);
session.securityAuthKeyLen = USM_AUTH_KU_LEN;
/* set the authentication method to MD5 */
session.securityAuthProto = snmp_duplicate_objid(usmHMACMD5AuthProtocol, OIDSIZE(usmHMACMD5AuthProtocol));
session.securityAuthProtoLen = OIDSIZE(usmHMACMD5AuthProtocol);
/* set the privacy protocol to none */
session.securityPrivProto = usmNoPrivProtocol;
session.securityPrivProtoLen = OIDSIZE(usmNoPrivProtocol);
session.securityPrivKeyLen = USM_PRIV_KU_LEN;
/* set the security level to authenticate, but not encrypted */
session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
/* set the authentication key to the hashed version. The password must me at least 8 char */
if (generate_Ku(session.securityAuthProto,
session.securityAuthProtoLen,
(u_char *) snmp_password,
strlen(snmp_password),
session.securityAuthKey,
&(session.securityAuthKeyLen)) != SNMPERR_SUCCESS) {
CACTID_LOG(("SNMP: Error generating SNMPv3 Ku from authentication pass phrase."));
}
}
/* open SNMP Session */
thread_mutex_lock(LOCK_SNMP);
sessp = snmp_sess_open(&session);
thread_mutex_unlock(LOCK_SNMP);
if (!sessp) {
CACTID_LOG(("ERROR: Problem initializing SNMP session '%s'\n", hostname));
}
return sessp;
}
/*! \fn void snmp_host_cleanup(void *snmp_session)
* \brief closes an established snmp session
*
* This function performs cleanup of the snmp sessions once polling is completed
* for a host.
*
*/
void snmp_host_cleanup(void *snmp_session) {
if (snmp_session != NULL) {
snmp_sess_close(snmp_session);
}
}
/*! \fn char *snmp_get(host_t *current_host, char *snmp_oid)
* \brief performs a single snmp_get for a specific snmp OID
*
* This function will poll a specific snmp OID for a host. The host snmp
* session must already be established.
*
* \return returns the character representaton of the snmp OID, or "U" if
* unsuccessful.
*
*/
char *snmp_get(host_t *current_host, char *snmp_oid) {
struct snmp_pdu *pdu = NULL;
struct snmp_pdu *response = NULL;
struct variable_list *vars = NULL;
oid anOID[MAX_OID_LEN];
size_t anOID_len = MAX_OID_LEN;
int status;
char *result_string;
if (!(result_string = (char *) malloc(BUFSIZE))) {
die("ERROR: Fatal malloc error: snmp.c snmp_get!\n");
}
result_string[0] = '\0';
status = STAT_DESCRIP_ERROR;
if (current_host->snmp_session != NULL) {
anOID_len = MAX_OID_LEN;
pdu = snmp_pdu_create(SNMP_MSG_GET);
if (!snmp_parse_oid(snmp_oid, anOID, &anOID_len)) {
CACTID_LOG(("ERROR: Problems parsing SNMP OID\n"));
SET_UNDEFINED(result_string);
return result_string;
}else{
snmp_add_null_var(pdu, anOID, anOID_len);
}
/* poll host */
status = snmp_sess_synch_response(current_host->snmp_session, pdu, &response);
/* liftoff, successful poll, process it!! */
if (status == STAT_SUCCESS) {
if (response == NULL) {
CACTID_LOG(("ERROR: Some internal error caused snmp to return null response in snmp_get\n"));
SET_UNDEFINED(result_string);
status = SNMPERR_UNKNOWN_ENG_ID;
}else{
if (response->errstat == SNMP_ERR_NOERROR) {
vars = response->variables;
#ifdef USE_NET_SNMP
snprint_value(result_string, BUFSIZE, anOID, anOID_len, vars);
#else
sprint_value(result_string, anOID, anOID_len, vars);
#endif
}
}
}
if (response) {
snmp_free_pdu(response);
response = NULL;
}
}else{
status = STAT_DESCRIP_ERROR;
}
if (status != STAT_SUCCESS) {
current_host->ignore_host = TRUE;
SET_UNDEFINED(result_string);
}
return result_string;
}
/*! \fn void snmp_snprint_value(char *obuf, size_t buf_len, const oid *objid, size_t objidlen, struct variable_list *variable)
*
* \brief replacement for the buggy net-snmp.org snprint_value function
*
* This function format an output buffer with the correct string representation
* of an snmp OID result fetched with snmp_get_multi. The buffer pointed to by
* the function is modified.
*
*/
void snmp_snprint_value(char *obuf, size_t buf_len, const oid *objid, size_t objidlen, struct variable_list *variable) {
u_char *buf = NULL;
size_t out_len = 0;
if ((buf = (u_char *) calloc(buf_len, 1)) != 0) {
if (sprint_realloc_value(&buf, &buf_len, &out_len, 1,
objid, objidlen, variable)) {
snprintf(obuf, buf_len, "%s", buf);
}else{
snprintf(obuf, buf_len, "%s [TRUNCATED]", buf);
}
}else{
SET_UNDEFINED(obuf);
}
free(buf);
}
/*! \fn char *snmp_get_multi(host_t *current_host, snmp_oids_t *snmp_oids, int num_oids)
* \brief performs multiple OID snmp_get's in a single network call
*
* This function will a group of snmp OID's for a host. The host snmp
* session must already be established. The function will modify elements of
* the snmp_oids array with the results from the snmp api call.
*
*/
void snmp_get_multi(host_t *current_host, snmp_oids_t *snmp_oids, int num_oids) {
struct snmp_pdu *pdu = NULL;
struct snmp_pdu *response = NULL;
struct variable_list *vars = NULL;
int status;
int i;
int max_repetitions = 1;
int non_repeaters = 0;
struct nameStruct {
oid name[MAX_OID_LEN];
size_t name_len;
} *name, *namep;
/* load up oids */
namep = name = (struct nameStruct *) calloc(num_oids, sizeof(*name));
pdu = snmp_pdu_create(SNMP_MSG_GET);
for (i = 0; i < num_oids; i++) {
namep->name_len = MAX_OID_LEN;
if (!snmp_parse_oid(snmp_oids[i].oid, namep->name, &namep->name_len)) {
CACTID_LOG(("Host[%i] ERROR: Problems parsing Multi SNMP OID! (oid: %s)\n", current_host->id, snmp_oids[i].oid));
/* Mark this OID as "bad" */
SET_UNDEFINED(snmp_oids[i].result);
}else{
snmp_add_null_var(pdu, namep->name, namep->name_len);
}
namep++;
}
status = STAT_DESCRIP_ERROR;
/* execute the multi-get request */
retry:
status = snmp_sess_synch_response(current_host->snmp_session, pdu, &response);
/* liftoff, successful poll, process it!! */
if (status == STAT_SUCCESS) {
if (response == NULL) {
CACTID_LOG(("ERROR: Some internal error caused snmp to return null response in snmp_get_multi.\n"));
status = SNMPERR_UNKNOWN_ENG_ID;
}else{
if (response->errstat == SNMP_ERR_NOERROR) {
vars = response->variables;
for(i = 0; i < num_oids && vars; i++) {
if (!IS_UNDEFINED(snmp_oids[i].result)) {
#ifdef USE_NET_SNMP
snmp_snprint_value(snmp_oids[i].result, sizeof(snmp_oids[i].result)-1, vars->name, vars->name_length, vars);
#else
sprint_value(snmp_oids[i].result, vars->name, vars->name_length, vars);
#endif
vars = vars->next_variable;
}
}
}else{
if (response->errindex != 0) {
/* removed errored OID and then retry */
int count;
/* Find our index against errindex */
count = 0;
for(i = 0; i < num_oids && count < response->errindex; i++) {
if ( ! IS_UNDEFINED(snmp_oids[i].result) ) {
count++;
}
}
i--;
SET_UNDEFINED(snmp_oids[i].result);
for (count = 1, vars = response->variables;
vars && count != response->errindex;
vars = vars->next_variable, count++) {
}
pdu = snmp_fix_pdu(response, SNMP_MSG_GET);
snmp_free_pdu(response);
response = NULL;
if (pdu != NULL) {
goto retry;
}else{
status = STAT_DESCRIP_ERROR;
}
}else{
status = STAT_DESCRIP_ERROR;
}
}
}
}
if (status != STAT_SUCCESS) {
current_host->ignore_host = 1;
for (i = 0; i < num_oids; i++) {
SET_UNDEFINED(snmp_oids[i].result);
}
}
if (response != NULL) {
snmp_free_pdu(response);
}
}
syntax highlighted by Code2HTML, v. 0.9.1