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 #include <sys/ioctl.h>
26 #include <sys/sockio.h>
27 
28 #if OpenBSD >= 201405			/* if OpenBSD 5.5 or newer */
29 #	if OpenBSD >= 201510		/* if Openbsd 5.8 or newer */
30 #		include <sys/malloc.h>	/* Workaround: include malloc.h here without _KERNEL to prevent its */
31 					/* inclusion later from if_var.h to avoid malloc() and free() redefinition. */
32 #		define _KERNEL	/* define _KERNEL to enable 'ifnet' and 'ifnet_head' definitions in if_var.h */
33 #	endif
34 #	include <net/if_var.h>  /* structs ifnet and ifnet_head are defined in this header since OpenBSD 5.5 */
35 #endif
36 
37 static struct nlist kernel_symbols[] =
38 {
39 	{"_ifnet", N_UNDF, 0, 0, 0},
40 	{"_tcbtable", N_UNDF, 0, 0, 0},
41 	{NULL, 0, 0, 0, 0}
42 };
43 
44 #define IFNET_ID 0
45 
get_ifdata(const char * if_name,zbx_uint64_t * ibytes,zbx_uint64_t * ipackets,zbx_uint64_t * ierrors,zbx_uint64_t * idropped,zbx_uint64_t * obytes,zbx_uint64_t * opackets,zbx_uint64_t * oerrors,zbx_uint64_t * tbytes,zbx_uint64_t * tpackets,zbx_uint64_t * terrors,zbx_uint64_t * icollisions,char ** error)46 static int	get_ifdata(const char *if_name,
47 		zbx_uint64_t *ibytes, zbx_uint64_t *ipackets, zbx_uint64_t *ierrors, zbx_uint64_t *idropped,
48 		zbx_uint64_t *obytes, zbx_uint64_t *opackets, zbx_uint64_t *oerrors,
49 		zbx_uint64_t *tbytes, zbx_uint64_t *tpackets, zbx_uint64_t *terrors,
50 		zbx_uint64_t *icollisions, char **error)
51 {
52 	struct ifnet_head	head;
53 	struct ifnet		*ifp;
54 
55 	kvm_t	*kp;
56 	int	len = 0;
57 	int	ret = SYSINFO_RET_FAIL;
58 
59 	if (NULL == if_name || '\0' == *if_name)
60 	{
61 		*error = zbx_strdup(NULL, "Network interface name cannot be empty.");
62 		return SYSINFO_RET_FAIL;
63 	}
64 
65 	/* if(i)_ibytes;	total number of octets received */
66 	/* if(i)_ipackets;	packets received on interface */
67 	/* if(i)_ierrors;	input errors on interface */
68 	/* if(i)_iqdrops;	dropped on input, this interface */
69 	/* if(i)_obytes;	total number of octets sent */
70 	/* if(i)_opackets;	packets sent on interface */
71 	/* if(i)_oerrors;	output errors on interface */
72 	/* if(i)_collisions;	collisions on csma interfaces */
73 
74 	if (ibytes)
75 		*ibytes = 0;
76 	if (ipackets)
77 		*ipackets = 0;
78 	if (ierrors)
79 		*ierrors = 0;
80 	if (idropped)
81 		*idropped = 0;
82 	if (obytes)
83 		*obytes = 0;
84 	if (opackets)
85 		*opackets = 0;
86 	if (oerrors)
87 		*oerrors = 0;
88 	if (tbytes)
89 		*tbytes = 0;
90 	if (tpackets)
91 		*tpackets = 0;
92 	if (terrors)
93 		*terrors = 0;
94 	if (icollisions)
95 		*icollisions = 0;
96 
97 	if (NULL != (kp = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL))) /* requires root privileges */
98 	{
99 		struct ifnet	v;
100 
101 		if (N_UNDF == kernel_symbols[IFNET_ID].n_type)
102 			if (0 != kvm_nlist(kp, &kernel_symbols[0]))
103 				kernel_symbols[IFNET_ID].n_type = N_UNDF;
104 
105 		if (N_UNDF != kernel_symbols[IFNET_ID].n_type)
106 		{
107 			len = sizeof(struct ifnet_head);
108 
109 			if (kvm_read(kp, kernel_symbols[IFNET_ID].n_value, &head, len) >= len)
110 			{
111 				len = sizeof(struct ifnet);
112 
113 				for (ifp = head.tqh_first; ifp; ifp = v.if_list.tqe_next)
114 				{
115 					if (kvm_read(kp, (u_long)ifp, &v, len) < len)
116 						break;
117 
118 					if (0 == strcmp(if_name, v.if_xname))
119 					{
120 						if (ibytes)
121 							*ibytes += v.if_ibytes;
122 						if (ipackets)
123 							*ipackets += v.if_ipackets;
124 						if (ierrors)
125 							*ierrors += v.if_ierrors;
126 						if (idropped)
127 							*idropped += v.if_iqdrops;
128 						if (obytes)
129 							*obytes += v.if_obytes;
130 						if (opackets)
131 							*opackets += v.if_opackets;
132 						if (oerrors)
133 							*oerrors += v.if_oerrors;
134 						if (tbytes)
135 							*tbytes += v.if_ibytes + v.if_obytes;
136 						if (tpackets)
137 							*tpackets += v.if_ipackets + v.if_opackets;
138 						if (terrors)
139 							*terrors += v.if_ierrors + v.if_oerrors;
140 						if (icollisions)
141 							*icollisions += v.if_collisions;
142 
143 						ret = SYSINFO_RET_OK;
144 					}
145 				}
146 			}
147 		}
148 		kvm_close(kp);
149 
150 		if (SYSINFO_RET_FAIL == ret)
151 			*error = zbx_strdup(NULL, "Cannot find information for this network interface.");
152 	}
153 	else
154 	{
155 		/* fallback to using SIOCGIFDATA */
156 
157 		int		if_s;
158 		struct ifreq	ifr;
159 		struct if_data	v;
160 
161 		if ((if_s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
162 		{
163 			*error = zbx_dsprintf(NULL, "Cannot create socket: %s", zbx_strerror(errno));
164 			goto clean;
165 		}
166 
167 		zbx_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ - 1);
168 		ifr.ifr_data = (caddr_t)&v;
169 
170 		if (ioctl(if_s, SIOCGIFDATA, &ifr) < 0)
171 		{
172 			*error = zbx_dsprintf(NULL, "Cannot set socket parameters: %s", zbx_strerror(errno));
173 			goto clean;
174 		}
175 
176 		if (ibytes)
177 			*ibytes += v.ifi_ibytes;
178 		if (ipackets)
179 			*ipackets += v.ifi_ipackets;
180 		if (ierrors)
181 			*ierrors += v.ifi_ierrors;
182 		if (idropped)
183 			*idropped += v.ifi_iqdrops;
184 		if (obytes)
185 			*obytes += v.ifi_obytes;
186 		if (opackets)
187 			*opackets += v.ifi_opackets;
188 		if (oerrors)
189 			*oerrors += v.ifi_oerrors;
190 		if (tbytes)
191 			*tbytes += v.ifi_ibytes + v.ifi_obytes;
192 		if (tpackets)
193 			*tpackets += v.ifi_ipackets + v.ifi_opackets;
194 		if (terrors)
195 			*terrors += v.ifi_ierrors + v.ifi_oerrors;
196 		if (icollisions)
197 			*icollisions += v.ifi_collisions;
198 
199 		ret = SYSINFO_RET_OK;
200 clean:
201 		if (if_s >= 0)
202 			close(if_s);
203 	}
204 
205 	return ret;
206 }
207 
NET_IF_IN(AGENT_REQUEST * request,AGENT_RESULT * result)208 int	NET_IF_IN(AGENT_REQUEST *request, AGENT_RESULT *result)
209 {
210 	char		*if_name, *mode, *error;
211 	zbx_uint64_t	ibytes, ipackets, ierrors, idropped;
212 
213 	if (2 < request->nparam)
214 	{
215 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
216 		return SYSINFO_RET_FAIL;
217 	}
218 
219 	if_name = get_rparam(request, 0);
220 	mode = get_rparam(request, 1);
221 
222 	if (SYSINFO_RET_OK != get_ifdata(if_name, &ibytes, &ipackets, &ierrors, &idropped, NULL, NULL, NULL, NULL, NULL,
223 			NULL, NULL, &error))
224 	{
225 		SET_MSG_RESULT(result, error);
226 		return SYSINFO_RET_FAIL;
227 	}
228 
229 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))	/* default parameter */
230 		SET_UI64_RESULT(result, ibytes);
231 	else if (0 == strcmp(mode, "packets"))
232 		SET_UI64_RESULT(result, ipackets);
233 	else if (0 == strcmp(mode, "errors"))
234 		SET_UI64_RESULT(result, ierrors);
235 	else if (0 == strcmp(mode, "dropped"))
236 		SET_UI64_RESULT(result, idropped);
237 	else
238 	{
239 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
240 		return SYSINFO_RET_FAIL;
241 	}
242 
243 	return SYSINFO_RET_OK;
244 }
245 
NET_IF_OUT(AGENT_REQUEST * request,AGENT_RESULT * result)246 int	NET_IF_OUT(AGENT_REQUEST *request, AGENT_RESULT *result)
247 {
248 	char		*if_name, *mode, *error;
249 	zbx_uint64_t	obytes, opackets, oerrors;
250 
251 	if (2 < request->nparam)
252 	{
253 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
254 		return SYSINFO_RET_FAIL;
255 	}
256 
257 	if_name = get_rparam(request, 0);
258 	mode = get_rparam(request, 1);
259 
260 	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, &obytes, &opackets, &oerrors, NULL, NULL,
261 			NULL, NULL, &error))
262 	{
263 		SET_MSG_RESULT(result, error);
264 		return SYSINFO_RET_FAIL;
265 	}
266 
267 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))	/* default parameter */
268 		SET_UI64_RESULT(result, obytes);
269 	else if (0 == strcmp(mode, "packets"))
270 		SET_UI64_RESULT(result, opackets);
271 	else if (0 == strcmp(mode, "errors"))
272 		SET_UI64_RESULT(result, oerrors);
273 	else
274 	{
275 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
276 		return SYSINFO_RET_FAIL;
277 	}
278 
279 	return SYSINFO_RET_OK;
280 }
281 
NET_IF_TOTAL(AGENT_REQUEST * request,AGENT_RESULT * result)282 int	NET_IF_TOTAL(AGENT_REQUEST *request, AGENT_RESULT *result)
283 {
284 	char		*if_name, *mode, *error;
285 	zbx_uint64_t	tbytes, tpackets, terrors;
286 
287 	if (2 < request->nparam)
288 	{
289 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
290 		return SYSINFO_RET_FAIL;
291 	}
292 
293 	if_name = get_rparam(request, 0);
294 	mode = get_rparam(request, 1);
295 
296 	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &tbytes, &tpackets,
297 			&terrors, NULL, &error))
298 	{
299 		SET_MSG_RESULT(result, error);
300 		return SYSINFO_RET_FAIL;
301 	}
302 
303 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))	/* default parameter */
304 		SET_UI64_RESULT(result, tbytes);
305 	else if (0 == strcmp(mode, "packets"))
306 		SET_UI64_RESULT(result, tpackets);
307 	else if (0 == strcmp(mode, "errors"))
308 		SET_UI64_RESULT(result, terrors);
309 	else
310 	{
311 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
312 		return SYSINFO_RET_FAIL;
313 	}
314 
315 	return SYSINFO_RET_OK;
316 }
317 
NET_IF_COLLISIONS(AGENT_REQUEST * request,AGENT_RESULT * result)318 int	NET_IF_COLLISIONS(AGENT_REQUEST *request, AGENT_RESULT *result)
319 {
320 	char		*if_name, *error;
321 	zbx_uint64_t	icollisions;
322 
323 	if (1 < request->nparam)
324 	{
325 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
326 		return SYSINFO_RET_FAIL;
327 	}
328 
329 	if_name = get_rparam(request, 0);
330 
331 	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
332 			NULL, &icollisions, &error))
333 	{
334 		SET_MSG_RESULT(result, error);
335 		return SYSINFO_RET_FAIL;
336 	}
337 
338 	SET_UI64_RESULT(result, icollisions);
339 
340 	return SYSINFO_RET_OK;
341 }
342 
NET_IF_DISCOVERY(AGENT_REQUEST * request,AGENT_RESULT * result)343 int	NET_IF_DISCOVERY(AGENT_REQUEST *request, AGENT_RESULT *result)
344 {
345 	int			i;
346 	struct zbx_json		j;
347 	struct if_nameindex	*interfaces;
348 
349 	if (NULL == (interfaces = if_nameindex()))
350 	{
351 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain system information: %s", zbx_strerror(errno)));
352 		return SYSINFO_RET_FAIL;
353 	}
354 
355 	zbx_json_initarray(&j, ZBX_JSON_STAT_BUF_LEN);
356 
357 	for (i = 0; 0 != interfaces[i].if_index; i++)
358 	{
359 		zbx_json_addobject(&j, NULL);
360 		zbx_json_addstring(&j, "{#IFNAME}", interfaces[i].if_name, ZBX_JSON_TYPE_STRING);
361 		zbx_json_close(&j);
362 	}
363 
364 	zbx_json_close(&j);
365 
366 	SET_STR_RESULT(result, strdup(j.buffer));
367 
368 	zbx_json_free(&j);
369 
370 	if_freenameindex(interfaces);
371 
372 	return SYSINFO_RET_OK;
373 }
374