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