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