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_init(&j, ZBX_JSON_STAT_BUF_LEN);
356
357 zbx_json_addarray(&j, ZBX_PROTO_TAG_DATA);
358
359 for (i = 0; 0 != interfaces[i].if_index; i++)
360 {
361 zbx_json_addobject(&j, NULL);
362 zbx_json_addstring(&j, "{#IFNAME}", interfaces[i].if_name, ZBX_JSON_TYPE_STRING);
363 zbx_json_close(&j);
364 }
365
366 zbx_json_close(&j);
367
368 SET_STR_RESULT(result, strdup(j.buffer));
369
370 zbx_json_free(&j);
371
372 if_freenameindex(interfaces);
373
374 return SYSINFO_RET_OK;
375 }
376