1/* $Id: getifstats.c,v 1.12 2013/04/29 10:18:20 nanard Exp $ */
2/* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2013 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <syslog.h>
11#include <string.h>
12#include <time.h>
13
14#include "../config.h"
15#include "../getifstats.h"
16
17#ifdef GET_WIRELESS_STATS
18#include <unistd.h>
19#include <sys/ioctl.h>
20#include <arpa/inet.h>
21#include <linux/wireless.h>
22#endif /* GET_WIRELESS_STATS */
23
24/* that is the answer */
25#define BAUDRATE_DEFAULT 4200000
26
27int
28getifstats(const char * ifname, struct ifdata * data)
29{
30	FILE *f;
31	char line[512];
32	char * p;
33	int i;
34	int r = -1;
35	char fname[64];
36#ifdef ENABLE_GETIFSTATS_CACHING
37	static time_t cache_timestamp = 0;
38	static struct ifdata cache_data;
39	time_t current_time;
40#endif /* ENABLE_GETIFSTATS_CACHING */
41	if(!data)
42		return -1;
43	data->baudrate = BAUDRATE_DEFAULT;
44	data->opackets = 0;
45	data->ipackets = 0;
46	data->obytes = 0;
47	data->ibytes = 0;
48	if(!ifname || ifname[0]=='\0')
49		return -1;
50#ifdef ENABLE_GETIFSTATS_CACHING
51	current_time = time(NULL);
52	if(current_time == ((time_t)-1)) {
53		syslog(LOG_ERR, "getifstats() : time() error : %m");
54	} else {
55		if(current_time < cache_timestamp + GETIFSTATS_CACHING_DURATION) {
56			/* return cached data */
57			memcpy(data, &cache_data, sizeof(struct ifdata));
58			return 0;
59		}
60	}
61#endif /* ENABLE_GETIFSTATS_CACHING */
62	f = fopen("/proc/net/dev", "r");
63	if(!f) {
64		syslog(LOG_ERR, "getifstats() : cannot open /proc/net/dev : %m");
65		return -1;
66	}
67	/* discard the two header lines */
68	if(!fgets(line, sizeof(line), f) || !fgets(line, sizeof(line), f)) {
69		syslog(LOG_ERR, "getifstats() : error reading /proc/net/dev : %m");
70	}
71	while(fgets(line, sizeof(line), f)) {
72		p = line;
73		while(*p==' ') p++;
74		i = 0;
75		while(ifname[i] == *p) {
76			p++; i++;
77		}
78		/* TODO : how to handle aliases ? */
79		if(ifname[i] || *p != ':')
80			continue;
81		p++;
82		while(*p==' ') p++;
83		data->ibytes = strtoul(p, &p, 0);
84		while(*p==' ') p++;
85		data->ipackets = strtoul(p, &p, 0);
86		/* skip 6 columns */
87		for(i=6; i>0 && *p!='\0'; i--) {
88			while(*p==' ') p++;
89			while(*p!=' ' && *p) p++;
90		}
91		while(*p==' ') p++;
92		data->obytes = strtoul(p, &p, 0);
93		while(*p==' ') p++;
94		data->opackets = strtoul(p, &p, 0);
95		r = 0;
96		break;
97	}
98	fclose(f);
99	/* get interface speed */
100	/* NB! some interfaces, like ppp, don't support speed queries */
101	snprintf(fname, sizeof(fname), "/sys/class/net/%s/speed", ifname);
102	f = fopen(fname, "r");
103	if(f) {
104		if(fgets(line, sizeof(line), f)) {
105			i = atoi(line);	/* 65535 means unknown */
106			if(i > 0 && i < 65535)
107				data->baudrate = 1000000*i;
108		}
109		fclose(f);
110	}
111#ifdef GET_WIRELESS_STATS
112	if(data->baudrate == BAUDRATE_DEFAULT) {
113		struct iwreq iwr;
114		int s;
115		s = socket(AF_INET, SOCK_DGRAM, 0);
116		if(s >= 0) {
117			strncpy(iwr.ifr_name, ifname, IFNAMSIZ);
118			if(ioctl(s, SIOCGIWRATE, &iwr) >= 0) {
119				data->baudrate = iwr.u.bitrate.value;
120			}
121			close(s);
122		}
123	}
124#endif /* GET_WIRELESS_STATS */
125#ifdef ENABLE_GETIFSTATS_CACHING
126	if(r==0 && current_time!=((time_t)-1)) {
127		/* cache the new data */
128		cache_timestamp = current_time;
129		memcpy(&cache_data, data, sizeof(struct ifdata));
130	}
131#endif /* ENABLE_GETIFSTATS_CACHING */
132	return r;
133}
134
135