1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "checks_snmp.h"
21
22 #ifdef HAVE_NETSNMP
23
24 #define SNMP_NO_DEBUGGING /* disabling debugging messages from Net-SNMP library */
25 #include <net-snmp/net-snmp-config.h>
26 #include <net-snmp/net-snmp-includes.h>
27
28 #include "comms.h"
29 #include "zbxalgo.h"
30 #include "zbxjson.h"
31
32 /*
33 * SNMP Dynamic Index Cache
34 * ========================
35 *
36 * Description
37 * -----------
38 *
39 * Zabbix caches the whole index table for the particular OID separately based on:
40 * * IP address;
41 * * port;
42 * * community string (SNMPv2c);
43 * * context, security name (SNMPv3).
44 *
45 * Zabbix revalidates each index before using it to get a value and rebuilds the index cache for the OID if the
46 * index is invalid.
47 *
48 * Example
49 * -------
50 *
51 * OID for getting memory usage of process by PID (index):
52 * HOST-RESOURCES-MIB::hrSWRunPerfMem:<PID>
53 *
54 * OID for getting PID (index) by process name (value):
55 * HOST-RESOURCES-MIB::hrSWRunPath:<PID> <NAME>
56 *
57 * SNMP OID as configured in Zabbix to get memory usage of "snmpd" process:
58 * HOST-RESOURCES-MIB::hrSWRunPerfMem["index","HOST-RESOURCES-MIB::hrSWRunPath","snmpd"]
59 *
60 * 1. Zabbix walks hrSWRunPath table and caches all <PID> and <NAME> pairs of particular SNMP agent/user.
61 * 2. Before each GET request Zabbix revalidates the cached <PID> by getting its <NAME> from hrSWRunPath table.
62 * 3. If the names match then Zabbix uses the cached <PID> in the GET request for the hrSWRunPerfMem.
63 * Otherwise Zabbix rebuilds the hrSWRunPath cache for the particular agent/user (see 1.).
64 *
65 * Implementation
66 * --------------
67 *
68 * The cache is implemented using hash tables. In ERD:
69 * zbx_snmpidx_main_key_t -------------------------------------------0< zbx_snmpidx_mapping_t
70 * (OID, host, <v2c: community|v3: (context, security name)>) (index, value)
71 */
72
73 /******************************************************************************
74 * *
75 * This is zbx_snmp_walk() callback function prototype. *
76 * *
77 * Parameters: arg - [IN] an user argument passed to zbx_snmp_walk() *
78 * function *
79 * snmp_oid - [IN] the OID the walk function is looking for *
80 * index - [IN] the index of found OID *
81 * value - [IN] the OID value *
82 * *
83 ******************************************************************************/
84 typedef void (zbx_snmp_walk_cb_func)(void *arg, const char *snmp_oid, const char *index, const char *value);
85
86 typedef struct
87 {
88 char *addr;
89 unsigned short port;
90 char *oid;
91 char *community_context; /* community (SNMPv1 or v2c) or contextName (SNMPv3) */
92 char *security_name; /* only SNMPv3, empty string in case of other versions */
93 zbx_hashset_t *mappings;
94 }
95 zbx_snmpidx_main_key_t;
96
97 typedef struct
98 {
99 char *value;
100 char *index;
101 }
102 zbx_snmpidx_mapping_t;
103
104 static zbx_hashset_t snmpidx; /* Dynamic Index Cache */
105 static char zbx_snmp_init_done;
106
__snmpidx_main_key_hash(const void * data)107 static zbx_hash_t __snmpidx_main_key_hash(const void *data)
108 {
109 const zbx_snmpidx_main_key_t *main_key = (const zbx_snmpidx_main_key_t *)data;
110
111 zbx_hash_t hash;
112
113 hash = ZBX_DEFAULT_STRING_HASH_FUNC(main_key->addr);
114 hash = ZBX_DEFAULT_STRING_HASH_ALGO(&main_key->port, sizeof(main_key->port), hash);
115 hash = ZBX_DEFAULT_STRING_HASH_ALGO(main_key->oid, strlen(main_key->oid), hash);
116 hash = ZBX_DEFAULT_STRING_HASH_ALGO(main_key->community_context, strlen(main_key->community_context), hash);
117 hash = ZBX_DEFAULT_STRING_HASH_ALGO(main_key->security_name, strlen(main_key->security_name), hash);
118
119 return hash;
120 }
121
__snmpidx_main_key_compare(const void * d1,const void * d2)122 static int __snmpidx_main_key_compare(const void *d1, const void *d2)
123 {
124 const zbx_snmpidx_main_key_t *main_key1 = (const zbx_snmpidx_main_key_t *)d1;
125 const zbx_snmpidx_main_key_t *main_key2 = (const zbx_snmpidx_main_key_t *)d2;
126
127 int ret;
128
129 if (0 != (ret = strcmp(main_key1->addr, main_key2->addr)))
130 return ret;
131
132 ZBX_RETURN_IF_NOT_EQUAL(main_key1->port, main_key2->port);
133
134 if (0 != (ret = strcmp(main_key1->community_context, main_key2->community_context)))
135 return ret;
136
137 if (0 != (ret = strcmp(main_key1->security_name, main_key2->security_name)))
138 return ret;
139
140 return strcmp(main_key1->oid, main_key2->oid);
141 }
142
__snmpidx_main_key_clean(void * data)143 static void __snmpidx_main_key_clean(void *data)
144 {
145 zbx_snmpidx_main_key_t *main_key = (zbx_snmpidx_main_key_t *)data;
146
147 zbx_free(main_key->addr);
148 zbx_free(main_key->oid);
149 zbx_free(main_key->community_context);
150 zbx_free(main_key->security_name);
151 zbx_hashset_destroy(main_key->mappings);
152 zbx_free(main_key->mappings);
153 }
154
__snmpidx_mapping_hash(const void * data)155 static zbx_hash_t __snmpidx_mapping_hash(const void *data)
156 {
157 const zbx_snmpidx_mapping_t *mapping = (const zbx_snmpidx_mapping_t *)data;
158
159 return ZBX_DEFAULT_STRING_HASH_FUNC(mapping->value);
160 }
161
__snmpidx_mapping_compare(const void * d1,const void * d2)162 static int __snmpidx_mapping_compare(const void *d1, const void *d2)
163 {
164 const zbx_snmpidx_mapping_t *mapping1 = (const zbx_snmpidx_mapping_t *)d1;
165 const zbx_snmpidx_mapping_t *mapping2 = (const zbx_snmpidx_mapping_t *)d2;
166
167 return strcmp(mapping1->value, mapping2->value);
168 }
169
__snmpidx_mapping_clean(void * data)170 static void __snmpidx_mapping_clean(void *data)
171 {
172 zbx_snmpidx_mapping_t *mapping = (zbx_snmpidx_mapping_t *)data;
173
174 zbx_free(mapping->value);
175 zbx_free(mapping->index);
176 }
177
get_item_community_context(const DC_ITEM * item)178 static char *get_item_community_context(const DC_ITEM *item)
179 {
180 if (ZBX_IF_SNMP_VERSION_1 == item->snmp_version || ZBX_IF_SNMP_VERSION_2 == item->snmp_version)
181 return item->snmp_community;
182 else if (ZBX_IF_SNMP_VERSION_3 == item->snmp_version)
183 return item->snmpv3_contextname;
184
185 THIS_SHOULD_NEVER_HAPPEN;
186 exit(EXIT_FAILURE);
187 }
188
get_item_security_name(const DC_ITEM * item)189 static char *get_item_security_name(const DC_ITEM *item)
190 {
191 if (ZBX_IF_SNMP_VERSION_3 == item->snmp_version)
192 return item->snmpv3_securityname;
193
194 return "";
195 }
196
197 /******************************************************************************
198 * *
199 * Function: cache_get_snmp_index *
200 * *
201 * Purpose: retrieve index that matches value from the relevant index cache *
202 * *
203 * Parameters: item - [IN] configuration of Zabbix item, contains *
204 * IP address, port, community string, context, *
205 * security name *
206 * snmp_oid - [IN] OID of the table which contains the indexes *
207 * value - [IN] value for which to look up the index *
208 * idx - [IN/OUT] destination pointer for the *
209 * heap-(re)allocated index *
210 * idx_alloc - [IN/OUT] size of the (re)allocated index *
211 * *
212 * Return value: FAIL - dynamic index cache is empty or cache does not *
213 * contain index matching the value *
214 * SUCCEED - idx contains the found index, *
215 * idx_alloc contains the current size of the *
216 * heap-(re)allocated idx *
217 * *
218 ******************************************************************************/
cache_get_snmp_index(const DC_ITEM * item,const char * snmp_oid,const char * value,char ** idx,size_t * idx_alloc)219 static int cache_get_snmp_index(const DC_ITEM *item, const char *snmp_oid, const char *value, char **idx, size_t *idx_alloc)
220 {
221 int ret = FAIL;
222 zbx_snmpidx_main_key_t *main_key, main_key_local;
223 zbx_snmpidx_mapping_t *mapping;
224 size_t idx_offset = 0;
225
226 zabbix_log(LOG_LEVEL_DEBUG, "In %s() OID:'%s' value:'%s'", __func__, snmp_oid, value);
227
228 if (NULL == snmpidx.slots)
229 goto end;
230
231 main_key_local.addr = item->interface.addr;
232 main_key_local.port = item->interface.port;
233 main_key_local.oid = (char *)snmp_oid;
234
235 main_key_local.community_context = get_item_community_context(item);
236 main_key_local.security_name = get_item_security_name(item);
237
238 if (NULL == (main_key = (zbx_snmpidx_main_key_t *)zbx_hashset_search(&snmpidx, &main_key_local)))
239 goto end;
240
241 if (NULL == (mapping = (zbx_snmpidx_mapping_t *)zbx_hashset_search(main_key->mappings, &value)))
242 goto end;
243
244 zbx_strcpy_alloc(idx, idx_alloc, &idx_offset, mapping->index);
245 ret = SUCCEED;
246 end:
247 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s idx:'%s'", __func__, zbx_result_string(ret),
248 SUCCEED == ret ? *idx : "");
249
250 return ret;
251 }
252
253 /******************************************************************************
254 * *
255 * Function: cache_put_snmp_index *
256 * *
257 * Purpose: store the index-value pair in the relevant index cache *
258 * *
259 * Parameters: item - [IN] configuration of Zabbix item, contains *
260 * IP address, port, community string, context, *
261 * security name *
262 * snmp_oid - [IN] OID of the table which contains the indexes *
263 * index - [IN] index part of the index-value pair *
264 * value - [IN] value part of the index-value pair *
265 * *
266 ******************************************************************************/
cache_put_snmp_index(const DC_ITEM * item,const char * snmp_oid,const char * index,const char * value)267 static void cache_put_snmp_index(const DC_ITEM *item, const char *snmp_oid, const char *index, const char *value)
268 {
269 zbx_snmpidx_main_key_t *main_key, main_key_local;
270 zbx_snmpidx_mapping_t *mapping, mapping_local;
271
272 zabbix_log(LOG_LEVEL_DEBUG, "In %s() OID:'%s' index:'%s' value:'%s'", __func__, snmp_oid, index, value);
273
274 if (NULL == snmpidx.slots)
275 {
276 zbx_hashset_create_ext(&snmpidx, 100,
277 __snmpidx_main_key_hash, __snmpidx_main_key_compare, __snmpidx_main_key_clean,
278 ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC);
279 }
280
281 main_key_local.addr = item->interface.addr;
282 main_key_local.port = item->interface.port;
283 main_key_local.oid = (char *)snmp_oid;
284
285 main_key_local.community_context = get_item_community_context(item);
286 main_key_local.security_name = get_item_security_name(item);
287
288 if (NULL == (main_key = (zbx_snmpidx_main_key_t *)zbx_hashset_search(&snmpidx, &main_key_local)))
289 {
290 main_key_local.addr = zbx_strdup(NULL, item->interface.addr);
291 main_key_local.oid = zbx_strdup(NULL, snmp_oid);
292
293 main_key_local.community_context = zbx_strdup(NULL, get_item_community_context(item));
294 main_key_local.security_name = zbx_strdup(NULL, get_item_security_name(item));
295
296 main_key_local.mappings = (zbx_hashset_t *)zbx_malloc(NULL, sizeof(zbx_hashset_t));
297 zbx_hashset_create_ext(main_key_local.mappings, 100,
298 __snmpidx_mapping_hash, __snmpidx_mapping_compare, __snmpidx_mapping_clean,
299 ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC);
300
301 main_key = (zbx_snmpidx_main_key_t *)zbx_hashset_insert(&snmpidx, &main_key_local, sizeof(main_key_local));
302 }
303
304 if (NULL == (mapping = (zbx_snmpidx_mapping_t *)zbx_hashset_search(main_key->mappings, &value)))
305 {
306 mapping_local.value = zbx_strdup(NULL, value);
307 mapping_local.index = zbx_strdup(NULL, index);
308
309 zbx_hashset_insert(main_key->mappings, &mapping_local, sizeof(mapping_local));
310 }
311 else if (0 != strcmp(mapping->index, index))
312 {
313 zbx_free(mapping->index);
314 mapping->index = zbx_strdup(NULL, index);
315 }
316
317 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
318 }
319
320 /******************************************************************************
321 * *
322 * Function: cache_del_snmp_index_subtree *
323 * *
324 * Purpose: delete index-value mappings from the specified index cache *
325 * *
326 * Parameters: item - [IN] configuration of Zabbix item, contains *
327 * IP address, port, community string, context, *
328 * security name *
329 * snmp_oid - [IN] OID of the table which contains the indexes *
330 * *
331 * Comments: does nothing if the index cache is empty or if it does not *
332 * contain the cache for the specified OID *
333 * *
334 ******************************************************************************/
cache_del_snmp_index_subtree(const DC_ITEM * item,const char * snmp_oid)335 static void cache_del_snmp_index_subtree(const DC_ITEM *item, const char *snmp_oid)
336 {
337 zbx_snmpidx_main_key_t *main_key, main_key_local;
338
339 zabbix_log(LOG_LEVEL_DEBUG, "In %s() OID:'%s'", __func__, snmp_oid);
340
341 if (NULL == snmpidx.slots)
342 goto end;
343
344 main_key_local.addr = item->interface.addr;
345 main_key_local.port = item->interface.port;
346 main_key_local.oid = (char *)snmp_oid;
347
348 main_key_local.community_context = get_item_community_context(item);
349 main_key_local.security_name = get_item_security_name(item);
350
351 if (NULL == (main_key = (zbx_snmpidx_main_key_t *)zbx_hashset_search(&snmpidx, &main_key_local)))
352 goto end;
353
354 zbx_hashset_clear(main_key->mappings);
355 end:
356 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
357 }
358
zbx_get_snmp_type_error(u_char type)359 static char *zbx_get_snmp_type_error(u_char type)
360 {
361 switch (type)
362 {
363 case SNMP_NOSUCHOBJECT:
364 return zbx_strdup(NULL, "No Such Object available on this agent at this OID");
365 case SNMP_NOSUCHINSTANCE:
366 return zbx_strdup(NULL, "No Such Instance currently exists at this OID");
367 case SNMP_ENDOFMIBVIEW:
368 return zbx_strdup(NULL, "No more variables left in this MIB View"
369 " (it is past the end of the MIB tree)");
370 default:
371 return zbx_dsprintf(NULL, "Value has unknown type 0x%02X", (unsigned int)type);
372 }
373 }
374
zbx_get_snmp_response_error(const struct snmp_session * ss,const DC_INTERFACE * interface,int status,const struct snmp_pdu * response,char * error,size_t max_error_len)375 static int zbx_get_snmp_response_error(const struct snmp_session *ss, const DC_INTERFACE *interface, int status,
376 const struct snmp_pdu *response, char *error, size_t max_error_len)
377 {
378 int ret;
379
380 if (STAT_SUCCESS == status)
381 {
382 zbx_snprintf(error, max_error_len, "SNMP error: %s", snmp_errstring(response->errstat));
383 ret = NOTSUPPORTED;
384 }
385 else if (STAT_ERROR == status)
386 {
387 zbx_snprintf(error, max_error_len, "Cannot connect to \"%s:%hu\": %s.",
388 interface->addr, interface->port, snmp_api_errstring(ss->s_snmp_errno));
389
390 switch (ss->s_snmp_errno)
391 {
392 case SNMPERR_UNKNOWN_USER_NAME:
393 case SNMPERR_UNSUPPORTED_SEC_LEVEL:
394 case SNMPERR_AUTHENTICATION_FAILURE:
395 ret = NOTSUPPORTED;
396 break;
397 default:
398 ret = NETWORK_ERROR;
399 }
400 }
401 else if (STAT_TIMEOUT == status)
402 {
403 zbx_snprintf(error, max_error_len, "Timeout while connecting to \"%s:%hu\".",
404 interface->addr, interface->port);
405 ret = NETWORK_ERROR;
406 }
407 else
408 {
409 zbx_snprintf(error, max_error_len, "SNMP error: [%d]", status);
410 ret = NOTSUPPORTED;
411 }
412
413 return ret;
414 }
415
zbx_snmp_open_session(const DC_ITEM * item,char * error,size_t max_error_len)416 static struct snmp_session *zbx_snmp_open_session(const DC_ITEM *item, char *error, size_t max_error_len)
417 {
418 struct snmp_session session, *ss = NULL;
419 char addr[128];
420 #ifdef HAVE_IPV6
421 int family;
422 #endif
423
424 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
425
426 snmp_sess_init(&session);
427
428 /* Allow using sub-OIDs higher than MAX_INT, like in 'snmpwalk -Ir'. */
429 /* Disables the validation of varbind values against the MIB definition for the relevant OID. */
430 if (SNMPERR_SUCCESS != netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_CHECK_RANGE, 1))
431 {
432 /* This error is not fatal and should never happen (see netsnmp_ds_set_boolean() implementation). */
433 /* Only items with sub-OIDs higher than MAX_INT will be unsupported. */
434 zabbix_log(LOG_LEVEL_WARNING, "cannot set \"DontCheckRange\" option for Net-SNMP");
435 }
436
437 switch (item->snmp_version)
438 {
439 case ZBX_IF_SNMP_VERSION_1:
440 session.version = SNMP_VERSION_1;
441 break;
442 case ZBX_IF_SNMP_VERSION_2:
443 session.version = SNMP_VERSION_2c;
444 break;
445 case ZBX_IF_SNMP_VERSION_3:
446 session.version = SNMP_VERSION_3;
447 break;
448 default:
449 THIS_SHOULD_NEVER_HAPPEN;
450 break;
451 }
452
453 session.timeout = CONFIG_TIMEOUT * 1000 * 1000; /* timeout of one attempt in microseconds */
454 /* (net-snmp default = 1 second) */
455
456 #ifdef HAVE_IPV6
457 if (SUCCEED != get_address_family(item->interface.addr, &family, error, max_error_len))
458 goto end;
459
460 if (PF_INET == family)
461 {
462 zbx_snprintf(addr, sizeof(addr), "%s:%hu", item->interface.addr, item->interface.port);
463 }
464 else
465 {
466 if (item->interface.useip)
467 zbx_snprintf(addr, sizeof(addr), "udp6:[%s]:%hu", item->interface.addr, item->interface.port);
468 else
469 zbx_snprintf(addr, sizeof(addr), "udp6:%s:%hu", item->interface.addr, item->interface.port);
470 }
471 #else
472 zbx_snprintf(addr, sizeof(addr), "%s:%hu", item->interface.addr, item->interface.port);
473 #endif
474 session.peername = addr;
475
476 if (SNMP_VERSION_1 == session.version || SNMP_VERSION_2c == session.version)
477 {
478 session.community = (u_char *)item->snmp_community;
479 session.community_len = strlen((char *)session.community);
480 zabbix_log(LOG_LEVEL_DEBUG, "SNMP [%s@%s]", session.community, session.peername);
481 }
482 else if (SNMP_VERSION_3 == session.version)
483 {
484 /* set the SNMPv3 user name */
485 session.securityName = item->snmpv3_securityname;
486 session.securityNameLen = strlen(session.securityName);
487
488 /* set the SNMPv3 context if specified */
489 if ('\0' != *item->snmpv3_contextname)
490 {
491 session.contextName = item->snmpv3_contextname;
492 session.contextNameLen = strlen(session.contextName);
493 }
494
495 /* set the security level to authenticated, but not encrypted */
496 switch (item->snmpv3_securitylevel)
497 {
498 case ITEM_SNMPV3_SECURITYLEVEL_NOAUTHNOPRIV:
499 session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
500 break;
501 case ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV:
502 session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
503
504 switch (item->snmpv3_authprotocol)
505 {
506 case ITEM_SNMPV3_AUTHPROTOCOL_MD5:
507 /* set the authentication protocol to MD5 */
508 session.securityAuthProto = usmHMACMD5AuthProtocol;
509 session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
510 break;
511 case ITEM_SNMPV3_AUTHPROTOCOL_SHA:
512 /* set the authentication protocol to SHA */
513 session.securityAuthProto = usmHMACSHA1AuthProtocol;
514 session.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
515 break;
516 default:
517 zbx_snprintf(error, max_error_len,
518 "Unsupported authentication protocol [%d]",
519 item->snmpv3_authprotocol);
520 goto end;
521 }
522
523 session.securityAuthKeyLen = USM_AUTH_KU_LEN;
524
525 if (SNMPERR_SUCCESS != generate_Ku(session.securityAuthProto,
526 session.securityAuthProtoLen, (u_char *)item->snmpv3_authpassphrase,
527 strlen(item->snmpv3_authpassphrase), session.securityAuthKey,
528 &session.securityAuthKeyLen))
529 {
530 zbx_strlcpy(error, "Error generating Ku from authentication pass phrase",
531 max_error_len);
532 goto end;
533 }
534 break;
535 case ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV:
536 session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
537
538 switch (item->snmpv3_authprotocol)
539 {
540 case ITEM_SNMPV3_AUTHPROTOCOL_MD5:
541 /* set the authentication protocol to MD5 */
542 session.securityAuthProto = usmHMACMD5AuthProtocol;
543 session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
544 break;
545 case ITEM_SNMPV3_AUTHPROTOCOL_SHA:
546 /* set the authentication protocol to SHA */
547 session.securityAuthProto = usmHMACSHA1AuthProtocol;
548 session.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
549 break;
550 default:
551 zbx_snprintf(error, max_error_len,
552 "Unsupported authentication protocol [%d]",
553 item->snmpv3_authprotocol);
554 goto end;
555 }
556
557 session.securityAuthKeyLen = USM_AUTH_KU_LEN;
558
559 if (SNMPERR_SUCCESS != generate_Ku(session.securityAuthProto,
560 session.securityAuthProtoLen, (u_char *)item->snmpv3_authpassphrase,
561 strlen(item->snmpv3_authpassphrase), session.securityAuthKey,
562 &session.securityAuthKeyLen))
563 {
564 zbx_strlcpy(error, "Error generating Ku from authentication pass phrase",
565 max_error_len);
566 goto end;
567 }
568
569 switch (item->snmpv3_privprotocol)
570 {
571 #ifdef HAVE_NETSNMP_SESSION_DES
572 case ITEM_SNMPV3_PRIVPROTOCOL_DES:
573 /* set the privacy protocol to DES */
574 session.securityPrivProto = usmDESPrivProtocol;
575 session.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
576 break;
577 #endif
578 case ITEM_SNMPV3_PRIVPROTOCOL_AES:
579 /* set the privacy protocol to AES */
580 session.securityPrivProto = usmAESPrivProtocol;
581 session.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN;
582 break;
583 default:
584 zbx_snprintf(error, max_error_len,
585 "Unsupported privacy protocol [%d]",
586 item->snmpv3_privprotocol);
587 goto end;
588 }
589
590 session.securityPrivKeyLen = USM_PRIV_KU_LEN;
591
592 if (SNMPERR_SUCCESS != generate_Ku(session.securityAuthProto,
593 session.securityAuthProtoLen, (u_char *)item->snmpv3_privpassphrase,
594 strlen(item->snmpv3_privpassphrase), session.securityPrivKey,
595 &session.securityPrivKeyLen))
596 {
597 zbx_strlcpy(error, "Error generating Ku from privacy pass phrase",
598 max_error_len);
599 goto end;
600 }
601 break;
602 }
603
604 zabbix_log(LOG_LEVEL_DEBUG, "SNMPv3 [%s@%s]", session.securityName, session.peername);
605 }
606
607 #ifdef HAVE_NETSNMP_SESSION_LOCALNAME
608 if (NULL != CONFIG_SOURCE_IP)
609 {
610 /* In some cases specifying just local host (without local port) is not enough. We do */
611 /* not care about the port number though so we let the OS select one by specifying 0. */
612 /* See marc.info/?l=net-snmp-bugs&m=115624676507760 for details. */
613
614 static char localname[64];
615
616 zbx_snprintf(localname, sizeof(localname), "%s:0", CONFIG_SOURCE_IP);
617 session.localname = localname;
618 }
619 #endif
620
621 SOCK_STARTUP;
622
623 if (NULL == (ss = snmp_open(&session)))
624 {
625 SOCK_CLEANUP;
626
627 zbx_strlcpy(error, "Cannot open SNMP session", max_error_len);
628 }
629 end:
630 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
631
632 return ss;
633 }
634
zbx_snmp_close_session(struct snmp_session * session)635 static void zbx_snmp_close_session(struct snmp_session *session)
636 {
637 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
638
639 snmp_close(session);
640 SOCK_CLEANUP;
641
642 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
643 }
644
zbx_snmp_get_octet_string(const struct variable_list * var,unsigned char * string_type)645 static char *zbx_snmp_get_octet_string(const struct variable_list *var, unsigned char *string_type)
646 {
647 const char *hint;
648 char buffer[MAX_BUFFER_LEN];
649 char *strval_dyn = NULL;
650 struct tree *subtree;
651 unsigned char type;
652
653 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
654
655 /* find the subtree to get display hint */
656 subtree = get_tree(var->name, var->name_length, get_tree_head());
657 hint = (NULL != subtree ? subtree->hint : NULL);
658
659 /* we will decide if we want the value from var->val or what snprint_value() returned later */
660 if (-1 == snprint_value(buffer, sizeof(buffer), var->name, var->name_length, var))
661 goto end;
662
663 zabbix_log(LOG_LEVEL_DEBUG, "%s() full value:'%s' hint:'%s'", __func__, buffer, ZBX_NULL2STR(hint));
664
665 if (0 == strncmp(buffer, "Hex-STRING: ", 12))
666 {
667 strval_dyn = zbx_strdup(strval_dyn, buffer + 12);
668 type = ZBX_SNMP_STR_HEX;
669 }
670 else if (NULL != hint && 0 == strncmp(buffer, "STRING: ", 8))
671 {
672 strval_dyn = zbx_strdup(strval_dyn, buffer + 8);
673 type = ZBX_SNMP_STR_STRING;
674 }
675 else if (0 == strncmp(buffer, "OID: ", 5))
676 {
677 strval_dyn = zbx_strdup(strval_dyn, buffer + 5);
678 type = ZBX_SNMP_STR_OID;
679 }
680 else if (0 == strncmp(buffer, "BITS: ", 6))
681 {
682 strval_dyn = zbx_strdup(strval_dyn, buffer + 6);
683 type = ZBX_SNMP_STR_BITS;
684 }
685 else
686 {
687 /* snprint_value() escapes hintless ASCII strings, so */
688 /* we are copying the raw unescaped value in this case */
689
690 strval_dyn = (char *)zbx_malloc(strval_dyn, var->val_len + 1);
691 memcpy(strval_dyn, var->val.string, var->val_len);
692 strval_dyn[var->val_len] = '\0';
693 type = ZBX_SNMP_STR_ASCII;
694 }
695
696 if (NULL != string_type)
697 *string_type = type;
698 end:
699 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():'%s'", __func__, ZBX_NULL2STR(strval_dyn));
700
701 return strval_dyn;
702 }
703
zbx_snmp_set_result(const struct variable_list * var,AGENT_RESULT * result,unsigned char * string_type)704 static int zbx_snmp_set_result(const struct variable_list *var, AGENT_RESULT *result, unsigned char *string_type)
705 {
706 char *strval_dyn;
707 int ret = SUCCEED;
708
709 zabbix_log(LOG_LEVEL_DEBUG, "In %s() type:%d", __func__, (int)var->type);
710
711 *string_type = ZBX_SNMP_STR_UNDEFINED;
712
713 if (ASN_OCTET_STR == var->type || ASN_OBJECT_ID == var->type)
714 {
715 if (NULL == (strval_dyn = zbx_snmp_get_octet_string(var, string_type)))
716 {
717 SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot receive string value: out of memory."));
718 ret = NOTSUPPORTED;
719 }
720 else
721 {
722 set_result_type(result, ITEM_VALUE_TYPE_TEXT, strval_dyn);
723 zbx_free(strval_dyn);
724 }
725 }
726 #ifdef OPAQUE_SPECIAL_TYPES
727 else if (ASN_UINTEGER == var->type || ASN_COUNTER == var->type || ASN_OPAQUE_U64 == var->type ||
728 ASN_TIMETICKS == var->type || ASN_GAUGE == var->type)
729 #else
730 else if (ASN_UINTEGER == var->type || ASN_COUNTER == var->type ||
731 ASN_TIMETICKS == var->type || ASN_GAUGE == var->type)
732 #endif
733 {
734 SET_UI64_RESULT(result, (unsigned long)*var->val.integer);
735 }
736 #ifdef OPAQUE_SPECIAL_TYPES
737 else if (ASN_COUNTER64 == var->type || ASN_OPAQUE_COUNTER64 == var->type)
738 #else
739 else if (ASN_COUNTER64 == var->type)
740 #endif
741 {
742 SET_UI64_RESULT(result, (((zbx_uint64_t)var->val.counter64->high) << 32) +
743 (zbx_uint64_t)var->val.counter64->low);
744 }
745 #ifdef OPAQUE_SPECIAL_TYPES
746 else if (ASN_INTEGER == var->type || ASN_OPAQUE_I64 == var->type)
747 #else
748 else if (ASN_INTEGER == var->type)
749 #endif
750 {
751 char buffer[21];
752
753 zbx_snprintf(buffer, sizeof(buffer), "%ld", *var->val.integer);
754
755 set_result_type(result, ITEM_VALUE_TYPE_TEXT, buffer);
756 }
757 #ifdef OPAQUE_SPECIAL_TYPES
758 else if (ASN_OPAQUE_FLOAT == var->type)
759 {
760 SET_DBL_RESULT(result, *var->val.floatVal);
761 }
762 else if (ASN_OPAQUE_DOUBLE == var->type)
763 {
764 SET_DBL_RESULT(result, *var->val.doubleVal);
765 }
766 #endif
767 else if (ASN_IPADDRESS == var->type)
768 {
769 SET_STR_RESULT(result, zbx_dsprintf(NULL, "%u.%u.%u.%u",
770 (unsigned int)var->val.string[0],
771 (unsigned int)var->val.string[1],
772 (unsigned int)var->val.string[2],
773 (unsigned int)var->val.string[3]));
774 }
775 else
776 {
777 SET_MSG_RESULT(result, zbx_get_snmp_type_error(var->type));
778 ret = NOTSUPPORTED;
779 }
780
781 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
782
783 return ret;
784 }
785
zbx_snmp_dump_oid(char * buffer,size_t buffer_len,const oid * objid,size_t objid_len)786 static void zbx_snmp_dump_oid(char *buffer, size_t buffer_len, const oid *objid, size_t objid_len)
787 {
788 size_t i, offset = 0;
789
790 *buffer = '\0';
791
792 for (i = 0; i < objid_len; i++)
793 offset += zbx_snprintf(buffer + offset, buffer_len - offset, ".%lu", (unsigned long)objid[i]);
794 }
795
796 #define ZBX_OID_INDEX_STRING 0
797 #define ZBX_OID_INDEX_NUMERIC 1
798
zbx_snmp_print_oid(char * buffer,size_t buffer_len,const oid * objid,size_t objid_len,int format)799 static int zbx_snmp_print_oid(char *buffer, size_t buffer_len, const oid *objid, size_t objid_len, int format)
800 {
801 if (SNMPERR_SUCCESS != netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_BREAKDOWN_OIDS,
802 format))
803 {
804 zabbix_log(LOG_LEVEL_WARNING, "cannot set \"dontBreakdownOids\" option to %d for Net-SNMP", format);
805 return -1;
806 }
807
808 return snprint_objid(buffer, buffer_len, objid, objid_len);
809 }
810
zbx_snmp_choose_index(char * buffer,size_t buffer_len,const oid * objid,size_t objid_len,size_t root_string_len,size_t root_numeric_len)811 static int zbx_snmp_choose_index(char *buffer, size_t buffer_len, const oid *objid, size_t objid_len,
812 size_t root_string_len, size_t root_numeric_len)
813 {
814 oid parsed_oid[MAX_OID_LEN];
815 size_t parsed_oid_len = MAX_OID_LEN;
816 char printed_oid[MAX_STRING_LEN];
817 char *printed_oid_escaped;
818
819 /**************************************************************************************************************/
820 /* */
821 /* When we are providing a value for {#SNMPINDEX}, we would like to provide a pretty value. This is only a */
822 /* concern for OIDs with string indices. For instance, suppose we are walking the following OID: */
823 /* */
824 /* SNMP-VIEW-BASED-ACM-MIB::vacmGroupName */
825 /* */
826 /* Suppose also that we are currently looking at this OID: */
827 /* */
828 /* SNMP-VIEW-BASED-ACM-MIB::vacmGroupName.3."authOnlyUser" */
829 /* */
830 /* Then, we would like to provide {#SNMPINDEX} with this value: */
831 /* */
832 /* 3."authOnlyUser" */
833 /* */
834 /* An alternative approach would be to provide {#SNMPINDEX} with numeric value. While it is equivalent to the */
835 /* string representation above, the string representation is more readable and thus more useful to users: */
836 /* */
837 /* 3.12.97.117.116.104.79.110.108.121.85.115.101.114 */
838 /* */
839 /* Here, 12 is the length of "authOnlyUser" and the rest is the string encoding using ASCII characters. */
840 /* */
841 /* There are two problems with always providing {#SNMPINDEX} that has an index representation as a string. */
842 /* */
843 /* The first problem is indices of type InetAddress. The Net-SNMP library has code for pretty-printing IP */
844 /* addresses, but no way to parse them back. As an example, consider the following OID: */
845 /* */
846 /* .1.3.6.1.2.1.4.34.1.4.1.4.192.168.3.255 */
847 /* */
848 /* Its pretty representation is like this: */
849 /* */
850 /* IP-MIB::ipAddressType.ipv4."192.168.3.255" */
851 /* */
852 /* However, when trying to parse it, it turns into this OID: */
853 /* */
854 /* .1.3.6.1.2.1.4.34.1.4.1.13.49.57.50.46.49.54.56.46.51.46.50.53.53 */
855 /* */
856 /* Apparently, this is different than the original. */
857 /* */
858 /* The second problem is indices of type OCTET STRING, which might contain unprintable characters: */
859 /* */
860 /* 1.3.6.1.2.1.17.4.3.1.1.0.0.240.122.113.21 */
861 /* */
862 /* Its pretty representation is like this (note the single quotes which stand for a fixed-length string): */
863 /* */
864 /* BRIDGE-MIB::dot1dTpFdbAddress.'...zq.' */
865 /* */
866 /* Here, '...zq.' stands for 0.0.240.122.113.21, where only 'z' (122) and 'q' (113) are printable. */
867 /* */
868 /* Apparently, this cannot be turned back into the numeric representation. */
869 /* */
870 /* So what we try to do is first print it pretty. If there is no string-looking index, return it as output. */
871 /* If there is such an index, we check that it can be parsed and that the result is the same as the original. */
872 /* */
873 /**************************************************************************************************************/
874
875 if (-1 == zbx_snmp_print_oid(printed_oid, sizeof(printed_oid), objid, objid_len, ZBX_OID_INDEX_STRING))
876 {
877 zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot print OID with string indices", __func__);
878 goto numeric;
879 }
880
881 if (NULL == strchr(printed_oid, '"') && NULL == strchr(printed_oid, '\''))
882 {
883 zbx_strlcpy(buffer, printed_oid + root_string_len + 1, buffer_len);
884 return SUCCEED;
885 }
886
887 printed_oid_escaped = zbx_dyn_escape_string(printed_oid, "\\");
888
889 if (NULL == snmp_parse_oid(printed_oid_escaped, parsed_oid, &parsed_oid_len))
890 {
891 zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot parse OID '%s'", __func__, printed_oid_escaped);
892 zbx_free(printed_oid_escaped);
893 goto numeric;
894 }
895 zbx_free(printed_oid_escaped);
896
897 if (parsed_oid_len == objid_len && 0 == memcmp(parsed_oid, objid, parsed_oid_len * sizeof(oid)))
898 {
899 zbx_strlcpy(buffer, printed_oid + root_string_len + 1, buffer_len);
900 return SUCCEED;
901 }
902 numeric:
903 if (-1 == zbx_snmp_print_oid(printed_oid, sizeof(printed_oid), objid, objid_len, ZBX_OID_INDEX_NUMERIC))
904 {
905 zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot print OID with numeric indices", __func__);
906 return FAIL;
907 }
908
909 zbx_strlcpy(buffer, printed_oid + root_numeric_len + 1, buffer_len);
910 return SUCCEED;
911 }
912
913 /******************************************************************************
914 * *
915 * Functions for detecting looping in SNMP OID sequence using hashset *
916 * *
917 * Once there is a possibility of looping we start putting OIDs into hashset. *
918 * We do it until a duplicate OID shows up or ZBX_OIDS_MAX_NUM OIDs have been *
919 * collected. *
920 * *
921 * The hashset key is array of elements of type 'oid'. Element 0 holds the *
922 * number of OID components (sub-OIDs), element 1 and so on - OID components *
923 * themselves. *
924 * *
925 * OIDs may contain up to 128 sub-OIDs, so 1 byte is sufficient to keep the *
926 * number of them. On the other hand, sub-OIDs are of type 'oid' which can be *
927 * defined in NetSNMP as 'uint8_t' or 'u_long'. Sub-OIDs are compared as *
928 * numbers, so some platforms may require they to be properly aligned in *
929 * memory. To ensure proper alignment we keep number of elements in element 0 *
930 * instead of using a separate structure element for it. *
931 * *
932 ******************************************************************************/
933
__oids_seen_key_hash(const void * data)934 static zbx_hash_t __oids_seen_key_hash(const void *data)
935 {
936 const oid *key = (const oid *)data;
937
938 return ZBX_DEFAULT_HASH_ALGO(key, (key[0] + 1) * sizeof(oid), ZBX_DEFAULT_HASH_SEED);
939 }
940
__oids_seen_key_compare(const void * d1,const void * d2)941 static int __oids_seen_key_compare(const void *d1, const void *d2)
942 {
943 const oid *k1 = (const oid *)d1;
944 const oid *k2 = (const oid *)d2;
945
946 if (d1 == d2)
947 return 0;
948
949 return snmp_oid_compare(k1 + 1, k1[0], k2 + 1, k2[0]);
950 }
951
zbx_detect_loop_init(zbx_hashset_t * hs)952 static void zbx_detect_loop_init(zbx_hashset_t *hs)
953 {
954 #define ZBX_OIDS_SEEN_INIT_SIZE 500 /* minimum initial number of slots in hashset */
955
956 zbx_hashset_create(hs, ZBX_OIDS_SEEN_INIT_SIZE, __oids_seen_key_hash, __oids_seen_key_compare);
957
958 #undef ZBX_OIDS_SEEN_INIT_SIZE
959 }
960
zbx_oid_is_new(zbx_hashset_t * hs,size_t root_len,const oid * p_oid,size_t oid_len)961 static int zbx_oid_is_new(zbx_hashset_t *hs, size_t root_len, const oid *p_oid, size_t oid_len)
962 {
963 #define ZBX_OIDS_MAX_NUM 1000000 /* max number of OIDs to store for checking duplicates */
964
965 const oid *var_oid; /* points to the first element in the variable part */
966 size_t var_len; /* number of elements in the variable part */
967 oid oid_k[MAX_OID_LEN + 1]; /* array for constructing a hashset key */
968
969 /* OIDs share a common initial part. Save space by storing only the variable part. */
970
971 var_oid = p_oid + root_len;
972 var_len = oid_len - root_len;
973
974 if (ZBX_OIDS_MAX_NUM == hs->num_data)
975 return FAIL;
976
977 oid_k[0] = var_len;
978 memcpy(oid_k + 1, var_oid, var_len * sizeof(oid));
979
980 if (NULL != zbx_hashset_search(hs, oid_k))
981 return FAIL; /* OID already seen */
982
983 if (NULL != zbx_hashset_insert(hs, oid_k, (var_len + 1) * sizeof(oid)))
984 return SUCCEED; /* new OID */
985
986 THIS_SHOULD_NEVER_HAPPEN;
987 return FAIL; /* hashset fail */
988
989 #undef ZBX_OIDS_MAX_NUM
990 }
991
992 /******************************************************************************
993 * *
994 * Function: zbx_snmp_walk *
995 * *
996 * Purpose: retrieve information by walking an OID tree *
997 * *
998 * Parameters: ss - [IN] SNMP session handle *
999 * item - [IN] configuration of Zabbix item *
1000 * OID - [IN] OID of table with values of interest *
1001 * error - [OUT] a buffer to store error message *
1002 * max_error_len - [IN] maximum error message length *
1003 * max_succeed - [OUT] value of "max_repetitions" that succeeded*
1004 * min_fail - [OUT] value of "max_repetitions" that failed *
1005 * max_vars - [IN] suggested value of "max_repetitions" *
1006 * bulk - [IN] whether GetBulkRequest-PDU should be used *
1007 * walk_cb_func - [IN] callback function to process discovered *
1008 * OIDs and their values *
1009 * walk_cb_arg - [IN] argument to pass to the callback function *
1010 * *
1011 * Return value: NOTSUPPORTED - OID does not exist, any other critical error *
1012 * NETWORK_ERROR - recoverable network error *
1013 * CONFIG_ERROR - item configuration error *
1014 * SUCCEED - if function successfully completed *
1015 * *
1016 * Author: Alexander Vladishev, Aleksandrs Saveljevs *
1017 * *
1018 ******************************************************************************/
zbx_snmp_walk(struct snmp_session * ss,const DC_ITEM * item,const char * snmp_oid,char * error,size_t max_error_len,int * max_succeed,int * min_fail,int max_vars,int bulk,zbx_snmp_walk_cb_func walk_cb_func,void * walk_cb_arg)1019 static int zbx_snmp_walk(struct snmp_session *ss, const DC_ITEM *item, const char *snmp_oid, char *error,
1020 size_t max_error_len, int *max_succeed, int *min_fail, int max_vars, int bulk,
1021 zbx_snmp_walk_cb_func walk_cb_func, void *walk_cb_arg)
1022 {
1023 struct snmp_pdu *pdu, *response;
1024 oid anOID[MAX_OID_LEN], rootOID[MAX_OID_LEN];
1025 size_t anOID_len = MAX_OID_LEN, rootOID_len = MAX_OID_LEN, root_string_len, root_numeric_len;
1026 char oid_index[MAX_STRING_LEN];
1027 struct variable_list *var;
1028 int status, level, running, num_vars, check_oid_increase = 1, ret = SUCCEED;
1029 AGENT_RESULT snmp_result;
1030 zbx_hashset_t oids_seen;
1031
1032 zabbix_log(LOG_LEVEL_DEBUG, "In %s() type:%d OID:'%s' bulk:%d", __func__, (int)item->type, snmp_oid, bulk);
1033
1034 if (ZBX_IF_SNMP_VERSION_1 == item->snmp_version) /* GetBulkRequest-PDU available since SNMPv2 */
1035 bulk = SNMP_BULK_DISABLED;
1036
1037 /* create OID from string */
1038 if (NULL == snmp_parse_oid(snmp_oid, rootOID, &rootOID_len))
1039 {
1040 zbx_snprintf(error, max_error_len, "snmp_parse_oid(): cannot parse OID \"%s\".", snmp_oid);
1041 ret = CONFIG_ERROR;
1042 goto out;
1043 }
1044
1045 if (-1 == zbx_snmp_print_oid(oid_index, sizeof(oid_index), rootOID, rootOID_len, ZBX_OID_INDEX_STRING))
1046 {
1047 zbx_snprintf(error, max_error_len, "zbx_snmp_print_oid(): cannot print OID \"%s\" with string indices.",
1048 snmp_oid);
1049 ret = CONFIG_ERROR;
1050 goto out;
1051 }
1052
1053 root_string_len = strlen(oid_index);
1054
1055 if (-1 == zbx_snmp_print_oid(oid_index, sizeof(oid_index), rootOID, rootOID_len, ZBX_OID_INDEX_NUMERIC))
1056 {
1057 zbx_snprintf(error, max_error_len, "zbx_snmp_print_oid(): cannot print OID \"%s\""
1058 " with numeric indices.", snmp_oid);
1059 ret = CONFIG_ERROR;
1060 goto out;
1061 }
1062
1063 root_numeric_len = strlen(oid_index);
1064
1065 /* copy rootOID to anOID */
1066 memcpy(anOID, rootOID, rootOID_len * sizeof(oid));
1067 anOID_len = rootOID_len;
1068
1069 /* initialize variables */
1070 level = 0;
1071 running = 1;
1072
1073 while (1 == running)
1074 {
1075 /* create PDU */
1076 if (NULL == (pdu = snmp_pdu_create(SNMP_BULK_ENABLED == bulk ? SNMP_MSG_GETBULK : SNMP_MSG_GETNEXT)))
1077 {
1078 zbx_strlcpy(error, "snmp_pdu_create(): cannot create PDU object.", max_error_len);
1079 ret = CONFIG_ERROR;
1080 break;
1081 }
1082
1083 if (NULL == snmp_add_null_var(pdu, anOID, anOID_len)) /* add OID as variable to PDU */
1084 {
1085 zbx_strlcpy(error, "snmp_add_null_var(): cannot add null variable.", max_error_len);
1086 ret = CONFIG_ERROR;
1087 snmp_free_pdu(pdu);
1088 break;
1089 }
1090
1091 if (SNMP_BULK_ENABLED == bulk)
1092 {
1093 pdu->non_repeaters = 0;
1094 pdu->max_repetitions = max_vars;
1095 }
1096
1097 ss->retries = (0 == bulk || (1 == max_vars && 0 == level) ? 1 : 0);
1098
1099 /* communicate with agent */
1100 status = snmp_synch_response(ss, pdu, &response);
1101
1102 zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() status:%d s_snmp_errno:%d errstat:%ld"
1103 " max_vars:%d", __func__, status, ss->s_snmp_errno,
1104 NULL == response ? (long)-1 : response->errstat, max_vars);
1105
1106 if (1 < max_vars &&
1107 ((STAT_SUCCESS == status && SNMP_ERR_TOOBIG == response->errstat) || STAT_TIMEOUT == status))
1108 {
1109 /* The logic of iteratively reducing request size here is the same as in function */
1110 /* zbx_snmp_get_values(). Please refer to the description there for explanation. */
1111
1112 if (*min_fail > max_vars)
1113 *min_fail = max_vars;
1114
1115 if (0 == level)
1116 {
1117 max_vars /= 2;
1118 }
1119 else if (1 == level)
1120 {
1121 max_vars = 1;
1122 }
1123
1124 level++;
1125
1126 goto next;
1127 }
1128 else if (STAT_SUCCESS != status || SNMP_ERR_NOERROR != response->errstat)
1129 {
1130 ret = zbx_get_snmp_response_error(ss, &item->interface, status, response, error, max_error_len);
1131 running = 0;
1132 goto next;
1133 }
1134
1135 /* process response */
1136 for (num_vars = 0, var = response->variables; NULL != var; num_vars++, var = var->next_variable)
1137 {
1138 char **str_res;
1139 unsigned char val_type;
1140
1141 /* verify if we are in the same subtree */
1142 if (SNMP_ENDOFMIBVIEW == var->type || var->name_length < rootOID_len ||
1143 0 != memcmp(rootOID, var->name, rootOID_len * sizeof(oid)))
1144 {
1145 /* reached the end or past this subtree */
1146 running = 0;
1147 break;
1148 }
1149 else if (SNMP_NOSUCHOBJECT != var->type && SNMP_NOSUCHINSTANCE != var->type)
1150 {
1151 /* not an exception value */
1152
1153 if (1 == check_oid_increase) /* typical case */
1154 {
1155 int res;
1156
1157 /* normally devices return OIDs in increasing order, */
1158 /* snmp_oid_compare() will return -1 in this case */
1159
1160 if (-1 != (res = snmp_oid_compare(anOID, anOID_len, var->name,
1161 var->name_length)))
1162 {
1163 if (0 == res) /* got the same OID */
1164 {
1165 zbx_strlcpy(error, "OID not changing.", max_error_len);
1166 ret = NOTSUPPORTED;
1167 running = 0;
1168 break;
1169 }
1170 else /* 1 == res */
1171 {
1172 /* OID decreased. Disable further checks of increasing */
1173 /* and set up a protection against endless looping. */
1174
1175 check_oid_increase = 0;
1176 zbx_detect_loop_init(&oids_seen);
1177 }
1178 }
1179 }
1180
1181 if (0 == check_oid_increase && FAIL == zbx_oid_is_new(&oids_seen, rootOID_len,
1182 var->name, var->name_length))
1183 {
1184 zbx_strlcpy(error, "OID loop detected or too many OIDs.", max_error_len);
1185 ret = NOTSUPPORTED;
1186 running = 0;
1187 break;
1188 }
1189
1190 if (SUCCEED != zbx_snmp_choose_index(oid_index, sizeof(oid_index), var->name,
1191 var->name_length, root_string_len, root_numeric_len))
1192 {
1193 zbx_snprintf(error, max_error_len, "zbx_snmp_choose_index():"
1194 " cannot choose appropriate index while walking for"
1195 " OID \"%s\".", snmp_oid);
1196 ret = NOTSUPPORTED;
1197 running = 0;
1198 break;
1199 }
1200
1201 str_res = NULL;
1202 init_result(&snmp_result);
1203
1204 if (SUCCEED == zbx_snmp_set_result(var, &snmp_result, &val_type))
1205 {
1206 if (ISSET_TEXT(&snmp_result) && ZBX_SNMP_STR_HEX == val_type)
1207 zbx_remove_chars(snmp_result.text, "\r\n");
1208
1209 str_res = GET_STR_RESULT(&snmp_result);
1210 }
1211
1212 if (NULL == str_res)
1213 {
1214 char **msg;
1215
1216 msg = GET_MSG_RESULT(&snmp_result);
1217
1218 zabbix_log(LOG_LEVEL_DEBUG, "cannot get index '%s' string value: %s",
1219 oid_index, NULL != msg && NULL != *msg ? *msg : "(null)");
1220 }
1221 else
1222 walk_cb_func(walk_cb_arg, snmp_oid, oid_index, snmp_result.str);
1223
1224 free_result(&snmp_result);
1225
1226 /* go to next variable */
1227 memcpy((char *)anOID, (char *)var->name, var->name_length * sizeof(oid));
1228 anOID_len = var->name_length;
1229 }
1230 else
1231 {
1232 /* an exception value, so stop */
1233 char *errmsg;
1234
1235 errmsg = zbx_get_snmp_type_error(var->type);
1236 zbx_strlcpy(error, errmsg, max_error_len);
1237 zbx_free(errmsg);
1238 ret = NOTSUPPORTED;
1239 running = 0;
1240 break;
1241 }
1242 }
1243
1244 if (*max_succeed < num_vars)
1245 *max_succeed = num_vars;
1246 next:
1247 if (NULL != response)
1248 snmp_free_pdu(response);
1249 }
1250
1251 if (0 == check_oid_increase)
1252 zbx_hashset_destroy(&oids_seen);
1253 out:
1254 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1255
1256 return ret;
1257 }
1258
zbx_snmp_get_values(struct snmp_session * ss,const DC_ITEM * items,char oids[][ITEM_SNMP_OID_LEN_MAX],AGENT_RESULT * results,int * errcodes,unsigned char * query_and_ignore_type,int num,int level,char * error,size_t max_error_len,int * max_succeed,int * min_fail,unsigned char poller_type)1259 static int zbx_snmp_get_values(struct snmp_session *ss, const DC_ITEM *items, char oids[][ITEM_SNMP_OID_LEN_MAX],
1260 AGENT_RESULT *results, int *errcodes, unsigned char *query_and_ignore_type, int num, int level,
1261 char *error, size_t max_error_len, int *max_succeed, int *min_fail, unsigned char poller_type)
1262 {
1263 int i, j, status, ret = SUCCEED;
1264 int mapping[MAX_SNMP_ITEMS], mapping_num = 0;
1265 oid parsed_oids[MAX_SNMP_ITEMS][MAX_OID_LEN];
1266 size_t parsed_oid_lens[MAX_SNMP_ITEMS];
1267 struct snmp_pdu *pdu, *response;
1268 struct variable_list *var;
1269 unsigned char val_type;
1270
1271 zabbix_log(LOG_LEVEL_DEBUG, "In %s() num:%d level:%d", __func__, num, level);
1272
1273 if (NULL == (pdu = snmp_pdu_create(SNMP_MSG_GET)))
1274 {
1275 zbx_strlcpy(error, "snmp_pdu_create(): cannot create PDU object.", max_error_len);
1276 ret = CONFIG_ERROR;
1277 goto out;
1278 }
1279
1280 for (i = 0; i < num; i++)
1281 {
1282 if (SUCCEED != errcodes[i])
1283 continue;
1284
1285 if (NULL != query_and_ignore_type && 0 == query_and_ignore_type[i])
1286 continue;
1287
1288 parsed_oid_lens[i] = MAX_OID_LEN;
1289
1290 if (NULL == snmp_parse_oid(oids[i], parsed_oids[i], &parsed_oid_lens[i]))
1291 {
1292 SET_MSG_RESULT(&results[i], zbx_dsprintf(NULL, "snmp_parse_oid(): cannot parse OID \"%s\".",
1293 oids[i]));
1294 errcodes[i] = CONFIG_ERROR;
1295 continue;
1296 }
1297
1298 if (NULL == snmp_add_null_var(pdu, parsed_oids[i], parsed_oid_lens[i]))
1299 {
1300 SET_MSG_RESULT(&results[i], zbx_strdup(NULL, "snmp_add_null_var(): cannot add null variable."));
1301 errcodes[i] = CONFIG_ERROR;
1302 continue;
1303 }
1304
1305 mapping[mapping_num++] = i;
1306 }
1307
1308 if (0 == mapping_num)
1309 {
1310 snmp_free_pdu(pdu);
1311 goto out;
1312 }
1313
1314 ss->retries = (1 == mapping_num && 0 == level && ZBX_POLLER_TYPE_UNREACHABLE != poller_type ? 1 : 0);
1315 retry:
1316 status = snmp_synch_response(ss, pdu, &response);
1317
1318 zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() status:%d s_snmp_errno:%d errstat:%ld mapping_num:%d",
1319 __func__, status, ss->s_snmp_errno, NULL == response ? (long)-1 : response->errstat,
1320 mapping_num);
1321
1322 if (STAT_SUCCESS == status && SNMP_ERR_NOERROR == response->errstat)
1323 {
1324 for (i = 0, var = response->variables;; i++, var = var->next_variable)
1325 {
1326 /* check that response variable binding matches the request variable binding */
1327
1328 if (i == mapping_num)
1329 {
1330 if (NULL != var)
1331 {
1332 zabbix_log(LOG_LEVEL_WARNING, "SNMP response from host \"%s\" contains"
1333 " too many variable bindings", items[0].host.host);
1334
1335 if (1 != mapping_num) /* give device a chance to handle a smaller request */
1336 goto halve;
1337
1338 zbx_strlcpy(error, "Invalid SNMP response: too many variable bindings.",
1339 max_error_len);
1340
1341 ret = NOTSUPPORTED;
1342 }
1343
1344 break;
1345 }
1346
1347 if (NULL == var)
1348 {
1349 zabbix_log(LOG_LEVEL_WARNING, "SNMP response from host \"%s\" contains"
1350 " too few variable bindings", items[0].host.host);
1351
1352 if (1 != mapping_num) /* give device a chance to handle a smaller request */
1353 goto halve;
1354
1355 zbx_strlcpy(error, "Invalid SNMP response: too few variable bindings.", max_error_len);
1356
1357 ret = NOTSUPPORTED;
1358 break;
1359 }
1360
1361 j = mapping[i];
1362
1363 if (parsed_oid_lens[j] != var->name_length ||
1364 0 != memcmp(parsed_oids[j], var->name, parsed_oid_lens[j] * sizeof(oid)))
1365 {
1366 char sent_oid[ITEM_SNMP_OID_LEN_MAX], received_oid[ITEM_SNMP_OID_LEN_MAX];
1367
1368 zbx_snmp_dump_oid(sent_oid, sizeof(sent_oid), parsed_oids[j], parsed_oid_lens[j]);
1369 zbx_snmp_dump_oid(received_oid, sizeof(received_oid), var->name, var->name_length);
1370
1371 if (1 != mapping_num)
1372 {
1373 zabbix_log(LOG_LEVEL_WARNING, "SNMP response from host \"%s\" contains"
1374 " variable bindings that do not match the request:"
1375 " sent \"%s\", received \"%s\"",
1376 items[0].host.host, sent_oid, received_oid);
1377
1378 goto halve; /* give device a chance to handle a smaller request */
1379 }
1380 else
1381 {
1382 zabbix_log(LOG_LEVEL_DEBUG, "SNMP response from host \"%s\" contains"
1383 " variable bindings that do not match the request:"
1384 " sent \"%s\", received \"%s\"",
1385 items[0].host.host, sent_oid, received_oid);
1386 }
1387 }
1388
1389 /* process received data */
1390
1391 if (NULL != query_and_ignore_type && 1 == query_and_ignore_type[j])
1392 (void)zbx_snmp_set_result(var, &results[j], &val_type);
1393 else
1394 errcodes[j] = zbx_snmp_set_result(var, &results[j], &val_type);
1395
1396 if (ISSET_TEXT(&results[j]) && ZBX_SNMP_STR_HEX == val_type)
1397 zbx_remove_chars(results[j].text, "\r\n");
1398 }
1399
1400 if (SUCCEED == ret)
1401 {
1402 if (*max_succeed < mapping_num)
1403 *max_succeed = mapping_num;
1404 }
1405 /* min_fail value is updated when bulk request is halved in the case of failure */
1406 }
1407 else if (STAT_SUCCESS == status && SNMP_ERR_NOSUCHNAME == response->errstat && 0 != response->errindex)
1408 {
1409 /* If a request PDU contains a bad variable, the specified behavior is different between SNMPv1 and */
1410 /* later versions. In SNMPv1, the whole PDU is rejected and "response->errindex" is set to indicate */
1411 /* the bad variable. In SNMPv2 and later, the SNMP agent processes the PDU by filling values for the */
1412 /* known variables and marking unknown variables individually in the variable binding list. However, */
1413 /* SNMPv2 allows SNMPv1 behavior, too. So regardless of the SNMP version used, if we get this error, */
1414 /* then we fix the PDU by removing the bad variable and retry the request. */
1415
1416 i = response->errindex - 1;
1417
1418 if (0 > i || i >= mapping_num)
1419 {
1420 zabbix_log(LOG_LEVEL_WARNING, "SNMP response from host \"%s\" contains"
1421 " an out of bounds error index: %ld", items[0].host.host, response->errindex);
1422
1423 zbx_strlcpy(error, "Invalid SNMP response: error index out of bounds.", max_error_len);
1424
1425 ret = NOTSUPPORTED;
1426 goto exit;
1427 }
1428
1429 j = mapping[i];
1430
1431 zabbix_log(LOG_LEVEL_DEBUG, "%s() snmp_synch_response() errindex:%ld OID:'%s'", __func__,
1432 response->errindex, oids[j]);
1433
1434 if (NULL == query_and_ignore_type || 0 == query_and_ignore_type[j])
1435 {
1436 errcodes[j] = zbx_get_snmp_response_error(ss, &items[0].interface, status, response, error,
1437 max_error_len);
1438 SET_MSG_RESULT(&results[j], zbx_strdup(NULL, error));
1439 *error = '\0';
1440 }
1441
1442 if (1 < mapping_num)
1443 {
1444 if (NULL != (pdu = snmp_fix_pdu(response, SNMP_MSG_GET)))
1445 {
1446 memmove(mapping + i, mapping + i + 1, sizeof(int) * (mapping_num - i - 1));
1447 mapping_num--;
1448
1449 snmp_free_pdu(response);
1450 goto retry;
1451 }
1452 else
1453 {
1454 zbx_strlcpy(error, "snmp_fix_pdu(): cannot fix PDU object.", max_error_len);
1455 ret = NOTSUPPORTED;
1456 }
1457 }
1458 }
1459 else if (1 < mapping_num &&
1460 ((STAT_SUCCESS == status && SNMP_ERR_TOOBIG == response->errstat) || STAT_TIMEOUT == status ||
1461 (STAT_ERROR == status && SNMPERR_TOO_LONG == ss->s_snmp_errno)))
1462 {
1463 /* Since we are trying to obtain multiple values from the SNMP agent, the response that it has to */
1464 /* generate might be too big. It seems to be required by the SNMP standard that in such cases the */
1465 /* error status should be set to "tooBig(1)". However, some devices simply do not respond to such */
1466 /* queries and we get a timeout. Moreover, some devices exhibit both behaviors - they either send */
1467 /* "tooBig(1)" or do not respond at all. So what we do is halve the number of variables to query - */
1468 /* it should work in the vast majority of cases, because, since we are now querying "num" values, */
1469 /* we know that querying "num/2" values succeeded previously. The case where it can still fail due */
1470 /* to exceeded maximum response size is if we are now querying values that are unusually large. So */
1471 /* if querying with half the number of the last values does not work either, we resort to querying */
1472 /* values one by one, and the next time configuration cache gives us items to query, it will give */
1473 /* us less. */
1474
1475 /* The explanation above is for the first two conditions. The third condition comes from SNMPv3, */
1476 /* where the size of the request that we are trying to send exceeds device's "msgMaxSize" limit. */
1477 halve:
1478 if (*min_fail > mapping_num)
1479 *min_fail = mapping_num;
1480
1481 if (0 == level)
1482 {
1483 /* halve the number of items */
1484
1485 int base;
1486
1487 ret = zbx_snmp_get_values(ss, items, oids, results, errcodes, query_and_ignore_type, num / 2,
1488 level + 1, error, max_error_len, max_succeed, min_fail, poller_type);
1489
1490 if (SUCCEED != ret)
1491 goto exit;
1492
1493 base = num / 2;
1494
1495 ret = zbx_snmp_get_values(ss, items + base, oids + base, results + base, errcodes + base,
1496 NULL == query_and_ignore_type ? NULL : query_and_ignore_type + base, num - base,
1497 level + 1, error, max_error_len, max_succeed, min_fail, poller_type);
1498 }
1499 else if (1 == level)
1500 {
1501 /* resort to querying items one by one */
1502
1503 for (i = 0; i < num; i++)
1504 {
1505 if (SUCCEED != errcodes[i])
1506 continue;
1507
1508 ret = zbx_snmp_get_values(ss, items + i, oids + i, results + i, errcodes + i,
1509 NULL == query_and_ignore_type ? NULL : query_and_ignore_type + i, 1,
1510 level + 1, error, max_error_len, max_succeed, min_fail, poller_type);
1511
1512 if (SUCCEED != ret)
1513 goto exit;
1514 }
1515 }
1516 }
1517 else
1518 ret = zbx_get_snmp_response_error(ss, &items[0].interface, status, response, error, max_error_len);
1519 exit:
1520 if (NULL != response)
1521 snmp_free_pdu(response);
1522 out:
1523 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1524
1525 return ret;
1526 }
1527
1528 /******************************************************************************
1529 * *
1530 * Function: zbx_snmp_translate *
1531 * *
1532 * Purpose: translate well-known object identifiers into numeric form *
1533 * *
1534 * Author: Alexei Vladishev *
1535 * *
1536 ******************************************************************************/
zbx_snmp_translate(char * oid_translated,const char * snmp_oid,size_t max_oid_len)1537 static void zbx_snmp_translate(char *oid_translated, const char *snmp_oid, size_t max_oid_len)
1538 {
1539 typedef struct
1540 {
1541 const size_t sz;
1542 const char *mib;
1543 const char *replace;
1544 }
1545 zbx_mib_norm_t;
1546
1547 #define LEN_STR(x) ZBX_CONST_STRLEN(x), x
1548 static zbx_mib_norm_t mibs[] =
1549 {
1550 /* the most popular items first */
1551 {LEN_STR("ifDescr"), ".1.3.6.1.2.1.2.2.1.2"},
1552 {LEN_STR("ifInOctets"), ".1.3.6.1.2.1.2.2.1.10"},
1553 {LEN_STR("ifOutOctets"), ".1.3.6.1.2.1.2.2.1.16"},
1554 {LEN_STR("ifAdminStatus"), ".1.3.6.1.2.1.2.2.1.7"},
1555 {LEN_STR("ifOperStatus"), ".1.3.6.1.2.1.2.2.1.8"},
1556 {LEN_STR("ifIndex"), ".1.3.6.1.2.1.2.2.1.1"},
1557 {LEN_STR("ifType"), ".1.3.6.1.2.1.2.2.1.3"},
1558 {LEN_STR("ifMtu"), ".1.3.6.1.2.1.2.2.1.4"},
1559 {LEN_STR("ifSpeed"), ".1.3.6.1.2.1.2.2.1.5"},
1560 {LEN_STR("ifPhysAddress"), ".1.3.6.1.2.1.2.2.1.6"},
1561 {LEN_STR("ifInUcastPkts"), ".1.3.6.1.2.1.2.2.1.11"},
1562 {LEN_STR("ifInNUcastPkts"), ".1.3.6.1.2.1.2.2.1.12"},
1563 {LEN_STR("ifInDiscards"), ".1.3.6.1.2.1.2.2.1.13"},
1564 {LEN_STR("ifInErrors"), ".1.3.6.1.2.1.2.2.1.14"},
1565 {LEN_STR("ifInUnknownProtos"), ".1.3.6.1.2.1.2.2.1.15"},
1566 {LEN_STR("ifOutUcastPkts"), ".1.3.6.1.2.1.2.2.1.17"},
1567 {LEN_STR("ifOutNUcastPkts"), ".1.3.6.1.2.1.2.2.1.18"},
1568 {LEN_STR("ifOutDiscards"), ".1.3.6.1.2.1.2.2.1.19"},
1569 {LEN_STR("ifOutErrors"), ".1.3.6.1.2.1.2.2.1.20"},
1570 {LEN_STR("ifOutQLen"), ".1.3.6.1.2.1.2.2.1.21"},
1571 {0}
1572 };
1573 #undef LEN_STR
1574
1575 int found = 0, i;
1576
1577 zabbix_log(LOG_LEVEL_DEBUG, "In %s() OID:'%s'", __func__, snmp_oid);
1578
1579 for (i = 0; 0 != mibs[i].sz; i++)
1580 {
1581 if (0 == strncmp(mibs[i].mib, snmp_oid, mibs[i].sz))
1582 {
1583 found = 1;
1584 zbx_snprintf(oid_translated, max_oid_len, "%s%s", mibs[i].replace, snmp_oid + mibs[i].sz);
1585 break;
1586 }
1587 }
1588
1589 if (0 == found)
1590 zbx_strlcpy(oid_translated, snmp_oid, max_oid_len);
1591
1592 zabbix_log(LOG_LEVEL_DEBUG, "End of %s() oid_translated:'%s'", __func__, oid_translated);
1593 }
1594
1595 /* discovered SNMP object, identified by its index */
1596 typedef struct
1597 {
1598 /* object index returned by zbx_snmp_walk */
1599 char *index;
1600
1601 /* an array of OID values stored in the same order as defined in OID key */
1602 char **values;
1603 }
1604 zbx_snmp_dobject_t;
1605
1606 /* helper data structure used by snmp discovery */
1607 typedef struct
1608 {
1609 /* the index of OID being currently processed (walked) */
1610 int num;
1611
1612 /* the discovered SNMP objects */
1613 zbx_hashset_t objects;
1614
1615 /* the index (order) of discovered SNMP objects */
1616 zbx_vector_ptr_t index;
1617
1618 /* request data structure used to parse discovery OID key */
1619 AGENT_REQUEST request;
1620 }
1621 zbx_snmp_ddata_t;
1622
1623 /* discovery objects hashset support */
zbx_snmp_dobject_hash(const void * data)1624 static zbx_hash_t zbx_snmp_dobject_hash(const void *data)
1625 {
1626 const char *index = *(const char **)data;
1627
1628 return ZBX_DEFAULT_STRING_HASH_ALGO(index, strlen(index), ZBX_DEFAULT_HASH_SEED);
1629 }
1630
zbx_snmp_dobject_compare(const void * d1,const void * d2)1631 static int zbx_snmp_dobject_compare(const void *d1, const void *d2)
1632 {
1633 const char *i1 = *(const char **)d1;
1634 const char *i2 = *(const char **)d2;
1635
1636 return strcmp(i1, i2);
1637 }
1638
1639 /******************************************************************************
1640 * *
1641 * Function: zbx_snmp_ddata_init *
1642 * *
1643 * Purpose: initializes snmp discovery data object *
1644 * *
1645 * Parameters: data - [IN] snmp discovery data object *
1646 * key - [IN] discovery OID key *
1647 * error - [OUT] a buffer to store error message *
1648 * max_error_len - [IN] maximum error message length *
1649 * *
1650 * Return value: CONFIG_ERROR - OID key configuration error *
1651 * SUCCEED - if function successfully completed *
1652 * *
1653 ******************************************************************************/
zbx_snmp_ddata_init(zbx_snmp_ddata_t * data,const char * key,char * error,size_t max_error_len)1654 static int zbx_snmp_ddata_init(zbx_snmp_ddata_t *data, const char *key, char *error, size_t max_error_len)
1655 {
1656 int i, j, ret = CONFIG_ERROR;
1657
1658 init_request(&data->request);
1659
1660 if (SUCCEED != parse_item_key(key, &data->request))
1661 {
1662 zbx_strlcpy(error, "Invalid SNMP OID: cannot parse expression.", max_error_len);
1663 goto out;
1664 }
1665
1666 if (0 == data->request.nparam || 0 != (data->request.nparam & 1))
1667 {
1668 zbx_strlcpy(error, "Invalid SNMP OID: pairs of macro and OID are expected.", max_error_len);
1669 goto out;
1670 }
1671
1672 for (i = 0; i < data->request.nparam; i += 2)
1673 {
1674 if (SUCCEED != is_discovery_macro(data->request.params[i]))
1675 {
1676 zbx_snprintf(error, max_error_len, "Invalid SNMP OID: macro \"%s\" is invalid",
1677 data->request.params[i]);
1678 goto out;
1679 }
1680
1681 if (0 == strcmp(data->request.params[i], "{#SNMPINDEX}"))
1682 {
1683 zbx_strlcpy(error, "Invalid SNMP OID: macro \"{#SNMPINDEX}\" is not allowed.", max_error_len);
1684 goto out;
1685 }
1686 }
1687
1688 for (i = 2; i < data->request.nparam; i += 2)
1689 {
1690 for (j = 0; j < i; j += 2)
1691 {
1692 if (0 == strcmp(data->request.params[i], data->request.params[j]))
1693 {
1694 zbx_strlcpy(error, "Invalid SNMP OID: unique macros are expected.", max_error_len);
1695 goto out;
1696 }
1697 }
1698 }
1699
1700 zbx_hashset_create(&data->objects, 10, zbx_snmp_dobject_hash, zbx_snmp_dobject_compare);
1701 zbx_vector_ptr_create(&data->index);
1702
1703 ret = SUCCEED;
1704 out:
1705 if (SUCCEED != ret)
1706 free_request(&data->request);
1707
1708 return ret;
1709 }
1710
1711 /******************************************************************************
1712 * *
1713 * Function: zbx_snmp_ddata_clean *
1714 * *
1715 * Purpose: releases data allocated by snmp discovery *
1716 * *
1717 * Parameters: data - [IN] snmp discovery data object *
1718 * *
1719 ******************************************************************************/
zbx_snmp_ddata_clean(zbx_snmp_ddata_t * data)1720 static void zbx_snmp_ddata_clean(zbx_snmp_ddata_t *data)
1721 {
1722 int i;
1723 zbx_hashset_iter_t iter;
1724 zbx_snmp_dobject_t *obj;
1725
1726 zbx_vector_ptr_destroy(&data->index);
1727
1728 zbx_hashset_iter_reset(&data->objects, &iter);
1729 while (NULL != (obj = (zbx_snmp_dobject_t *)zbx_hashset_iter_next(&iter)))
1730 {
1731 for (i = 0; i < data->request.nparam / 2; i++)
1732 zbx_free(obj->values[i]);
1733
1734 zbx_free(obj->index);
1735 zbx_free(obj->values);
1736 }
1737
1738 zbx_hashset_destroy(&data->objects);
1739
1740 free_request(&data->request);
1741 }
1742
zbx_snmp_walk_discovery_cb(void * arg,const char * snmp_oid,const char * index,const char * value)1743 static void zbx_snmp_walk_discovery_cb(void *arg, const char *snmp_oid, const char *index, const char *value)
1744 {
1745 zbx_snmp_ddata_t *data = (zbx_snmp_ddata_t *)arg;
1746 zbx_snmp_dobject_t *obj;
1747
1748 ZBX_UNUSED(snmp_oid);
1749
1750 if (NULL == (obj = (zbx_snmp_dobject_t *)zbx_hashset_search(&data->objects, &index)))
1751 {
1752 zbx_snmp_dobject_t new_obj;
1753
1754 new_obj.index = zbx_strdup(NULL, index);
1755 new_obj.values = (char **)zbx_malloc(NULL, sizeof(char *) * data->request.nparam / 2);
1756 memset(new_obj.values, 0, sizeof(char *) * data->request.nparam / 2);
1757
1758 obj = (zbx_snmp_dobject_t *)zbx_hashset_insert(&data->objects, &new_obj, sizeof(new_obj));
1759 zbx_vector_ptr_append(&data->index, obj);
1760 }
1761
1762 obj->values[data->num] = zbx_strdup(NULL, value);
1763 }
1764
zbx_snmp_process_discovery(struct snmp_session * ss,const DC_ITEM * item,AGENT_RESULT * result,int * errcode,char * error,size_t max_error_len,int * max_succeed,int * min_fail,int max_vars,int bulk)1765 static int zbx_snmp_process_discovery(struct snmp_session *ss, const DC_ITEM *item, AGENT_RESULT *result,
1766 int *errcode, char *error, size_t max_error_len, int *max_succeed, int *min_fail, int max_vars,
1767 int bulk)
1768 {
1769 int i, j, ret;
1770 char oid_translated[ITEM_SNMP_OID_LEN_MAX];
1771 struct zbx_json js;
1772 zbx_snmp_ddata_t data;
1773 zbx_snmp_dobject_t *obj;
1774
1775 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1776
1777 if (SUCCEED != (ret = zbx_snmp_ddata_init(&data, item->snmp_oid, error, max_error_len)))
1778 goto out;
1779
1780 for (data.num = 0; data.num < data.request.nparam / 2; data.num++)
1781 {
1782 zbx_snmp_translate(oid_translated, data.request.params[data.num * 2 + 1], sizeof(oid_translated));
1783
1784 if (SUCCEED != (ret = zbx_snmp_walk(ss, item, oid_translated, error, max_error_len,
1785 max_succeed, min_fail, max_vars, bulk, zbx_snmp_walk_discovery_cb, (void *)&data)))
1786 {
1787 goto clean;
1788 }
1789 }
1790
1791 zbx_json_initarray(&js, ZBX_JSON_STAT_BUF_LEN);
1792
1793 for (i = 0; i < data.index.values_num; i++)
1794 {
1795 obj = (zbx_snmp_dobject_t *)data.index.values[i];
1796
1797 zbx_json_addobject(&js, NULL);
1798 zbx_json_addstring(&js, "{#SNMPINDEX}", obj->index, ZBX_JSON_TYPE_STRING);
1799
1800 for (j = 0; j < data.request.nparam / 2; j++)
1801 {
1802 if (NULL == obj->values[j])
1803 continue;
1804
1805 zbx_json_addstring(&js, data.request.params[j * 2], obj->values[j], ZBX_JSON_TYPE_STRING);
1806 }
1807 zbx_json_close(&js);
1808 }
1809
1810 zbx_json_close(&js);
1811
1812 SET_TEXT_RESULT(result, zbx_strdup(NULL, js.buffer));
1813
1814 zbx_json_free(&js);
1815 clean:
1816 zbx_snmp_ddata_clean(&data);
1817 out:
1818 if (SUCCEED != (*errcode = ret))
1819 SET_MSG_RESULT(result, zbx_strdup(NULL, error));
1820
1821 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
1822
1823 return ret;
1824 }
1825
zbx_snmp_walk_cache_cb(void * arg,const char * snmp_oid,const char * index,const char * value)1826 static void zbx_snmp_walk_cache_cb(void *arg, const char *snmp_oid, const char *index, const char *value)
1827 {
1828 cache_put_snmp_index((const DC_ITEM *)arg, snmp_oid, index, value);
1829 }
1830
zbx_snmp_process_dynamic(struct snmp_session * ss,const DC_ITEM * items,AGENT_RESULT * results,int * errcodes,int num,char * error,size_t max_error_len,int * max_succeed,int * min_fail,int bulk,unsigned char poller_type)1831 static int zbx_snmp_process_dynamic(struct snmp_session *ss, const DC_ITEM *items, AGENT_RESULT *results,
1832 int *errcodes, int num, char *error, size_t max_error_len, int *max_succeed, int *min_fail, int bulk,
1833 unsigned char poller_type)
1834 {
1835 int i, j, k, ret;
1836 int to_walk[MAX_SNMP_ITEMS], to_walk_num = 0;
1837 int to_verify[MAX_SNMP_ITEMS], to_verify_num = 0;
1838 char to_verify_oids[MAX_SNMP_ITEMS][ITEM_SNMP_OID_LEN_MAX];
1839 unsigned char query_and_ignore_type[MAX_SNMP_ITEMS];
1840 char index_oids[MAX_SNMP_ITEMS][ITEM_SNMP_OID_LEN_MAX];
1841 char index_values[MAX_SNMP_ITEMS][ITEM_SNMP_OID_LEN_MAX];
1842 char oids_translated[MAX_SNMP_ITEMS][ITEM_SNMP_OID_LEN_MAX];
1843 char *idx = NULL, *pl;
1844 size_t idx_alloc = 32;
1845
1846 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1847
1848 idx = (char *)zbx_malloc(idx, idx_alloc);
1849
1850 /* perform initial item validation */
1851
1852 for (i = 0; i < num; i++)
1853 {
1854 char method[8];
1855
1856 if (SUCCEED != errcodes[i])
1857 continue;
1858
1859 if (3 != num_key_param(items[i].snmp_oid))
1860 {
1861 SET_MSG_RESULT(&results[i], zbx_dsprintf(NULL, "OID \"%s\" contains unsupported parameters.",
1862 items[i].snmp_oid));
1863 errcodes[i] = CONFIG_ERROR;
1864 continue;
1865 }
1866
1867 get_key_param(items[i].snmp_oid, 1, method, sizeof(method));
1868 get_key_param(items[i].snmp_oid, 2, index_oids[i], sizeof(index_oids[i]));
1869 get_key_param(items[i].snmp_oid, 3, index_values[i], sizeof(index_values[i]));
1870
1871 if (0 != strcmp("index", method))
1872 {
1873 SET_MSG_RESULT(&results[i], zbx_dsprintf(NULL, "Unsupported method \"%s\" in the OID \"%s\".",
1874 method, items[i].snmp_oid));
1875 errcodes[i] = CONFIG_ERROR;
1876 continue;
1877 }
1878
1879 zbx_snmp_translate(oids_translated[i], index_oids[i], sizeof(oids_translated[i]));
1880
1881 if (SUCCEED == cache_get_snmp_index(&items[i], oids_translated[i], index_values[i], &idx, &idx_alloc))
1882 {
1883 zbx_snprintf(to_verify_oids[i], sizeof(to_verify_oids[i]), "%s.%s", oids_translated[i], idx);
1884
1885 to_verify[to_verify_num++] = i;
1886 query_and_ignore_type[i] = 1;
1887 }
1888 else
1889 {
1890 to_walk[to_walk_num++] = i;
1891 query_and_ignore_type[i] = 0;
1892 }
1893 }
1894
1895 /* verify that cached indices are still valid */
1896
1897 if (0 != to_verify_num)
1898 {
1899 ret = zbx_snmp_get_values(ss, items, to_verify_oids, results, errcodes, query_and_ignore_type, num, 0,
1900 error, max_error_len, max_succeed, min_fail, poller_type);
1901
1902 if (SUCCEED != ret && NOTSUPPORTED != ret)
1903 goto exit;
1904
1905 for (i = 0; i < to_verify_num; i++)
1906 {
1907 j = to_verify[i];
1908
1909 if (SUCCEED != errcodes[j])
1910 continue;
1911
1912 if (NULL == GET_STR_RESULT(&results[j]) || 0 != strcmp(results[j].str, index_values[j]))
1913 {
1914 to_walk[to_walk_num++] = j;
1915 }
1916 else
1917 {
1918 /* ready to construct the final OID with index */
1919
1920 size_t len;
1921
1922 len = strlen(oids_translated[j]);
1923
1924 pl = strchr(items[j].snmp_oid, '[');
1925
1926 *pl = '\0';
1927 zbx_snmp_translate(oids_translated[j], items[j].snmp_oid, sizeof(oids_translated[j]));
1928 *pl = '[';
1929
1930 zbx_strlcat(oids_translated[j], to_verify_oids[j] + len, sizeof(oids_translated[j]));
1931 }
1932
1933 free_result(&results[j]);
1934 }
1935 }
1936
1937 /* walk OID trees to build index cache for cache misses */
1938
1939 if (0 != to_walk_num)
1940 {
1941 for (i = 0; i < to_walk_num; i++)
1942 {
1943 int errcode;
1944
1945 j = to_walk[i];
1946
1947 /* see whether this OID tree was already walked for another item */
1948
1949 for (k = 0; k < i; k++)
1950 {
1951 if (0 == strcmp(oids_translated[to_walk[k]], oids_translated[j]))
1952 break;
1953 }
1954
1955 if (k != i)
1956 continue;
1957
1958 /* walk */
1959
1960 cache_del_snmp_index_subtree(&items[j], oids_translated[j]);
1961
1962 errcode = zbx_snmp_walk(ss, &items[j], oids_translated[j], error, max_error_len, max_succeed,
1963 min_fail, num, bulk, zbx_snmp_walk_cache_cb, (void *)&items[j]);
1964
1965 if (NETWORK_ERROR == errcode)
1966 {
1967 /* consider a network error as relating to all items passed to */
1968 /* this function, including those we did not just try to walk for */
1969
1970 ret = NETWORK_ERROR;
1971 goto exit;
1972 }
1973
1974 if (CONFIG_ERROR == errcode || NOTSUPPORTED == errcode)
1975 {
1976 /* consider a configuration or "not supported" error as */
1977 /* relating only to the items we have just tried to walk for */
1978
1979 for (k = i; k < to_walk_num; k++)
1980 {
1981 if (0 == strcmp(oids_translated[to_walk[k]], oids_translated[j]))
1982 {
1983 SET_MSG_RESULT(&results[to_walk[k]], zbx_strdup(NULL, error));
1984 errcodes[to_walk[k]] = errcode;
1985 }
1986 }
1987 }
1988 }
1989
1990 for (i = 0; i < to_walk_num; i++)
1991 {
1992 j = to_walk[i];
1993
1994 if (SUCCEED != errcodes[j])
1995 continue;
1996
1997 if (SUCCEED == cache_get_snmp_index(&items[j], oids_translated[j], index_values[j], &idx,
1998 &idx_alloc))
1999 {
2000 /* ready to construct the final OID with index */
2001
2002 pl = strchr(items[j].snmp_oid, '[');
2003
2004 *pl = '\0';
2005 zbx_snmp_translate(oids_translated[j], items[j].snmp_oid, sizeof(oids_translated[j]));
2006 *pl = '[';
2007
2008 zbx_strlcat(oids_translated[j], ".", sizeof(oids_translated[j]));
2009 zbx_strlcat(oids_translated[j], idx, sizeof(oids_translated[j]));
2010 }
2011 else
2012 {
2013 SET_MSG_RESULT(&results[j], zbx_dsprintf(NULL,
2014 "Cannot find index of \"%s\" in \"%s\".",
2015 index_values[j], index_oids[j]));
2016 errcodes[j] = NOTSUPPORTED;
2017 }
2018 }
2019 }
2020
2021 /* query values based on the indices verified and/or determined above */
2022
2023 ret = zbx_snmp_get_values(ss, items, oids_translated, results, errcodes, NULL, num, 0, error, max_error_len,
2024 max_succeed, min_fail, poller_type);
2025 exit:
2026 zbx_free(idx);
2027
2028 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
2029
2030 return ret;
2031 }
2032
zbx_snmp_process_standard(struct snmp_session * ss,const DC_ITEM * items,AGENT_RESULT * results,int * errcodes,int num,char * error,size_t max_error_len,int * max_succeed,int * min_fail,unsigned char poller_type)2033 static int zbx_snmp_process_standard(struct snmp_session *ss, const DC_ITEM *items, AGENT_RESULT *results,
2034 int *errcodes, int num, char *error, size_t max_error_len, int *max_succeed, int *min_fail,
2035 unsigned char poller_type)
2036 {
2037 int i, ret;
2038 char oids_translated[MAX_SNMP_ITEMS][ITEM_SNMP_OID_LEN_MAX];
2039
2040 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
2041
2042 for (i = 0; i < num; i++)
2043 {
2044 if (SUCCEED != errcodes[i])
2045 continue;
2046
2047 if (0 != num_key_param(items[i].snmp_oid))
2048 {
2049 SET_MSG_RESULT(&results[i], zbx_dsprintf(NULL, "OID \"%s\" contains unsupported parameters.",
2050 items[i].snmp_oid));
2051 errcodes[i] = CONFIG_ERROR;
2052 continue;
2053 }
2054
2055 zbx_snmp_translate(oids_translated[i], items[i].snmp_oid, sizeof(oids_translated[i]));
2056 }
2057
2058 ret = zbx_snmp_get_values(ss, items, oids_translated, results, errcodes, NULL, num, 0, error, max_error_len,
2059 max_succeed, min_fail, poller_type);
2060
2061 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
2062
2063 return ret;
2064 }
2065
get_value_snmp(const DC_ITEM * item,AGENT_RESULT * result,unsigned char poller_type)2066 int get_value_snmp(const DC_ITEM *item, AGENT_RESULT *result, unsigned char poller_type)
2067 {
2068 int errcode = SUCCEED;
2069
2070 get_values_snmp(item, result, &errcode, 1, poller_type);
2071
2072 return errcode;
2073 }
2074
zbx_init_snmp(void)2075 static void zbx_init_snmp(void)
2076 {
2077 sigset_t mask, orig_mask;
2078
2079 if (1 == zbx_snmp_init_done)
2080 return;
2081
2082 sigemptyset(&mask);
2083 sigaddset(&mask, SIGTERM);
2084 sigaddset(&mask, SIGUSR2);
2085 sigaddset(&mask, SIGHUP);
2086 sigaddset(&mask, SIGQUIT);
2087 sigprocmask(SIG_BLOCK, &mask, &orig_mask);
2088
2089 init_snmp(progname);
2090 zbx_snmp_init_done = 1;
2091
2092 sigprocmask(SIG_SETMASK, &orig_mask, NULL);
2093 }
2094
get_values_snmp(const DC_ITEM * items,AGENT_RESULT * results,int * errcodes,int num,unsigned char poller_type)2095 void get_values_snmp(const DC_ITEM *items, AGENT_RESULT *results, int *errcodes, int num, unsigned char poller_type)
2096 {
2097 struct snmp_session *ss;
2098 char error[MAX_STRING_LEN];
2099 int i, j, err = SUCCEED, max_succeed = 0, min_fail = MAX_SNMP_ITEMS + 1,
2100 bulk = SNMP_BULK_ENABLED;
2101
2102 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'%s' addr:'%s' num:%d",
2103 __func__, items[0].host.host, items[0].interface.addr, num);
2104
2105 zbx_init_snmp(); /* avoid high CPU usage by only initializing SNMP once used */
2106
2107 for (j = 0; j < num; j++) /* locate first supported item to use as a reference */
2108 {
2109 if (SUCCEED == errcodes[j])
2110 break;
2111 }
2112
2113 if (j == num) /* all items already NOTSUPPORTED (with invalid key, port or SNMP parameters) */
2114 goto out;
2115
2116 if (NULL == (ss = zbx_snmp_open_session(&items[j], error, sizeof(error))))
2117 {
2118 err = NETWORK_ERROR;
2119 goto exit;
2120 }
2121
2122 if (0 != (ZBX_FLAG_DISCOVERY_RULE & items[j].flags) || 0 == strncmp(items[j].snmp_oid, "discovery[", 10))
2123 {
2124 int max_vars;
2125
2126 max_vars = DCconfig_get_suggested_snmp_vars(items[j].interface.interfaceid, &bulk);
2127
2128 err = zbx_snmp_process_discovery(ss, &items[j], &results[j], &errcodes[j], error, sizeof(error),
2129 &max_succeed, &min_fail, max_vars, bulk);
2130 }
2131 else if (NULL != strchr(items[j].snmp_oid, '['))
2132 {
2133 (void)DCconfig_get_suggested_snmp_vars(items[j].interface.interfaceid, &bulk);
2134
2135 err = zbx_snmp_process_dynamic(ss, items + j, results + j, errcodes + j, num - j, error, sizeof(error),
2136 &max_succeed, &min_fail, bulk, poller_type);
2137 }
2138 else
2139 {
2140 err = zbx_snmp_process_standard(ss, items + j, results + j, errcodes + j, num - j, error, sizeof(error),
2141 &max_succeed, &min_fail, poller_type);
2142 }
2143
2144 zbx_snmp_close_session(ss);
2145 exit:
2146 if (SUCCEED != err)
2147 {
2148 zabbix_log(LOG_LEVEL_DEBUG, "getting SNMP values failed: %s", error);
2149
2150 for (i = j; i < num; i++)
2151 {
2152 if (SUCCEED != errcodes[i])
2153 continue;
2154
2155 SET_MSG_RESULT(&results[i], zbx_strdup(NULL, error));
2156 errcodes[i] = err;
2157 }
2158 }
2159 else if (SNMP_BULK_ENABLED == bulk && (0 != max_succeed || MAX_SNMP_ITEMS + 1 != min_fail))
2160 {
2161 DCconfig_update_interface_snmp_stats(items[j].interface.interfaceid, max_succeed, min_fail);
2162 }
2163 out:
2164 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
2165 }
2166
zbx_shutdown_snmp(void)2167 static void zbx_shutdown_snmp(void)
2168 {
2169 sigset_t mask, orig_mask;
2170
2171 sigemptyset(&mask);
2172 sigaddset(&mask, SIGTERM);
2173 sigaddset(&mask, SIGUSR2);
2174 sigaddset(&mask, SIGHUP);
2175 sigaddset(&mask, SIGQUIT);
2176 sigprocmask(SIG_BLOCK, &mask, &orig_mask);
2177
2178 snmp_shutdown(progname);
2179 zbx_snmp_init_done = 0;
2180
2181 sigprocmask(SIG_SETMASK, &orig_mask, NULL);
2182 }
2183
zbx_clear_cache_snmp(unsigned char process_type,int process_num)2184 void zbx_clear_cache_snmp(unsigned char process_type, int process_num)
2185 {
2186 zabbix_log(LOG_LEVEL_WARNING, "forced reloading of the snmp cache on [%s #%d]", get_process_type_string(process_type),
2187 process_num);
2188
2189 if (0 == zbx_snmp_init_done)
2190 return;
2191
2192 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, 1);
2193 zbx_shutdown_snmp();
2194 }
2195
2196 #endif /* HAVE_NETSNMP */
2197