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 <unistd.h>
21 #include <stropts.h>
22 #include <sys/dlpi.h>
23 #include <sys/dlpi_ext.h>
24 #include <sys/mib.h>
25 
26 #include "common.h"
27 #include "sysinfo.h"
28 #include "zbxjson.h"
29 
30 #define PPA(n) (*(dl_hp_ppa_info_t *)(ppa_data_buf + n * sizeof(dl_hp_ppa_info_t)))
31 
32 static char	buf_ctl[1024];
33 
34 /* Low Level Discovery needs a way to get the list of network interfaces available */
35 /* on the monitored system. HP-UX versions starting from 11.31 have if_nameindex() */
36 /* available in libc, older versions have it in libipv6 which we do not want to    */
37 /* depend on. So for older versions we use different code to get that list.        */
38 /* More information:                                                               */
39 /* h20000.www2.hp.com/bc/docs/support/SupportManual/c02258083/c02258083.pdf        */
40 
41 static struct strbuf	ctlbuf =
42 {
43 	sizeof(buf_ctl),
44 	0,
45 	buf_ctl
46 };
47 
48 #if HPUX_VERSION < 1131
49 
50 #define ZBX_IF_SEP	','
51 
add_if_name(char ** if_list,size_t * if_list_alloc,size_t * if_list_offset,const char * name)52 static void	add_if_name(char **if_list, size_t *if_list_alloc, size_t *if_list_offset, const char *name)
53 {
54 	if (FAIL == str_in_list(*if_list, name, ZBX_IF_SEP))
55 	{
56 		if ('\0' != **if_list)
57 			zbx_chrcpy_alloc(if_list, if_list_alloc, if_list_offset, ZBX_IF_SEP);
58 
59 		zbx_strcpy_alloc(if_list, if_list_alloc, if_list_offset, name);
60 	}
61 }
62 
get_if_names(char ** if_list,size_t * if_list_alloc,size_t * if_list_offset)63 static int	get_if_names(char **if_list, size_t *if_list_alloc, size_t *if_list_offset)
64 {
65 	int			s, ifreq_size, numifs, i, family = AF_INET;
66 	struct sockaddr		*from;
67 	u_char			*buffer = NULL;
68 	struct ifconf		ifc;
69 	struct ifreq		*ifr;
70 	struct if_laddrconf	lifc;
71 	struct if_laddrreq	*lifr;
72 
73 	if (-1 == (s = socket(family, SOCK_DGRAM, 0)))
74 		return FAIL;
75 
76 	ifc.ifc_buf = 0;
77 	ifc.ifc_len = 0;
78 
79 	if (0 == ioctl(s, SIOCGIFCONF, (caddr_t)&ifc) && 0 != ifc.ifc_len)
80 		ifreq_size = 2 * ifc.ifc_len;
81 	else
82 		ifreq_size = 2 * 512;
83 
84 	buffer = zbx_malloc(buffer, ifreq_size);
85 	memset(buffer, 0, ifreq_size);
86 
87 	ifc.ifc_buf = (caddr_t)buffer;
88 	ifc.ifc_len = ifreq_size;
89 
90 	if (-1 == ioctl(s, SIOCGIFCONF, &ifc))
91 		goto next;
92 
93 	/* check all IPv4 interfaces */
94 	ifr = (struct ifreq *)ifc.ifc_req;
95 	while ((u_char *)ifr < (u_char *)(buffer + ifc.ifc_len))
96 	{
97 		from = &ifr->ifr_addr;
98 
99 		if (AF_INET6 != from->sa_family && AF_INET != from->sa_family)
100 			continue;
101 
102 		add_if_name(if_list, if_list_alloc, if_list_offset, ifr->ifr_name);
103 
104 #ifdef _SOCKADDR_LEN
105 		ifr = (struct ifreq *)((char *)ifr + sizeof(*ifr) + (from->sa_len > sizeof(*from) ? from->sa_len - sizeof(*from) : 0));
106 #else
107 		ifr++;
108 #endif
109 	}
110 next:
111 	zbx_free(buffer);
112 	close(s);
113 
114 #if defined (SIOCGLIFCONF)
115 	family = AF_INET6;
116 
117 	if (-1 == (s = socket(family, SOCK_DGRAM, 0)))
118 		return FAIL;
119 
120 	i = ioctl(s, SIOCGLIFNUM, (char *)&numifs);
121 	if (0 == numifs)
122 	{
123 		close(s);
124 		return SUCCEED;
125 	}
126 
127 	lifc.iflc_len = numifs * sizeof(struct if_laddrreq);
128 	lifc.iflc_buf = zbx_malloc(NULL, lifc.iflc_len);
129 	buffer = (u_char *)lifc.iflc_buf;
130 
131 	if (-1 == ioctl(s, SIOCGLIFCONF, &lifc))
132 		goto end;
133 
134 	/* check all IPv6 interfaces */
135 	for (lifr = lifc.iflc_req; '\0' != *lifr->iflr_name; lifr++)
136 	{
137 		from = (struct sockaddr *)&lifr->iflr_addr;
138 
139 		if (AF_INET6 != from->sa_family && AF_INET != from->sa_family)
140 			continue;
141 
142 		add_if_name(if_list, if_list_alloc, if_list_offset, lifr->iflr_name);
143 	}
144 end:
145 	zbx_free(buffer);
146 	close(s);
147 #else
148 	ZBX_UNUSED(numifs);
149 	ZBX_UNUSED(i);
150 	ZBX_UNUSED(lifc);
151 	ZBX_UNUSED(lifr);
152 #endif
153 	return SUCCEED;
154 }
155 
156 #endif	/* HPUX_VERSION < 1131 */
157 
NET_IF_DISCOVERY(AGENT_REQUEST * request,AGENT_RESULT * result)158 int	NET_IF_DISCOVERY(AGENT_REQUEST *request, AGENT_RESULT *result)
159 {
160 	struct zbx_json	j;
161 	char		*if_name;
162 #if HPUX_VERSION < 1131
163 	char		*if_list = NULL, *if_name_end;
164 	size_t		if_list_alloc = 64, if_list_offset = 0;
165 
166 	if_list = zbx_malloc(if_list, if_list_alloc);
167 	*if_list = '\0';
168 
169 	if (FAIL == get_if_names(&if_list, &if_list_alloc, &if_list_offset))
170 	{
171 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain network interface information."));
172 		zbx_free(if_list);
173 		return SYSINFO_RET_FAIL;
174 	}
175 
176 	zbx_json_initarray(&j, ZBX_JSON_STAT_BUF_LEN);
177 
178 	if_name = if_list;
179 
180 	while (NULL != if_name)
181 	{
182 		if (NULL != (if_name_end = strchr(if_name, ZBX_IF_SEP)))
183 			*if_name_end = '\0';
184 
185 		zbx_json_addobject(&j, NULL);
186 		zbx_json_addstring(&j, "{#IFNAME}", if_name, ZBX_JSON_TYPE_STRING);
187 		zbx_json_close(&j);
188 
189 		if (NULL != if_name_end)
190 		{
191 			*if_name_end = ZBX_IF_SEP;
192 			if_name = if_name_end + 1;
193 		}
194 		else
195 			if_name = NULL;
196 	}
197 
198 	zbx_free(if_list);
199 #else
200 	struct if_nameindex	*ni;
201 	int			i;
202 
203 	if (NULL == (ni = if_nameindex()))
204 	{
205 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain system information: %s", zbx_strerror(errno)));
206 		return SYSINFO_RET_FAIL;
207 	}
208 
209 	zbx_json_initarray(&j, ZBX_JSON_STAT_BUF_LEN);
210 
211 	for (i = 0; 0 != ni[i].if_index; i++)
212 	{
213 		zbx_json_addobject(&j, NULL);
214 		zbx_json_addstring(&j, "{#IFNAME}", ni[i].if_name, ZBX_JSON_TYPE_STRING);
215 		zbx_json_close(&j);
216 	}
217 
218 	if_freenameindex(ni);
219 #endif
220 	zbx_json_close(&j);
221 
222 	SET_STR_RESULT(result, zbx_strdup(NULL, j.buffer));
223 
224 	zbx_json_free(&j);
225 
226 	return SYSINFO_RET_OK;
227 }
228 
229 /* attaches to a PPA via an already open stream to DLPI provider */
dlpi_attach(int fd,int ppa)230 static int	dlpi_attach(int fd, int ppa)
231 {
232 	dl_attach_req_t		attach_req;
233 	int			flags = RS_HIPRI;
234 
235 	attach_req.dl_primitive = DL_ATTACH_REQ;
236 	attach_req.dl_ppa = ppa;
237 
238 	ctlbuf.len = sizeof(attach_req);
239 	ctlbuf.buf = (char *)&attach_req;
240 
241 	if (0 != putmsg(fd, &ctlbuf, NULL, flags))
242 		return FAIL;
243 
244 	ctlbuf.buf = buf_ctl;
245 	ctlbuf.maxlen = sizeof(buf_ctl);
246 
247 	if (0 > getmsg(fd, &ctlbuf, NULL, &flags))
248 		return FAIL;
249 
250 	if (DL_OK_ACK != *(int *)buf_ctl)
251 		return FAIL;
252 
253 	/* Successfully attached to a PPA. */
254 	return SUCCEED;
255 }
256 
257 /* Detaches from a PPA via an already open stream to DLPI provider. */
dlpi_detach(int fd)258 static int	dlpi_detach(int fd)
259 {
260 	dl_detach_req_t		detach_req;
261 	int			flags = RS_HIPRI;
262 
263 	detach_req.dl_primitive = DL_DETACH_REQ;
264 
265 	ctlbuf.len = sizeof(detach_req);
266 	ctlbuf.buf = (char *)&detach_req;
267 
268 	if (0 != putmsg(fd, &ctlbuf, NULL, flags))
269 		return FAIL;
270 
271 	ctlbuf.buf = buf_ctl;
272 	ctlbuf.maxlen = sizeof(buf_ctl);
273 
274 	if (0 > getmsg(fd, &ctlbuf, NULL, &flags))
275 		return FAIL;
276 
277 	if (DL_OK_ACK != *(int *)buf_ctl)
278 		return FAIL;
279 
280 	/* successfully detached */
281 	return SUCCEED;
282 }
283 
dlpi_get_stats(int fd,Ext_mib_t * mib)284 static int	dlpi_get_stats(int fd, Ext_mib_t *mib)
285 {
286 	dl_get_statistics_req_t		stat_req;
287 	dl_get_statistics_ack_t		stat_msg;
288 	int				flags = RS_HIPRI;
289 
290 	stat_req.dl_primitive = DL_GET_STATISTICS_REQ;
291 
292 	ctlbuf.len = sizeof(stat_req);
293 	ctlbuf.buf = (char *)&stat_req;
294 
295 	if (0 != putmsg(fd, &ctlbuf, NULL, flags))
296 		return FAIL;
297 
298 	ctlbuf.buf = buf_ctl;
299 	ctlbuf.maxlen = sizeof(buf_ctl);
300 
301 	if (0 > getmsg(fd, &ctlbuf, NULL, &flags))
302 		return FAIL;
303 
304 	if (DL_GET_STATISTICS_ACK != *(int *)buf_ctl)
305 		return FAIL;
306 
307 	stat_msg = *(dl_get_statistics_ack_t *)buf_ctl;
308 
309 	memcpy(mib, (Ext_mib_t *)(buf_ctl + stat_msg.dl_stat_offset), sizeof(Ext_mib_t));
310 
311 	return SUCCEED;
312 }
313 
get_ppa(int fd,const char * if_name,int * ppa)314 static int get_ppa(int fd, const char *if_name, int *ppa)
315 {
316 	dl_hp_ppa_req_t		ppa_req;
317 	dl_hp_ppa_ack_t		*dlp;
318 	int			i, ret = FAIL, flags = RS_HIPRI, res;
319 	char			*buf = NULL, *ppa_data_buf = NULL;
320 
321 	ppa_req.dl_primitive = DL_HP_PPA_REQ;
322 
323 	ctlbuf.len = sizeof(ppa_req);
324 	ctlbuf.buf = (char *)&ppa_req;
325 
326 	if (0 != putmsg(fd, &ctlbuf, NULL, flags))
327 		return ret;
328 
329 	ctlbuf.buf = buf_ctl;
330 	ctlbuf.maxlen = DL_HP_PPA_ACK_SIZE;
331 
332 	res = getmsg(fd, &ctlbuf, NULL, &flags);
333 
334 	/* get the head first */
335 	if (0 > res)
336 		return ret;
337 
338 	dlp = (dl_hp_ppa_ack_t *)ctlbuf.buf;
339 
340 	if (DL_HP_PPA_ACK != dlp->dl_primitive)
341 		return ret;
342 
343 	if (DL_HP_PPA_ACK_SIZE > ctlbuf.len)
344 		return ret;
345 
346 	if (MORECTL == res)
347 	{
348 		size_t	if_name_sz = strlen(if_name) + 1;
349 
350 		ctlbuf.maxlen = dlp->dl_count * sizeof(dl_hp_ppa_info_t);
351 		ctlbuf.len = 0;
352 
353 		ppa_data_buf = zbx_malloc(ppa_data_buf, (size_t)ctlbuf.maxlen);
354 
355 		ctlbuf.buf = ppa_data_buf;
356 
357 		/* get the data */
358 		if (0 > getmsg(fd, &ctlbuf, NULL, &flags) || ctlbuf.len < dlp->dl_length)
359 		{
360 			zbx_free(ppa_data_buf);
361 			return ret;
362 		}
363 
364 		buf = zbx_malloc(buf, if_name_sz);
365 
366 		for (i = 0; i < dlp->dl_count; i++)
367 		{
368 			zbx_snprintf(buf, if_name_sz, "%s%d", PPA(i).dl_module_id_1, PPA(i).dl_ppa);
369 
370 			if (0 == strcmp(if_name, buf))
371 			{
372 				*ppa = PPA(i).dl_ppa;
373 				ret = SUCCEED;
374 				break;
375 			}
376 		}
377 
378 		zbx_free(buf);
379 		zbx_free(ppa_data_buf);
380 	}
381 
382 	return ret;
383 }
384 
get_net_stat(Ext_mib_t * mib,const char * if_name)385 static int	get_net_stat(Ext_mib_t *mib, const char *if_name)
386 {
387 	int	fd, ppa;
388 
389 	if (-1 == (fd = open("/dev/dlpi", O_RDWR)))
390 		return FAIL;
391 
392 	if (FAIL == get_ppa(fd, if_name, &ppa))
393 	{
394 		close(fd);
395 		return FAIL;
396 	}
397 
398 	if (FAIL == dlpi_attach(fd, ppa))
399 		return FAIL;
400 
401 	if (FAIL == dlpi_get_stats(fd, mib))
402 		return FAIL;
403 
404 	dlpi_detach(fd);
405 
406 	close(fd);
407 
408 	return SUCCEED;
409 }
410 
NET_IF_IN(AGENT_REQUEST * request,AGENT_RESULT * result)411 int	NET_IF_IN(AGENT_REQUEST *request, AGENT_RESULT *result)
412 {
413 	char		*if_name, *mode;
414 	Ext_mib_t	mib;
415 
416 	if (2 < request->nparam)
417 	{
418 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
419 		return SYSINFO_RET_FAIL;
420 	}
421 
422 	if_name = get_rparam(request, 0);
423 	mode = get_rparam(request, 1);
424 
425 	if (FAIL == get_net_stat(&mib, if_name))
426 	{
427 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain network interface information."));
428 		return SYSINFO_RET_FAIL;
429 	}
430 
431 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))
432 		SET_UI64_RESULT(result, mib.mib_if.ifInOctets);
433 	else if (0 == strcmp(mode, "packets"))
434 		SET_UI64_RESULT(result, mib.mib_if.ifInUcastPkts + mib.mib_if.ifInNUcastPkts);
435 	else if (0 == strcmp(mode, "errors"))
436 		SET_UI64_RESULT(result, mib.mib_if.ifInErrors);
437 	else if (0 == strcmp(mode, "dropped"))
438 		SET_UI64_RESULT(result, mib.mib_if.ifInDiscards);
439 	else
440 	{
441 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
442 		return SYSINFO_RET_FAIL;
443 	}
444 
445 	return SYSINFO_RET_OK;
446 }
447 
NET_IF_OUT(AGENT_REQUEST * request,AGENT_RESULT * result)448 int	NET_IF_OUT(AGENT_REQUEST *request, AGENT_RESULT *result)
449 {
450 	char		*if_name, *mode;
451 	Ext_mib_t	mib;
452 
453 	if (2 < request->nparam)
454 	{
455 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
456 		return SYSINFO_RET_FAIL;
457 	}
458 
459 	if_name = get_rparam(request, 0);
460 	mode = get_rparam(request, 1);
461 
462 	if (FAIL == get_net_stat(&mib, if_name))
463 	{
464 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain network interface information."));
465 		return SYSINFO_RET_FAIL;
466 	}
467 
468 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))
469 		SET_UI64_RESULT(result, mib.mib_if.ifOutOctets);
470 	else if (0 == strcmp(mode, "packets"))
471 		SET_UI64_RESULT(result, mib.mib_if.ifOutUcastPkts + mib.mib_if.ifOutNUcastPkts);
472 	else if (0 == strcmp(mode, "errors"))
473 		SET_UI64_RESULT(result, mib.mib_if.ifOutErrors);
474 	else if (0 == strcmp(mode, "dropped"))
475 		SET_UI64_RESULT(result, mib.mib_if.ifOutDiscards);
476 	else
477 	{
478 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
479 		return SYSINFO_RET_FAIL;
480 	}
481 
482 	return SYSINFO_RET_OK;
483 }
484 
NET_IF_TOTAL(AGENT_REQUEST * request,AGENT_RESULT * result)485 int	NET_IF_TOTAL(AGENT_REQUEST *request, AGENT_RESULT *result)
486 {
487 	char		*if_name, *mode;
488 	Ext_mib_t	mib;
489 
490 	if (2 < request->nparam)
491 	{
492 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
493 		return SYSINFO_RET_FAIL;
494 	}
495 
496 	if_name = get_rparam(request, 0);
497 	mode = get_rparam(request, 1);
498 
499 	if (FAIL == get_net_stat(&mib, if_name))
500 	{
501 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain network interface information."));
502 		return SYSINFO_RET_FAIL;
503 	}
504 
505 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))
506 	{
507 		SET_UI64_RESULT(result, mib.mib_if.ifInOctets + mib.mib_if.ifOutOctets);
508 	}
509 	else if (0 == strcmp(mode, "packets"))
510 	{
511 		SET_UI64_RESULT(result, mib.mib_if.ifInUcastPkts + mib.mib_if.ifInNUcastPkts
512 				+ mib.mib_if.ifOutUcastPkts + mib.mib_if.ifOutNUcastPkts);
513 	}
514 	else if (0 == strcmp(mode, "errors"))
515 	{
516 		SET_UI64_RESULT(result, mib.mib_if.ifInErrors + mib.mib_if.ifOutErrors);
517 	}
518 	else if (0 == strcmp(mode, "dropped"))
519 	{
520 		SET_UI64_RESULT(result, mib.mib_if.ifInDiscards + mib.mib_if.ifOutDiscards);
521 	}
522 	else
523 	{
524 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
525 		return SYSINFO_RET_FAIL;
526 	}
527 
528 	return SYSINFO_RET_OK;
529 }
530