ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/network_stats.c
Revision: 1.44
Committed: Sat Feb 14 16:58:19 2004 UTC (20 years, 3 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.43: +56 -34 lines
Log Message:
Rework iface_stat code for Solaris so that it uses SIOCGIFFLAGS to find out
whether an interface is up or not. This means that we need to link with
-lsocket -lnsl now to get socket().

Also make the comment about when bytes-transferred stats a bit more explicit.
We're now in the slightly odd situation where it's possible for an interface to
show up in either or both of get_network_stats and get_network_iface_stats: for
instance, on raptor at the moment, ce0 is in use so shows up in both, lo0
doesn't have kstats for bytes transferred and thus only shows up in the second,
and ce1 exists but hasn't been plumbed so only shows up in the first. I'd be
mildly inclined to "fix" this by making get_network_stats do a SIOCGIFFLAGS
ioctl so unplumbed interfaces don't show up at all (and add packets
received/transmited to the get_network_stats result so we can at least show
something for lo0).

File Contents

# Content
1 /*
2 * i-scream central monitoring system
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2004 i-scream
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 * 02111-1307 USA
20 *
21 * $Id: network_stats.c,v 1.43 2004/02/14 15:48:42 ats Exp $
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include "statgrab.h"
31 #include <time.h>
32 #ifdef SOLARIS
33 #include <kstat.h>
34 #include <sys/sysinfo.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 #include <net/if.h>
39 #include <netinet/in.h>
40 #include <sys/sockio.h>
41 #endif
42 #ifdef LINUX
43 #include <stdio.h>
44 #include <sys/types.h>
45 #include <regex.h>
46 #include <sys/ioctl.h>
47 #include <sys/socket.h>
48 #include <net/if.h>
49 #include <ctype.h>
50 #include "tools.h"
51 /* Stuff which could be defined by defining KERNEL, but
52 * that would be a bad idea, so we'll just declare it here
53 */
54 typedef __uint8_t u8;
55 typedef __uint16_t u16;
56 typedef __uint32_t u32;
57 typedef __uint64_t u64;
58 #include <linux/ethtool.h>
59 #include <linux/sockios.h>
60 #include <unistd.h>
61 #endif
62 #ifdef ALLBSD
63 #include <sys/types.h>
64 #include <sys/socket.h>
65 #include <ifaddrs.h>
66 #include <net/if.h>
67 #include <net/if_media.h>
68 #include <sys/ioctl.h>
69 #endif
70
71 static network_stat_t *network_stats=NULL;
72 static int interfaces=0;
73
74 void network_stat_init(int start, int end, network_stat_t *net_stats){
75
76 for(net_stats+=start; start<end; start++){
77 net_stats->interface_name=NULL;
78 net_stats->tx=0;
79 net_stats->rx=0;
80 net_stats++;
81 }
82 }
83
84 network_stat_t *network_stat_malloc(int needed_entries, int *cur_entries, network_stat_t *net_stats){
85
86 if(net_stats==NULL){
87
88 if((net_stats=malloc(needed_entries * sizeof(network_stat_t)))==NULL){
89 return NULL;
90 }
91 network_stat_init(0, needed_entries, net_stats);
92 *cur_entries=needed_entries;
93
94 return net_stats;
95 }
96
97
98 if(*cur_entries<needed_entries){
99 net_stats=realloc(net_stats, (sizeof(network_stat_t)*needed_entries));
100 if(net_stats==NULL){
101 return NULL;
102 }
103 network_stat_init(*cur_entries, needed_entries, net_stats);
104 *cur_entries=needed_entries;
105 }
106
107 return net_stats;
108 }
109
110
111 network_stat_t *get_network_stats(int *entries){
112
113 static int sizeof_network_stats=0;
114 network_stat_t *network_stat_ptr;
115
116 #ifdef SOLARIS
117 kstat_ctl_t *kc;
118 kstat_t *ksp;
119 kstat_named_t *knp;
120 #endif
121
122 #ifdef LINUX
123 FILE *f;
124 /* Horrible big enough, but it should be easily big enough */
125 char line[8096];
126 regex_t regex;
127 regmatch_t line_match[4];
128 #endif
129 #ifdef ALLBSD
130 struct ifaddrs *net, *net_ptr;
131 struct if_data *net_data;
132 #endif
133
134 #ifdef ALLBSD
135 if(getifaddrs(&net) != 0){
136 return NULL;
137 }
138
139 interfaces=0;
140
141 for(net_ptr=net;net_ptr!=NULL;net_ptr=net_ptr->ifa_next){
142 if(net_ptr->ifa_addr->sa_family != AF_LINK) continue;
143 network_stats=network_stat_malloc((interfaces+1), &sizeof_network_stats, network_stats);
144 if(network_stats==NULL){
145 return NULL;
146 }
147 network_stat_ptr=network_stats+interfaces;
148
149 if(network_stat_ptr->interface_name!=NULL) free(network_stat_ptr->interface_name);
150 network_stat_ptr->interface_name=strdup(net_ptr->ifa_name);
151 if(network_stat_ptr->interface_name==NULL) return NULL;
152 net_data=(struct if_data *)net_ptr->ifa_data;
153 network_stat_ptr->rx=net_data->ifi_ibytes;
154 network_stat_ptr->tx=net_data->ifi_obytes;
155 network_stat_ptr->systime=time(NULL);
156 interfaces++;
157 }
158 freeifaddrs(net);
159 #endif
160
161 #ifdef SOLARIS
162 if ((kc = kstat_open()) == NULL) {
163 return NULL;
164 }
165
166 interfaces=0;
167
168 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
169 if (!strcmp(ksp->ks_class, "net")) {
170 kstat_read(kc, ksp, NULL);
171
172 #ifdef SOL7
173 #define RLOOKUP "rbytes"
174 #define WLOOKUP "obytes"
175 #define VALTYPE value.ui32
176 #else
177 #define RLOOKUP "rbytes64"
178 #define WLOOKUP "obytes64"
179 #define VALTYPE value.ui64
180 #endif
181
182 if((knp=kstat_data_lookup(ksp, RLOOKUP))==NULL){
183 /* This is a network interface, but it doesn't
184 * have the rbytes/obytes values; for instance,
185 * the loopback devices have this behaviour
186 * (although they do track packets in/out). */
187 continue;
188 }
189
190 network_stats=network_stat_malloc((interfaces+1), &sizeof_network_stats, network_stats);
191 if(network_stats==NULL){
192 return NULL;
193 }
194 network_stat_ptr=network_stats+interfaces;
195 network_stat_ptr->rx=knp->VALTYPE;
196
197 if((knp=kstat_data_lookup(ksp, WLOOKUP))==NULL){
198 continue;
199 }
200 network_stat_ptr->tx=knp->VALTYPE;
201 if(network_stat_ptr->interface_name!=NULL){
202 free(network_stat_ptr->interface_name);
203 }
204 network_stat_ptr->interface_name=strdup(ksp->ks_name);
205
206 network_stat_ptr->systime=time(NULL);
207 interfaces++;
208 }
209 }
210
211 kstat_close(kc);
212 #endif
213 #ifdef LINUX
214 f=fopen("/proc/net/dev", "r");
215 if(f==NULL){
216 return NULL;
217 }
218 /* read the 2 lines.. Its the title, so we dont care :) */
219 fgets(line, sizeof(line), f);
220 fgets(line, sizeof(line), f);
221
222
223 if((regcomp(&regex, "^[[:space:]]*([^:]+):[[:space:]]*([[:digit:]]+)[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+([[:digit:]]+)", REG_EXTENDED))!=0){
224 return NULL;
225 }
226
227 interfaces=0;
228
229 while((fgets(line, sizeof(line), f)) != NULL){
230 if((regexec(&regex, line, 4, line_match, 0))!=0){
231 continue;
232 }
233 network_stats=network_stat_malloc((interfaces+1), &sizeof_network_stats, network_stats);
234 if(network_stats==NULL){
235 return NULL;
236 }
237 network_stat_ptr=network_stats+interfaces;
238
239 if(network_stat_ptr->interface_name!=NULL){
240 free(network_stat_ptr->interface_name);
241 }
242
243 network_stat_ptr->interface_name=get_string_match(line, &line_match[1]);
244 network_stat_ptr->rx=get_ll_match(line, &line_match[2]);
245 network_stat_ptr->tx=get_ll_match(line, &line_match[3]);
246 network_stat_ptr->systime=time(NULL);
247
248 interfaces++;
249 }
250 fclose(f);
251 regfree(&regex);
252
253 #endif
254
255 #ifdef CYGWIN
256 return NULL;
257 #endif
258
259 *entries=interfaces;
260
261 return network_stats;
262 }
263
264 long long transfer_diff(long long new, long long old){
265 #if defined(SOL7) || defined(LINUX) || defined(FREEBSD)
266 #define MAXVAL 4294967296LL
267 #else
268 #define MAXVAL 18446744073709551616LL
269 #endif
270 long long result;
271 if(new>=old){
272 result = (new-old);
273 }else{
274 result = (MAXVAL+(new-old));
275 }
276
277 return result;
278
279 }
280
281 network_stat_t *get_network_stats_diff(int *entries) {
282 static network_stat_t *diff = NULL;
283 static int diff_count = 0;
284 network_stat_t *src, *dest;
285 int i, j, new_count;
286
287 if (network_stats == NULL) {
288 /* No previous stats, so we can't calculate a difference. */
289 return get_network_stats(entries);
290 }
291
292 /* Resize the results array to match the previous stats. */
293 diff = network_stat_malloc(interfaces, &diff_count, diff);
294 if (diff == NULL) {
295 return NULL;
296 }
297
298 /* Copy the previous stats into the result. */
299 for (i = 0; i < diff_count; i++) {
300 src = &network_stats[i];
301 dest = &diff[i];
302
303 if (dest->interface_name != NULL) {
304 free(dest->interface_name);
305 }
306 dest->interface_name = strdup(src->interface_name);
307 dest->rx = src->rx;
308 dest->tx = src->tx;
309 dest->systime = src->systime;
310 }
311
312 /* Get a new set of stats. */
313 if (get_network_stats(&new_count) == NULL) {
314 return NULL;
315 }
316
317 /* For each previous stat... */
318 for (i = 0; i < diff_count; i++) {
319 dest = &diff[i];
320
321 /* ... find the corresponding new stat ... */
322 for (j = 0; j < new_count; j++) {
323 /* Try the new stat in the same position first,
324 since that's most likely to be it. */
325 src = &network_stats[(i + j) % new_count];
326 if (strcmp(src->interface_name, dest->interface_name) == 0) {
327 break;
328 }
329 }
330 if (j == new_count) {
331 /* No match found. */
332 continue;
333 }
334
335 /* ... and subtract the previous stat from it to get the
336 difference. */
337 dest->rx = transfer_diff(src->rx, dest->rx);
338 dest->tx = transfer_diff(src->tx, dest->tx);
339 dest->systime = src->systime - dest->systime;
340 }
341
342 *entries = diff_count;
343 return diff;
344 }
345 /* NETWORK INTERFACE STATS */
346
347 void network_iface_stat_init(int start, int end, network_iface_stat_t *net_stats){
348
349 for(net_stats+=start; start<end; start++){
350 net_stats->interface_name=NULL;
351 net_stats->speed=0;
352 net_stats->dup=UNKNOWN_DUPLEX;
353 net_stats++;
354 }
355 }
356
357 network_iface_stat_t *network_iface_stat_malloc(int needed_entries, int *cur_entries, network_iface_stat_t *net_stats){
358
359 if(net_stats==NULL){
360
361 if((net_stats=malloc(needed_entries * sizeof(network_iface_stat_t)))==NULL){
362 return NULL;
363 }
364 network_iface_stat_init(0, needed_entries, net_stats);
365 *cur_entries=needed_entries;
366
367 return net_stats;
368 }
369
370
371 if(*cur_entries<needed_entries){
372 net_stats=realloc(net_stats, (sizeof(network_iface_stat_t)*needed_entries));
373 if(net_stats==NULL){
374 return NULL;
375 }
376 network_iface_stat_init(*cur_entries, needed_entries, net_stats);
377 *cur_entries=needed_entries;
378 }
379
380 return net_stats;
381 }
382
383 network_iface_stat_t *get_network_iface_stats(int *entries){
384 static network_iface_stat_t *network_iface_stats;
385 network_iface_stat_t *network_iface_stat_ptr;
386 static int sizeof_network_iface_stats=0;
387 int ifaces = 0;
388
389 #ifdef SOLARIS
390 kstat_ctl_t *kc;
391 kstat_t *ksp;
392 kstat_named_t *knp;
393 int sock;
394 #endif
395 #ifdef ALLBSD
396 struct ifaddrs *net, *net_ptr;
397 struct ifmediareq ifmed;
398 struct ifreq ifr;
399 int sock;
400 int x;
401 #endif
402 #ifdef LINUX
403 FILE *f;
404 /* Horrible big enough, but it should be easily big enough */
405 char line[8096];
406 int sock;
407 #endif
408
409 #ifdef ALLBSD
410 if(getifaddrs(&net) != 0){
411 return NULL;
412 }
413
414 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == 0) return NULL;
415
416 for(net_ptr=net; net_ptr!=NULL; net_ptr=net_ptr->ifa_next){
417 if(net_ptr->ifa_addr->sa_family != AF_LINK) continue;
418 network_iface_stats=network_iface_stat_malloc((ifaces+1), &sizeof_network_iface_stats, network_iface_stats);
419 if(network_iface_stats==NULL){
420 return NULL;
421 }
422 network_iface_stat_ptr = network_iface_stats + ifaces;
423
424 memset(&ifr, 0, sizeof(ifr));
425 strncpy(ifr.ifr_name, net_ptr->ifa_name, sizeof(ifr.ifr_name));
426
427 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0){
428 continue;
429 }
430 if((ifr.ifr_flags & IFF_UP) != 0){
431 network_iface_stat_ptr->up = 1;
432 }else{
433 network_iface_stat_ptr->up = 0;
434 }
435
436 if (network_iface_stat_ptr->interface_name != NULL) free(network_iface_stat_ptr->interface_name);
437 network_iface_stat_ptr->interface_name = strdup(net_ptr->ifa_name);
438 if (network_iface_stat_ptr->interface_name == NULL) return NULL;
439
440 network_iface_stat_ptr->speed = 0;
441 network_iface_stat_ptr->dup = UNKNOWN_DUPLEX;
442 ifaces++;
443
444 memset(&ifmed, 0, sizeof(struct ifmediareq));
445 strlcpy(ifmed.ifm_name, net_ptr->ifa_name, sizeof(ifmed.ifm_name));
446 if(ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmed) == -1){
447 /* Not all interfaces support the media ioctls. */
448 continue;
449 }
450
451 /* We may need to change this if we start doing wireless devices too */
452 if( (ifmed.ifm_active | IFM_ETHER) != ifmed.ifm_active ){
453 /* Not a ETHER device */
454 continue;
455 }
456
457 /* Only intrested in the first 4 bits) - Assuming only ETHER devices */
458 x = ifmed.ifm_active & 0x0f;
459 switch(x){
460 /* 10 Mbit connections. Speedy :) */
461 case(IFM_10_T):
462 case(IFM_10_2):
463 case(IFM_10_5):
464 case(IFM_10_STP):
465 case(IFM_10_FL):
466 network_iface_stat_ptr->speed = 10;
467 break;
468 /* 100 Mbit conneections */
469 case(IFM_100_TX):
470 case(IFM_100_FX):
471 case(IFM_100_T4):
472 case(IFM_100_VG):
473 case(IFM_100_T2):
474 network_iface_stat_ptr->speed = 100;
475 break;
476 /* 1000 Mbit connections */
477 case(IFM_1000_SX):
478 case(IFM_1000_LX):
479 case(IFM_1000_CX):
480 #if defined(FREEBSD) && !defined(FREEBSD5)
481 case(IFM_1000_TX):
482 case(IFM_1000_FX):
483 #else
484 case(IFM_1000_T):
485 #endif
486 network_iface_stat_ptr->speed = 1000;
487 break;
488 /* We don't know what it is */
489 default:
490 network_iface_stat_ptr->speed = 0;
491 break;
492 }
493
494 if( (ifmed.ifm_active | IFM_FDX) == ifmed.ifm_active ){
495 network_iface_stat_ptr->dup = FULL_DUPLEX;
496 }else if( (ifmed.ifm_active | IFM_HDX) == ifmed.ifm_active ){
497 network_iface_stat_ptr->dup = HALF_DUPLEX;
498 }else{
499 network_iface_stat_ptr->dup = UNKNOWN_DUPLEX;
500 }
501
502 }
503 freeifaddrs(net);
504 close(sock);
505 #endif
506
507 #ifdef SOLARIS
508 if ((kc = kstat_open()) == NULL) {
509 return NULL;
510 }
511
512 if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
513 return NULL;
514 }
515
516 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
517 if (!strcmp(ksp->ks_class, "net")) {
518 struct ifreq ifr;
519
520 kstat_read(kc, ksp, NULL);
521
522 strncpy(ifr.ifr_name, ksp->ks_name, sizeof ifr.ifr_name);
523 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
524 /* Not a network interface. */
525 continue;
526 }
527
528 network_iface_stats = network_iface_stat_malloc(ifaces + 1, &sizeof_network_iface_stats, network_iface_stats);
529 if (network_iface_stats == NULL) {
530 return NULL;
531 }
532 network_iface_stat_ptr = network_iface_stats + ifaces;
533 ifaces++;
534
535 if (network_iface_stat_ptr->interface_name != NULL) free(network_iface_stat_ptr->interface_name);
536 network_iface_stat_ptr->interface_name = strdup(ksp->ks_name);
537 if (network_iface_stat_ptr->interface_name == NULL) return NULL;
538
539 if ((ifr.ifr_flags & IFF_UP) != 0) {
540 network_iface_stat_ptr->up = 1;
541 } else {
542 network_iface_stat_ptr->up = 1;
543 }
544
545 if ((knp = kstat_data_lookup(ksp, "ifspeed")) != NULL) {
546 network_iface_stat_ptr->speed = knp->value.ui64 / (1000 * 1000);
547 } else {
548 network_iface_stat_ptr->speed = 0;
549 }
550
551 network_iface_stat_ptr->dup = UNKNOWN_DUPLEX;
552 if ((knp = kstat_data_lookup(ksp, "link_duplex")) != NULL) {
553 switch (knp->value.ui32) {
554 case 1:
555 network_iface_stat_ptr->dup = HALF_DUPLEX;
556 break;
557 case 2:
558 network_iface_stat_ptr->dup = FULL_DUPLEX;
559 break;
560 }
561 }
562 }
563 }
564
565 close(sock);
566 kstat_close(kc);
567 #endif
568 #ifdef LINUX
569 f = fopen("/proc/net/dev", "r");
570 if(f == NULL){
571 return NULL;
572 }
573
574 /* Setup stuff so we can do the ioctl to get the info */
575 if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
576 return NULL;
577 }
578
579 /* Ignore first 2 lines.. Just headings */
580 if((fgets(line, sizeof(line), f)) == NULL) return NULL;
581 if((fgets(line, sizeof(line), f)) == NULL) return NULL;
582
583 while((fgets(line, sizeof(line), f)) != NULL){
584 char *name, *ptr;
585 struct ifreq ifr;
586 struct ethtool_cmd ethcmd;
587 int err;
588
589 /* Get the interface name */
590 ptr = strchr(line, ':');
591 if (ptr == NULL) continue;
592 *ptr='\0';
593 name = line;
594 while(isspace(*(name))){
595 name++;
596 }
597
598 memset(&ifr, 0, sizeof ifr);
599 strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
600
601 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
602 continue;
603 }
604
605 /* We have a good interface to add */
606 network_iface_stats=network_iface_stat_malloc((ifaces+1), &sizeof_network_iface_stats, network_iface_stats);
607 if(network_iface_stats==NULL){
608 return NULL;
609 }
610 network_iface_stat_ptr = network_iface_stats + ifaces;
611 network_iface_stat_ptr->interface_name = strdup(name);
612 if ((ifr.ifr_flags & IFF_UP) != 0) {
613 network_iface_stat_ptr->up = 1;
614 } else {
615 network_iface_stat_ptr->up = 0;
616 }
617
618 memset(&ethcmd, 0, sizeof ethcmd);
619 ethcmd.cmd = ETHTOOL_GSET;
620 ifr.ifr_data = (caddr_t) &ethcmd;
621
622 err = ioctl(sock, SIOCETHTOOL, &ifr);
623 if (err == 0) {
624 network_iface_stat_ptr->speed = ethcmd.speed;
625
626 switch (ethcmd.duplex) {
627 case 0x00:
628 network_iface_stat_ptr->dup = FULL_DUPLEX;
629 break;
630 case 0x01:
631 network_iface_stat_ptr->dup = HALF_DUPLEX;
632 break;
633 default:
634 network_iface_stat_ptr->dup = UNKNOWN_DUPLEX;
635 }
636 } else {
637 /* Not all interfaces support the ethtool ioctl. */
638 network_iface_stat_ptr->speed = 0;
639 network_iface_stat_ptr->dup = UNKNOWN_DUPLEX;
640 }
641
642 ifaces++;
643 }
644 close(sock);
645 fclose(f);
646 #endif
647 *entries = ifaces;
648 return network_iface_stats;
649 }
650