X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fsnmp.c;h=d80ee9222615b6ff3a824376252d3ef65f352284;hb=41288c6a9ed050b41ad47184aa1b53668c3588cc;hp=7caa625e6cb86704b215cb2fc0782a1525860122;hpb=c1219a1c9db2e8400e2ee94b87f86ccd441485d5;p=collectd.git diff --git a/src/snmp.c b/src/snmp.c index 7caa625e..d80ee922 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -2,33 +2,39 @@ * collectd - src/snmp.c * Copyright (C) 2007-2012 Florian octo Forster * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; only version 2 of the License is applicable. + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: * - * 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 - * General Public License for more details. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. * * Authors: * Florian octo Forster **/ #include "collectd.h" + #include "common.h" #include "plugin.h" #include "utils_complain.h" -#include - #include #include +#include + /* * Private data structes */ @@ -50,14 +56,17 @@ struct data_definition_s { char *name; /* used to reference this from the `Collect' option */ char *type; /* used to find the data_set */ - int is_table; + _Bool is_table; instance_t instance; char *instance_prefix; oid_t *values; - int values_len; + size_t values_len; double scale; double shift; struct data_definition_s *next; + char **ignores; + size_t ignores_len; + int invert_match; }; typedef struct data_definition_s data_definition_t; @@ -65,8 +74,22 @@ struct host_definition_s { char *name; char *address; - char *community; int version; + + /* snmpv1/2 options */ + char *community; + + /* snmpv3 security options */ + char *username; + oid *auth_protocol; + size_t auth_protocol_len; + char *auth_passphrase; + oid *priv_protocol; + size_t priv_protocol_len; + char *priv_passphrase; + int security_level; + char *context; + void *sess_handle; c_complain_t complaint; cdtime_t interval; @@ -142,9 +165,8 @@ static int csnmp_oid_to_string (char *buffer, size_t buffer_size, { char oid_str[MAX_OID_LEN][16]; char *oid_str_ptr[MAX_OID_LEN]; - size_t i; - for (i = 0; i < o->oid_len; i++) + for (size_t i = 0; i < o->oid_len; i++) { ssnprintf (oid_str[i], sizeof (oid_str[i]), "%lu", (unsigned long) o->oid[i]); oid_str_ptr[i] = oid_str[i]; @@ -183,6 +205,10 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */ sfree (hd->name); sfree (hd->address); sfree (hd->community); + sfree (hd->username); + sfree (hd->auth_passphrase); + sfree (hd->priv_passphrase); + sfree (hd->context); sfree (hd->data_list); sfree (hd); @@ -197,16 +223,15 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */ * csnmp_config * +-> call_snmp_init_once * +-> csnmp_config_add_data - * ! +-> csnmp_config_add_data_type - * ! +-> csnmp_config_add_data_table * ! +-> csnmp_config_add_data_instance * ! +-> csnmp_config_add_data_instance_prefix * ! +-> csnmp_config_add_data_values * +-> csnmp_config_add_host - * +-> csnmp_config_add_host_address - * +-> csnmp_config_add_host_community * +-> csnmp_config_add_host_version * +-> csnmp_config_add_host_collect + * +-> csnmp_config_add_host_auth_protocol + * +-> csnmp_config_add_host_priv_protocol + * +-> csnmp_config_add_host_security_level */ static void call_snmp_init_once (void) { @@ -217,60 +242,31 @@ static void call_snmp_init_once (void) have_init = 1; } /* void call_snmp_init_once */ -static int csnmp_config_add_data_type (data_definition_t *dd, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("snmp plugin: `Type' needs exactly one string argument."); - return (-1); - } - - sfree (dd->type); - dd->type = strdup (ci->values[0].value.string); - if (dd->type == NULL) - return (-1); - - return (0); -} /* int csnmp_config_add_data_type */ - -static int csnmp_config_add_data_table (data_definition_t *dd, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) - { - WARNING ("snmp plugin: `Table' needs exactly one boolean argument."); - return (-1); - } - - dd->is_table = ci->values[0].value.boolean ? 1 : 0; - - return (0); -} /* int csnmp_config_add_data_table */ - static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t *ci) { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("snmp plugin: `Instance' needs exactly one string argument."); - return (-1); - } + char buffer[DATA_MAX_NAME_LEN]; + int status; + + status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer)); + if (status != 0) + return status; if (dd->is_table) { /* Instance is an OID */ dd->instance.oid.oid_len = MAX_OID_LEN; - if (!read_objid (ci->values[0].value.string, + if (!read_objid (buffer, dd->instance.oid.oid, &dd->instance.oid.oid_len)) { - ERROR ("snmp plugin: read_objid (%s) failed.", - ci->values[0].value.string); + ERROR ("snmp plugin: read_objid (%s) failed.", buffer); return (-1); } } else { /* Instance is a simple string */ - sstrncpy (dd->instance.string, ci->values[0].value.string, + sstrncpy (dd->instance.string, buffer, sizeof (dd->instance.string)); } @@ -280,11 +276,7 @@ static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t static int csnmp_config_add_data_instance_prefix (data_definition_t *dd, oconfig_item_t *ci) { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("snmp plugin: `InstancePrefix' needs exactly one string argument."); - return (-1); - } + int status; if (!dd->is_table) { @@ -293,25 +285,19 @@ static int csnmp_config_add_data_instance_prefix (data_definition_t *dd, return (-1); } - sfree (dd->instance_prefix); - dd->instance_prefix = strdup (ci->values[0].value.string); - if (dd->instance_prefix == NULL) - return (-1); - - return (0); + status = cf_util_get_string(ci, &dd->instance_prefix); + return status; } /* int csnmp_config_add_data_instance_prefix */ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *ci) { - int i; - if (ci->values_num < 1) { WARNING ("snmp plugin: `Values' needs at least one argument."); return (-1); } - for (i = 0; i < ci->values_num; i++) + for (int i = 0; i < ci->values_num; i++) if (ci->values[i].type != OCONFIG_TYPE_STRING) { WARNING ("snmp plugin: `Values' needs only string argument."); @@ -320,12 +306,12 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t * sfree (dd->values); dd->values_len = 0; - dd->values = (oid_t *) malloc (sizeof (oid_t) * ci->values_num); + dd->values = malloc (sizeof (*dd->values) * ci->values_num); if (dd->values == NULL) return (-1); - dd->values_len = ci->values_num; + dd->values_len = (size_t) ci->values_num; - for (i = 0; i < ci->values_num; i++) + for (int i = 0; i < ci->values_num; i++) { dd->values[i].oid_len = MAX_OID_LEN; @@ -344,69 +330,75 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t * return (0); } /* int csnmp_config_add_data_instance */ -static int csnmp_config_add_data_shift (data_definition_t *dd, oconfig_item_t *ci) +static int csnmp_config_add_data_blacklist(data_definition_t *dd, oconfig_item_t *ci) { - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + if (ci->values_num < 1) + return (0); + + for (int i = 0; i < ci->values_num; i++) { - WARNING ("snmp plugin: The `Shift' config option needs exactly one number argument."); - return (-1); + if (ci->values[i].type != OCONFIG_TYPE_STRING) + { + WARNING ("snmp plugin: `Ignore' needs only string argument."); + return (-1); + } } - dd->shift = ci->values[0].value.number; + dd->ignores_len = 0; + dd->ignores = NULL; - return (0); -} /* int csnmp_config_add_data_shift */ + for (int i = 0; i < ci->values_num; ++i) + { + if (strarray_add(&(dd->ignores), &(dd->ignores_len), ci->values[i].value.string) != 0) + { + ERROR("snmp plugin: Can't allocate memory"); + strarray_free(dd->ignores, dd->ignores_len); + return (ENOMEM); + } + } + return 0; +} /* int csnmp_config_add_data_blacklist */ -static int csnmp_config_add_data_scale (data_definition_t *dd, oconfig_item_t *ci) +static int csnmp_config_add_data_blacklist_match_inverted(data_definition_t *dd, oconfig_item_t *ci) { - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) { - WARNING ("snmp plugin: The `Scale' config option needs exactly one number argument."); + WARNING ("snmp plugin: `InvertMatch' needs exactly one boolean argument."); return (-1); } - dd->scale = ci->values[0].value.number; + dd->invert_match = ci->values[0].value.boolean ? 1 : 0; return (0); -} /* int csnmp_config_add_data_scale */ +} /* int csnmp_config_add_data_blacklist_match_inverted */ static int csnmp_config_add_data (oconfig_item_t *ci) { data_definition_t *dd; int status = 0; - int i; - - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("snmp plugin: The `Data' config option needs exactly one string argument."); - return (-1); - } - dd = (data_definition_t *) malloc (sizeof (data_definition_t)); + dd = calloc (1, sizeof (*dd)); if (dd == NULL) return (-1); - memset (dd, '\0', sizeof (data_definition_t)); - dd->name = strdup (ci->values[0].value.string); - if (dd->name == NULL) + status = cf_util_get_string(ci, &dd->name); + if (status != 0) { free (dd); return (-1); } + dd->scale = 1.0; dd->shift = 0.0; - for (i = 0; i < ci->children_num; i++) + for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *option = ci->children + i; if (strcasecmp ("Type", option->key) == 0) - status = csnmp_config_add_data_type (dd, option); + status = cf_util_get_string(option, &dd->type); else if (strcasecmp ("Table", option->key) == 0) - status = csnmp_config_add_data_table (dd, option); + status = cf_util_get_boolean(option, &dd->is_table); else if (strcasecmp ("Instance", option->key) == 0) status = csnmp_config_add_data_instance (dd, option); else if (strcasecmp ("InstancePrefix", option->key) == 0) @@ -414,9 +406,13 @@ static int csnmp_config_add_data (oconfig_item_t *ci) else if (strcasecmp ("Values", option->key) == 0) status = csnmp_config_add_data_values (dd, option); else if (strcasecmp ("Shift", option->key) == 0) - status = csnmp_config_add_data_shift (dd, option); + status = cf_util_get_double(option, &dd->shift); else if (strcasecmp ("Scale", option->key) == 0) - status = csnmp_config_add_data_scale (dd, option); + status = cf_util_get_double(option, &dd->scale); + else if (strcasecmp ("Ignore", option->key) == 0) + status = csnmp_config_add_data_blacklist(dd, option); + else if (strcasecmp ("InvertMatch", option->key) == 0) + status = csnmp_config_add_data_blacklist_match_inverted(dd, option); else { WARNING ("snmp plugin: Option `%s' not allowed here.", option->key); @@ -450,11 +446,12 @@ static int csnmp_config_add_data (oconfig_item_t *ci) sfree (dd->name); sfree (dd->instance_prefix); sfree (dd->values); + sfree (dd->ignores); sfree (dd); return (-1); } - DEBUG ("snmp plugin: dd = { name = %s, type = %s, is_table = %s, values_len = %i }", + DEBUG ("snmp plugin: dd = { name = %s, type = %s, is_table = %s, values_len = %zu }", dd->name, dd->type, (dd->is_table != 0) ? "true" : "false", dd->values_len); if (data_head == NULL) @@ -471,50 +468,6 @@ static int csnmp_config_add_data (oconfig_item_t *ci) return (0); } /* int csnmp_config_add_data */ -static int csnmp_config_add_host_address (host_definition_t *hd, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("snmp plugin: The `Address' config option needs exactly one string argument."); - return (-1); - } - - if (hd->address == NULL) - free (hd->address); - - hd->address = strdup (ci->values[0].value.string); - if (hd->address == NULL) - return (-1); - - DEBUG ("snmp plugin: host = %s; host->address = %s;", - hd->name, hd->address); - - return (0); -} /* int csnmp_config_add_host_address */ - -static int csnmp_config_add_host_community (host_definition_t *hd, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("snmp plugin: The `Community' config option needs exactly one string argument."); - return (-1); - } - - if (hd->community == NULL) - free (hd->community); - - hd->community = strdup (ci->values[0].value.string); - if (hd->community == NULL) - return (-1); - - DEBUG ("snmp plugin: host = %s; host->community = %s;", - hd->name, hd->community); - - return (0); -} /* int csnmp_config_add_host_community */ - static int csnmp_config_add_host_version (host_definition_t *hd, oconfig_item_t *ci) { int version; @@ -527,9 +480,9 @@ static int csnmp_config_add_host_version (host_definition_t *hd, oconfig_item_t } version = (int) ci->values[0].value.number; - if ((version != 1) && (version != 2)) + if ((version < 1) || (version > 3)) { - WARNING ("snmp plugin: `Version' must either be `1' or `2'."); + WARNING ("snmp plugin: `Version' must either be `1', `2', or `3'."); return (-1); } @@ -544,7 +497,6 @@ static int csnmp_config_add_host_collect (host_definition_t *host, data_definition_t *data; data_definition_t **data_list; int data_list_len; - int i; if (ci->values_num < 1) { @@ -552,7 +504,7 @@ static int csnmp_config_add_host_collect (host_definition_t *host, return (-1); } - for (i = 0; i < ci->values_num; i++) + for (int i = 0; i < ci->values_num; i++) if (ci->values[i].type != OCONFIG_TYPE_STRING) { WARNING ("snmp plugin: All arguments to `Collect' must be strings."); @@ -560,13 +512,13 @@ static int csnmp_config_add_host_collect (host_definition_t *host, } data_list_len = host->data_list_len + ci->values_num; - data_list = (data_definition_t **) realloc (host->data_list, + data_list = realloc (host->data_list, sizeof (data_definition_t *) * data_list_len); if (data_list == NULL) return (-1); host->data_list = data_list; - for (i = 0; i < ci->values_num; i++) + for (int i = 0; i < ci->values_num; i++) { for (data = data_head; data != NULL; data = data->next) if (strcasecmp (ci->values[i].value.string, data->name) == 0) @@ -589,55 +541,145 @@ static int csnmp_config_add_host_collect (host_definition_t *host, return (0); } /* int csnmp_config_add_host_collect */ +static int csnmp_config_add_host_auth_protocol (host_definition_t *hd, oconfig_item_t *ci) +{ + char buffer[4]; + int status; + + status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer)); + if (status != 0) + return status; + + if (strcasecmp("MD5", buffer) == 0) { + hd->auth_protocol = usmHMACMD5AuthProtocol; + hd->auth_protocol_len = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid); + } + else if (strcasecmp("SHA", buffer) == 0) { + hd->auth_protocol = usmHMACSHA1AuthProtocol; + hd->auth_protocol_len = sizeof(usmHMACSHA1AuthProtocol)/sizeof(oid); + } + else + { + WARNING ("snmp plugin: The `AuthProtocol' config option must be `MD5' or `SHA'."); + return (-1); + } + + DEBUG ("snmp plugin: host = %s; host->auth_protocol = %s;", + hd->name, hd->auth_protocol == usmHMACMD5AuthProtocol ? "MD5" : "SHA"); + + return (0); +} /* int csnmp_config_add_host_auth_protocol */ + +static int csnmp_config_add_host_priv_protocol (host_definition_t *hd, oconfig_item_t *ci) +{ + char buffer[4]; + int status; + + status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer)); + if (status != 0) + return status; + + if (strcasecmp("AES", buffer) == 0) + { + hd->priv_protocol = usmAESPrivProtocol; + hd->priv_protocol_len = sizeof(usmAESPrivProtocol)/sizeof(oid); + } + else if (strcasecmp("DES", buffer) == 0) { + hd->priv_protocol = usmDESPrivProtocol; + hd->priv_protocol_len = sizeof(usmDESPrivProtocol)/sizeof(oid); + } + else + { + WARNING ("snmp plugin: The `PrivProtocol' config option must be `AES' or `DES'."); + return (-1); + } + + DEBUG ("snmp plugin: host = %s; host->priv_protocol = %s;", + hd->name, hd->priv_protocol == usmAESPrivProtocol ? "AES" : "DES"); + + return (0); +} /* int csnmp_config_add_host_priv_protocol */ + +static int csnmp_config_add_host_security_level (host_definition_t *hd, oconfig_item_t *ci) +{ + char buffer[16]; + int status; + + status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer)); + if (status != 0) + return status; + + if (strcasecmp("noAuthNoPriv", buffer) == 0) + hd->security_level = SNMP_SEC_LEVEL_NOAUTH; + else if (strcasecmp("authNoPriv", buffer) == 0) + hd->security_level = SNMP_SEC_LEVEL_AUTHNOPRIV; + else if (strcasecmp("authPriv", buffer) == 0) + hd->security_level = SNMP_SEC_LEVEL_AUTHPRIV; + else + { + WARNING ("snmp plugin: The `SecurityLevel' config option must be `noAuthNoPriv', `authNoPriv', or `authPriv'."); + return (-1); + } + + DEBUG ("snmp plugin: host = %s; host->security_level = %d;", + hd->name, hd->security_level); + + return (0); +} /* int csnmp_config_add_host_security_level */ + static int csnmp_config_add_host (oconfig_item_t *ci) { host_definition_t *hd; int status = 0; - int i; /* Registration stuff. */ char cb_name[DATA_MAX_NAME_LEN]; - user_data_t cb_data; - struct timespec cb_interval; - - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("snmp plugin: `Host' needs exactly one string argument."); - return (-1); - } - hd = (host_definition_t *) malloc (sizeof (host_definition_t)); + hd = calloc (1, sizeof (*hd)); if (hd == NULL) return (-1); - memset (hd, '\0', sizeof (host_definition_t)); hd->version = 2; C_COMPLAIN_INIT (&hd->complaint); - hd->name = strdup (ci->values[0].value.string); - if (hd->name == NULL) + status = cf_util_get_string(ci, &hd->name); + if (status != 0) { - free (hd); - return (-1); + sfree (hd); + return status; } hd->sess_handle = NULL; hd->interval = 0; - for (i = 0; i < ci->children_num; i++) + for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *option = ci->children + i; status = 0; if (strcasecmp ("Address", option->key) == 0) - status = csnmp_config_add_host_address (hd, option); + status = cf_util_get_string(option, &hd->address); else if (strcasecmp ("Community", option->key) == 0) - status = csnmp_config_add_host_community (hd, option); + status = cf_util_get_string(option, &hd->community); else if (strcasecmp ("Version", option->key) == 0) status = csnmp_config_add_host_version (hd, option); else if (strcasecmp ("Collect", option->key) == 0) csnmp_config_add_host_collect (hd, option); else if (strcasecmp ("Interval", option->key) == 0) cf_util_get_cdtime (option, &hd->interval); + else if (strcasecmp ("Username", option->key) == 0) + status = cf_util_get_string(option, &hd->username); + else if (strcasecmp ("AuthProtocol", option->key) == 0) + status = csnmp_config_add_host_auth_protocol (hd, option); + else if (strcasecmp ("PrivacyProtocol", option->key) == 0) + status = csnmp_config_add_host_priv_protocol (hd, option); + else if (strcasecmp ("AuthPassphrase", option->key) == 0) + status = cf_util_get_string(option, &hd->auth_passphrase); + else if (strcasecmp ("PrivacyPassphrase", option->key) == 0) + status = cf_util_get_string(option, &hd->priv_passphrase); + else if (strcasecmp ("SecurityLevel", option->key) == 0) + status = csnmp_config_add_host_security_level (hd, option); + else if (strcasecmp ("Context", option->key) == 0) + status = cf_util_get_string(option, &hd->context); else { WARNING ("snmp plugin: csnmp_config_add_host: Option `%s' not allowed here.", option->key); @@ -656,12 +698,57 @@ static int csnmp_config_add_host (oconfig_item_t *ci) status = -1; break; } - if (hd->community == NULL) + if (hd->community == NULL && hd->version < 3) { WARNING ("snmp plugin: `Community' not given for host `%s'", hd->name); status = -1; break; } + if (hd->version == 3) + { + if (hd->username == NULL) + { + WARNING ("snmp plugin: `Username' not given for host `%s'", hd->name); + status = -1; + break; + } + if (hd->security_level == 0) + { + WARNING ("snmp plugin: `SecurityLevel' not given for host `%s'", hd->name); + status = -1; + break; + } + if (hd->security_level == SNMP_SEC_LEVEL_AUTHNOPRIV || hd->security_level == SNMP_SEC_LEVEL_AUTHPRIV) + { + if (hd->auth_protocol == NULL) + { + WARNING ("snmp plugin: `AuthProtocol' not given for host `%s'", hd->name); + status = -1; + break; + } + if (hd->auth_passphrase == NULL) + { + WARNING ("snmp plugin: `AuthPassphrase' not given for host `%s'", hd->name); + status = -1; + break; + } + } + if (hd->security_level == SNMP_SEC_LEVEL_AUTHPRIV) + { + if (hd->priv_protocol == NULL) + { + WARNING ("snmp plugin: `PrivacyProtocol' not given for host `%s'", hd->name); + status = -1; + break; + } + if (hd->priv_passphrase == NULL) + { + WARNING ("snmp plugin: `PrivacyPassphrase' not given for host `%s'", hd->name); + status = -1; + break; + } + } + } break; } /* while (status == 0) */ @@ -677,15 +764,11 @@ static int csnmp_config_add_host (oconfig_item_t *ci) ssnprintf (cb_name, sizeof (cb_name), "snmp-%s", hd->name); - memset (&cb_data, 0, sizeof (cb_data)); - cb_data.data = hd; - cb_data.free_func = csnmp_host_definition_destroy; - - CDTIME_T_TO_TIMESPEC (hd->interval, &cb_interval); - status = plugin_register_complex_read (/* group = */ NULL, cb_name, - csnmp_read_host, /* interval = */ &cb_interval, - /* user_data = */ &cb_data); + csnmp_read_host, hd->interval, &(user_data_t) { + .data = hd, + .free_func = csnmp_host_definition_destroy, + }); if (status != 0) { ERROR ("snmp plugin: Registering complex read function failed."); @@ -698,11 +781,9 @@ static int csnmp_config_add_host (oconfig_item_t *ci) static int csnmp_config (oconfig_item_t *ci) { - int i; - call_snmp_init_once (); - for (i = 0; i < ci->children_num; i++) + for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; if (strcasecmp ("Data", child->key) == 0) @@ -723,15 +804,75 @@ static int csnmp_config (oconfig_item_t *ci) static void csnmp_host_open_session (host_definition_t *host) { struct snmp_session sess; + int error; if (host->sess_handle != NULL) csnmp_host_close_session (host); snmp_sess_init (&sess); sess.peername = host->address; - sess.community = (u_char *) host->community; - sess.community_len = strlen (host->community); - sess.version = (host->version == 1) ? SNMP_VERSION_1 : SNMP_VERSION_2c; + switch (host->version) + { + case 1: + sess.version = SNMP_VERSION_1; + break; + case 3: + sess.version = SNMP_VERSION_3; + break; + default: + sess.version = SNMP_VERSION_2c; + break; + } + + if (host->version == 3) + { + sess.securityName = host->username; + sess.securityNameLen = strlen (host->username); + sess.securityLevel = host->security_level; + + if (sess.securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV || sess.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) + { + sess.securityAuthProto = host->auth_protocol; + sess.securityAuthProtoLen = host->auth_protocol_len; + sess.securityAuthKeyLen = USM_AUTH_KU_LEN; + error = generate_Ku (sess.securityAuthProto, + sess.securityAuthProtoLen, + (u_char *) host->auth_passphrase, + strlen(host->auth_passphrase), + sess.securityAuthKey, + &sess.securityAuthKeyLen); + if (error != SNMPERR_SUCCESS) { + ERROR ("snmp plugin: host %s: Error generating Ku from auth_passphrase. (Error %d)", host->name, error); + } + } + + if (sess.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) + { + sess.securityPrivProto = host->priv_protocol; + sess.securityPrivProtoLen = host->priv_protocol_len; + sess.securityPrivKeyLen = USM_PRIV_KU_LEN; + error = generate_Ku (sess.securityAuthProto, + sess.securityAuthProtoLen, + (u_char *) host->priv_passphrase, + strlen(host->priv_passphrase), + sess.securityPrivKey, + &sess.securityPrivKeyLen); + if (error != SNMPERR_SUCCESS) { + ERROR ("snmp plugin: host %s: Error generating Ku from priv_passphrase. (Error %d)", host->name, error); + } + } + + if (host->context != NULL) + { + sess.contextName = host->context; + sess.contextNameLen = strlen (host->context); + } + } + else /* SNMPv1/2 "authenticates" with community string */ + { + sess.community = (u_char *) host->community; + sess.community_len = strlen (host->community); + } /* snmp_sess_open will copy the `struct snmp_session *'. */ host->sess_handle = snmp_sess_open (&sess); @@ -790,9 +931,8 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type, } else { - char oid_buffer[1024]; + char oid_buffer[1024] = { 0 }; - memset (oid_buffer, 0, sizeof (oid_buffer)); snprint_objid (oid_buffer, sizeof (oid_buffer) - 1, vl->name, vl->name_length); @@ -834,7 +974,8 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type, status = parse_value (string, &ret, type); if (status != 0) { - ERROR ("snmp plugin: csnmp_value_list_to_value: Parsing string as %s failed: %s", + ERROR ("snmp plugin: host %s: csnmp_value_list_to_value: Parsing string as %s failed: %s", + (host_name != NULL) ? host_name : "UNKNOWN", DS_TYPE_TO_STRING (type), string); } } @@ -894,50 +1035,67 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type, return (ret); } /* value_t csnmp_value_list_to_value */ +/* csnmp_strvbcopy_hexstring converts the bit string contained in "vb" to a hex + * representation and writes it to dst. Returns zero on success and ENOMEM if + * dst is not large enough to hold the string. dst is guaranteed to be + * nul-terminated. */ static int csnmp_strvbcopy_hexstring (char *dst, /* {{{ */ const struct variable_list *vb, size_t dst_size) { char *buffer_ptr; size_t buffer_free; - size_t i; + + dst[0] = 0; buffer_ptr = dst; buffer_free = dst_size; - for (i = 0; i < vb->val_len; i++) + for (size_t i = 0; i < vb->val_len; i++) { int status; status = snprintf (buffer_ptr, buffer_free, (i == 0) ? "%02x" : ":%02x", (unsigned int) vb->val.bitstring[i]); + assert (status >= 0); - if (status >= buffer_free) + if (((size_t) status) >= buffer_free) /* truncated */ { - buffer_ptr += (buffer_free - 1); - *buffer_ptr = 0; - return (dst_size + (buffer_free - status)); + dst[dst_size - 1] = 0; + return ENOMEM; } else /* if (status < buffer_free) */ { - buffer_ptr += status; - buffer_free -= status; + buffer_ptr += (size_t) status; + buffer_free -= (size_t) status; } } - return ((int) (dst_size - buffer_free)); + return 0; } /* }}} int csnmp_strvbcopy_hexstring */ +/* csnmp_strvbcopy copies the octet string or bit string contained in vb to + * dst. If non-printable characters are detected, it will switch to a hex + * representation of the string. Returns zero on success, EINVAL if vb does not + * contain a string and ENOMEM if dst is not large enough to contain the + * string. */ static int csnmp_strvbcopy (char *dst, /* {{{ */ const struct variable_list *vb, size_t dst_size) { char *src; size_t num_chars; - size_t i; if (vb->type == ASN_OCTET_STR) src = (char *) vb->val.string; else if (vb->type == ASN_BIT_STR) src = (char *) vb->val.bitstring; + else if (vb->type == ASN_IPADDRESS) + { + return ssnprintf (dst, dst_size, "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"", + (uint8_t) vb->val.string[0], + (uint8_t) vb->val.string[1], + (uint8_t) vb->val.string[2], + (uint8_t) vb->val.string[3]); + } else { dst[0] = 0; @@ -948,7 +1106,7 @@ static int csnmp_strvbcopy (char *dst, /* {{{ */ if (num_chars > vb->val_len) num_chars = vb->val_len; - for (i = 0; i < num_chars; i++) + for (size_t i = 0; i < num_chars; i++) { /* Check for control characters. */ if ((unsigned char)src[i] < 32) @@ -956,8 +1114,12 @@ static int csnmp_strvbcopy (char *dst, /* {{{ */ dst[i] = src[i]; } dst[num_chars] = 0; + dst[dst_size - 1] = 0; + + if (dst_size <= vb->val_len) + return ENOMEM; - return ((int) vb->val_len); + return 0; } /* }}} int csnmp_strvbcopy */ static int csnmp_instance_list_add (csnmp_list_instances_t **head, @@ -969,6 +1131,7 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head, struct variable_list *vb; oid_t vb_name; int status; + uint32_t is_matched; /* Set vb on the last variable */ for (vb = res->variables; @@ -980,13 +1143,12 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head, csnmp_oid_init (&vb_name, vb->name, vb->name_length); - il = malloc (sizeof (*il)); + il = calloc (1, sizeof (*il)); if (il == NULL) { - ERROR ("snmp plugin: malloc failed."); + ERROR ("snmp plugin: calloc failed."); return (-1); } - memset (il, 0, sizeof (*il)); il->next = NULL; status = csnmp_oid_suffix (&il->suffix, &vb_name, &dd->instance.oid); @@ -997,12 +1159,34 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head, } /* Get instance name */ - if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR)) + if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR) || (vb->type == ASN_IPADDRESS)) { char *ptr; csnmp_strvbcopy (il->instance, vb, sizeof (il->instance)); - + is_matched = 0; + for (uint32_t i = 0; i < dd->ignores_len; i++) + { + status = fnmatch(dd->ignores[i], il->instance, 0); + if (status == 0) + { + if (dd->invert_match == 0) + { + sfree(il); + return 0; + } + else + { + is_matched = 1; + break; + } + } + } + if (dd->invert_match != 0 && is_matched == 0) + { + sfree(il); + return 0; + } for (ptr = il->instance; *ptr != '\0'; ptr++) { if ((*ptr > 0) && (*ptr < 32)) @@ -1041,7 +1225,7 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat csnmp_list_instances_t *instance_list_ptr; csnmp_table_values_t **value_table_ptr; - int i; + size_t i; _Bool have_more; oid_t current_suffix; @@ -1056,7 +1240,7 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat instance_list_ptr = instance_list; - value_table_ptr = calloc ((size_t) data->values_len, sizeof (*value_table_ptr)); + value_table_ptr = calloc (data->values_len, sizeof (*value_table_ptr)); if (value_table_ptr == NULL) return (-1); for (i = 0; i < data->values_len; i++) @@ -1077,7 +1261,6 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat vl.interval = host->interval; have_more = 1; - memset (¤t_suffix, 0, sizeof (current_suffix)); while (have_more) { _Bool suffix_skipped = 0; @@ -1176,8 +1359,12 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat for (i = 0; i < data->values_len; i++) vl.values[i] = value_table_ptr[i]->value; - /* If we get here `vl.type_instance' and all `vl.values' have been set */ - plugin_dispatch_values (&vl); + /* If we get here `vl.type_instance' and all `vl.values' have been set + * vl.type_instance can be empty, i.e. a blank port description on a + * switch if you're using IF-MIB::ifDescr as Instance. + */ + if (vl.type_instance[0] != '\0') + plugin_dispatch_values (&vl); if (instance_list != NULL) instance_list_ptr = instance_list_ptr->next; @@ -1199,7 +1386,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) const data_set_t *ds; - uint32_t oid_list_len = (uint32_t) (data->values_len + 1); + size_t oid_list_len = data->values_len + 1; /* Holds the last OID returned by the device. We use this in the GETNEXT * request to proceed. */ oid_t oid_list[oid_list_len]; @@ -1208,8 +1395,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) _Bool oid_list_todo[oid_list_len]; int status; - int i; - uint32_t j; + size_t i; /* `value_list_head' and `value_list_tail' implement a linked list for each * value. `instance_list_head' and `instance_list_tail' implement a linked list of @@ -1237,7 +1423,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) if (ds->ds_num != data->values_len) { - ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i", + ERROR ("snmp plugin: DataSet `%s' requires %zu values, but config talks about %zu", data->type, ds->ds_num, data->values_len); return (-1); } @@ -1250,8 +1436,8 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) else /* no InstanceFrom option specified. */ oid_list_len--; - for (j = 0; j < oid_list_len; j++) - oid_list_todo[j] = 1; + for (i = 0; i < oid_list_len; i++) + oid_list_todo[i] = 1; /* We're going to construct n linked lists, one for each "value". * value_list_head will contain pointers to the heads of these linked lists, @@ -1283,13 +1469,13 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) } oid_list_todo_num = 0; - for (j = 0; j < oid_list_len; j++) + for (i = 0; i < oid_list_len; i++) { /* Do not rerequest already finished OIDs */ - if (!oid_list_todo[j]) + if (!oid_list_todo[i]) continue; oid_list_todo_num++; - snmp_add_null_var (req, oid_list[j].oid, oid_list[j].oid_len); + snmp_add_null_var (req, oid_list[i].oid, oid_list[i].oid_len); } if (oid_list_todo_num == 0) @@ -1365,7 +1551,8 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) if (csnmp_instance_list_add (&instance_list_head, &instance_list_tail, res, host, data) != 0) { - ERROR ("snmp plugin: csnmp_instance_list_add failed."); + ERROR ("snmp plugin: host %s: csnmp_instance_list_add failed.", + host->name); status = -1; break; } @@ -1384,7 +1571,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) ret = csnmp_oid_suffix (&suffix, &vb_name, data->values + i); if (ret != 0) { - DEBUG ("snmp plugin: host = %s; data = %s; i = %i; " + DEBUG ("snmp plugin: host = %s; data = %s; i = %zu; " "Value probably left its subtree.", host->name, data->name, i); oid_list_todo[i] = 0; @@ -1396,21 +1583,20 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) if ((value_list_tail[i] != NULL) && (csnmp_oid_compare (&suffix, &value_list_tail[i]->suffix) <= 0)) { - DEBUG ("snmp plugin: host = %s; data = %s; i = %i; " + DEBUG ("snmp plugin: host = %s; data = %s; i = %zu; " "Suffix is not increasing.", host->name, data->name, i); oid_list_todo[i] = 0; continue; } - vt = malloc (sizeof (*vt)); + vt = calloc (1, sizeof (*vt)); if (vt == NULL) { - ERROR ("snmp plugin: malloc failed."); + ERROR ("snmp plugin: calloc failed."); status = -1; break; } - memset (vt, 0, sizeof (*vt)); vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type, data->scale, data->shift, host->name, data->name); @@ -1473,14 +1659,14 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data) static int csnmp_read_value (host_definition_t *host, data_definition_t *data) { struct snmp_pdu *req; - struct snmp_pdu *res; + struct snmp_pdu *res = NULL; struct variable_list *vb; const data_set_t *ds; value_list_t vl = VALUE_LIST_INIT; int status; - int i; + size_t i; DEBUG ("snmp plugin: csnmp_read_value (host = %s, data = %s)", host->name, data->name); @@ -1500,13 +1686,13 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) if (ds->ds_num != data->values_len) { - ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i", + ERROR ("snmp plugin: DataSet `%s' requires %zu values, but config talks about %zu", data->type, ds->ds_num, data->values_len); return (-1); } vl.values_len = ds->ds_num; - vl.values = (value_t *) malloc (sizeof (value_t) * vl.values_len); + vl.values = malloc (sizeof (*vl.values) * vl.values_len); if (vl.values == NULL) return (-1); for (i = 0; i < vl.values_len; i++) @@ -1535,7 +1721,6 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) for (i = 0; i < data->values_len; i++) snmp_add_null_var (req, data->values[i].oid, data->values[i].oid_len); - res = NULL; status = snmp_sess_synch_response (host->sess_handle, req, &res); if ((status != STAT_SUCCESS) || (res == NULL)) @@ -1548,7 +1733,6 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) if (res != NULL) snmp_free_pdu (res); - res = NULL; sfree (errstr); sfree (vl.values); @@ -1574,9 +1758,7 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) data->scale, data->shift, host->name, data->name); } /* for (res->variables) */ - if (res != NULL) - snmp_free_pdu (res); - res = NULL; + snmp_free_pdu (res); DEBUG ("snmp plugin: -> plugin_dispatch_values (&vl);"); plugin_dispatch_values (&vl); @@ -1588,8 +1770,6 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) static int csnmp_read_host (user_data_t *ud) { host_definition_t *host; - cdtime_t time_start; - cdtime_t time_end; int status; int success; int i; @@ -1599,8 +1779,6 @@ static int csnmp_read_host (user_data_t *ud) if (host->interval == 0) host->interval = plugin_get_interval (); - time_start = cdtime (); - if (host->sess_handle == NULL) csnmp_host_open_session (host); @@ -1621,16 +1799,6 @@ static int csnmp_read_host (user_data_t *ud) success++; } - time_end = cdtime (); - if ((time_end - time_start) > host->interval) - { - WARNING ("snmp plugin: Host `%s' should be queried every %.3f " - "seconds, but reading all values takes %.3f seconds.", - host->name, - CDTIME_T_TO_DOUBLE (host->interval), - CDTIME_T_TO_DOUBLE (time_end - time_start)); - } - if (success == 0) return (-1); @@ -1662,6 +1830,7 @@ static int csnmp_shutdown (void) sfree (data_this->name); sfree (data_this->type); sfree (data_this->values); + sfree (data_this->ignores); sfree (data_this); data_this = data_next;