summaryrefslogtreecommitdiffstats
path: root/plugins/check_snmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_snmp.c')
-rw-r--r--plugins/check_snmp.c1765
1 files changed, 788 insertions, 977 deletions
diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c
index c1d8e2dd..f470d222 100644
--- a/plugins/check_snmp.c
+++ b/plugins/check_snmp.c
@@ -32,716 +32,492 @@ const char *progname = "check_snmp";
32const char *copyright = "1999-2024"; 32const char *copyright = "1999-2024";
33const char *email = "devel@monitoring-plugins.org"; 33const char *email = "devel@monitoring-plugins.org";
34 34
35#include "common.h" 35#include "./common.h"
36#include "runcmd.h" 36#include "./runcmd.h"
37#include "utils.h" 37#include "./utils.h"
38#include "utils_cmd.h" 38#include "../lib/states.h"
39 39
40#define DEFAULT_COMMUNITY "public" 40#include "../lib/utils_base.h"
41#define DEFAULT_PORT "161" 41#include "../lib/output.h"
42#define DEFAULT_MIBLIST "ALL" 42#include "check_snmp.d/check_snmp_helpers.h"
43#define DEFAULT_PROTOCOL "1" 43
44#define DEFAULT_RETRIES 5 44#include <strings.h>
45#define DEFAULT_AUTH_PROTOCOL "MD5" 45#include <stdint.h>
46#define DEFAULT_PRIV_PROTOCOL "DES" 46
47#define DEFAULT_DELIMITER "=" 47#include "check_snmp.d/config.h"
48#define DEFAULT_OUTPUT_DELIMITER " " 48#include <stdlib.h>
49#define DEFAULT_BUFFER_SIZE 100 49#include <arpa/inet.h>
50 50#include <net-snmp/library/parse.h>
51#define mark(a) ((a) != 0 ? "*" : "") 51#include <net-snmp/net-snmp-config.h>
52 52#include <net-snmp/net-snmp-includes.h>
53#define CHECK_UNDEF 0 53#include <net-snmp/library/snmp.h>
54#define CRIT_PRESENT 1 54#include <net-snmp/library/keytools.h>
55#define CRIT_STRING 2 55#include <net-snmp/library/snmp_api.h>
56#define CRIT_REGEX 4 56#include <net-snmp/session_api.h>
57#define WARN_PRESENT 8 57#include <net-snmp/definitions.h>
58 58#include <net-snmp/library/asn1.h>
59#define OID_COUNT_STEP 8 59#include <net-snmp/mib_api.h>
60 60#include <net-snmp/library/snmp_impl.h>
61/* Longopts only arguments */ 61#include <string.h>
62#define L_CALCULATE_RATE CHAR_MAX + 1 62#include "../gl/regex.h"
63#define L_RATE_MULTIPLIER CHAR_MAX + 2 63#include "../gl/base64.h"
64#define L_INVERT_SEARCH CHAR_MAX + 3 64#include <assert.h>
65#define L_OFFSET CHAR_MAX + 4 65
66#define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX + 5 66const char DEFAULT_COMMUNITY[] = "public";
67 67const char DEFAULT_MIBLIST[] = "ALL";
68/* Gobble to string - stop incrementing c when c[0] match one of the 68#define DEFAULT_AUTH_PROTOCOL "MD5"
69 * characters in s */ 69
70#define GOBBLE_TOS(c, s) \ 70#ifdef HAVE_USM_DES_PRIV_PROTOCOL
71 while (c[0] != '\0' && strchr(s, c[0]) == NULL) { \ 71# define DEFAULT_PRIV_PROTOCOL "DES"
72 c++; \ 72#else
73# define DEFAULT_PRIV_PROTOCOL "AES"
74#endif
75
76typedef struct proces_arguments_wrapper {
77 int errorcode;
78 check_snmp_config config;
79} process_arguments_wrapper;
80
81static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
82static char *trim_whitespaces_and_check_quoting(char *str);
83static char *get_next_argument(char *str);
84void print_usage(void);
85void print_help(void);
86
87int verbose = 0;
88
89typedef struct {
90 int errorcode;
91 char *state_string;
92} gen_state_string_type;
93gen_state_string_type gen_state_string(check_snmp_state_entry *entries, size_t num_of_entries) {
94 char *encoded_string = NULL;
95 gen_state_string_type result = {.errorcode = OK, .state_string = NULL};
96
97 if (verbose > 1) {
98 printf("%s:\n", __FUNCTION__);
99 for (size_t i = 0; i < num_of_entries; i++) {
100 printf("Entry timestamp %lu: %s", entries[i].timestamp, ctime(&entries[i].timestamp));
101 switch (entries[i].type) {
102 case ASN_GAUGE:
103 printf("Type GAUGE\n");
104 break;
105 case ASN_TIMETICKS:
106 printf("Type TIMETICKS\n");
107 break;
108 case ASN_COUNTER:
109 printf("Type COUNTER\n");
110 break;
111 case ASN_UINTEGER:
112 printf("Type UINTEGER\n");
113 break;
114 case ASN_COUNTER64:
115 printf("Type COUNTER64\n");
116 break;
117 case ASN_FLOAT:
118 printf("Type FLOAT\n");
119 case ASN_DOUBLE:
120 printf("Type DOUBLE\n");
121 break;
122 case ASN_INTEGER:
123 printf("Type INTEGER\n");
124 break;
125 }
126
127 switch (entries[i].type) {
128 case ASN_GAUGE:
129 case ASN_TIMETICKS:
130 case ASN_COUNTER:
131 case ASN_UINTEGER:
132 case ASN_COUNTER64:
133 printf("Value %llu\n", entries[i].value.uIntVal);
134 break;
135 case ASN_FLOAT:
136 case ASN_DOUBLE:
137 printf("Value %f\n", entries[i].value.doubleVal);
138 break;
139 case ASN_INTEGER:
140 printf("Value %lld\n", entries[i].value.intVal);
141 break;
142 }
143 }
144 }
145
146 idx_t encoded = base64_encode_alloc((const char *)entries,
147 (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)),
148 &encoded_string);
149
150 if (encoded > 0 && encoded_string != NULL) {
151 // success
152 if (verbose > 1) {
153 printf("encoded string: %s\n", encoded_string);
154 printf("encoded string length: %lu\n", strlen(encoded_string));
155 }
156 result.state_string = encoded_string;
157 return result;
73 } 158 }
74/* Given c, keep track of backslashes (bk) and double-quotes (dq) 159 result.errorcode = ERROR;
75 * from c[0] */ 160 return result;
76#define COUNT_SEQ(c, bk, dq) \ 161}
77 switch (c[0]) { \ 162
78 case '\\': \ 163typedef struct {
79 if (bk) \ 164 int errorcode;
80 bk--; \ 165 check_snmp_state_entry *state;
81 else \ 166} recover_state_data_type;
82 bk++; \ 167recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) {
83 break; \ 168 recover_state_data_type result = {.errorcode = OK, .state = NULL};
84 case '"': \ 169
85 if (!dq) { \ 170 if (verbose > 1) {
86 dq++; \ 171 printf("%s:\n", __FUNCTION__);
87 } else if (!bk) { \ 172 printf("State string: %s\n", state_string);
88 dq--; \ 173 printf("State string length: %lu\n", state_string_length);
89 } else { \
90 bk--; \
91 } \
92 break; \
93 } 174 }
94 175
95static int process_arguments(int, char **); 176 idx_t outlen = 0;
96static int validate_arguments(void); 177 bool decoded =
97static char *thisarg(char *str); 178 base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen);
98static char *nextarg(char *str); 179
99void print_usage(void); 180 if (!decoded) {
100static void print_help(void); 181 if (verbose) {
101static char *multiply(char *str); 182 printf("Failed to decode state string\n");
102 183 }
103#include "regex.h" 184 // failure to decode
104static char regex_expect[MAX_INPUT_BUFFER] = ""; 185 result.errorcode = ERROR;
105static regex_t preg; 186 return result;
106static regmatch_t pmatch[10]; 187 }
107static char errbuf[MAX_INPUT_BUFFER] = ""; 188
108static char perfstr[MAX_INPUT_BUFFER] = "| "; 189 if (result.state == NULL) {
109static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 190 // Memory Error?
110static int eflags = 0; 191 result.errorcode = ERROR;
111static int errcode, excode; 192 return result;
112 193 }
113static char *server_address = NULL; 194
114static char *community = NULL; 195 if (verbose > 1) {
115static char **contextargs = NULL; 196 printf("Recovered %lu entries of size %lu\n",
116static char *context = NULL; 197 (size_t)outlen / sizeof(check_snmp_state_entry), outlen);
117static char **authpriv = NULL; 198
118static char *proto = NULL; 199 for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) {
119static char *seclevel = NULL; 200 printf("Entry timestamp %lu: %s", result.state[i].timestamp,
120static char *secname = NULL; 201 ctime(&result.state[i].timestamp));
121static char *authproto = NULL; 202 switch (result.state[i].type) {
122static char *privproto = NULL; 203 case ASN_GAUGE:
123static char *authpasswd = NULL; 204 printf("Type GAUGE\n");
124static char *privpasswd = NULL; 205 break;
125static int nulloid = STATE_UNKNOWN; 206 case ASN_TIMETICKS:
126static char **oids = NULL; 207 printf("Type TIMETICKS\n");
127static size_t oids_size = 0; 208 break;
128static char *label; 209 case ASN_COUNTER:
129static char *units; 210 printf("Type COUNTER\n");
130static char *port; 211 break;
131static char *snmpcmd; 212 case ASN_UINTEGER:
132static char string_value[MAX_INPUT_BUFFER] = ""; 213 printf("Type UINTEGER\n");
133static int invert_search = 0; 214 break;
134static char **labels = NULL; 215 case ASN_COUNTER64:
135static char **unitv = NULL; 216 printf("Type COUNTER64\n");
136static size_t nlabels = 0; 217 break;
137static size_t labels_size = OID_COUNT_STEP; 218 case ASN_FLOAT:
138static size_t nunits = 0; 219 printf("Type FLOAT\n");
139static size_t unitv_size = OID_COUNT_STEP; 220 case ASN_DOUBLE:
140static size_t numoids = 0; 221 printf("Type DOUBLE\n");
141static int numauthpriv = 0; 222 break;
142static int numcontext = 0; 223 case ASN_INTEGER:
143static int verbose = 0; 224 printf("Type INTEGER\n");
144static bool usesnmpgetnext = false; 225 break;
145static char *warning_thresholds = NULL; 226 }
146static char *critical_thresholds = NULL; 227
147static thresholds **thlds; 228 switch (result.state[i].type) {
148static size_t thlds_size = OID_COUNT_STEP; 229 case ASN_GAUGE:
149static double *response_value; 230 case ASN_TIMETICKS:
150static size_t response_size = OID_COUNT_STEP; 231 case ASN_COUNTER:
151static int retries = 0; 232 case ASN_UINTEGER:
152static int *eval_method; 233 case ASN_COUNTER64:
153static size_t eval_size = OID_COUNT_STEP; 234 printf("Value %llu\n", result.state[i].value.uIntVal);
154static char *delimiter; 235 break;
155static char *output_delim; 236 case ASN_FLOAT:
156static char *miblist = NULL; 237 case ASN_DOUBLE:
157static bool needmibs = false; 238 printf("Value %f\n", result.state[i].value.doubleVal);
158static int calculate_rate = 0; 239 break;
159static double offset = 0.0; 240 case ASN_INTEGER:
160static int rate_multiplier = 1; 241 printf("Value %lld\n", result.state[i].value.intVal);
161static state_data *previous_state; 242 break;
162static double *previous_value; 243 }
163static size_t previous_size = OID_COUNT_STEP; 244 }
164static int perf_labels = 1; 245 }
165static char *ip_version = ""; 246
166static double multiplier = 1.0; 247 return result;
167static char *fmtstr = "";
168static bool fmtstr_set = false;
169static char buffer[DEFAULT_BUFFER_SIZE];
170static bool ignore_mib_parsing_errors = false;
171
172static char *fix_snmp_range(char *th) {
173 double left;
174 double right;
175 char *colon;
176 char *ret;
177
178 if ((colon = strchr(th, ':')) == NULL || *(colon + 1) == '\0')
179 return th;
180
181 left = strtod(th, NULL);
182 right = strtod(colon + 1, NULL);
183 if (right >= left)
184 return th;
185
186 if ((ret = malloc(strlen(th) + 2)) == NULL)
187 die(STATE_UNKNOWN, _("Cannot malloc"));
188 *colon = '\0';
189 sprintf(ret, "@%s:%s", colon + 1, th);
190 free(th);
191 return ret;
192} 248}
193 249
194int main(int argc, char **argv) { 250int main(int argc, char **argv) {
195 int len;
196 int total_oids;
197 size_t line;
198 unsigned int bk_count = 0;
199 unsigned int dq_count = 0;
200 int iresult = STATE_UNKNOWN;
201 int result = STATE_UNKNOWN;
202 int return_code = 0;
203 int external_error = 0;
204 char **command_line = NULL;
205 char *cl_hidden_auth = NULL;
206 char *oidname = NULL;
207 char *response = NULL;
208 char *mult_resp = NULL;
209 char *outbuff;
210 char *ptr = NULL;
211 char *show = NULL;
212 char *th_warn = NULL;
213 char *th_crit = NULL;
214 char type[8] = "";
215 output chld_out;
216 output chld_err;
217 char *previous_string = NULL;
218 char *ap = NULL;
219 char *state_string = NULL;
220 size_t response_length;
221 size_t current_length;
222 size_t string_length;
223 char *temp_string = NULL;
224 char *quote_string = NULL;
225 time_t current_time;
226 double temp_double;
227 time_t duration;
228 char *conv = "12345678";
229 int is_counter = 0;
230
231 setlocale(LC_ALL, ""); 251 setlocale(LC_ALL, "");
232 bindtextdomain(PACKAGE, LOCALEDIR); 252 bindtextdomain(PACKAGE, LOCALEDIR);
233 textdomain(PACKAGE); 253 textdomain(PACKAGE);
234 254
235 labels = malloc(labels_size * sizeof(*labels));
236 unitv = malloc(unitv_size * sizeof(*unitv));
237 thlds = malloc(thlds_size * sizeof(*thlds));
238 response_value = malloc(response_size * sizeof(*response_value));
239 previous_value = malloc(previous_size * sizeof(*previous_value));
240 eval_method = calloc(eval_size, sizeof(*eval_method));
241 oids = calloc(oids_size, sizeof(char *));
242
243 label = strdup("SNMP");
244 units = strdup("");
245 port = strdup(DEFAULT_PORT);
246 outbuff = strdup("");
247 delimiter = strdup(" = ");
248 output_delim = strdup(DEFAULT_OUTPUT_DELIMITER);
249 timeout_interval = DEFAULT_SOCKET_TIMEOUT; 255 timeout_interval = DEFAULT_SOCKET_TIMEOUT;
250 retries = DEFAULT_RETRIES;
251 256
252 np_init((char *)progname, argc, argv); 257 np_init((char *)progname, argc, argv);
253 258
259 state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv);
260
254 /* Parse extra opts if any */ 261 /* Parse extra opts if any */
255 argv = np_extra_opts(&argc, argv, progname); 262 argv = np_extra_opts(&argc, argv, progname);
256 263
257 np_set_args(argc, argv); 264 np_set_args(argc, argv);
258 265
259 time(&current_time); 266 // Initialize net-snmp before touching the session we are going to use
267 init_snmp("check_snmp");
260 268
261 if (process_arguments(argc, argv) == ERROR) 269 process_arguments_wrapper paw_tmp = process_arguments(argc, argv);
270 if (paw_tmp.errorcode == ERROR) {
262 usage4(_("Could not parse arguments")); 271 usage4(_("Could not parse arguments"));
263
264 if (calculate_rate) {
265 if (!strcmp(label, "SNMP"))
266 label = strdup("SNMP RATE");
267
268 size_t i = 0;
269
270 previous_state = np_state_read();
271 if (previous_state != NULL) {
272 /* Split colon separated values */
273 previous_string = strdup((char *)previous_state->data);
274 while ((ap = strsep(&previous_string, ":")) != NULL) {
275 if (verbose > 2)
276 printf("State for %zd=%s\n", i, ap);
277 while (i >= previous_size) {
278 previous_size += OID_COUNT_STEP;
279 previous_value = realloc(previous_value, previous_size * sizeof(*previous_value));
280 }
281 previous_value[i++] = strtod(ap, NULL);
282 }
283 }
284 } 272 }
285 273
286 /* Populate the thresholds */ 274 check_snmp_config config = paw_tmp.config;
287 th_warn = warning_thresholds;
288 th_crit = critical_thresholds;
289 for (size_t i = 0; i < numoids; i++) {
290 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
291 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
292 /* translate "2:1" to "@1:2" for backwards compatibility */
293 w = w ? fix_snmp_range(w) : NULL;
294 c = c ? fix_snmp_range(c) : NULL;
295
296 while (i >= thlds_size) {
297 thlds_size += OID_COUNT_STEP;
298 thlds = realloc(thlds, thlds_size * sizeof(*thlds));
299 }
300
301 /* Skip empty thresholds, while avoiding segfault */
302 set_thresholds(&thlds[i], w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL, c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL);
303 if (w) {
304 th_warn = strchr(th_warn, ',');
305 if (th_warn)
306 th_warn++;
307 free(w);
308 }
309 if (c) {
310 th_crit = strchr(th_crit, ',');
311 if (th_crit)
312 th_crit++;
313 free(c);
314 }
315 }
316 275
317 /* Create the command array to execute */ 276 if (config.output_format_is_set) {
318 if (usesnmpgetnext) { 277 mp_set_format(config.output_format);
319 snmpcmd = strdup(PATH_TO_SNMPGETNEXT);
320 } else {
321 snmpcmd = strdup(PATH_TO_SNMPGET);
322 } 278 }
323 279
324 /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */ 280 /* Set signal handling and alarm */
325 281 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
326 unsigned index = 0; 282 usage4(_("Cannot catch SIGALRM"));
327 command_line = calloc(11 + numcontext + numauthpriv + 1 + numoids + 1, sizeof(char *));
328
329 command_line[index++] = snmpcmd;
330 command_line[index++] = strdup("-Le");
331 command_line[index++] = strdup("-t");
332 xasprintf(&command_line[index++], "%d", timeout_interval);
333 command_line[index++] = strdup("-r");
334 xasprintf(&command_line[index++], "%d", retries);
335 command_line[index++] = strdup("-m");
336 command_line[index++] = strdup(miblist);
337 command_line[index++] = "-v";
338 command_line[index++] = strdup(proto);
339
340 xasprintf(&cl_hidden_auth, "%s -Le -t %d -r %d -m %s -v %s", snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''",
341 proto);
342
343 if (ignore_mib_parsing_errors) {
344 command_line[index++] = "-Pe";
345 xasprintf(&cl_hidden_auth, "%s -Pe", cl_hidden_auth);
346 } 283 }
347 284
348 for (int i = 0; i < numcontext; i++) { 285 time_t current_time;
349 command_line[index++] = contextargs[i]; 286 time(&current_time);
350 }
351 287
352 for (int i = 0; i < numauthpriv; i++) { 288 if (verbose > 2) {
353 command_line[index++] = authpriv[i]; 289 printf("current time: %s (timestamp: %lu)\n", ctime(&current_time), current_time);
354 } 290 }
355 291
356 xasprintf(&command_line[index++], "%s:%s", server_address, port); 292 snmp_responces response = do_snmp_query(config.snmp_params);
357 293
358 xasprintf(&cl_hidden_auth, "%s [context] [authpriv] %s:%s", cl_hidden_auth, server_address, port); 294 mp_check overall = mp_check_init();
359 295
360 for (size_t i = 0; i < numoids; i++) { 296 if (response.errorcode == OK) {
361 command_line[index++] = oids[i]; 297 mp_subcheck sc_successfull_query = mp_subcheck_init();
362 xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]); 298 xasprintf(&sc_successfull_query.output, "SNMP query was successful");
299 sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK);
300 mp_add_subcheck_to_check(&overall, sc_successfull_query);
301 } else {
302 // Error treatment here, either partial or whole
303 mp_subcheck sc_failed_query = mp_subcheck_init();
304 xasprintf(&sc_failed_query.output, "SNMP query failed");
305 sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK);
306 mp_add_subcheck_to_check(&overall, sc_failed_query);
307 mp_exit(overall);
363 } 308 }
364 309
365 command_line[index++] = NULL; 310 check_snmp_state_entry *prev_state = NULL;
311 bool have_previous_state = false;
366 312
367 if (verbose) { 313 if (config.evaluation_params.calculate_rate) {
368 printf("%s\n", cl_hidden_auth); 314 state_data *previous_state = np_state_read(stateKey);
369 } 315 if (previous_state == NULL) {
316 // failed to recover state
317 // or no previous state
318 have_previous_state = false;
319 } else {
320 // sanity check
321 recover_state_data_type prev_state_wrapper =
322 recover_state_data(previous_state->data, (idx_t)previous_state->length);
370 323
371 /* Set signal handling and alarm */ 324 if (prev_state_wrapper.errorcode == OK) {
372 if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { 325 have_previous_state = true;
373 usage4(_("Cannot catch SIGALRM")); 326 prev_state = prev_state_wrapper.state;
374 } 327 } else {
375 alarm(timeout_interval * retries + 5); 328 have_previous_state = false;
376 329 prev_state = NULL;
377 /* Run the command */
378 return_code = cmd_run_array(command_line, &chld_out, &chld_err, 0);
379
380 /* disable alarm again */
381 alarm(0);
382
383 /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
384 only return state unknown if return code is non zero or there is no stdout.
385 Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
386 */
387 if (return_code != 0)
388 external_error = 1;
389 if (chld_out.lines == 0)
390 external_error = 1;
391 if (external_error) {
392 if (chld_err.lines > 0) {
393 printf(_("External command error: %s\n"), chld_err.line[0]);
394 for (size_t i = 1; i < chld_err.lines; i++) {
395 printf("%s\n", chld_err.line[i]);
396 } 330 }
397 } else {
398 printf(_("External command error with no output (return code: %d)\n"), return_code);
399 } 331 }
400 exit(STATE_UNKNOWN);
401 } 332 }
402 333
403 if (verbose) { 334 check_snmp_state_entry *new_state = NULL;
404 for (size_t i = 0; i < chld_out.lines; i++) { 335 if (config.evaluation_params.calculate_rate) {
405 printf("%s\n", chld_out.line[i]); 336 new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry));
337 if (new_state == NULL) {
338 die(STATE_UNKNOWN, "memory allocation failed");
406 } 339 }
407 } 340 }
408 341
409 line = 0; 342 // We got the the query results, now process them
410 total_oids = 0; 343 for (size_t loop_index = 0; loop_index < config.snmp_params.num_of_test_units; loop_index++) {
411 for (size_t i = 0; line < chld_out.lines && i < numoids; line++, i++, total_oids++) { 344 if (verbose > 0) {
412 if (calculate_rate) 345 printf("loop_index: %zu\n", loop_index);
413 conv = "%.10g";
414 else
415 conv = "%.0f";
416
417 ptr = chld_out.line[line];
418 oidname = strpcpy(oidname, ptr, delimiter);
419 response = strstr(ptr, delimiter);
420 if (response == NULL)
421 break;
422
423 if (verbose > 2) {
424 printf("Processing oid %zi (line %zi)\n oidname: %s\n response: %s\n", i + 1, line + 1, oidname, response);
425 } 346 }
426 347
427 /* Clean up type array - Sol10 does not necessarily zero it out */ 348 check_snmp_state_entry previous_unit_state = {};
428 bzero(type, sizeof(type)); 349 if (config.evaluation_params.calculate_rate && have_previous_state) {
429 350 previous_unit_state = prev_state[loop_index];
430 is_counter = 0; 351 }
431 /* We strip out the datatype indicator for PHBs */
432 if (strstr(response, "Gauge: ")) {
433 show = multiply(strstr(response, "Gauge: ") + 7);
434 } else if (strstr(response, "Gauge32: ")) {
435 show = multiply(strstr(response, "Gauge32: ") + 9);
436 } else if (strstr(response, "Counter32: ")) {
437 show = strstr(response, "Counter32: ") + 11;
438 is_counter = 1;
439 if (!calculate_rate)
440 strcpy(type, "c");
441 } else if (strstr(response, "Counter64: ")) {
442 show = strstr(response, "Counter64: ") + 11;
443 is_counter = 1;
444 if (!calculate_rate)
445 strcpy(type, "c");
446 } else if (strstr(response, "INTEGER: ")) {
447 show = multiply(strstr(response, "INTEGER: ") + 9);
448
449 if (fmtstr_set) {
450 conv = fmtstr;
451 }
452 } else if (strstr(response, "OID: ")) {
453 show = strstr(response, "OID: ") + 5;
454 } else if (strstr(response, "STRING: ")) {
455 show = strstr(response, "STRING: ") + 8;
456 conv = "%.10g";
457
458 /* Get the rest of the string on multi-line strings */
459 ptr = show;
460 COUNT_SEQ(ptr, bk_count, dq_count)
461 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
462 ptr++;
463 GOBBLE_TOS(ptr, "\n\"\\")
464 COUNT_SEQ(ptr, bk_count, dq_count)
465 }
466
467 if (dq_count) { /* unfinished line */
468 /* copy show verbatim first */
469 if (!mult_resp)
470 mult_resp = strdup("");
471 xasprintf(&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
472 /* then strip out unmatched double-quote from single-line output */
473 if (show[0] == '"')
474 show++;
475
476 /* Keep reading until we match end of double-quoted string */
477 for (line++; line < chld_out.lines; line++) {
478 ptr = chld_out.line[line];
479 xasprintf(&mult_resp, "%s%s\n", mult_resp, ptr);
480
481 COUNT_SEQ(ptr, bk_count, dq_count)
482 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
483 ptr++;
484 GOBBLE_TOS(ptr, "\n\"\\")
485 COUNT_SEQ(ptr, bk_count, dq_count)
486 }
487 /* Break for loop before next line increment when done */
488 if (!dq_count)
489 break;
490 }
491 }
492
493 } else if (strstr(response, "Timeticks: ")) {
494 show = strstr(response, "Timeticks: ");
495 } else
496 show = response + 3;
497 352
498 iresult = STATE_DEPENDENT; 353 check_snmp_evaluation single_eval =
354 evaluate_single_unit(response.response_values[loop_index], config.evaluation_params,
355 config.snmp_params.test_units[loop_index], current_time,
356 previous_unit_state, have_previous_state);
499 357
500 /* Process this block for numeric comparisons */ 358 if (config.evaluation_params.calculate_rate &&
501 /* Make some special values,like Timeticks numeric only if a threshold is defined */ 359 mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) {
502 if (thlds[i]->warning || thlds[i]->critical || calculate_rate) { 360 new_state[loop_index] = single_eval.state;
503 if (verbose > 2) {
504 print_thresholds(" thresholds", thlds[i]);
505 }
506 ptr = strpbrk(show, "-0123456789");
507 if (ptr == NULL) {
508 if (nulloid == 3)
509 die(STATE_UNKNOWN, _("No valid data returned (%s)\n"), show);
510 else if (nulloid == 0)
511 die(STATE_OK, _("No valid data returned (%s)\n"), show);
512 else if (nulloid == 1)
513 die(STATE_WARNING, _("No valid data returned (%s)\n"), show);
514 else if (nulloid == 2)
515 die(STATE_CRITICAL, _("No valid data returned (%s)\n"), show);
516 }
517 while (i >= response_size) {
518 response_size += OID_COUNT_STEP;
519 response_value = realloc(response_value, response_size * sizeof(*response_value));
520 }
521 response_value[i] = strtod(ptr, NULL) + offset;
522
523 if (calculate_rate) {
524 if (previous_state != NULL) {
525 duration = current_time - previous_state->time;
526 if (duration <= 0)
527 die(STATE_UNKNOWN, _("Time duration between plugin calls is invalid"));
528 temp_double = response_value[i] - previous_value[i];
529 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
530 if (is_counter) {
531 if (temp_double < (double)0.0)
532 temp_double += (double)4294967296.0; /* 2^32 */
533 if (temp_double < (double)0.0)
534 temp_double += (double)18446744069414584320.0; /* 2^64-2^32 */
535 ;
536 }
537 /* Convert to per second, then use multiplier */
538 temp_double = temp_double / duration * rate_multiplier;
539 iresult = get_status(temp_double, thlds[i]);
540 xasprintf(&show, conv, temp_double);
541 }
542 } else {
543 iresult = get_status(response_value[i], thlds[i]);
544 xasprintf(&show, conv, response_value[i]);
545 }
546 } 361 }
547 362
548 /* Process this block for string matching */ 363 mp_add_subcheck_to_check(&overall, single_eval.sc);
549 else if (eval_size > i && eval_method[i] & CRIT_STRING) { 364 }
550 if (strcmp(show, string_value))
551 iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK;
552 else
553 iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL;
554 }
555 365
556 /* Process this block for regex matching */ 366 if (config.evaluation_params.calculate_rate) {
557 else if (eval_size > i && eval_method[i] & CRIT_REGEX) { 367 // store state
558 excode = regexec(&preg, response, 10, pmatch, eflags); 368 gen_state_string_type current_state_wrapper =
559 if (excode == 0) { 369 gen_state_string(new_state, config.snmp_params.num_of_test_units);
560 iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL;
561 } else if (excode != REG_NOMATCH) {
562 regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER);
563 printf(_("Execute Error: %s\n"), errbuf);
564 exit(STATE_CRITICAL);
565 } else {
566 iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK;
567 }
568 }
569 370
570 /* Process this block for existence-nonexistence checks */ 371 if (current_state_wrapper.errorcode == OK) {
571 /* TV: Should this be outside of this else block? */ 372 np_state_write_string(stateKey, current_time, current_state_wrapper.state_string);
572 else { 373 } else {
573 if (eval_size > i && eval_method[i] & CRIT_PRESENT) 374 die(STATE_UNKNOWN, "failed to create state string");
574 iresult = STATE_CRITICAL;
575 else if (eval_size > i && eval_method[i] & WARN_PRESENT)
576 iresult = STATE_WARNING;
577 else if (response && iresult == STATE_DEPENDENT)
578 iresult = STATE_OK;
579 } 375 }
376 }
377 mp_exit(overall);
378}
580 379
581 /* Result is the worst outcome of all the OIDs tested */ 380/* process command-line arguments */
582 result = max_state(result, iresult); 381static process_arguments_wrapper process_arguments(int argc, char **argv) {
583 382 enum {
584 /* Prepend a label for this OID if there is one */ 383 /* Longopts only arguments */
585 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) 384 invert_search_index = CHAR_MAX + 1,
586 xasprintf(&outbuff, "%s%s%s %s%s%s", outbuff, (i == 0) ? " " : output_delim, labels[i], mark(iresult), show, mark(iresult)); 385 offset_index,
587 else 386 ignore_mib_parsing_errors_index,
588 xasprintf(&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim, mark(iresult), show, mark(iresult)); 387 connection_prefix_index,
589 388 output_format_index,
590 /* Append a unit string for this OID if there is one */ 389 calculate_rate,
591 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL) 390 rate_multiplier
592 xasprintf(&outbuff, "%s %s", outbuff, unitv[i]); 391 };
593 392
594 /* Write perfdata with whatever can be parsed by strtod, if possible */ 393 static struct option longopts[] = {
595 ptr = NULL; 394 STD_LONG_OPTS,
596 strtod(show, &ptr); 395 {"community", required_argument, 0, 'C'},
597 if (ptr > show) { 396 {"oid", required_argument, 0, 'o'},
598 if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) 397 {"object", required_argument, 0, 'o'},
599 temp_string = labels[i]; 398 {"delimiter", required_argument, 0, 'd'},
600 else 399 {"nulloid", required_argument, 0, 'z'},
601 temp_string = oidname; 400 {"output-delimiter", required_argument, 0, 'D'},
602 if (strpbrk(temp_string, " ='\"") == NULL) { 401 {"string", required_argument, 0, 's'},
603 strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); 402 {"timeout", required_argument, 0, 't'},
604 } else { 403 {"regex", required_argument, 0, 'r'},
605 if (strpbrk(temp_string, "'") == NULL) { 404 {"ereg", required_argument, 0, 'r'},
606 quote_string = "'"; 405 {"eregi", required_argument, 0, 'R'},
607 } else { 406 {"label", required_argument, 0, 'l'},
608 quote_string = "\""; 407 {"units", required_argument, 0, 'u'},
609 } 408 {"port", required_argument, 0, 'p'},
610 strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); 409 {"retries", required_argument, 0, 'e'},
611 strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); 410 {"miblist", required_argument, 0, 'm'},
612 strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); 411 {"protocol", required_argument, 0, 'P'},
613 } 412 {"context", required_argument, 0, 'N'},
614 strncat(perfstr, "=", sizeof(perfstr) - strlen(perfstr) - 1); 413 {"seclevel", required_argument, 0, 'L'},
615 len = sizeof(perfstr) - strlen(perfstr) - 1; 414 {"secname", required_argument, 0, 'U'},
616 strncat(perfstr, show, len > ptr - show ? ptr - show : len); 415 {"authproto", required_argument, 0, 'a'},
416 {"privproto", required_argument, 0, 'x'},
417 {"authpasswd", required_argument, 0, 'A'},
418 {"privpasswd", required_argument, 0, 'X'},
419 {"next", no_argument, 0, 'n'},
420 {"offset", required_argument, 0, offset_index},
421 {"invert-search", no_argument, 0, invert_search_index},
422 {"perf-oids", no_argument, 0, 'O'},
423 {"ipv4", no_argument, 0, '4'},
424 {"ipv6", no_argument, 0, '6'},
425 {"multiplier", required_argument, 0, 'M'},
426 {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index},
427 {"connection-prefix", required_argument, 0, connection_prefix_index},
428 {"output-format", required_argument, 0, output_format_index},
429 {"rate", no_argument, 0, calculate_rate},
430 {"rate-multiplier", required_argument, 0, rate_multiplier},
431 {0, 0, 0, 0}};
432
433 if (argc < 2) {
434 process_arguments_wrapper result = {
435 .errorcode = ERROR,
436 };
437 return result;
438 }
617 439
618 if (strcmp(type, "") != 0) { 440 // Count number of OIDs here first
619 strncat(perfstr, type, sizeof(perfstr) - strlen(perfstr) - 1); 441 int option = 0;
620 } 442 size_t oid_counter = 0;
443 while (true) {
444 int option_char = getopt_long(
445 argc, argv,
446 "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option);
621 447
622 if (warning_thresholds) { 448 if (option_char == -1 || option_char == EOF) {
623 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 449 break;
624 if (thlds[i]->warning && thlds[i]->warning->text) 450 }
625 strncat(perfstr, thlds[i]->warning->text, sizeof(perfstr) - strlen(perfstr) - 1);
626 }
627 451
628 if (critical_thresholds) { 452 switch (option_char) {
629 if (!warning_thresholds) 453 case 'o': {
630 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 454 // we are going to parse this again, so we work on a copy of that string
631 strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); 455 char *tmp_oids = strdup(optarg);
632 if (thlds[i]->critical && thlds[i]->critical->text) 456 if (tmp_oids == NULL) {
633 strncat(perfstr, thlds[i]->critical->text, sizeof(perfstr) - strlen(perfstr) - 1); 457 die(STATE_UNKNOWN, "strdup failed");
634 } 458 }
635 459
636 strncat(perfstr, " ", sizeof(perfstr) - strlen(perfstr) - 1); 460 for (char *ptr = strtok(tmp_oids, ", "); ptr != NULL;
637 } 461 ptr = strtok(NULL, ", "), oid_counter++) {
638 }
639
640 /* Save state data, as all data collected now */
641 if (calculate_rate) {
642 string_length = 1024;
643 state_string = malloc(string_length);
644 if (state_string == NULL)
645 die(STATE_UNKNOWN, _("Cannot malloc"));
646
647 current_length = 0;
648 for (int i = 0; i < total_oids; i++) {
649 xasprintf(&temp_string, "%.0f", response_value[i]);
650 if (temp_string == NULL)
651 die(STATE_UNKNOWN, _("Cannot asprintf()"));
652 response_length = strlen(temp_string);
653 if (current_length + response_length > string_length) {
654 string_length = current_length + 1024;
655 state_string = realloc(state_string, string_length);
656 if (state_string == NULL)
657 die(STATE_UNKNOWN, _("Cannot realloc()"));
658 } 462 }
659 strcpy(&state_string[current_length], temp_string); 463 break;
660 current_length = current_length + response_length;
661 state_string[current_length] = ':';
662 current_length++;
663 free(temp_string);
664 } 464 }
665 state_string[--current_length] = '\0'; 465 case '?': /* usage */
666 if (verbose > 2) 466 usage5();
667 printf("State string=%s\n", state_string); 467 // fallthrough
468 case 'h': /* help */
469 print_help();
470 exit(STATE_UNKNOWN);
471 case 'V': /* version */
472 print_revision(progname, NP_VERSION);
473 exit(STATE_UNKNOWN);
668 474
669 /* This is not strictly the same as time now, but any subtle variations will cancel out */ 475 default:
670 np_state_write_string(current_time, state_string); 476 continue;
671 if (previous_state == NULL) {
672 /* Or should this be highest state? */
673 die(STATE_OK, _("No previous data to calculate rate - assume okay"));
674 } 477 }
675 } 478 }
676 479
677 printf("%s %s -%s %s\n", label, state_text(result), outbuff, perfstr); 480 /* Check whether at least one OID was given */
678 if (mult_resp) 481 if (oid_counter == 0) {
679 printf("%s", mult_resp); 482 die(STATE_UNKNOWN, _("No OIDs specified\n"));
483 }
680 484
681 return result; 485 // Allocate space for test units
682} 486 check_snmp_test_unit *tmp = calloc(oid_counter, sizeof(check_snmp_test_unit));
487 if (tmp == NULL) {
488 die(STATE_UNKNOWN, "Failed to calloc");
489 }
683 490
684/* process command-line arguments */ 491 for (size_t i = 0; i < oid_counter; i++) {
685int process_arguments(int argc, char **argv) { 492 tmp[i] = check_snmp_test_unit_init();
686 static struct option longopts[] = {STD_LONG_OPTS,
687 {"community", required_argument, 0, 'C'},
688 {"oid", required_argument, 0, 'o'},
689 {"object", required_argument, 0, 'o'},
690 {"delimiter", required_argument, 0, 'd'},
691 {"nulloid", required_argument, 0, 'z'},
692 {"output-delimiter", required_argument, 0, 'D'},
693 {"string", required_argument, 0, 's'},
694 {"timeout", required_argument, 0, 't'},
695 {"regex", required_argument, 0, 'r'},
696 {"ereg", required_argument, 0, 'r'},
697 {"eregi", required_argument, 0, 'R'},
698 {"label", required_argument, 0, 'l'},
699 {"units", required_argument, 0, 'u'},
700 {"port", required_argument, 0, 'p'},
701 {"retries", required_argument, 0, 'e'},
702 {"miblist", required_argument, 0, 'm'},
703 {"protocol", required_argument, 0, 'P'},
704 {"context", required_argument, 0, 'N'},
705 {"seclevel", required_argument, 0, 'L'},
706 {"secname", required_argument, 0, 'U'},
707 {"authproto", required_argument, 0, 'a'},
708 {"privproto", required_argument, 0, 'x'},
709 {"authpasswd", required_argument, 0, 'A'},
710 {"privpasswd", required_argument, 0, 'X'},
711 {"next", no_argument, 0, 'n'},
712 {"rate", no_argument, 0, L_CALCULATE_RATE},
713 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
714 {"offset", required_argument, 0, L_OFFSET},
715 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
716 {"perf-oids", no_argument, 0, 'O'},
717 {"ipv4", no_argument, 0, '4'},
718 {"ipv6", no_argument, 0, '6'},
719 {"multiplier", required_argument, 0, 'M'},
720 {"fmtstr", required_argument, 0, 'f'},
721 {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS},
722 {0, 0, 0, 0}};
723
724 if (argc < 2)
725 return ERROR;
726
727 /* reverse compatibility for very old non-POSIX usage forms */
728 for (int c = 1; c < argc; c++) {
729 if (strcmp("-to", argv[c]) == 0)
730 strcpy(argv[c], "-t");
731 if (strcmp("-wv", argv[c]) == 0)
732 strcpy(argv[c], "-w");
733 if (strcmp("-cv", argv[c]) == 0)
734 strcpy(argv[c], "-c");
735 } 493 }
736 494
737 size_t j = 0; 495 check_snmp_config config = check_snmp_config_init();
738 size_t jj = 0; 496 config.snmp_params.test_units = tmp;
497 config.snmp_params.num_of_test_units = oid_counter;
498
499 option = 0;
500 optind = 1; // Reset argument scanner
501 size_t tmp_oid_counter = 0;
502 size_t eval_counter = 0;
503 size_t unitv_counter = 0;
504 size_t labels_counter = 0;
505 unsigned char *authpasswd = NULL;
506 unsigned char *privpasswd = NULL;
507 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
508 char *port = NULL;
509 char *miblist = NULL;
510 char *connection_prefix = NULL;
511 bool snmp_version_set_explicitely = false;
512 // TODO error checking
739 while (true) { 513 while (true) {
740 int option = 0; 514 int option_char = getopt_long(
741 int option_char = getopt_long(argc, argv, "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option); 515 argc, argv,
516 "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option);
742 517
743 if (option_char == -1 || option_char == EOF) 518 if (option_char == -1 || option_char == EOF) {
744 break; 519 break;
520 }
745 521
746 switch (option_char) { 522 switch (option_char) {
747 case '?': /* usage */ 523 case '?': /* usage */
@@ -758,64 +534,155 @@ int process_arguments(int argc, char **argv) {
758 534
759 /* Connection info */ 535 /* Connection info */
760 case 'C': /* group or community */ 536 case 'C': /* group or community */
761 community = optarg; 537 config.snmp_params.snmp_session.community = (unsigned char *)optarg;
538 config.snmp_params.snmp_session.community_len = strlen(optarg);
762 break; 539 break;
763 case 'H': /* Host or server */ 540 case 'H': /* Host or server */
764 server_address = optarg; 541 config.snmp_params.snmp_session.peername = optarg;
765 break; 542 break;
766 case 'p': /* TCP port number */ 543 case 'p': /*port number */
544 // Add port to "peername" below to not rely on argument order
767 port = optarg; 545 port = optarg;
768 break; 546 break;
769 case 'm': /* List of MIBS */ 547 case 'm': /* List of MIBS */
770 miblist = optarg; 548 miblist = optarg;
771 break; 549 break;
772 case 'n': /* usesnmpgetnext */ 550 case 'n': /* use_getnext instead of get */
773 usesnmpgetnext = true; 551 config.snmp_params.use_getnext = true;
774 break; 552 break;
775 case 'P': /* SNMP protocol version */ 553 case 'P': /* SNMP protocol version */
776 proto = optarg; 554 if (strcasecmp("1", optarg) == 0) {
555 config.snmp_params.snmp_session.version = SNMP_VERSION_1;
556 } else if (strcasecmp("2c", optarg) == 0) {
557 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
558 } else if (strcasecmp("3", optarg) == 0) {
559 config.snmp_params.snmp_session.version = SNMP_VERSION_3;
560 } else {
561 die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg);
562 }
563 snmp_version_set_explicitely = true;
564
777 break; 565 break;
778 case 'N': /* SNMPv3 context */ 566 case 'N': /* SNMPv3 context name */
779 context = optarg; 567 config.snmp_params.snmp_session.contextName = optarg;
568 config.snmp_params.snmp_session.contextNameLen = strlen(optarg);
780 break; 569 break;
781 case 'L': /* security level */ 570 case 'L': /* security level */
782 seclevel = optarg; 571 if (strcasecmp("noAuthNoPriv", optarg) == 0) {
572 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
573 } else if (strcasecmp("authNoPriv", optarg) == 0) {
574 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
575 } else if (strcasecmp("authPriv", optarg) == 0) {
576 config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
577 } else {
578 die(STATE_UNKNOWN, "invalid security level: %s", optarg);
579 }
783 break; 580 break;
784 case 'U': /* security username */ 581 case 'U': /* security username */
785 secname = optarg; 582 config.snmp_params.snmp_session.securityName = optarg;
583 config.snmp_params.snmp_session.securityNameLen = strlen(optarg);
786 break; 584 break;
787 case 'a': /* auth protocol */ 585 case 'a': /* auth protocol */
788 authproto = optarg; 586 // SNMPv3: SHA or MD5
587 // TODO Test for availability of individual protocols
588 if (strcasecmp("MD5", optarg) == 0) {
589 config.snmp_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol;
590 config.snmp_params.snmp_session.securityAuthProtoLen =
591 OID_LENGTH(usmHMACMD5AuthProtocol);
592 } else if (strcasecmp("SHA", optarg) == 0) {
593 config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol;
594 config.snmp_params.snmp_session.securityAuthProtoLen =
595 OID_LENGTH(usmHMACSHA1AuthProtocol);
596 } else if (strcasecmp("SHA224", optarg) == 0) {
597 config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol;
598 config.snmp_params.snmp_session.securityAuthProtoLen =
599 OID_LENGTH(usmHMAC128SHA224AuthProtocol);
600 } else if (strcasecmp("SHA256", optarg) == 0) {
601 config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol;
602 config.snmp_params.snmp_session.securityAuthProtoLen =
603 OID_LENGTH(usmHMAC192SHA256AuthProtocol);
604 } else if (strcasecmp("SHA384", optarg) == 0) {
605 config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol;
606 config.snmp_params.snmp_session.securityAuthProtoLen =
607 OID_LENGTH(usmHMAC256SHA384AuthProtocol);
608 } else if (strcasecmp("SHA512", optarg) == 0) {
609 config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol;
610 config.snmp_params.snmp_session.securityAuthProtoLen =
611 OID_LENGTH(usmHMAC384SHA512AuthProtocol);
612 } else {
613 die(STATE_UNKNOWN, "Unknown authentication protocol");
614 }
789 break; 615 break;
790 case 'x': /* priv protocol */ 616 case 'x': /* priv protocol */
791 privproto = optarg; 617 if (strcasecmp("DES", optarg) == 0) {
618#ifdef HAVE_USM_DES_PRIV_PROTOCOL
619 config.snmp_params.snmp_session.securityAuthProto = usmDESPrivProtocol;
620 config.snmp_params.snmp_session.securityAuthProtoLen =
621 OID_LENGTH(usmDESPrivProtocol);
622#else
623 die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform");
624#endif
625 } else if (strcasecmp("AES", optarg) == 0) {
626 config.snmp_params.snmp_session.securityAuthProto = usmAESPrivProtocol;
627 config.snmp_params.snmp_session.securityAuthProtoLen =
628 OID_LENGTH(usmAESPrivProtocol);
629 // } else if (strcasecmp("AES128", optarg)) {
630 // config.snmp_session.securityAuthProto = usmAES128PrivProtocol;
631 // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol)
632 // / OID_LENGTH(oid);
633 } else if (strcasecmp("AES192", optarg) == 0) {
634 config.snmp_params.snmp_session.securityAuthProto = usmAES192PrivProtocol;
635 config.snmp_params.snmp_session.securityAuthProtoLen =
636 OID_LENGTH(usmAES192PrivProtocol);
637 } else if (strcasecmp("AES256", optarg) == 0) {
638 config.snmp_params.snmp_session.securityAuthProto = usmAES256PrivProtocol;
639 config.snmp_params.snmp_session.securityAuthProtoLen =
640 OID_LENGTH(usmAES256PrivProtocol);
641 // } else if (strcasecmp("AES192Cisco", optarg)) {
642 // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol;
643 // config.snmp_session.securityAuthProtoLen =
644 // sizeof(usmAES192CiscoPrivProtocol) / sizeof(oid); } else if
645 // (strcasecmp("AES256Cisco", optarg)) { config.snmp_session.securityAuthProto =
646 // usmAES256CiscoPrivProtocol; config.snmp_session.securityAuthProtoLen =
647 // sizeof(usmAES256CiscoPrivProtocol) / sizeof(oid); } else if
648 // (strcasecmp("AES192Cisco2", optarg)) { config.snmp_session.securityAuthProto
649 // = usmAES192Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen =
650 // sizeof(usmAES192Cisco2PrivProtocol) / sizeof(oid); } else if
651 // (strcasecmp("AES256Cisco2", optarg)) { config.snmp_session.securityAuthProto
652 // = usmAES256Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen =
653 // sizeof(usmAES256Cisco2PrivProtocol) / sizeof(oid);
654 } else {
655 die(STATE_UNKNOWN, "Unknown privacy protocol");
656 }
792 break; 657 break;
793 case 'A': /* auth passwd */ 658 case 'A': /* auth passwd */
794 authpasswd = optarg; 659 authpasswd = (unsigned char *)optarg;
795 break; 660 break;
796 case 'X': /* priv passwd */ 661 case 'X': /* priv passwd */
797 privpasswd = optarg; 662 privpasswd = (unsigned char *)optarg;
663 break;
664 case 'e':
665 case 'E':
666 if (!is_integer(optarg)) {
667 usage2(_("Retries interval must be a positive integer"), optarg);
668 } else {
669 config.snmp_params.snmp_session.retries = atoi(optarg);
670 }
798 break; 671 break;
799 case 't': /* timeout period */ 672 case 't': /* timeout period */
800 if (!is_integer(optarg)) 673 if (!is_integer(optarg)) {
801 usage2(_("Timeout interval must be a positive integer"), optarg); 674 usage2(_("Timeout interval must be a positive integer"), optarg);
802 else 675 } else {
803 timeout_interval = atoi(optarg); 676 timeout_interval = (unsigned int)atoi(optarg);
677 }
804 break; 678 break;
805 679
806 /* Test parameters */ 680 /* Test parameters */
807 case 'c': /* critical threshold */ 681 case 'c': /* critical threshold */
808 critical_thresholds = optarg; 682 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true);
809 break; 683 break;
810 case 'w': /* warning threshold */ 684 case 'w': /* warning threshold */
811 warning_thresholds = optarg; 685 check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false);
812 break;
813 case 'e': /* PRELIMINARY - may change */
814 case 'E': /* PRELIMINARY - may change */
815 if (!is_integer(optarg))
816 usage2(_("Retries interval must be a positive integer"), optarg);
817 else
818 retries = atoi(optarg);
819 break; 686 break;
820 case 'o': /* object identifier */ 687 case 'o': /* object identifier */
821 if (strspn(optarg, "0123456789.,") != strlen(optarg)) { 688 if (strspn(optarg, "0123456789.,") != strlen(optarg)) {
@@ -824,306 +691,292 @@ int process_arguments(int argc, char **argv) {
824 * so we have a mib variable, rather than just an SNMP OID, 691 * so we have a mib variable, rather than just an SNMP OID,
825 * so we have to actually read the mib files 692 * so we have to actually read the mib files
826 */ 693 */
827 needmibs = true; 694 config.snmp_params.need_mibs = true;
828 }
829 for (char *ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), j++) {
830 while (j >= oids_size) {
831 oids_size += OID_COUNT_STEP;
832 oids = realloc(oids, oids_size * sizeof(*oids));
833 }
834 oids[j] = strdup(ptr);
835 } 695 }
836 numoids = j; 696
837 if (option_char == 'E' || option_char == 'e') { 697 for (char *ptr = strtok(optarg, ", "); ptr != NULL;
838 jj++; 698 ptr = strtok(NULL, ", "), tmp_oid_counter++) {
839 while (j + 1 >= eval_size) { 699 config.snmp_params.test_units[tmp_oid_counter].oid = strdup(ptr);
840 eval_size += OID_COUNT_STEP;
841 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
842 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
843 }
844 if (option_char == 'E')
845 eval_method[j + 1] |= WARN_PRESENT;
846 else if (option_char == 'e')
847 eval_method[j + 1] |= CRIT_PRESENT;
848 } 700 }
849 break; 701 break;
850 case 'z': /* Null OID Return Check */ 702 case 'z': /* Null OID Return Check */
851 if (!is_integer(optarg)) 703 if (!is_integer(optarg)) {
852 usage2(_("Exit status must be a positive integer"), optarg); 704 usage2(_("Exit status must be a positive integer"), optarg);
853 else 705 } else {
854 nulloid = atoi(optarg); 706 config.evaluation_params.nulloid_result = atoi(optarg);
707 }
855 break; 708 break;
856 case 's': /* string or substring */ 709 case 's': /* string or substring */
857 strncpy(string_value, optarg, sizeof(string_value) - 1); 710 strncpy(config.evaluation_params.string_cmp_value, optarg,
858 string_value[sizeof(string_value) - 1] = 0; 711 sizeof(config.evaluation_params.string_cmp_value) - 1);
859 while (jj >= eval_size) { 712 config.evaluation_params
860 eval_size += OID_COUNT_STEP; 713 .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0;
861 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); 714 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_string = true;
862 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
863 }
864 eval_method[jj++] = CRIT_STRING;
865 break; 715 break;
866 case 'R': /* regex */ 716 case 'R': /* regex */
867 cflags = REG_ICASE; 717 cflags = REG_ICASE;
868 // fall through 718 // fall through
869 case 'r': /* regex */ 719 case 'r': /* regex */
720 {
721 char regex_expect[MAX_INPUT_BUFFER] = "";
870 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; 722 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
871 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); 723 strncpy(regex_expect, optarg, sizeof(regex_expect) - 1);
872 regex_expect[sizeof(regex_expect) - 1] = 0; 724 regex_expect[sizeof(regex_expect) - 1] = 0;
873 errcode = regcomp(&preg, regex_expect, cflags); 725 int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags);
874 if (errcode != 0) { 726 if (errcode != 0) {
875 regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); 727 char errbuf[MAX_INPUT_BUFFER] = "";
876 printf(_("Could Not Compile Regular Expression")); 728 regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf,
877 return ERROR; 729 MAX_INPUT_BUFFER);
878 } 730 printf("Could Not Compile Regular Expression: %s", errbuf);
879 while (jj >= eval_size) { 731 process_arguments_wrapper result = {
880 eval_size += OID_COUNT_STEP; 732 .errorcode = ERROR,
881 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); 733 };
882 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); 734 return result;
883 } 735 }
884 eval_method[jj++] = CRIT_REGEX; 736 config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true;
885 break; 737 } break;
886
887 /* Format */
888 case 'd': /* delimiter */
889 delimiter = strscpy(delimiter, optarg);
890 break;
891 case 'D': /* output-delimiter */
892 output_delim = strscpy(output_delim, optarg);
893 break;
894 case 'l': /* label */ 738 case 'l': /* label */
895 nlabels++; 739 {
896 if (nlabels > labels_size) { 740 if (labels_counter >= config.snmp_params.num_of_test_units) {
897 labels_size += 8; 741 break;
898 labels = realloc(labels, labels_size * sizeof(*labels)); 742 }
899 if (labels == NULL) 743 char *ptr = trim_whitespaces_and_check_quoting(optarg);
900 die(STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels); 744 if (ptr[0] == '\'') {
745 config.snmp_params.test_units[labels_counter].label = ptr + 1;
746 } else {
747 config.snmp_params.test_units[labels_counter].label = ptr;
901 } 748 }
902 labels[nlabels - 1] = optarg; 749
903 char *ptr = thisarg(optarg); 750 while (ptr && (ptr = get_next_argument(ptr))) {
904 labels[nlabels - 1] = ptr; 751 labels_counter++;
905 if (ptr[0] == '\'') 752 ptr = trim_whitespaces_and_check_quoting(ptr);
906 labels[nlabels - 1] = ptr + 1; 753 if (ptr[0] == '\'') {
907 while (ptr && (ptr = nextarg(ptr))) { 754 config.snmp_params.test_units[labels_counter].label = ptr + 1;
908 nlabels++; 755 } else {
909 if (nlabels > labels_size) { 756 config.snmp_params.test_units[labels_counter].label = ptr;
910 labels_size += 8;
911 labels = realloc(labels, labels_size * sizeof(*labels));
912 if (labels == NULL)
913 die(STATE_UNKNOWN, _("Could not reallocate labels\n"));
914 } 757 }
915 ptr = thisarg(ptr);
916 if (ptr[0] == '\'')
917 labels[nlabels - 1] = ptr + 1;
918 else
919 labels[nlabels - 1] = ptr;
920 } 758 }
921 break; 759 labels_counter++;
760 } break;
922 case 'u': /* units */ 761 case 'u': /* units */
923 units = optarg; 762 {
924 nunits++; 763 if (unitv_counter >= config.snmp_params.num_of_test_units) {
925 if (nunits > unitv_size) { 764 break;
926 unitv_size += 8;
927 unitv = realloc(unitv, unitv_size * sizeof(*unitv));
928 if (unitv == NULL)
929 die(STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
930 } 765 }
931 unitv[nunits - 1] = optarg; 766 char *ptr = trim_whitespaces_and_check_quoting(optarg);
932 ptr = thisarg(optarg); 767 if (ptr[0] == '\'') {
933 unitv[nunits - 1] = ptr; 768 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
934 if (ptr[0] == '\'') 769 } else {
935 unitv[nunits - 1] = ptr + 1; 770 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
936 while (ptr && (ptr = nextarg(ptr))) { 771 }
937 if (nunits > unitv_size) { 772 while (ptr && (ptr = get_next_argument(ptr))) {
938 unitv_size += 8; 773 unitv_counter++;
939 unitv = realloc(unitv, unitv_size * sizeof(*unitv)); 774 ptr = trim_whitespaces_and_check_quoting(ptr);
940 if (units == NULL) 775 if (ptr[0] == '\'') {
941 die(STATE_UNKNOWN, _("Could not realloc() units\n")); 776 config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1;
777 } else {
778 config.snmp_params.test_units[unitv_counter].unit_value = ptr;
942 } 779 }
943 nunits++;
944 ptr = thisarg(ptr);
945 if (ptr[0] == '\'')
946 unitv[nunits - 1] = ptr + 1;
947 else
948 unitv[nunits - 1] = ptr;
949 } 780 }
781 unitv_counter++;
782 } break;
783 case offset_index:
784 config.evaluation_params.offset = strtod(optarg, NULL);
785 config.evaluation_params.offset_set = true;
950 break; 786 break;
951 case L_CALCULATE_RATE: 787 case invert_search_index:
952 if (calculate_rate == 0) 788 config.evaluation_params.invert_search = false;
953 np_enable_state(NULL, 1);
954 calculate_rate = 1;
955 break;
956 case L_RATE_MULTIPLIER:
957 if (!is_integer(optarg) || ((rate_multiplier = atoi(optarg)) <= 0))
958 usage2(_("Rate multiplier must be a positive integer"), optarg);
959 break;
960 case L_OFFSET:
961 offset = strtod(optarg, NULL);
962 break;
963 case L_INVERT_SEARCH:
964 invert_search = 1;
965 break; 789 break;
966 case 'O': 790 case 'O':
967 perf_labels = 0; 791 config.evaluation_params.use_oid_as_perf_data_label = true;
968 break; 792 break;
969 case '4': 793 case '4':
794 // The default, do something here to be exclusive to -6 instead of doing nothing?
795 connection_prefix = "udp";
970 break; 796 break;
971 case '6': 797 case '6':
972 xasprintf(&ip_version, "udp6:"); 798 connection_prefix = "udp6";
973 if (verbose > 2) 799 break;
974 printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n"); 800 case connection_prefix_index:
801 connection_prefix = optarg;
975 break; 802 break;
976 case 'M': 803 case 'M':
977 if (strspn(optarg, "0123456789.,") == strlen(optarg)) { 804 if (strspn(optarg, "0123456789.,") == strlen(optarg)) {
978 multiplier = strtod(optarg, NULL); 805 config.evaluation_params.multiplier = strtod(optarg, NULL);
806 config.evaluation_params.multiplier_set = true;
979 } 807 }
980 break; 808 break;
981 case 'f': 809 case ignore_mib_parsing_errors_index:
982 if (multiplier != 1.0) { 810 config.snmp_params.ignore_mib_parsing_errors = true;
983 fmtstr = optarg; 811 break;
984 fmtstr_set = true; 812 case 'f': // Deprecated format option for floating point values
813 break;
814 case output_format_index: {
815 parsed_output_format parser = mp_parse_output_format(optarg);
816 if (!parser.parsing_success) {
817 // TODO List all available formats here, maybe add anothoer usage function
818 printf("Invalid output format: %s\n", optarg);
819 exit(STATE_UNKNOWN);
820 }
821
822 config.output_format_is_set = true;
823 config.output_format = parser.output_format;
824 break;
825 }
826 case calculate_rate:
827 config.evaluation_params.calculate_rate = true;
828 break;
829 case rate_multiplier:
830 if (!is_integer(optarg) ||
831 ((config.evaluation_params.rate_multiplier = (unsigned int)atoi(optarg)) <= 0)) {
832 usage2(_("Rate multiplier must be a positive integer"), optarg);
985 } 833 }
986 break; 834 break;
987 case L_IGNORE_MIB_PARSING_ERRORS: 835 default:
988 ignore_mib_parsing_errors = true; 836 die(STATE_UNKNOWN, "Unknown option");
989 } 837 }
990 } 838 }
991 839
992 if (server_address == NULL) 840 if (config.snmp_params.snmp_session.peername == NULL) {
993 server_address = argv[optind]; 841 config.snmp_params.snmp_session.peername = argv[optind];
994 842 }
995 if (community == NULL)
996 community = strdup(DEFAULT_COMMUNITY);
997
998 return validate_arguments();
999}
1000
1001/******************************************************************************
1002
1003@@-
1004<sect3>
1005<title>validate_arguments</title>
1006
1007<para>&PROTO_validate_arguments;</para>
1008
1009<para>Checks to see if the default miblist needs to be loaded. Also verifies
1010the authentication and authorization combinations based on protocol version
1011selected.</para>
1012
1013<para></para>
1014
1015</sect3>
1016-@@
1017******************************************************************************/
1018 843
1019static int validate_arguments() { 844 // Build true peername here if necessary
1020 /* check whether to load locally installed MIBS (CPU/disk intensive) */ 845 if (connection_prefix != NULL) {
1021 if (miblist == NULL) { 846 // We got something in the connection prefix
1022 if (needmibs) { 847 if (strcasecmp(connection_prefix, "udp") == 0) {
1023 miblist = strdup(DEFAULT_MIBLIST); 848 // The default, do nothing
849 } else if (strcasecmp(connection_prefix, "tcp") == 0) {
850 // use tcp/ipv4
851 xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s",
852 config.snmp_params.snmp_session.peername);
853 } else if (strcasecmp(connection_prefix, "tcp6") == 0 ||
854 strcasecmp(connection_prefix, "tcpv6") == 0 ||
855 strcasecmp(connection_prefix, "tcpipv6") == 0 ||
856 strcasecmp(connection_prefix, "udp6") == 0 ||
857 strcasecmp(connection_prefix, "udpipv6") == 0 ||
858 strcasecmp(connection_prefix, "udpv6") == 0) {
859 // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it
860 // works anyway therefore do nothing here
861 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", connection_prefix,
862 config.snmp_params.snmp_session.peername);
863 } else if (strcmp(connection_prefix, "tls") == 0) {
864 // TODO: Anything else to do here?
865 xasprintf(&config.snmp_params.snmp_session.peername, "tls:%s",
866 config.snmp_params.snmp_session.peername);
867 } else if (strcmp(connection_prefix, "dtls") == 0) {
868 // TODO: Anything else to do here?
869 xasprintf(&config.snmp_params.snmp_session.peername, "dtls:%s",
870 config.snmp_params.snmp_session.peername);
871 } else if (strcmp(connection_prefix, "unix") == 0) {
872 // TODO: Check whether this is a valid path?
873 xasprintf(&config.snmp_params.snmp_session.peername, "unix:%s",
874 config.snmp_params.snmp_session.peername);
875 } else if (strcmp(connection_prefix, "ipx") == 0) {
876 xasprintf(&config.snmp_params.snmp_session.peername, "ipx:%s",
877 config.snmp_params.snmp_session.peername);
1024 } else { 878 } else {
1025 miblist = ""; /* don't read any mib files for numeric oids */ 879 // Don't know that prefix, die here
880 die(STATE_UNKNOWN, "Unknown connection prefix");
1026 } 881 }
1027 } 882 }
1028 883
1029 /* Check server_address is given */ 884 /* Check server_address is given */
1030 if (server_address == NULL) 885 if (config.snmp_params.snmp_session.peername == NULL) {
1031 die(STATE_UNKNOWN, _("No host specified\n")); 886 die(STATE_UNKNOWN, _("No host specified\n"));
887 }
1032 888
1033 /* Check oid is given */ 889 if (port != NULL) {
1034 if (numoids == 0) 890 xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s",
1035 die(STATE_UNKNOWN, _("No OIDs specified\n")); 891 config.snmp_params.snmp_session.peername, port);
892 }
1036 893
1037 if (proto == NULL) 894 /* check whether to load locally installed MIBS (CPU/disk intensive) */
1038 xasprintf(&proto, DEFAULT_PROTOCOL); 895 if (miblist == NULL) {
1039 896 if (config.snmp_params.need_mibs) {
1040 if ((strcmp(proto, "1") == 0) || (strcmp(proto, "2c") == 0)) { /* snmpv1 or snmpv2c */ 897 setenv("MIBLS", DEFAULT_MIBLIST, 1);
1041 numauthpriv = 2; 898 } else {
1042 authpriv = calloc(numauthpriv, sizeof(char *)); 899 setenv("MIBLS", "NONE", 1);
1043 authpriv[0] = strdup("-c"); 900 miblist = ""; /* don't read any mib files for numeric oids */
1044 authpriv[1] = strdup(community);
1045 } else if (strcmp(proto, "3") == 0) { /* snmpv3 args */
1046 if (!(context == NULL)) {
1047 numcontext = 2;
1048 contextargs = calloc(numcontext, sizeof(char *));
1049 contextargs[0] = strdup("-n");
1050 contextargs[1] = strdup(context);
1051 } 901 }
902 } else {
903 // Blatantly stolen from snmplib/snmp_parse_args
904 setenv("MIBS", miblist, 1);
905 }
1052 906
1053 if (seclevel == NULL) 907 // Historical default is SNMP v2c
1054 xasprintf(&seclevel, "noAuthNoPriv"); 908 if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) {
909 config.snmp_params.snmp_session.version = SNMP_VERSION_2c;
910 }
1055 911
1056 if (secname == NULL) 912 if ((config.snmp_params.snmp_session.version == SNMP_VERSION_1) ||
913 (config.snmp_params.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */
914 /*
915 config.numauthpriv = 2;
916 config.authpriv = calloc(config.numauthpriv, sizeof(char *));
917 config.authpriv[0] = strdup("-c");
918 config.authpriv[1] = strdup(community);
919 */
920 } else if (config.snmp_params.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */
921 // generate keys for priv and auth here (if demanded)
922
923 if (config.snmp_params.snmp_session.securityName == NULL) {
1057 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); 924 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
925 }
1058 926
1059 if (strcmp(seclevel, "noAuthNoPriv") == 0) { 927 switch (config.snmp_params.snmp_session.securityLevel) {
1060 numauthpriv = 4; 928 case SNMP_SEC_LEVEL_AUTHPRIV: {
1061 authpriv = calloc(numauthpriv, sizeof(char *)); 929 if (authpasswd == NULL) {
1062 authpriv[0] = strdup("-l"); 930 die(STATE_UNKNOWN,
1063 authpriv[1] = strdup("noAuthNoPriv"); 931 "No authentication passphrase was given, but authorization was requested");
1064 authpriv[2] = strdup("-u");
1065 authpriv[3] = strdup(secname);
1066 } else {
1067 if (!((strcmp(seclevel, "authNoPriv") == 0) || (strcmp(seclevel, "authPriv") == 0))) {
1068 usage2(_("Invalid seclevel"), seclevel);
1069 } 932 }
1070 933 // auth and priv
1071 if (authproto == NULL) 934 int priv_key_generated = generate_Ku(
1072 xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL); 935 config.snmp_params.snmp_session.securityPrivProto,
1073 936 (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd,
1074 if (authpasswd == NULL) 937 strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey,
1075 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd"); 938 &config.snmp_params.snmp_session.securityPrivKeyLen);
1076 939
1077 if (strcmp(seclevel, "authNoPriv") == 0) { 940 if (priv_key_generated != SNMPERR_SUCCESS) {
1078 numauthpriv = 8; 941 die(STATE_UNKNOWN, "Failed to generate privacy key");
1079 authpriv = calloc(numauthpriv, sizeof(char *));
1080 authpriv[0] = strdup("-l");
1081 authpriv[1] = strdup("authNoPriv");
1082 authpriv[2] = strdup("-a");
1083 authpriv[3] = strdup(authproto);
1084 authpriv[4] = strdup("-u");
1085 authpriv[5] = strdup(secname);
1086 authpriv[6] = strdup("-A");
1087 authpriv[7] = strdup(authpasswd);
1088 } else if (strcmp(seclevel, "authPriv") == 0) {
1089 if (privproto == NULL)
1090 xasprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
1091
1092 if (privpasswd == NULL)
1093 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
1094
1095 numauthpriv = 12;
1096 authpriv = calloc(numauthpriv, sizeof(char *));
1097 authpriv[0] = strdup("-l");
1098 authpriv[1] = strdup("authPriv");
1099 authpriv[2] = strdup("-a");
1100 authpriv[3] = strdup(authproto);
1101 authpriv[4] = strdup("-u");
1102 authpriv[5] = strdup(secname);
1103 authpriv[6] = strdup("-A");
1104 authpriv[7] = strdup(authpasswd);
1105 authpriv[8] = strdup("-x");
1106 authpriv[9] = strdup(privproto);
1107 authpriv[10] = strdup("-X");
1108 authpriv[11] = strdup(privpasswd);
1109 } 942 }
1110 } 943 }
1111 944 // fall through
1112 } else { 945 case SNMP_SEC_LEVEL_AUTHNOPRIV: {
1113 usage2(_("Invalid SNMP version"), proto); 946 if (privpasswd == NULL) {
947 die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested");
948 }
949 int auth_key_generated = generate_Ku(
950 config.snmp_params.snmp_session.securityAuthProto,
951 (unsigned int)config.snmp_params.snmp_session.securityAuthProtoLen, privpasswd,
952 strlen((const char *)privpasswd), config.snmp_params.snmp_session.securityAuthKey,
953 &config.snmp_params.snmp_session.securityAuthKeyLen);
954
955 if (auth_key_generated != SNMPERR_SUCCESS) {
956 die(STATE_UNKNOWN, "Failed to generate privacy key");
957 }
958 } break;
959 case SNMP_SEC_LEVEL_NOAUTH:
960 // No auth, no priv, not much todo
961 break;
962 }
1114 } 963 }
1115 964
1116 return OK; 965 process_arguments_wrapper result = {
966 .config = config,
967 .errorcode = OK,
968 };
969 return result;
1117} 970}
1118 971
1119/* trim leading whitespace 972/* trim leading whitespace
1120 if there is a leading quote, make sure it balances */ 973 if there is a leading quote, make sure it balances */
1121 974char *trim_whitespaces_and_check_quoting(char *str) {
1122static char *thisarg(char *str) {
1123 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ 975 str += strspn(str, " \t\r\n"); /* trim any leading whitespace */
1124 if (str[0] == '\'') { /* handle SIMPLE quoted strings */ 976 if (str[0] == '\'') { /* handle SIMPLE quoted strings */
1125 if (strlen(str) == 1 || !strstr(str + 1, "'")) 977 if (strlen(str) == 1 || !strstr(str + 1, "'")) {
1126 die(STATE_UNKNOWN, _("Unbalanced quotes\n")); 978 die(STATE_UNKNOWN, _("Unbalanced quotes\n"));
979 }
1127 } 980 }
1128 return str; 981 return str;
1129} 982}
@@ -1132,23 +985,21 @@ static char *thisarg(char *str) {
1132 set the trailing quote to '\x0' 985 set the trailing quote to '\x0'
1133 if the string continues, advance beyond the comma */ 986 if the string continues, advance beyond the comma */
1134 987
1135static char *nextarg(char *str) { 988char *get_next_argument(char *str) {
1136 if (str[0] == '\'') { 989 if (str[0] == '\'') {
1137 str[0] = 0; 990 str[0] = 0;
1138 if (strlen(str) > 1) { 991 if (strlen(str) > 1) {
1139 str = strstr(str + 1, "'"); 992 str = strstr(str + 1, "'");
1140 return (++str); 993 return (++str);
1141 } else {
1142 return NULL;
1143 } 994 }
995 return NULL;
1144 } 996 }
1145 if (str[0] == ',') { 997 if (str[0] == ',') {
1146 str[0] = 0; 998 str[0] = 0;
1147 if (strlen(str) > 1) { 999 if (strlen(str) > 1) {
1148 return (++str); 1000 return (++str);
1149 } else {
1150 return NULL;
1151 } 1001 }
1002 return NULL;
1152 } 1003 }
1153 if ((str = strstr(str, ",")) && strlen(str) > 1) { 1004 if ((str = strstr(str, ",")) && strlen(str) > 1) {
1154 str[0] = 0; 1005 str[0] = 0;
@@ -1157,41 +1008,7 @@ static char *nextarg(char *str) {
1157 return NULL; 1008 return NULL;
1158} 1009}
1159 1010
1160/* multiply result (values 0 < n < 1 work as divider) */ 1011void print_help(void) {
1161static char *multiply(char *str) {
1162 if (multiplier == 1)
1163 return (str);
1164
1165 if (verbose > 2)
1166 printf(" multiply input: %s\n", str);
1167
1168 char *endptr;
1169 double val = strtod(str, &endptr);
1170 if ((val == 0.0) && (endptr == str)) {
1171 die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, str);
1172 }
1173
1174 if (verbose > 2)
1175 printf(" multiply extracted double: %f\n", val);
1176
1177 val *= multiplier;
1178 char *conv = "%f";
1179 if (fmtstr_set) {
1180 conv = fmtstr;
1181 }
1182 if (val == (int)val) {
1183 snprintf(buffer, DEFAULT_BUFFER_SIZE, "%.0f", val);
1184 } else {
1185 if (verbose > 2)
1186 printf(" multiply using format: %s\n", conv);
1187 snprintf(buffer, DEFAULT_BUFFER_SIZE, conv, val);
1188 }
1189 if (verbose > 2)
1190 printf(" multiply result: %s\n", buffer);
1191 return buffer;
1192}
1193
1194static void print_help(void) {
1195 print_revision(progname, NP_VERSION); 1012 print_revision(progname, NP_VERSION);
1196 1013
1197 printf(COPYRIGHT, copyright, email); 1014 printf(COPYRIGHT, copyright, email);
@@ -1204,8 +1021,6 @@ static void print_help(void) {
1204 1021
1205 printf(UT_HELP_VRSN); 1022 printf(UT_HELP_VRSN);
1206 printf(UT_EXTRA_OPTS); 1023 printf(UT_EXTRA_OPTS);
1207 printf(UT_IPv46);
1208
1209 printf(UT_HOST_PORT, 'p', DEFAULT_PORT); 1024 printf(UT_HOST_PORT, 'p', DEFAULT_PORT);
1210 1025
1211 /* SNMP and Authentication Protocol */ 1026 /* SNMP and Authentication Protocol */
@@ -1217,13 +1032,15 @@ static void print_help(void) {
1217 printf(" %s\n", _("SNMPv3 context")); 1032 printf(" %s\n", _("SNMPv3 context"));
1218 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); 1033 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1219 printf(" %s\n", _("SNMPv3 securityLevel")); 1034 printf(" %s\n", _("SNMPv3 securityLevel"));
1220 printf(" %s\n", "-a, --authproto=AUTHENTICATION_PROTOCOL"); 1035 printf(" %s\n", "-a, --authproto=[MD5|SHA]");
1221 printf(" %s\n", 1036 printf(" %s\n", _("SNMPv3 auth proto"));
1222 _("SNMPv3 authentication protocol (default MD5), available options depend on the specific version of the net-snmp tools")); 1037#ifdef HAVE_USM_DES_PRIV_PROTOCOL
1223 printf(" %s\n", _("if < 5.8 SHA (1) and MD5 should be available, if >= 5.8 additionally SHA-224, SHA-256, SHA-384 and SHA-512")); 1038 printf(" %s\n", "-x, --privproto=[DES|AES]");
1224 printf(" %s\n", "-x, --privproto=PRIVACY_PROTOCOL"); 1039 printf(" %s\n", _("SNMPv3 priv proto (default DES)"));
1225 printf(" %s\n", _("SNMPv3 privacy protocol (default DES), available options depend on the specific version of the net-snmp tools")); 1040#else
1226 printf(" %s\n", _("if < 5.8 DES and AES should be available, if >= 5.8 additionally AES-192 and AES-256")); 1041 printf(" %s\n", "-x, --privproto=[AES]");
1042 printf(" %s\n", _("SNMPv3 priv proto (default AES)"));
1043#endif
1227 1044
1228 /* Authentication Tokens*/ 1045 /* Authentication Tokens*/
1229 printf(" %s\n", "-C, --community=STRING"); 1046 printf(" %s\n", "-C, --community=STRING");
@@ -1235,15 +1052,18 @@ static void print_help(void) {
1235 printf(" %s\n", _("SNMPv3 authentication password")); 1052 printf(" %s\n", _("SNMPv3 authentication password"));
1236 printf(" %s\n", "-X, --privpasswd=PASSWORD"); 1053 printf(" %s\n", "-X, --privpasswd=PASSWORD");
1237 printf(" %s\n", _("SNMPv3 privacy password")); 1054 printf(" %s\n", _("SNMPv3 privacy password"));
1055 printf(" %s\n", "--connection-prefix");
1056 printf(" Connection prefix, may be one of udp, udp6, tcp, unix, ipx, udp6, udpv6, udpipv6, "
1057 "tcp6, tcpv6, tcpipv6, tls, dtls - "
1058 "default is \"udp\"\n");
1238 1059
1239 /* OID Stuff */ 1060 /* OID Stuff */
1240 printf(" %s\n", "-o, --oid=OID(s)"); 1061 printf(" %s\n", "-o, --oid=OID(s)");
1241 printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query")); 1062 printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1242 printf(" %s\n", "-m, --miblist=STRING"); 1063 printf(" %s\n", "-m, --miblist=STRING");
1243 printf(" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); 1064 printf(" %s\n",
1065 _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1244 printf(" %s\n", _("for symbolic OIDs.)")); 1066 printf(" %s\n", _("for symbolic OIDs.)"));
1245 printf(" %s\n", "-d, --delimiter=STRING");
1246 printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1247 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered")); 1067 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1248 printf(" %s\n", _("to be the data that should be used in the evaluation.")); 1068 printf(" %s\n", _("to be the data that should be used in the evaluation."));
1249 printf(" %s\n", "-z, --nulloid=#"); 1069 printf(" %s\n", "-z, --nulloid=#");
@@ -1260,10 +1080,6 @@ static void print_help(void) {
1260 printf(" %s\n", _("Warning threshold range(s)")); 1080 printf(" %s\n", _("Warning threshold range(s)"));
1261 printf(" %s\n", "-c, --critical=THRESHOLD(s)"); 1081 printf(" %s\n", "-c, --critical=THRESHOLD(s)");
1262 printf(" %s\n", _("Critical threshold range(s)")); 1082 printf(" %s\n", _("Critical threshold range(s)"));
1263 printf(" %s\n", "--rate");
1264 printf(" %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1265 printf(" %s\n", "--rate-multiplier");
1266 printf(" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1267 printf(" %s\n", "--offset=OFFSET"); 1083 printf(" %s\n", "--offset=OFFSET");
1268 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data")); 1084 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data"));
1269 1085
@@ -1271,9 +1087,11 @@ static void print_help(void) {
1271 printf(" %s\n", "-s, --string=STRING"); 1087 printf(" %s\n", "-s, --string=STRING");
1272 printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match")); 1088 printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1273 printf(" %s\n", "-r, --ereg=REGEX"); 1089 printf(" %s\n", "-r, --ereg=REGEX");
1274 printf(" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches")); 1090 printf(" %s\n",
1091 _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1275 printf(" %s\n", "-R, --eregi=REGEX"); 1092 printf(" %s\n", "-R, --eregi=REGEX");
1276 printf(" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); 1093 printf(" %s\n",
1094 _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1277 printf(" %s\n", "--invert-search"); 1095 printf(" %s\n", "--invert-search");
1278 printf(" %s\n", _("Invert search result (CRITICAL if found)")); 1096 printf(" %s\n", _("Invert search result (CRITICAL if found)"));
1279 1097
@@ -1282,53 +1100,46 @@ static void print_help(void) {
1282 printf(" %s\n", _("Prefix label for output from plugin")); 1100 printf(" %s\n", _("Prefix label for output from plugin"));
1283 printf(" %s\n", "-u, --units=STRING"); 1101 printf(" %s\n", "-u, --units=STRING");
1284 printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.').")); 1102 printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1285 printf(" %s\n", "-D, --output-delimiter=STRING");
1286 printf(" %s\n", _("Separates output on multiple OID requests"));
1287 printf(" %s\n", "-M, --multiplier=FLOAT"); 1103 printf(" %s\n", "-M, --multiplier=FLOAT");
1288 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1")); 1104 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1"));
1289 printf(" %s\n", "-f, --fmtstr=STRING"); 1105 printf(UT_OUTPUT_FORMAT);
1290 printf(" %s\n", _("C-style format string for float values (see option -M)"));
1291 1106
1292 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1107 printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1293 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: timeout_interval * retries + 5")); 1108 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: "
1109 "timeout_interval * retries + 5"));
1294 printf(" %s\n", "-e, --retries=INTEGER"); 1110 printf(" %s\n", "-e, --retries=INTEGER");
1295 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), DEFAULT_RETRIES); 1111 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "),
1112 DEFAULT_RETRIES);
1296 1113
1297 printf(" %s\n", "-O, --perf-oids"); 1114 printf(" %s\n", "-O, --perf-oids");
1298 printf(" %s\n", _("Label performance data with OIDs instead of --label's")); 1115 printf(" %s\n", _("Label performance data with OIDs instead of --label's"));
1299 1116
1300 printf(" %s\n", "--ignore-mib-parsing-errors"); 1117 printf(" %s\n", "--ignore-mib-parsing-errors");
1301 printf(" %s\n", _("Tell snmpget to not print errors encountered when parsing MIB files")); 1118 printf(" %s\n", _("Do to not print errors encountered when parsing MIB files"));
1302 1119
1303 printf(UT_VERBOSE); 1120 printf(UT_VERBOSE);
1304 1121
1305 printf("\n"); 1122 printf("\n");
1306 printf("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package.")); 1123 printf("%s\n", _("This plugin relies (links against) on the NET-SNMP libraries."));
1307 printf("%s\n", _("if you don't have the package installed, you will need to download it from")); 1124 printf("%s\n",
1125 _("if you don't have the libraries installed, you will need to download them from"));
1308 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin.")); 1126 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1309 1127
1310 printf("\n"); 1128 printf("\n");
1311 printf("%s\n", _("Notes:")); 1129 printf("%s\n", _("Notes:"));
1312 printf(" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited ")); 1130 printf(" %s\n",
1131 _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1313 printf(" %s\n", _("list (lists with internal spaces must be quoted).")); 1132 printf(" %s\n", _("list (lists with internal spaces must be quoted)."));
1314 1133
1315 printf(" -%s", UT_THRESHOLDS_NOTES); 1134 printf(" -%s", UT_THRESHOLDS_NOTES);
1316 1135
1317 printf(" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); 1136 printf(" %s\n",
1137 _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1318 printf(" %s\n", _("- Note that only one string and one regex may be checked at present")); 1138 printf(" %s\n", _("- Note that only one string and one regex may be checked at present"));
1319 printf(" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value")); 1139 printf(" %s\n",
1140 _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1320 printf(" %s\n", _("returned from the SNMP query is an unsigned integer.")); 1141 printf(" %s\n", _("returned from the SNMP query is an unsigned integer."));
1321 1142
1322 printf("\n");
1323 printf("%s\n", _("Rate Calculation:"));
1324 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1325 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1326 printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1327 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1328 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1329 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1330 printf(" %s\n", _("changing the arguments will create a new state file."));
1331
1332 printf(UT_SUPPORT); 1143 printf(UT_SUPPORT);
1333} 1144}
1334 1145
@@ -1339,5 +1150,5 @@ void print_usage(void) {
1339 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n"); 1150 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1340 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n"); 1151 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n");
1341 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n"); 1152 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n");
1342 printf("[-M multiplier [-f format]]\n"); 1153 printf("[-M multiplier]\n");
1343} 1154}