1 /*
2 * Copyright (C) Tildeslash Ltd. All rights reserved.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Affero General Public License for more details.
11 *
12 * You should have received a copy of the GNU Affero General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 * In addition, as a special exception, the copyright holders give
16 * permission to link the code of portions of this program with the
17 * OpenSSL library under certain conditions as described in each
18 * individual source file, and distribute linked combinations
19 * including the two.
20 *
21 * You must obey the GNU Affero General Public License in all respects
22 * for all of the code used other than OpenSSL.
23 */
24
25
26 #include "config.h"
27
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netdb.h>
32 #include <unistd.h>
33 #include <sys/ioctl.h>
34 #ifdef HAVE_IFADDRS_H
35 #include <ifaddrs.h>
36 #endif
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
39 #endif
40 #ifdef HAVE_NET_IF_H
41 #include <net/if.h>
42 #endif
43 #ifdef HAVE_NET_IF_MEDIA_H
44 #include <net/if_media.h>
45 #endif
46 #ifdef HAVE_KSTAT_H
47 #include <kstat.h>
48 #endif
49 #ifdef HAVE_SYS_SYSCTL_H
50 #include <sys/sysctl.h>
51 #endif
52 #ifdef HAVE_NET_ROUTE_H
53 #include <net/route.h>
54 #endif
55 #ifdef HAVE_NET_IF_DL_H
56 #include <net/if_dl.h>
57 #endif
58 #ifdef HAVE_LIBPERFSTAT_H
59 #include <sys/protosw.h>
60 #include <libperfstat.h>
61 #endif
62
63 #include "monit.h"
64
65 // libmonit
66 #include "system/Time.h"
67 #include "system/System.h"
68 #include "util/Str.h"
69 #include "exceptions/AssertException.h"
70
71
72
73 /**
74 * Implementation of the Statistics Facade for Unix Systems.
75 *
76 * @author http://www.tildeslash.com/
77 * @see http://www.mmonit.com/
78 * @file
79 */
80
81
82 /* ------------------------------------------------------------- Definitions */
83
84
85 #define T Link_T
86
87
88 static struct {
89 struct ifaddrs *addrs;
90 unsigned long long timestamp;
91 } _stats = {};
92
93
94 typedef struct LinkData_T {
95 #ifndef __LP64__
96 unsigned long long raw;
97 #endif
98 long long now;
99 long long last;
100 unsigned long long minute[60];
101 unsigned long long hour[24];
102 } LinkData_T;
103
104
105 struct T {
106 char *object;
107 const char *(*resolve)(const char *object); // Resolve Object -> Interface, set during Link_T instantiation by constructor (currently we implement only IPAddress -> Interface lookup)
108 struct {
109 long long last;
110 long long now;
111 } timestamp;
112 int state; // State (-1 = N/A, 0 = down, 1 = up)
113 int duplex; // Duplex (-1 = N/A, 0 = half, 1 = full)
114 long long speed; // Speed [bps]
115 LinkData_T ipackets; // Packets received on interface
116 LinkData_T ierrors; // Input errors on interface
117 LinkData_T ibytes; // Total number of octets received
118 LinkData_T opackets; // Packets sent on interface
119 LinkData_T oerrors; // Output errors on interface
120 LinkData_T obytes; // Total number of octets sent
121 };
122
123
124 /* ----------------------------------------------------- Static destructor */
125
126
_destructor(void)127 static void __attribute__ ((destructor)) _destructor(void) {
128 #ifdef HAVE_IFADDRS_H
129 if (_stats.addrs)
130 freeifaddrs(_stats.addrs);
131 #endif
132 }
133
134
135 /* --------------------------------------------------------------- Private */
136
137
_updateValue(LinkData_T * data,unsigned long long raw)138 static void _updateValue(LinkData_T *data, unsigned long long raw) {
139 unsigned long long value = raw;
140 #ifndef __LP64__
141 if (raw < data->raw)
142 value = data->now + ULONG_MAX + 1ULL - data->raw + raw; // Counter wrapped
143 else
144 value = data->now + raw - data->raw;
145 data->raw = raw;
146 #endif
147 data->last = data->now;
148 data->now = value;
149 }
150
151
152 #if defined DARWIN
153 #include "os/macosx/Link.inc"
154 #elif defined FREEBSD
155 #include "os/freebsd/Link.inc"
156 #elif defined OPENBSD
157 #include "os/openbsd/Link.inc"
158 #elif defined NETBSD
159 #include "os/netbsd/Link.inc"
160 #elif defined DRAGONFLY
161 #include "os/dragonfly/Link.inc"
162 #elif defined LINUX
163 #include "os/linux/Link.inc"
164 #elif defined SOLARIS
165 #include "os/solaris/Link.inc"
166 #elif defined AIX
167 #include "os/aix/Link.inc"
168 #endif
169
170
_resetData(LinkData_T * data,unsigned long long value)171 static void _resetData(LinkData_T *data, unsigned long long value) {
172 #ifndef __LP64__
173 data->raw = value;
174 #endif
175 data->last = data->now = value;
176 for (int i = 0; i < 60; i++)
177 data->minute[i] = value;
178 for (int i = 0; i < 24; i++)
179 data->hour[i] = value;
180 }
181
182
_reset(T L)183 static void _reset(T L) {
184 L->timestamp.last = L->timestamp.now = 0ULL;
185 L->speed = -1LL;
186 L->state = L->duplex = -1;
187 _resetData(&(L->ibytes), 0ULL);
188 _resetData(&(L->ipackets), 0ULL);
189 _resetData(&(L->ierrors), 0ULL);
190 _resetData(&(L->obytes), 0ULL);
191 _resetData(&(L->opackets), 0ULL);
192 _resetData(&(L->oerrors), 0ULL);
193 }
194
195
_deltaSecond(T L,LinkData_T * data)196 static long long _deltaSecond(T L, LinkData_T *data) {
197 if (L->timestamp.last > 0 && L->timestamp.now > L->timestamp.last)
198 if (data->last > 0 && data->now > data->last)
199 return (long long)((data->now - data->last) * 1000. / (L->timestamp.now - L->timestamp.last));
200 return 0ULL;
201 }
202
203
_deltaMinute(T L,LinkData_T * data,int count)204 static long long _deltaMinute(T L, LinkData_T *data, int count) {
205 int stop = Time_minutes(L->timestamp.now / 1000.);
206 int delta = stop - count;
207 int start = delta < 0 ? 60 + delta : delta;
208 if (start == stop) // count == 60 (wrap)
209 start = start < 59 ? start + 1 : 0;
210 assert(start >= 0 && start < 60);
211 assert(stop >= 0 && stop < 60);
212 return data->minute[stop] - data->minute[start];
213 }
214
215
_deltaHour(T L,LinkData_T * data,int count)216 static long long _deltaHour(T L, LinkData_T *data, int count) {
217 int stop = Time_hour(L->timestamp.now / 1000.);
218 int delta = stop - count;
219 int start = delta < 0 ? 24 + delta : delta;
220 if (start == stop) // count == 24 (wrap)
221 start = start < 23 ? start + 1 : 0;
222 assert(start >= 0 && start < 24);
223 assert(stop >= 0 && stop < 24);
224 return data->hour[stop] - data->hour[start];
225 }
226
227
_findInterfaceForAddress(const char * address)228 static const char *_findInterfaceForAddress(const char *address) {
229 #ifdef HAVE_IFADDRS_H
230 for (struct ifaddrs *a = _stats.addrs; a != NULL; a = a->ifa_next) {
231 if (a->ifa_addr == NULL)
232 continue;
233 int s;
234 char host[NI_MAXHOST];
235 if (a->ifa_addr->sa_family == AF_INET)
236 s = getnameinfo(a->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
237 #ifdef HAVE_IPV6
238 else if (a->ifa_addr->sa_family == AF_INET6)
239 s = getnameinfo(a->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
240 #endif
241 else
242 continue;
243 if (s != 0)
244 THROW(AssertException, "Cannot translate address to interface -- %s", gai_strerror(s));
245 if (Str_isEqual(address, host))
246 return a->ifa_name;
247 }
248 THROW(AssertException, "Address %s not found", address);
249 #else
250 THROW(AssertException, "Network monitoring by IP address is not supported on this platform, please use 'check network <foo> with interface <bar>' instead");
251 #endif
252 return NULL;
253 }
254
255
_returnInterface(const char * interface)256 static const char *_returnInterface(const char *interface) {
257 return interface;
258 }
259
260
_updateHistory(T L)261 static void _updateHistory(T L) {
262 if (L->timestamp.last == 0ULL) {
263 // Initialize the history on first update, so we can start accounting for total data immediately. Any delta will show difference between the very first value and then given point in time, until regular update cycle
264 _resetData(&(L->ibytes), L->ibytes.now);
265 _resetData(&(L->ipackets), L->ipackets.now);
266 _resetData(&(L->ierrors), L->ierrors.now);
267 _resetData(&(L->obytes), L->obytes.now);
268 _resetData(&(L->opackets), L->opackets.now);
269 _resetData(&(L->oerrors), L->oerrors.now);
270 } else {
271 // Update relative values only
272 time_t now = L->timestamp.now / 1000.;
273 int minute = Time_minutes(now);
274 int hour = Time_hour(now);
275 L->ibytes.minute[minute] = L->ibytes.hour[hour] = L->ibytes.now;
276 L->ipackets.minute[minute] = L->ipackets.hour[hour] = L->ipackets.now;
277 L->ierrors.minute[minute] = L->ierrors.hour[hour] = L->ierrors.now;
278 L->obytes.minute[minute] = L->obytes.hour[hour] = L->obytes.now;
279 L->opackets.minute[minute] = L->opackets.hour[hour] = L->opackets.now;
280 L->oerrors.minute[minute] = L->oerrors.hour[hour] = L->oerrors.now;
281 }
282 }
283
284
_updateCache(void)285 static void _updateCache(void) {
286 #ifdef HAVE_IFADDRS_H
287 unsigned long long now = Time_milli();
288 // Refresh only if the statistics are older then 1 second (handle also backward time jumps)
289 if (now > _stats.timestamp + 1000 || now < _stats.timestamp - 1000) {
290 _stats.timestamp = now;
291 if (_stats.addrs) {
292 freeifaddrs(_stats.addrs);
293 _stats.addrs = NULL;
294 }
295 if (getifaddrs(&(_stats.addrs)) == -1) {
296 _stats.timestamp = 0ULL;
297 THROW(AssertException, "Cannot get network statistics -- %s", System_getError(errno));
298 }
299 }
300 #endif
301 }
302
303
304 /* ---------------------------------------------------------------- Public */
305
306
Link_createForAddress(const char * address)307 T Link_createForAddress(const char *address) {
308 assert(address);
309 T L;
310 NEW(L);
311 _reset(L);
312 L->object = Str_dup(address);
313 L->resolve = _findInterfaceForAddress;
314 return L;
315 }
316
317
Link_createForInterface(const char * interface)318 T Link_createForInterface(const char *interface) {
319 assert(interface);
320 T L;
321 NEW(L);
322 _reset(L);
323 L->object = Str_dup(interface);
324 L->resolve = _returnInterface;
325 return L;
326 }
327
328
Link_free(T * L)329 void Link_free(T *L) {
330 FREE((*L)->object);
331 FREE(*L);
332 }
333
334
Link_reset(T L)335 void Link_reset(T L) {
336 _reset(L);
337 }
338
339
Link_isGetByAddressSupported(void)340 bool Link_isGetByAddressSupported(void) {
341 #ifdef HAVE_IFADDRS_H
342 return true;
343 #else
344 return false;
345 #endif
346 }
347
348
Link_update(T L)349 void Link_update(T L) {
350 _updateCache();
351 const char *interface = L->resolve(L->object);
352 if (_update(L, interface))
353 _updateHistory(L);
354 else
355 THROW(AssertException, "Cannot udate network statistics -- interface %s not found", interface);
356 }
357
358
Link_getBytesInPerSecond(T L)359 long long Link_getBytesInPerSecond(T L) {
360 assert(L);
361 return L->state > 0 ? _deltaSecond(L, &(L->ibytes)) : -1LL;
362 }
363
364
Link_getBytesInPerMinute(T L,int count)365 long long Link_getBytesInPerMinute(T L, int count) {
366 assert(L);
367 return L->state > 0 ? _deltaMinute(L, &(L->ibytes), count) : -1LL;
368 }
369
370
Link_getBytesInPerHour(T L,int count)371 long long Link_getBytesInPerHour(T L, int count) {
372 assert(L);
373 return L->state > 0 ? _deltaHour(L, &(L->ibytes), count) : -1LL;
374 }
375
376
Link_getBytesInTotal(T L)377 long long Link_getBytesInTotal(T L) {
378 assert(L);
379 return L->state > 0 ? L->ibytes.now : -1LL;
380 }
381
382
Link_getSaturationInPerSecond(T L)383 double Link_getSaturationInPerSecond(T L) {
384 assert(L);
385 return L->state > 0 && L->speed ? (double)Link_getBytesInPerSecond(L) * 8. * 100. / L->speed : -1.;
386 }
387
388
Link_getPacketsInPerSecond(T L)389 long long Link_getPacketsInPerSecond(T L) {
390 assert(L);
391 return L->state > 0 ? _deltaSecond(L, &(L->ipackets)) : -1LL;
392 }
393
394
Link_getPacketsInPerMinute(T L,int count)395 long long Link_getPacketsInPerMinute(T L, int count) {
396 assert(L);
397 return L->state > 0 ? _deltaMinute(L, &(L->ipackets), count) : -1LL;
398 }
399
400
Link_getPacketsInPerHour(T L,int count)401 long long Link_getPacketsInPerHour(T L, int count) {
402 assert(L);
403 return L->state > 0 ? _deltaHour(L, &(L->ipackets), count) : -1LL;
404 }
405
406
Link_getPacketsInTotal(T L)407 long long Link_getPacketsInTotal(T L) {
408 assert(L);
409 return L->state > 0 ? L->ipackets.now : -1LL;
410 }
411
412
Link_getErrorsInPerSecond(T L)413 long long Link_getErrorsInPerSecond(T L) {
414 assert(L);
415 return L->state > 0 ? _deltaSecond(L, &(L->ierrors)) : -1LL;
416 }
417
418
Link_getErrorsInPerMinute(T L,int count)419 long long Link_getErrorsInPerMinute(T L, int count) {
420 assert(L);
421 return L->state > 0 ? _deltaMinute(L, &(L->ierrors), count) : -1LL;
422 }
423
424
Link_getErrorsInPerHour(T L,int count)425 long long Link_getErrorsInPerHour(T L, int count) {
426 assert(L);
427 return L->state > 0 ? _deltaHour(L, &(L->ierrors), count) : -1LL;
428 }
429
430
Link_getErrorsInTotal(T L)431 long long Link_getErrorsInTotal(T L) {
432 assert(L);
433 return L->state > 0 ? L->ierrors.now : -1LL;
434 }
435
436
Link_getBytesOutPerSecond(T L)437 long long Link_getBytesOutPerSecond(T L) {
438 assert(L);
439 return L->state > 0 ? _deltaSecond(L, &(L->obytes)) : -1LL;
440 }
441
442
Link_getBytesOutPerMinute(T L,int count)443 long long Link_getBytesOutPerMinute(T L, int count) {
444 assert(L);
445 return L->state > 0 ? _deltaMinute(L, &(L->obytes), count) : -1LL;
446 }
447
448
Link_getBytesOutPerHour(T L,int count)449 long long Link_getBytesOutPerHour(T L, int count) {
450 assert(L);
451 return L->state > 0 ? _deltaHour(L, &(L->obytes), count) : -1LL;
452 }
453
454
Link_getBytesOutTotal(T L)455 long long Link_getBytesOutTotal(T L) {
456 assert(L);
457 return L->state > 0 ? L->obytes.now : -1LL;
458 }
459
460
Link_getSaturationOutPerSecond(T L)461 double Link_getSaturationOutPerSecond(T L) {
462 assert(L);
463 return L->state > 0 && L->speed ? (double)Link_getBytesOutPerSecond(L) * 8. * 100. / L->speed : -1.;
464 }
465
466
Link_getPacketsOutPerSecond(T L)467 long long Link_getPacketsOutPerSecond(T L) {
468 assert(L);
469 return L->state > 0 ? _deltaSecond(L, &(L->opackets)) : -1LL;
470 }
471
472
Link_getPacketsOutPerMinute(T L,int count)473 long long Link_getPacketsOutPerMinute(T L, int count) {
474 assert(L);
475 return L->state > 0 ? _deltaMinute(L, &(L->opackets), count) : -1LL;
476 }
477
478
Link_getPacketsOutPerHour(T L,int count)479 long long Link_getPacketsOutPerHour(T L, int count) {
480 assert(L);
481 return L->state > 0 ? _deltaHour(L, &(L->opackets), count) : -1LL;
482 }
483
484
Link_getPacketsOutTotal(T L)485 long long Link_getPacketsOutTotal(T L) {
486 assert(L);
487 return L->state > 0 ? L->opackets.now : -1LL;
488 }
489
490
Link_getErrorsOutPerSecond(T L)491 long long Link_getErrorsOutPerSecond(T L) {
492 assert(L);
493 return L->state > 0 ? _deltaSecond(L, &(L->oerrors)) : -1LL;
494 }
495
496
Link_getErrorsOutPerMinute(T L,int count)497 long long Link_getErrorsOutPerMinute(T L, int count) {
498 assert(L);
499 return L->state > 0 ? _deltaMinute(L, &(L->oerrors), count) : -1LL;
500 }
501
502
Link_getErrorsOutPerHour(T L,int count)503 long long Link_getErrorsOutPerHour(T L, int count) {
504 assert(L);
505 return L->state > 0 ? _deltaHour(L, &(L->oerrors), count) : -1LL;
506 }
507
508
Link_getErrorsOutTotal(T L)509 long long Link_getErrorsOutTotal(T L) {
510 assert(L);
511 return L->state > 0 ? L->oerrors.now : -1LL;
512 }
513
514
Link_getState(T L)515 int Link_getState(T L) {
516 assert(L);
517 return L->state;
518 }
519
520
Link_getSpeed(T L)521 long long Link_getSpeed(T L) {
522 assert(L);
523 return L->state > 0 ? L->speed : -1LL;
524 }
525
526
Link_getDuplex(T L)527 int Link_getDuplex(T L) {
528 assert(L);
529 return L->state > 0 ? L->duplex : -1;
530 }
531
532