ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/libstatgrab/network_stats.c
Revision: 1.27
Committed: Thu Feb 12 21:25:02 2004 UTC (20 years, 3 months ago) by pajs
Content type: text/plain
Branch: MAIN
Changes since 1.26: +92 -2 lines
Log Message:
Added network interface stats for linux.

Points to remember:
a) This code will need to be run as root. Otherwise, it should
   safely return null.
b) the "speed" may be incorrect if the interface is not actually up
   and talking to anything. However, duplex will be set to NO_DUPLEX
   so as long as they use that test first, there should be no problem.
   (This is safe assumption to work on, even for other platforms)
c) This was a nastyish and hard bit of code. Maybe bugs, would appricate
   a look over by people :)

File Contents

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