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

# 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.26 2004/01/25 20:13:57 pajs 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 #endif
36 #ifdef LINUX
37 #include <stdio.h>
38 #include <sys/types.h>
39 #include <regex.h>
40 #include <sys/ioctl.h>
41 #include <sys/socket.h>
42 #include <net/if.h>
43 #include <ctype.h>
44 #include "tools.h"
45 /* 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 #endif
54 #ifdef ALLBSD
55 #include <sys/types.h>
56 #include <sys/socket.h>
57 #include <ifaddrs.h>
58 #include <net/if.h>
59 #include <net/if_media.h>
60 #include <sys/ioctl.h>
61 #endif
62
63 static network_stat_t *network_stats=NULL;
64 static int interfaces=0;
65
66 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 return NULL;
82 }
83 network_stat_init(0, needed_entries, net_stats);
84 *cur_entries=needed_entries;
85
86 return net_stats;
87 }
88
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 return NULL;
94 }
95 network_stat_init(*cur_entries, needed_entries, net_stats);
96 *cur_entries=needed_entries;
97 }
98
99 return net_stats;
100 }
101
102
103 network_stat_t *get_network_stats(int *entries){
104
105 static int sizeof_network_stats=0;
106 network_stat_t *network_stat_ptr;
107
108 #ifdef SOLARIS
109 kstat_ctl_t *kc;
110 kstat_t *ksp;
111 kstat_named_t *knp;
112 #endif
113
114 #ifdef LINUX
115 FILE *f;
116 /* Horrible big enough, but it should be easily big enough */
117 char line[8096];
118 regex_t regex;
119 regmatch_t line_match[4];
120 #endif
121 #ifdef ALLBSD
122 struct ifaddrs *net, *net_ptr;
123 struct if_data *net_data;
124 #endif
125
126 #ifdef ALLBSD
127 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 #endif
152
153 #ifdef SOLARIS
154 if ((kc = kstat_open()) == NULL) {
155 return NULL;
156 }
157
158 interfaces=0;
159
160 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 #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 /* Not a network interface, so skip to the next entry */
176 continue;
177 }
178
179 network_stats=network_stat_malloc((interfaces+1), &sizeof_network_stats, network_stats);
180 if(network_stats==NULL){
181 return NULL;
182 }
183 network_stat_ptr=network_stats+interfaces;
184 network_stat_ptr->rx=knp->VALTYPE;
185
186 if((knp=kstat_data_lookup(ksp, WLOOKUP))==NULL){
187 /* Not a network interface, so skip to the next entry */
188 continue;
189 }
190 network_stat_ptr->tx=knp->VALTYPE;
191 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
196 network_stat_ptr->systime=time(NULL);
197 interfaces++;
198 }
199 }
200
201 kstat_close(kc);
202 #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
219 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 fclose(f);
241 regfree(&regex);
242
243 #endif
244
245 #ifdef CYGWIN
246 return NULL;
247 #endif
248
249 *entries=interfaces;
250
251 return network_stats;
252 }
253
254 long long transfer_diff(long long new, long long old){
255 #if defined(SOL7) || defined(LINUX) || defined(FREEBSD)
256 #define MAXVAL 4294967296LL
257 #else
258 #define MAXVAL 18446744073709551616LL
259 #endif
260 long long result;
261 if(new>=old){
262 result = (new-old);
263 }else{
264 result = (MAXVAL+(new-old));
265 }
266
267 return result;
268
269 }
270
271 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 }
281
282 /* Resize the results array to match the previous stats. */
283 diff = network_stat_malloc(interfaces, &diff_count, diff);
284 if (diff == NULL) {
285 return NULL;
286 }
287
288 /* 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
293 if (dest->interface_name != NULL) {
294 free(dest->interface_name);
295 }
296 dest->interface_name = strdup(src->interface_name);
297 dest->rx = src->rx;
298 dest->tx = src->tx;
299 dest->systime = src->systime;
300 }
301
302 /* Get a new set of stats. */
303 if (get_network_stats(&new_count) == NULL) {
304 return NULL;
305 }
306
307 /* 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 }
324
325 /* ... 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 }
331
332 *entries = diff_count;
333 return diff;
334 }
335 /* 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 static int ifaces;
378
379 #ifdef SOLARIS
380 kstat_ctl_t *kc;
381 kstat_t *ksp;
382 kstat_named_t *knp;
383 #endif
384 #ifdef ALLBSD
385 struct ifaddrs *net, *net_ptr;
386 struct ifmediareq ifmed;
387 int s;
388 int x;
389 #endif
390 #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 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
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 network_iface_stat_ptr = network_iface_stats + ifaces;
491 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 if(network_iface_stat_ptr->interface_name != NULL) free(network_iface_stat_ptr->interface_name);
505 network_iface_stat_ptr->interface_name = strdup(ksp->ks_name);
506 if(network_iface_stat_ptr->interface_name == NULL) return NULL;
507 ifaces++;
508 }
509 }
510 kstat_close(kc);
511
512 #endif
513 #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 *entries = ifaces;
585 return network_iface_stats;
586 }
587