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