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 static struct nlist kernel_symbols[] =
26 {
27 	{"_ifnet", N_UNDF, 0, 0, 0},
28 	{"_tcbtable", N_UNDF, 0, 0, 0},
29 	{NULL, 0, 0, 0, 0}
30 };
31 
32 #define IFNET_ID 0
33 
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)34 static int	get_ifdata(const char *if_name,
35 		zbx_uint64_t *ibytes, zbx_uint64_t *ipackets, zbx_uint64_t *ierrors, zbx_uint64_t *idropped,
36 		zbx_uint64_t *obytes, zbx_uint64_t *opackets, zbx_uint64_t *oerrors,
37 		zbx_uint64_t *tbytes, zbx_uint64_t *tpackets, zbx_uint64_t *terrors,
38 		zbx_uint64_t *icollisions, char **error)
39 {
40 	struct ifnet_head	head;
41 	struct ifnet		*ifp;
42 	struct ifnet		v;
43 
44 	kvm_t	*kp;
45 	int	len = 0;
46 	int	ret = SYSINFO_RET_FAIL;
47 
48 	if (NULL == if_name || '\0' == *if_name)
49 	{
50 		*error = zbx_strdup(NULL, "Network interface name cannot be empty.");
51 		return FAIL;
52 	}
53 
54 	if (NULL == (kp = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL))) /* requires root privileges */
55 	{
56 		*error = zbx_strdup(NULL, "Cannot obtain a descriptor to access kernel virtual memory.");
57 		return FAIL;
58 	}
59 
60 	if (N_UNDF == kernel_symbols[IFNET_ID].n_type)
61 		if (0 != kvm_nlist(kp, &kernel_symbols[0]))
62 			kernel_symbols[IFNET_ID].n_type = N_UNDF;
63 
64 	if (N_UNDF != kernel_symbols[IFNET_ID].n_type)
65 	{
66 		len = sizeof(struct ifnet_head);
67 
68 		if (kvm_read(kp, kernel_symbols[IFNET_ID].n_value, &head, len) >= len)
69 		{
70 			len = sizeof(struct ifnet);
71 
72 			/* if_ibytes;		total number of octets received */
73 			/* if_ipackets;		packets received on interface */
74 			/* if_ierrors;		input errors on interface */
75 			/* if_iqdrops;		dropped on input, this interface */
76 			/* if_obytes;		total number of octets sent */
77 			/* if_opackets;		packets sent on interface */
78 			/* if_oerrors;		output errors on interface */
79 			/* if_collisions;	collisions on csma interfaces */
80 
81 			if (ibytes)
82 				*ibytes = 0;
83 			if (ipackets)
84 				*ipackets = 0;
85 			if (ierrors)
86 				*ierrors = 0;
87 			if (idropped)
88 				*idropped = 0;
89 			if (obytes)
90 				*obytes = 0;
91 			if (opackets)
92 				*opackets = 0;
93 			if (oerrors)
94 				*oerrors = 0;
95 			if (tbytes)
96 				*tbytes = 0;
97 			if (tpackets)
98 				*tpackets = 0;
99 			if (terrors)
100 				*terrors = 0;
101 			if (icollisions)
102 				*icollisions = 0;
103 
104 			for (ifp = head.tqh_first; ifp; ifp = v.if_list.tqe_next)
105 			{
106 				if (kvm_read(kp, (u_long)ifp, &v, len) < len)
107 					break;
108 
109 				if (0 == strcmp(if_name, v.if_xname))
110 				{
111 					if (ibytes)
112 						*ibytes += v.if_ibytes;
113 					if (ipackets)
114 						*ipackets += v.if_ipackets;
115 					if (ierrors)
116 						*ierrors += v.if_ierrors;
117 					if (idropped)
118 						*idropped += v.if_iqdrops;
119 					if (obytes)
120 						*obytes += v.if_obytes;
121 					if (opackets)
122 						*opackets += v.if_opackets;
123 					if (oerrors)
124 						*oerrors += v.if_oerrors;
125 					if (tbytes)
126 						*tbytes += v.if_ibytes + v.if_obytes;
127 					if (tpackets)
128 						*tpackets += v.if_ipackets + v.if_opackets;
129 					if (terrors)
130 						*terrors += v.if_ierrors + v.if_oerrors;
131 					if (icollisions)
132 						*icollisions += v.if_collisions;
133 					ret = SYSINFO_RET_OK;
134 				}
135 			}
136 		}
137 	}
138 
139 	kvm_close(kp);
140 
141 	if (SYSINFO_RET_FAIL == ret)
142 	{
143 		*error = zbx_strdup(NULL, "Cannot find information for this network interface.");
144 		return SYSINFO_RET_FAIL;
145 	}
146 
147 	return ret;
148 }
149 
NET_IF_IN(AGENT_REQUEST * request,AGENT_RESULT * result)150 int	NET_IF_IN(AGENT_REQUEST *request, AGENT_RESULT *result)
151 {
152 	char		*if_name, *mode, *error;
153 	zbx_uint64_t	ibytes, ipackets, ierrors, idropped;
154 
155 	if (2 < request->nparam)
156 	{
157 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
158 		return SYSINFO_RET_FAIL;
159 	}
160 
161 	if_name = get_rparam(request, 0);
162 	mode = get_rparam(request, 1);
163 
164 	if (SYSINFO_RET_OK != get_ifdata(if_name, &ibytes, &ipackets, &ierrors, &idropped, NULL, NULL, NULL, NULL, NULL,
165 			NULL, NULL, &error))
166 	{
167 		SET_MSG_RESULT(result, error);
168 		return SYSINFO_RET_FAIL;
169 	}
170 
171 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))	/* default parameter */
172 		SET_UI64_RESULT(result, ibytes);
173 	else if (0 == strcmp(mode, "packets"))
174 		SET_UI64_RESULT(result, ipackets);
175 	else if (0 == strcmp(mode, "errors"))
176 		SET_UI64_RESULT(result, ierrors);
177 	else if (0 == strcmp(mode, "dropped"))
178 		SET_UI64_RESULT(result, idropped);
179 	else
180 	{
181 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
182 		return SYSINFO_RET_FAIL;
183 	}
184 
185 	return SYSINFO_RET_OK;
186 }
187 
NET_IF_OUT(AGENT_REQUEST * request,AGENT_RESULT * result)188 int	NET_IF_OUT(AGENT_REQUEST *request, AGENT_RESULT *result)
189 {
190 	char		*if_name, *mode, *error;
191 	zbx_uint64_t	obytes, opackets, oerrors;
192 
193 	if (2 < request->nparam)
194 	{
195 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
196 		return SYSINFO_RET_FAIL;
197 	}
198 
199 	if_name = get_rparam(request, 0);
200 	mode = get_rparam(request, 1);
201 
202 	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, &obytes, &opackets, &oerrors, NULL, NULL,
203 			NULL, NULL, &error))
204 	{
205 		SET_MSG_RESULT(result, error);
206 		return SYSINFO_RET_FAIL;
207 	}
208 
209 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))	/* default parameter */
210 		SET_UI64_RESULT(result, obytes);
211 	else if (0 == strcmp(mode, "packets"))
212 		SET_UI64_RESULT(result, opackets);
213 	else if (0 == strcmp(mode, "errors"))
214 		SET_UI64_RESULT(result, oerrors);
215 	else
216 	{
217 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
218 		return SYSINFO_RET_FAIL;
219 	}
220 
221 	return SYSINFO_RET_OK;
222 }
223 
NET_IF_TOTAL(AGENT_REQUEST * request,AGENT_RESULT * result)224 int	NET_IF_TOTAL(AGENT_REQUEST *request, AGENT_RESULT *result)
225 {
226 	char		*if_name, *mode, *error;
227 	zbx_uint64_t	tbytes, tpackets, terrors;
228 
229 	if (2 < request->nparam)
230 	{
231 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
232 		return SYSINFO_RET_FAIL;
233 	}
234 
235 	if_name = get_rparam(request, 0);
236 	mode = get_rparam(request, 1);
237 
238 	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &tbytes, &tpackets,
239 			&terrors, NULL, &error))
240 	{
241 		SET_MSG_RESULT(result, error);
242 		return SYSINFO_RET_FAIL;
243 	}
244 
245 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "bytes"))	/* default parameter */
246 		SET_UI64_RESULT(result, tbytes);
247 	else if (0 == strcmp(mode, "packets"))
248 		SET_UI64_RESULT(result, tpackets);
249 	else if (0 == strcmp(mode, "errors"))
250 		SET_UI64_RESULT(result, terrors);
251 	else
252 	{
253 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
254 		return SYSINFO_RET_FAIL;
255 	}
256 
257 	return SYSINFO_RET_OK;
258 }
259 
NET_IF_COLLISIONS(AGENT_REQUEST * request,AGENT_RESULT * result)260 int	NET_IF_COLLISIONS(AGENT_REQUEST *request, AGENT_RESULT *result)
261 {
262 	char		*if_name, *error;
263 	zbx_uint64_t	icollisions;
264 
265 	if (1 < request->nparam)
266 	{
267 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
268 		return SYSINFO_RET_FAIL;
269 	}
270 
271 	if_name = get_rparam(request, 0);
272 
273 	if (SYSINFO_RET_OK != get_ifdata(if_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
274 			NULL, &icollisions, &error))
275 	{
276 		SET_MSG_RESULT(result, error);
277 		return SYSINFO_RET_FAIL;
278 	}
279 
280 	SET_UI64_RESULT(result, icollisions);
281 
282 	return SYSINFO_RET_OK;
283 }
284 
NET_IF_DISCOVERY(AGENT_REQUEST * request,AGENT_RESULT * result)285 int	NET_IF_DISCOVERY(AGENT_REQUEST *request, AGENT_RESULT *result)
286 {
287 	int			i;
288 	struct zbx_json		j;
289 	struct if_nameindex	*interfaces;
290 
291 	if (NULL == (interfaces = if_nameindex()))
292 	{
293 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain system information: %s", zbx_strerror(errno)));
294 		return SYSINFO_RET_FAIL;
295 	}
296 
297 	zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN);
298 
299 	zbx_json_addarray(&j, ZBX_PROTO_TAG_DATA);
300 
301 	for (i = 0; 0 != interfaces[i].if_index; i++)
302 	{
303 		zbx_json_addobject(&j, NULL);
304 		zbx_json_addstring(&j, "{#IFNAME}", interfaces[i].if_name, ZBX_JSON_TYPE_STRING);
305 		zbx_json_close(&j);
306 	}
307 
308 	zbx_json_close(&j);
309 
310 	SET_STR_RESULT(result, strdup(j.buffer));
311 
312 	zbx_json_free(&j);
313 
314 	if_freenameindex(interfaces);
315 
316 	return SYSINFO_RET_OK;
317 }
318