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 #include "sysinfo.h"
22 #include "zbxjson.h"
23 #include "log.h"
24 
25 typedef struct
26 {
27 	zbx_uint64_t ibytes;
28 	zbx_uint64_t ipackets;
29 	zbx_uint64_t ierr;
30 	zbx_uint64_t idrop;
31 	zbx_uint64_t ififo;
32 	zbx_uint64_t iframe;
33 	zbx_uint64_t icompressed;
34 	zbx_uint64_t imulticast;
35 	zbx_uint64_t obytes;
36 	zbx_uint64_t opackets;
37 	zbx_uint64_t oerr;
38 	zbx_uint64_t odrop;
39 	zbx_uint64_t ocolls;
40 	zbx_uint64_t ofifo;
41 	zbx_uint64_t ocarrier;
42 	zbx_uint64_t ocompressed;
43 }
44 net_stat_t;
45 
46 #if HAVE_INET_DIAG
47 #	include <sys/socket.h>
48 #	include <linux/netlink.h>
49 #	include <linux/inet_diag.h>
50 
51 enum
52 {
53 	STATE_UNKNOWN = 0,
54 	STATE_ESTABLISHED,
55 	STATE_SYN_SENT,
56 	STATE_SYN_RECV,
57 	STATE_FIN_WAIT1,
58 	STATE_FIN_WAIT2,
59 	STATE_TIME_WAIT,
60 	STATE_CLOSE,
61 	STATE_CLOSE_WAIT,
62 	STATE_LAST_ACK,
63 	STATE_LISTEN,
64 	STATE_CLOSING,
65 	STATE_MAXSTATES
66 };
67 
68 enum
69 {
70 	NLERR_OK = 0,
71 	NLERR_UNKNOWN,
72 	NLERR_SOCKCREAT,
73 	NLERR_BADSEND,
74 	NLERR_BADRECV,
75 	NLERR_RECVTIMEOUT,
76 	NLERR_RESPTRUNCAT,
77 	NLERR_OPNOTSUPPORTED,
78 	NLERR_UNKNOWNMSGTYPE
79 };
80 
81 static int	nlerr;
82 
find_tcp_port_by_state_nl(unsigned short port,int state,int * found)83 static int	find_tcp_port_by_state_nl(unsigned short port, int state, int *found)
84 {
85 	struct
86 	{
87 		struct nlmsghdr		nlhdr;
88 		struct inet_diag_req	r;
89 	}
90 	request;
91 
92 	int			ret = FAIL, fd, status, i;
93 	int			families[] = {AF_INET, AF_INET6, AF_UNSPEC};
94 	unsigned int		sequence = 0x58425A;
95 	struct timeval		timeout = { 1, 500 * 1000 };
96 
97 	struct sockaddr_nl	s_sa = { AF_NETLINK, 0, 0, 0 };
98 	struct iovec		s_io[1] = { { &request, sizeof(request) } };
99 	struct msghdr		s_msg = { (void *)&s_sa, sizeof(struct sockaddr_nl), s_io, 1, NULL, 0, 0};
100 
101 	char			buffer[BUFSIZ] = { 0 };
102 
103 	struct sockaddr_nl	r_sa = { AF_NETLINK, 0, 0, 0 };
104 	struct iovec		r_io[1] = { { buffer, BUFSIZ } };
105 	struct msghdr		r_msg = { (void *)&r_sa, sizeof(struct sockaddr_nl), r_io, 1, NULL, 0, 0};
106 
107 	struct nlmsghdr		*r_hdr;
108 
109 	*found = 0;
110 
111 	request.nlhdr.nlmsg_len = sizeof(request);
112 	request.nlhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH;
113 	request.nlhdr.nlmsg_pid = 0;
114 	request.nlhdr.nlmsg_seq = sequence;
115 	request.nlhdr.nlmsg_type = TCPDIAG_GETSOCK;
116 
117 	memset(&request.r, 0, sizeof(request.r));
118 	request.r.idiag_states = (1 << state);
119 
120 	if (-1 == (fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_INET_DIAG)) ||
121 			0 != setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval)))
122 	{
123 		nlerr = NLERR_SOCKCREAT;
124 		goto out;
125 	}
126 
127 	nlerr = NLERR_OK;
128 
129 	for (i = 0; AF_UNSPEC != families[i]; i++)
130 	{
131 		request.r.idiag_family = families[i];
132 
133 		if (-1 == sendmsg(fd, &s_msg, 0))
134 		{
135 			nlerr = NLERR_BADSEND;
136 			goto out;
137 		}
138 
139 		while (NLERR_OK == nlerr)
140 		{
141 			status = recvmsg(fd, &r_msg, 0);
142 
143 			if (0 > status)
144 			{
145 				if (EAGAIN == errno || EWOULDBLOCK == errno)
146 					nlerr = NLERR_RECVTIMEOUT;
147 				else if (EINTR != errno)
148 					nlerr = NLERR_BADRECV;
149 
150 				continue;
151 			}
152 
153 			if (0 == status)
154 				break;
155 
156 			for (r_hdr = (struct nlmsghdr *)buffer; NLMSG_OK(r_hdr, (unsigned)status);
157 					r_hdr = NLMSG_NEXT(r_hdr, status))
158 			{
159 				struct inet_diag_msg	*r = (struct inet_diag_msg *)NLMSG_DATA(r_hdr);
160 
161 				if (sequence != r_hdr->nlmsg_seq)
162 					continue;
163 
164 				switch (r_hdr->nlmsg_type)
165 				{
166 					case NLMSG_DONE:
167 						goto out;
168 					case NLMSG_ERROR:
169 					{
170 						struct nlmsgerr	*err = (struct nlmsgerr *)NLMSG_DATA(r_hdr);
171 
172 						if (NLMSG_LENGTH(sizeof(struct nlmsgerr)) > r_hdr->nlmsg_len)
173 						{
174 							nlerr = NLERR_RESPTRUNCAT;
175 						}
176 						else
177 						{
178 							nlerr = (EOPNOTSUPP == -err->error ? NLERR_OPNOTSUPPORTED :
179 								NLERR_UNKNOWN);
180 						}
181 
182 						goto out;
183 					}
184 					case 0x12:
185 						if (state == r->idiag_state && port == ntohs(r->id.idiag_sport))
186 						{
187 							*found = 1;
188 							goto out;
189 						}
190 						break;
191 					default:
192 						nlerr = NLERR_UNKNOWNMSGTYPE;
193 						break;
194 				}
195 			}
196 		}
197 	}
198 out:
199 	if (-1 != fd)
200 		close(fd);
201 
202 	if (NLERR_OK == nlerr)
203 		ret = SUCCEED;
204 
205 	return ret;
206 }
207 #endif
208 
get_net_stat(const char * if_name,net_stat_t * result,char ** error)209 static int	get_net_stat(const char *if_name, net_stat_t *result, char **error)
210 {
211 	int	ret = SYSINFO_RET_FAIL;
212 	char	line[MAX_STRING_LEN], name[MAX_STRING_LEN], *p;
213 	FILE	*f;
214 
215 	if (NULL == if_name || '\0' == *if_name)
216 	{
217 		*error = zbx_strdup(NULL, "Network interface name cannot be empty.");
218 		return SYSINFO_RET_FAIL;
219 	}
220 
221 	if (NULL == (f = fopen("/proc/net/dev", "r")))
222 	{
223 		*error = zbx_dsprintf(NULL, "Cannot open /proc/net/dev: %s", zbx_strerror(errno));
224 		return SYSINFO_RET_FAIL;
225 	}
226 
227 	while (NULL != fgets(line, sizeof(line), f))
228 	{
229 		if (NULL == (p = strstr(line, ":")))
230 			continue;
231 
232 		*p = '\t';
233 
234 		if (17 == sscanf(line, "%s\t" ZBX_FS_UI64 "\t" ZBX_FS_UI64 "\t"
235 				ZBX_FS_UI64 "\t" ZBX_FS_UI64 "\t"
236 				ZBX_FS_UI64 "\t" ZBX_FS_UI64 "\t"
237 				ZBX_FS_UI64 "\t" ZBX_FS_UI64 "\t"
238 				ZBX_FS_UI64 "\t" ZBX_FS_UI64 "\t"
239 				ZBX_FS_UI64 "\t" ZBX_FS_UI64 "\t"
240 				ZBX_FS_UI64 "\t" ZBX_FS_UI64 "\t"
241 				ZBX_FS_UI64 "\t" ZBX_FS_UI64 "\n",
242 				name,
243 				&result->ibytes,	/* bytes */
244 				&result->ipackets,	/* packets */
245 				&result->ierr,		/* errs */
246 				&result->idrop,		/* drop */
247 				&result->ififo,		/* fifo (overruns) */
248 				&result->iframe,	/* frame */
249 				&result->icompressed,	/* compressed */
250 				&result->imulticast,	/* multicast */
251 				&result->obytes,	/* bytes */
252 				&result->opackets,	/* packets */
253 				&result->oerr,		/* errs */
254 				&result->odrop,		/* drop */
255 				&result->ofifo,		/* fifo (overruns)*/
256 				&result->ocolls,	/* colls (collisions) */
257 				&result->ocarrier,	/* carrier */
258 				&result->ocompressed))	/* compressed */
259 		{
260 			if (0 == strcmp(name, if_name))
261 			{
262 				ret = SYSINFO_RET_OK;
263 				break;
264 			}
265 		}
266 	}
267 
268 	zbx_fclose(f);
269 
270 	if (SYSINFO_RET_FAIL == ret)
271 	{
272 		*error = zbx_strdup(NULL, "Cannot find information for this network interface in /proc/net/dev.");
273 		return SYSINFO_RET_FAIL;
274 	}
275 
276 	return SYSINFO_RET_OK;
277 }
278 
279 /******************************************************************************
280  *                                                                            *
281  * Function: proc_read_tcp_listen                                             *
282  *                                                                            *
283  * Purpose: reads /proc/net/tcp(6) file by chunks until the last line in      *
284  *          in buffer has non-listening socket state                          *
285  *                                                                            *
286  * Parameters: filename     - [IN] the file to read                           *
287  *             buffer       - [IN/OUT] the output buffer                      *
288  *             buffer_alloc - [IN/OUT] the output buffer size                 *
289  *                                                                            *
290  * Return value: -1 error occurred during reading                             *
291  *                0 empty file (shouldn't happen)                             *
292  *               >0 the number of bytes read                                  *
293  *                                                                            *
294  ******************************************************************************/
proc_read_tcp_listen(const char * filename,char ** buffer,int * buffer_alloc)295 static int    proc_read_tcp_listen(const char *filename, char **buffer, int *buffer_alloc)
296 {
297 	int     n, fd, ret = -1, offset = 0;
298 	char    *start, *end;
299 
300 	if (-1 == (fd = open(filename, O_RDONLY)))
301 		return -1;
302 
303 	while (0 != (n = read(fd, *buffer + offset, *buffer_alloc - offset)))
304 	{
305 		int    count = 0;
306 
307 		if (-1 == n)
308 			goto out;
309 
310 		offset += n;
311 
312 		if (offset == *buffer_alloc)
313 		{
314 			*buffer_alloc *= 2;
315 			*buffer = (char *)zbx_realloc(*buffer, *buffer_alloc);
316 		}
317 
318 		(*buffer)[offset] = '\0';
319 
320 		/* find the last full line */
321 		for (start = *buffer + offset - 1; start > *buffer; start--)
322 		{
323 			if ('\n' == *start)
324 			{
325 				if (++count == 2)
326 					break;
327 
328 				end = start;
329 			}
330 		}
331 
332 		/* check if the socket is in listening state */
333 		if (2 == count)
334 		{
335 			start++;
336 			count = 0;
337 
338 			while (' ' == *start++)
339 				;
340 
341 			while (count < 3 && start < end)
342 			{
343 				while (' ' != *start)
344 					start++;
345 
346 				while (' ' == *start)
347 					start++;
348 
349 				count++;
350 			}
351 
352 			if (3 == count && 0 != strncmp(start, "0A", 2) && 0 != strncmp(start, "03", 2))
353 				break;
354 		}
355 	}
356 
357 	ret = offset;
358 out:
359 	close(fd);
360 
361 	return ret;
362 }
363 
364 /******************************************************************************
365  *                                                                            *
366  * Function: proc_read_file                                                   *
367  *                                                                            *
368  * Purpose: reads whole file into a buffer in a single read operation         *
369  *                                                                            *
370  * Parameters: filename     - [IN] the file to read                           *
371  *             buffer       - [IN/OUT] the output buffer                      *
372  *             buffer_alloc - [IN/OUT] the output buffer size                 *
373  *                                                                            *
374  * Return value: -1 error occurred during reading                             *
375  *                0 empty file (shouldn't happen)                             *
376  *               >0 the number of bytes read                                  *
377  *                                                                            *
378  ******************************************************************************/
proc_read_file(const char * filename,char ** buffer,int * buffer_alloc)379 static int	proc_read_file(const char *filename, char **buffer, int *buffer_alloc)
380 {
381 	int	n, fd, ret = -1, offset = 0;
382 
383 	if (-1 == (fd = open(filename, O_RDONLY)))
384 		return -1;
385 
386 	while (0 != (n = read(fd, *buffer + offset, *buffer_alloc - offset)))
387 	{
388 		if (-1 == n)
389 			goto out;
390 
391 		offset += n;
392 
393 		if (offset == *buffer_alloc)
394 		{
395 			*buffer_alloc *= 2;
396 			*buffer = (char *)zbx_realloc(*buffer, *buffer_alloc);
397 		}
398 	}
399 
400 	ret = offset;
401 out:
402 	close(fd);
403 
404 	return ret;
405 }
406 
NET_IF_IN(AGENT_REQUEST * request,AGENT_RESULT * result)407 int	NET_IF_IN(AGENT_REQUEST *request, AGENT_RESULT *result)
408 {
409 	net_stat_t	ns;
410 	char		*if_name, *mode, *error;
411 
412 	if (2 < request->nparam)
413 	{
414 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
415 		return SYSINFO_RET_FAIL;
416 	}
417 
418 	if_name = get_rparam(request, 0);
419 	mode = get_rparam(request, 1);
420 
421 	if (SYSINFO_RET_OK != get_net_stat(if_name, &ns, &error))
422 	{
423 		SET_MSG_RESULT(result, error);
424 		return SYSINFO_RET_FAIL;
425 	}
426 
427 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))	/* default parameter */
428 		SET_UI64_RESULT(result, ns.ibytes);
429 	else if (0 == strcmp(mode, "packets"))
430 		SET_UI64_RESULT(result, ns.ipackets);
431 	else if (0 == strcmp(mode, "errors"))
432 		SET_UI64_RESULT(result, ns.ierr);
433 	else if (0 == strcmp(mode, "dropped"))
434 		SET_UI64_RESULT(result, ns.idrop);
435 	else if (0 == strcmp(mode, "overruns"))
436 		SET_UI64_RESULT(result, ns.ififo);
437 	else if (0 == strcmp(mode, "frame"))
438 		SET_UI64_RESULT(result, ns.iframe);
439 	else if (0 == strcmp(mode, "compressed"))
440 		SET_UI64_RESULT(result, ns.icompressed);
441 	else if (0 == strcmp(mode, "multicast"))
442 		SET_UI64_RESULT(result, ns.imulticast);
443 	else
444 	{
445 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
446 		return SYSINFO_RET_FAIL;
447 	}
448 
449 	return SYSINFO_RET_OK;
450 }
451 
NET_IF_OUT(AGENT_REQUEST * request,AGENT_RESULT * result)452 int	NET_IF_OUT(AGENT_REQUEST *request, AGENT_RESULT *result)
453 {
454 	net_stat_t	ns;
455 	char		*if_name, *mode, *error;
456 
457 	if (2 < request->nparam)
458 	{
459 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
460 		return SYSINFO_RET_FAIL;
461 	}
462 
463 	if_name = get_rparam(request, 0);
464 	mode = get_rparam(request, 1);
465 
466 	if (SYSINFO_RET_OK != get_net_stat(if_name, &ns, &error))
467 	{
468 		SET_MSG_RESULT(result, error);
469 		return SYSINFO_RET_FAIL;
470 	}
471 
472 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))	/* default parameter */
473 		SET_UI64_RESULT(result, ns.obytes);
474 	else if (0 == strcmp(mode, "packets"))
475 		SET_UI64_RESULT(result, ns.opackets);
476 	else if (0 == strcmp(mode, "errors"))
477 		SET_UI64_RESULT(result, ns.oerr);
478 	else if (0 == strcmp(mode, "dropped"))
479 		SET_UI64_RESULT(result, ns.odrop);
480 	else if (0 == strcmp(mode, "overruns"))
481 		SET_UI64_RESULT(result, ns.ofifo);
482 	else if (0 == strcmp(mode, "collisions"))
483 		SET_UI64_RESULT(result, ns.ocolls);
484 	else if (0 == strcmp(mode, "carrier"))
485 		SET_UI64_RESULT(result, ns.ocarrier);
486 	else if (0 == strcmp(mode, "compressed"))
487 		SET_UI64_RESULT(result, ns.ocompressed);
488 	else
489 	{
490 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
491 		return SYSINFO_RET_FAIL;
492 	}
493 
494 	return SYSINFO_RET_OK;
495 }
496 
NET_IF_TOTAL(AGENT_REQUEST * request,AGENT_RESULT * result)497 int	NET_IF_TOTAL(AGENT_REQUEST *request, AGENT_RESULT *result)
498 {
499 	net_stat_t	ns;
500 	char		*if_name, *mode, *error;
501 
502 	if (2 < request->nparam)
503 	{
504 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
505 		return SYSINFO_RET_FAIL;
506 	}
507 
508 	if_name = get_rparam(request, 0);
509 	mode = get_rparam(request, 1);
510 
511 	if (SYSINFO_RET_OK != get_net_stat(if_name, &ns, &error))
512 	{
513 		SET_MSG_RESULT(result, error);
514 		return SYSINFO_RET_FAIL;
515 	}
516 
517 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))	/* default parameter */
518 		SET_UI64_RESULT(result, ns.ibytes + ns.obytes);
519 	else if (0 == strcmp(mode, "packets"))
520 		SET_UI64_RESULT(result, ns.ipackets + ns.opackets);
521 	else if (0 == strcmp(mode, "errors"))
522 		SET_UI64_RESULT(result, ns.ierr + ns.oerr);
523 	else if (0 == strcmp(mode, "dropped"))
524 		SET_UI64_RESULT(result, ns.idrop + ns.odrop);
525 	else if (0 == strcmp(mode, "overruns"))
526 		SET_UI64_RESULT(result, ns.ififo + ns.ofifo);
527 	else if (0 == strcmp(mode, "compressed"))
528 		SET_UI64_RESULT(result, ns.icompressed + ns.ocompressed);
529 	else
530 	{
531 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
532 		return SYSINFO_RET_FAIL;
533 	}
534 
535 	return SYSINFO_RET_OK;
536 }
537 
NET_IF_COLLISIONS(AGENT_REQUEST * request,AGENT_RESULT * result)538 int	NET_IF_COLLISIONS(AGENT_REQUEST *request, AGENT_RESULT *result)
539 {
540 	net_stat_t	ns;
541 	char		*if_name, *error;
542 
543 	if (1 < request->nparam)
544 	{
545 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
546 		return SYSINFO_RET_FAIL;
547 	}
548 
549 	if_name = get_rparam(request, 0);
550 
551 	if (SYSINFO_RET_OK != get_net_stat(if_name, &ns, &error))
552 	{
553 		SET_MSG_RESULT(result, error);
554 		return SYSINFO_RET_FAIL;
555 	}
556 
557 	SET_UI64_RESULT(result, ns.ocolls);
558 
559 	return SYSINFO_RET_OK;
560 }
561 
NET_IF_DISCOVERY(AGENT_REQUEST * request,AGENT_RESULT * result)562 int	NET_IF_DISCOVERY(AGENT_REQUEST *request, AGENT_RESULT *result)
563 {
564 	char		line[MAX_STRING_LEN], *p;
565 	FILE		*f;
566 	struct zbx_json	j;
567 
568 	ZBX_UNUSED(request);
569 
570 	if (NULL == (f = fopen("/proc/net/dev", "r")))
571 	{
572 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot open /proc/net/dev: %s", zbx_strerror(errno)));
573 		return SYSINFO_RET_FAIL;
574 	}
575 
576 	zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN);
577 
578 	zbx_json_addarray(&j, ZBX_PROTO_TAG_DATA);
579 
580 	while (NULL != fgets(line, sizeof(line), f))
581 	{
582 		if (NULL == (p = strstr(line, ":")))
583 			continue;
584 
585 		*p = '\0';
586 
587 		/* trim left spaces */
588 		for (p = line; ' ' == *p && '\0' != *p; p++)
589 			;
590 
591 		zbx_json_addobject(&j, NULL);
592 		zbx_json_addstring(&j, "{#IFNAME}", p, ZBX_JSON_TYPE_STRING);
593 		zbx_json_close(&j);
594 	}
595 
596 	zbx_fclose(f);
597 
598 	zbx_json_close(&j);
599 
600 	SET_STR_RESULT(result, strdup(j.buffer));
601 
602 	zbx_json_free(&j);
603 
604 	return SYSINFO_RET_OK;
605 }
606 
NET_TCP_LISTEN(AGENT_REQUEST * request,AGENT_RESULT * result)607 int	NET_TCP_LISTEN(AGENT_REQUEST *request, AGENT_RESULT *result)
608 {
609 	char		pattern[64], *port_str, *buffer = NULL;
610 	unsigned short	port;
611 	zbx_uint64_t	listen = 0;
612 	int		ret = SYSINFO_RET_FAIL, n, buffer_alloc = 64 * ZBX_KIBIBYTE;
613 #ifdef HAVE_INET_DIAG
614 	int		found;
615 #endif
616 	if (1 < request->nparam)
617 	{
618 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
619 		return SYSINFO_RET_FAIL;
620 	}
621 
622 	port_str = get_rparam(request, 0);
623 
624 	if (NULL == port_str || SUCCEED != is_ushort(port_str, &port))
625 	{
626 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
627 		return SYSINFO_RET_FAIL;
628 	}
629 
630 #ifdef HAVE_INET_DIAG
631 	if (SUCCEED == find_tcp_port_by_state_nl(port, STATE_LISTEN, &found))
632 	{
633 		ret = SYSINFO_RET_OK;
634 		listen = found;
635 	}
636 	else
637 	{
638 		const char	*error;
639 
640 		switch (nlerr)
641 		{
642 			case NLERR_UNKNOWN:
643 				error = "unrecognized netlink error occurred";
644 				break;
645 			case NLERR_SOCKCREAT:
646 				error = "cannot create netlink socket";
647 				break;
648 			case NLERR_BADSEND:
649 				error = "cannot send netlink message to kernel";
650 				break;
651 			case NLERR_BADRECV:
652 				error = "cannot receive netlink message from kernel";
653 				break;
654 			case NLERR_RECVTIMEOUT:
655 				error = "receiving netlink response timed out";
656 				break;
657 			case NLERR_RESPTRUNCAT:
658 				error = "received truncated netlink response from kernel";
659 				break;
660 			case NLERR_OPNOTSUPPORTED:
661 				error = "netlink operation not supported";
662 				break;
663 			case NLERR_UNKNOWNMSGTYPE:
664 				error = "received message of unrecognized type from kernel";
665 				break;
666 			default:
667 				error = "unknown error";
668 		}
669 
670 		zabbix_log(LOG_LEVEL_DEBUG, "netlink interface error: %s", error);
671 		zabbix_log(LOG_LEVEL_DEBUG, "falling back on reading /proc/net/tcp...");
672 #endif
673 		buffer = (char *)zbx_malloc(NULL, buffer_alloc);
674 
675 		if (0 < (n = proc_read_tcp_listen("/proc/net/tcp", &buffer, &buffer_alloc)))
676 		{
677 			ret = SYSINFO_RET_OK;
678 
679 			zbx_snprintf(pattern, sizeof(pattern), "%04X 00000000:0000 0A", (unsigned int)port);
680 
681 			if (NULL != strstr(buffer, pattern))
682 			{
683 				listen = 1;
684 				goto out;
685 			}
686 		}
687 
688 		if (0 < (n = proc_read_tcp_listen("/proc/net/tcp6", &buffer, &buffer_alloc)))
689 		{
690 			ret = SYSINFO_RET_OK;
691 
692 			zbx_snprintf(pattern, sizeof(pattern), "%04X 00000000000000000000000000000000:0000 0A",
693 					(unsigned int)port);
694 
695 			if (NULL != strstr(buffer, pattern))
696 				listen = 1;
697 		}
698 out:
699 		zbx_free(buffer);
700 #ifdef HAVE_INET_DIAG
701 	}
702 #endif
703 	SET_UI64_RESULT(result, listen);
704 
705 	return ret;
706 }
707 
NET_UDP_LISTEN(AGENT_REQUEST * request,AGENT_RESULT * result)708 int	NET_UDP_LISTEN(AGENT_REQUEST *request, AGENT_RESULT *result)
709 {
710 	char		pattern[64], *port_str, *buffer = NULL;
711 	unsigned short	port;
712 	zbx_uint64_t	listen = 0;
713 	int		ret = SYSINFO_RET_FAIL, n, buffer_alloc = 64 * ZBX_KIBIBYTE;
714 
715 	if (1 < request->nparam)
716 	{
717 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
718 		return SYSINFO_RET_FAIL;
719 	}
720 
721 	port_str = get_rparam(request, 0);
722 
723 	if (NULL == port_str || SUCCEED != is_ushort(port_str, &port))
724 	{
725 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
726 		return SYSINFO_RET_FAIL;
727 	}
728 
729 	buffer = (char *)zbx_malloc(NULL, buffer_alloc);
730 
731 	if (0 < (n = proc_read_file("/proc/net/udp", &buffer, &buffer_alloc)))
732 	{
733 		ret = SYSINFO_RET_OK;
734 
735 		zbx_snprintf(pattern, sizeof(pattern), "%04X 00000000:0000 07", (unsigned int)port);
736 
737 		buffer[n] = '\0';
738 
739 		if (NULL != strstr(buffer, pattern))
740 		{
741 			listen = 1;
742 			goto out;
743 		}
744 	}
745 
746 	if (0 < (n = proc_read_file("/proc/net/udp6", &buffer, &buffer_alloc)))
747 	{
748 		ret = SYSINFO_RET_OK;
749 
750 		zbx_snprintf(pattern, sizeof(pattern), "%04X 00000000000000000000000000000000:0000 07",
751 				(unsigned int)port);
752 
753 		buffer[n] = '\0';
754 
755 		if (NULL != strstr(buffer, pattern))
756 			listen = 1;
757 	}
758 out:
759 	zbx_free(buffer);
760 
761 	SET_UI64_RESULT(result, listen);
762 
763 	return ret;
764 }
765