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 "common.h"
21 
22 #include "dbcache.h"
23 #include "log.h"
24 #include "zbxserver.h"
25 #include "zbxicmpping.h"
26 #include "daemon.h"
27 #include "zbxself.h"
28 
29 #include "pinger.h"
30 
31 /* defines for `fping' and `fping6' to successfully process pings */
32 #define MIN_COUNT	1
33 #define MAX_COUNT	10000
34 #define MIN_INTERVAL	20
35 #define MIN_SIZE	24
36 #define MAX_SIZE	65507
37 #define MIN_TIMEOUT	50
38 
39 extern unsigned char	process_type, program_type;
40 extern int		server_num, process_num;
41 
42 /******************************************************************************
43  *                                                                            *
44  * Function: process_value                                                    *
45  *                                                                            *
46  * Purpose: process new item value                                            *
47  *                                                                            *
48  * Parameters:                                                                *
49  *                                                                            *
50  * Return value:                                                              *
51  *                                                                            *
52  * Author: Alexei Vladishev, Alexander Vladishev                              *
53  *                                                                            *
54  * Comments:                                                                  *
55  *                                                                            *
56  ******************************************************************************/
process_value(zbx_uint64_t itemid,zbx_uint64_t * value_ui64,double * value_dbl,zbx_timespec_t * ts,int ping_result,char * error)57 static void	process_value(zbx_uint64_t itemid, zbx_uint64_t *value_ui64, double *value_dbl,	zbx_timespec_t *ts,
58 		int ping_result, char *error)
59 {
60 	const char	*__function_name = "process_value";
61 	DC_ITEM		item;
62 	int		errcode;
63 	AGENT_RESULT	value;
64 
65 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
66 
67 	DCconfig_get_items_by_itemids(&item, &itemid, &errcode, 1);
68 
69 	if (SUCCEED != errcode)
70 		goto clean;
71 
72 	if (ITEM_STATUS_ACTIVE != item.status)
73 		goto clean;
74 
75 	if (HOST_STATUS_MONITORED != item.host.status)
76 		goto clean;
77 
78 	if (NOTSUPPORTED == ping_result)
79 	{
80 		item.state = ITEM_STATE_NOTSUPPORTED;
81 		dc_add_history(item.itemid, item.value_type, item.flags, NULL, ts, item.state, error);
82 	}
83 	else
84 	{
85 		init_result(&value);
86 
87 		if (NULL != value_ui64)
88 			SET_UI64_RESULT(&value, *value_ui64);
89 		else
90 			SET_DBL_RESULT(&value, *value_dbl);
91 
92 		item.state = ITEM_STATE_NORMAL;
93 		dc_add_history(item.itemid, item.value_type, item.flags, &value, ts, item.state, NULL);
94 
95 		free_result(&value);
96 	}
97 clean:
98 	DCrequeue_items(&item.itemid, &item.state, &ts->sec, NULL, NULL, &errcode, 1);
99 
100 	DCconfig_clean_items(&item, &errcode, 1);
101 
102 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
103 }
104 
105 /******************************************************************************
106  *                                                                            *
107  * Function: process_values                                                   *
108  *                                                                            *
109  * Purpose: process new item values                                           *
110  *                                                                            *
111  * Parameters:                                                                *
112  *                                                                            *
113  * Return value:                                                              *
114  *                                                                            *
115  * Author: Alexei Vladishev, Alexander Vladishev                              *
116  *                                                                            *
117  * Comments:                                                                  *
118  *                                                                            *
119  ******************************************************************************/
process_values(icmpitem_t * items,int first_index,int last_index,ZBX_FPING_HOST * hosts,int hosts_count,zbx_timespec_t * ts,int ping_result,char * error)120 static void	process_values(icmpitem_t *items, int first_index, int last_index, ZBX_FPING_HOST *hosts,
121 		int hosts_count, zbx_timespec_t *ts, int ping_result, char *error)
122 {
123 	const char	*__function_name = "process_values";
124 	int		i, h;
125 	zbx_uint64_t	value_uint64;
126 	double		value_dbl;
127 
128 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
129 
130 	for (h = 0; h < hosts_count; h++)
131 	{
132 		const ZBX_FPING_HOST	*host = &hosts[h];
133 
134 		if (NOTSUPPORTED == ping_result)
135 		{
136 			zabbix_log(LOG_LEVEL_DEBUG, "host [%s] %s", host->addr, error);
137 		}
138 		else
139 		{
140 			zabbix_log(LOG_LEVEL_DEBUG, "host [%s] cnt=%d rcv=%d"
141 					" min=" ZBX_FS_DBL " max=" ZBX_FS_DBL " sum=" ZBX_FS_DBL,
142 					host->addr, host->cnt, host->rcv, host->min, host->max, host->sum);
143 		}
144 
145 		for (i = first_index; i < last_index; i++)
146 		{
147 			const icmpitem_t	*item = &items[i];
148 
149 			if (0 != strcmp(item->addr, host->addr))
150 				continue;
151 
152 			if (NOTSUPPORTED == ping_result)
153 			{
154 				process_value(item->itemid, NULL, NULL, ts, NOTSUPPORTED, error);
155 				continue;
156 			}
157 
158 			if (0 == host->cnt)
159 			{
160 				process_value(item->itemid, NULL, NULL, ts, NOTSUPPORTED,
161 						"Cannot send ICMP ping packets to this host.");
162 				continue;
163 			}
164 
165 			switch (item->icmpping)
166 			{
167 				case ICMPPING:
168 					value_uint64 = (0 != host->rcv ? 1 : 0);
169 					process_value(item->itemid, &value_uint64, NULL, ts, SUCCEED, NULL);
170 					break;
171 				case ICMPPINGSEC:
172 					switch (item->type)
173 					{
174 						case ICMPPINGSEC_MIN:
175 							value_dbl = host->min;
176 							break;
177 						case ICMPPINGSEC_MAX:
178 							value_dbl = host->max;
179 							break;
180 						case ICMPPINGSEC_AVG:
181 							value_dbl = (0 != host->rcv ? host->sum / host->rcv : 0);
182 							break;
183 					}
184 
185 					if (0 < value_dbl && ZBX_FLOAT_PRECISION > value_dbl)
186 						value_dbl = ZBX_FLOAT_PRECISION;
187 
188 					process_value(item->itemid, NULL, &value_dbl, ts, SUCCEED, NULL);
189 					break;
190 				case ICMPPINGLOSS:
191 					value_dbl = (100 * (host->cnt - host->rcv)) / (double)host->cnt;
192 					process_value(item->itemid, NULL, &value_dbl, ts, SUCCEED, NULL);
193 					break;
194 			}
195 		}
196 	}
197 
198 	dc_flush_history();
199 
200 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
201 }
202 
parse_key_params(const char * key,const char * host_addr,icmpping_t * icmpping,char ** addr,int * count,int * interval,int * size,int * timeout,icmppingsec_type_t * type,char * error,int max_error_len)203 static int	parse_key_params(const char *key, const char *host_addr, icmpping_t *icmpping, char **addr, int *count,
204 		int *interval, int *size, int *timeout, icmppingsec_type_t *type, char *error, int max_error_len)
205 {
206 	const char	*tmp;
207 	int		ret = NOTSUPPORTED;
208 	AGENT_REQUEST	request;
209 
210 	init_request(&request);
211 
212 	if (SUCCEED != parse_item_key(key, &request))
213 	{
214 		zbx_snprintf(error, max_error_len, "Invalid item key format.");
215 		goto out;
216 	}
217 
218 	if (0 == strcmp(get_rkey(&request), SERVER_ICMPPING_KEY))
219 	{
220 		*icmpping = ICMPPING;
221 	}
222 	else if (0 == strcmp(get_rkey(&request), SERVER_ICMPPINGLOSS_KEY))
223 	{
224 		*icmpping = ICMPPINGLOSS;
225 	}
226 	else if (0 == strcmp(get_rkey(&request), SERVER_ICMPPINGSEC_KEY))
227 	{
228 		*icmpping = ICMPPINGSEC;
229 	}
230 	else
231 	{
232 		zbx_snprintf(error, max_error_len, "Unsupported pinger key.");
233 		goto out;
234 	}
235 
236 	if (6 < get_rparams_num(&request) || (ICMPPINGSEC != *icmpping && 5 < get_rparams_num(&request)))
237 	{
238 		zbx_snprintf(error, max_error_len, "Too many arguments.");
239 		goto out;
240 	}
241 
242 	if (NULL == (tmp = get_rparam(&request, 1)) || '\0' == *tmp)
243 	{
244 		*count = 3;
245 	}
246 	else if (FAIL == is_uint31(tmp, count) || MIN_COUNT > *count || *count > MAX_COUNT)
247 	{
248 		zbx_snprintf(error, max_error_len, "Number of packets \"%s\" is not between %d and %d.",
249 				tmp, MIN_COUNT, MAX_COUNT);
250 		goto out;
251 	}
252 
253 	if (NULL == (tmp = get_rparam(&request, 2)) || '\0' == *tmp)
254 	{
255 		*interval = 0;
256 	}
257 	else if (FAIL == is_uint31(tmp, interval) || MIN_INTERVAL > *interval)
258 	{
259 		zbx_snprintf(error, max_error_len, "Interval \"%s\" should be at least %d.", tmp, MIN_INTERVAL);
260 		goto out;
261 	}
262 
263 	if (NULL == (tmp = get_rparam(&request, 3)) || '\0' == *tmp)
264 	{
265 		*size = 0;
266 	}
267 	else if (FAIL == is_uint31(tmp, size) || MIN_SIZE > *size || *size > MAX_SIZE)
268 	{
269 		zbx_snprintf(error, max_error_len, "Packet size \"%s\" is not between %d and %d.",
270 				tmp, MIN_SIZE, MAX_SIZE);
271 		goto out;
272 	}
273 
274 	if (NULL == (tmp = get_rparam(&request, 4)) || '\0' == *tmp)
275 	{
276 		*timeout = 0;
277 	}
278 	else if (FAIL == is_uint31(tmp, timeout) || MIN_TIMEOUT > *timeout)
279 	{
280 		zbx_snprintf(error, max_error_len, "Timeout \"%s\" should be at least %d.", tmp, MIN_TIMEOUT);
281 		goto out;
282 	}
283 
284 	if (NULL == (tmp = get_rparam(&request, 5)) || '\0' == *tmp)
285 	{
286 		*type = ICMPPINGSEC_AVG;
287 	}
288 	else
289 	{
290 		if (0 == strcmp(tmp, "min"))
291 		{
292 			*type = ICMPPINGSEC_MIN;
293 		}
294 		else if (0 == strcmp(tmp, "avg"))
295 		{
296 			*type = ICMPPINGSEC_AVG;
297 		}
298 		else if (0 == strcmp(tmp, "max"))
299 		{
300 			*type = ICMPPINGSEC_MAX;
301 		}
302 		else
303 		{
304 			zbx_snprintf(error, max_error_len, "Mode \"%s\" is not supported.", tmp);
305 			goto out;
306 		}
307 	}
308 
309 	if (NULL == (tmp = get_rparam(&request, 0)) || '\0' == *tmp)
310 		*addr = strdup(host_addr);
311 	else
312 		*addr = strdup(tmp);
313 
314 	ret = SUCCEED;
315 out:
316 	free_request(&request);
317 
318 	return ret;
319 }
320 
get_icmpping_nearestindex(icmpitem_t * items,int items_count,int count,int interval,int size,int timeout)321 static int	get_icmpping_nearestindex(icmpitem_t *items, int items_count, int count, int interval, int size, int timeout)
322 {
323 	int		first_index, last_index, index;
324 	icmpitem_t	*item;
325 
326 	if (items_count == 0)
327 		return 0;
328 
329 	first_index = 0;
330 	last_index = items_count - 1;
331 	while (1)
332 	{
333 		index = first_index + (last_index - first_index) / 2;
334 		item = &items[index];
335 
336 		if (item->count == count && item->interval == interval && item->size == size && item->timeout == timeout)
337 			return index;
338 		else if (last_index == first_index)
339 		{
340 			if (item->count < count ||
341 					(item->count == count && item->interval < interval) ||
342 					(item->count == count && item->interval == interval && item->size < size) ||
343 					(item->count == count && item->interval == interval && item->size == size && item->timeout < timeout))
344 				index++;
345 			return index;
346 		}
347 		else if (item->count < count ||
348 				(item->count == count && item->interval < interval) ||
349 				(item->count == count && item->interval == interval && item->size < size) ||
350 				(item->count == count && item->interval == interval && item->size == size && item->timeout < timeout))
351 			first_index = index + 1;
352 		else
353 			last_index = index;
354 	}
355 }
356 
add_icmpping_item(icmpitem_t ** items,int * items_alloc,int * items_count,int count,int interval,int size,int timeout,zbx_uint64_t itemid,char * addr,icmpping_t icmpping,icmppingsec_type_t type)357 static void	add_icmpping_item(icmpitem_t **items, int *items_alloc, int *items_count, int count, int interval,
358 		int size, int timeout, zbx_uint64_t itemid, char *addr, icmpping_t icmpping, icmppingsec_type_t type)
359 {
360 	const char	*__function_name = "add_icmpping_item";
361 	int		index;
362 	icmpitem_t	*item;
363 	size_t		sz;
364 
365 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() addr:'%s' count:%d interval:%d size:%d timeout:%d",
366 			__function_name, addr, count, interval, size, timeout);
367 
368 	index = get_icmpping_nearestindex(*items, *items_count, count, interval, size, timeout);
369 
370 	if (*items_alloc == *items_count)
371 	{
372 		*items_alloc += 4;
373 		sz = *items_alloc * sizeof(icmpitem_t);
374 		*items = zbx_realloc(*items, sz);
375 	}
376 
377 	memmove(&(*items)[index + 1], &(*items)[index], sizeof(icmpitem_t) * (*items_count - index));
378 
379 	item = &(*items)[index];
380 	item->count	= count;
381 	item->interval	= interval;
382 	item->size	= size;
383 	item->timeout	= timeout;
384 	item->itemid	= itemid;
385 	item->addr	= addr;
386 	item->icmpping	= icmpping;
387 	item->type	= type;
388 
389 	(*items_count)++;
390 
391 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
392 }
393 
394 /******************************************************************************
395  *                                                                            *
396  * Function: get_pinger_hosts                                                 *
397  *                                                                            *
398  * Purpose: creates buffer which contains list of hosts to ping               *
399  *                                                                            *
400  * Parameters:                                                                *
401  *                                                                            *
402  * Return value: SUCCEED - the file was created successfully                  *
403  *               FAIL - otherwise                                             *
404  *                                                                            *
405  * Author: Alexei Vladishev, Alexander Vladishev                              *
406  *                                                                            *
407  * Comments:                                                                  *
408  *                                                                            *
409  ******************************************************************************/
get_pinger_hosts(icmpitem_t ** icmp_items,int * icmp_items_alloc,int * icmp_items_count)410 static void	get_pinger_hosts(icmpitem_t **icmp_items, int *icmp_items_alloc, int *icmp_items_count)
411 {
412 	const char		*__function_name = "get_pinger_hosts";
413 	DC_ITEM			items[MAX_PINGER_ITEMS];
414 	int			i, num, count, interval, size, timeout, rc, errcode = SUCCEED;
415 	char			error[MAX_STRING_LEN], *addr = NULL;
416 	icmpping_t		icmpping;
417 	icmppingsec_type_t	type;
418 
419 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
420 
421 	num = DCconfig_get_poller_items(ZBX_POLLER_TYPE_PINGER, items);
422 
423 	for (i = 0; i < num; i++)
424 	{
425 		ZBX_STRDUP(items[i].key, items[i].key_orig);
426 		rc = substitute_key_macros(&items[i].key, NULL, &items[i], NULL, MACRO_TYPE_ITEM_KEY,
427 				error, sizeof(error));
428 
429 		if (SUCCEED == rc)
430 		{
431 			rc = parse_key_params(items[i].key, items[i].interface.addr, &icmpping, &addr, &count,
432 					&interval, &size, &timeout, &type, error, sizeof(error));
433 		}
434 
435 		if (SUCCEED == rc)
436 		{
437 			add_icmpping_item(icmp_items, icmp_items_alloc, icmp_items_count, count, interval, size,
438 				timeout, items[i].itemid, addr, icmpping, type);
439 		}
440 		else
441 		{
442 			zbx_timespec_t	ts;
443 
444 			zbx_timespec(&ts);
445 
446 			items[i].state = ITEM_STATE_NOTSUPPORTED;
447 			dc_add_history(items[i].itemid, items[i].value_type, items[i].flags, NULL, &ts,
448 					items[i].state, error);
449 
450 			DCrequeue_items(&items[i].itemid, &items[i].state, &ts.sec, NULL, NULL, &errcode, 1);
451 		}
452 
453 		zbx_free(items[i].key);
454 	}
455 
456 	DCconfig_clean_items(items, NULL, num);
457 
458 	dc_flush_history();
459 
460 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d", __function_name, *icmp_items_count);
461 }
462 
free_hosts(icmpitem_t ** items,int * items_count)463 static void	free_hosts(icmpitem_t **items, int *items_count)
464 {
465 	int	i;
466 
467 	for (i = 0; i < *items_count; i++)
468 		zbx_free((*items)[i].addr);
469 
470 	*items_count = 0;
471 }
472 
add_pinger_host(ZBX_FPING_HOST ** hosts,int * hosts_alloc,int * hosts_count,char * addr)473 static void	add_pinger_host(ZBX_FPING_HOST **hosts, int *hosts_alloc, int *hosts_count, char *addr)
474 {
475 	const char	*__function_name = "add_pinger_host";
476 
477 	int		i;
478 	size_t		sz;
479 	ZBX_FPING_HOST	*h;
480 
481 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() addr:'%s'", __function_name, addr);
482 
483 	for (i = 0; i < *hosts_count; i++)
484 	{
485 		if (0 == strcmp(addr, (*hosts)[i].addr))
486 			return;
487 	}
488 
489 	(*hosts_count)++;
490 
491 	if (*hosts_alloc < *hosts_count)
492 	{
493 		*hosts_alloc += 4;
494 		sz = *hosts_alloc * sizeof(ZBX_FPING_HOST);
495 		*hosts = zbx_realloc(*hosts, sz);
496 	}
497 
498 	h = &(*hosts)[*hosts_count - 1];
499 	memset(h, 0, sizeof(ZBX_FPING_HOST));
500 	h->addr = addr;
501 
502 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
503 }
504 
505 /******************************************************************************
506  *                                                                            *
507  * Function: process_pinger_hosts                                             *
508  *                                                                            *
509  * Purpose:                                                                   *
510  *                                                                            *
511  * Parameters:                                                                *
512  *                                                                            *
513  * Return value:                                                              *
514  *                                                                            *
515  * Author: Alexander Vladishev                                                *
516  *                                                                            *
517  * Comments:                                                                  *
518  *                                                                            *
519  ******************************************************************************/
process_pinger_hosts(icmpitem_t * items,int items_count)520 static void	process_pinger_hosts(icmpitem_t *items, int items_count)
521 {
522 	const char		*__function_name = "process_pinger_hosts";
523 	int			i, first_index = 0, ping_result;
524 	char			error[ITEM_ERROR_LEN_MAX];
525 	static ZBX_FPING_HOST	*hosts = NULL;
526 	static int		hosts_alloc = 4;
527 	int			hosts_count = 0;
528 	zbx_timespec_t		ts;
529 
530 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
531 
532 	if (NULL == hosts)
533 		hosts = zbx_malloc(hosts, sizeof(ZBX_FPING_HOST) * hosts_alloc);
534 
535 	for (i = 0; i < items_count; i++)
536 	{
537 		add_pinger_host(&hosts, &hosts_alloc, &hosts_count, items[i].addr);
538 
539 		if (i == items_count - 1 || items[i].count != items[i + 1].count || items[i].interval != items[i + 1].interval ||
540 				items[i].size != items[i + 1].size || items[i].timeout != items[i + 1].timeout)
541 		{
542 			zbx_setproctitle("%s #%d [pinging hosts]", get_process_type_string(process_type), process_num);
543 
544 			zbx_timespec(&ts);
545 
546 			ping_result = do_ping(hosts, hosts_count,
547 						items[i].count, items[i].interval, items[i].size, items[i].timeout,
548 						error, sizeof(error));
549 
550 			process_values(items, first_index, i + 1, hosts, hosts_count, &ts, ping_result, error);
551 
552 			hosts_count = 0;
553 			first_index = i + 1;
554 		}
555 	}
556 
557 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
558 }
559 
560 /******************************************************************************
561  *                                                                            *
562  * Function: main_pinger_loop                                                 *
563  *                                                                            *
564  * Purpose: periodically perform ICMP pings                                   *
565  *                                                                            *
566  * Parameters:                                                                *
567  *                                                                            *
568  * Return value:                                                              *
569  *                                                                            *
570  * Author: Alexei Vladishev                                                   *
571  *                                                                            *
572  * Comments: never returns                                                    *
573  *                                                                            *
574  ******************************************************************************/
ZBX_THREAD_ENTRY(pinger_thread,args)575 ZBX_THREAD_ENTRY(pinger_thread, args)
576 {
577 	int			nextcheck, sleeptime, items_count = 0, itc;
578 	double			sec;
579 	static icmpitem_t	*items = NULL;
580 	static int		items_alloc = 4;
581 
582 	process_type = ((zbx_thread_args_t *)args)->process_type;
583 	server_num = ((zbx_thread_args_t *)args)->server_num;
584 	process_num = ((zbx_thread_args_t *)args)->process_num;
585 
586 	zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(program_type),
587 			server_num, get_process_type_string(process_type), process_num);
588 
589 	if (NULL == items)
590 		items = zbx_malloc(items, sizeof(icmpitem_t) * items_alloc);
591 
592 	for (;;)
593 	{
594 		zbx_handle_log();
595 
596 		zbx_setproctitle("%s #%d [getting values]", get_process_type_string(process_type), process_num);
597 
598 		sec = zbx_time();
599 		get_pinger_hosts(&items, &items_alloc, &items_count);
600 		process_pinger_hosts(items, items_count);
601 		sec = zbx_time() - sec;
602 		itc = items_count;
603 
604 		free_hosts(&items, &items_count);
605 
606 		nextcheck = DCconfig_get_poller_nextcheck(ZBX_POLLER_TYPE_PINGER);
607 		sleeptime = calculate_sleeptime(nextcheck, POLLER_DELAY);
608 
609 		zbx_setproctitle("%s #%d [got %d values in " ZBX_FS_DBL " sec, idle %d sec]",
610 				get_process_type_string(process_type), process_num, itc, sec, sleeptime);
611 
612 		zbx_sleep_loop(sleeptime);
613 
614 #if !defined(_WINDOWS) && defined(HAVE_RESOLV_H)
615 		zbx_update_resolver_conf();	/* handle /etc/resolv.conf update */
616 #endif
617 	}
618 }
619