510 lines
14 KiB
C
510 lines
14 KiB
C
/* wmnet -- X IP accounting monitor
|
|
* Copyright 1998 Jesse B. Off <joff@iastate.edu>
|
|
*
|
|
* $Id: drivers.c,v 1.1 1998/10/07 03:42:21 joff Exp joff $
|
|
*
|
|
* This software is released under the GNU Public License agreement.
|
|
* No warranties, whatever.... you know the usuals.... this is free
|
|
* software. if you use it, great... if you wanna make a change to it,
|
|
* great, but please send me the diff.
|
|
*/
|
|
|
|
#include<stdlib.h>
|
|
#include<stdio.h>
|
|
#include<string.h>
|
|
#include<X11/Xlib.h>
|
|
#include<fcntl.h>
|
|
#include<sys/types.h>
|
|
#include<sys/stat.h>
|
|
#include<sys/socket.h>
|
|
#include<unistd.h>
|
|
#include"config.h"
|
|
|
|
|
|
/* For FreeBSD */
|
|
#ifdef USE_KVM
|
|
#include<net/if.h>
|
|
#include<kvm.h>
|
|
#include<nlist.h>
|
|
|
|
kvm_t *kvmfd;
|
|
struct nlist symbols[] = {
|
|
{ "_ifnet" },
|
|
{ NULL }
|
|
};
|
|
unsigned long ifnet_savedaddr;
|
|
int kvm_test(void);
|
|
int kvm_updateStats(void);
|
|
#endif /* USE_KVM */
|
|
|
|
|
|
#ifdef USE_LINUX_PPP
|
|
#include<net/ppp_defs.h>
|
|
|
|
#ifdef linux_libc5
|
|
# include<linux/if_ppp.h>
|
|
#else
|
|
# include<net/if_ppp.h>
|
|
#endif
|
|
|
|
#include<sys/ioctl.h>
|
|
int pppfd;
|
|
int ppp_test(void);
|
|
int updateStats_ppp(void);
|
|
static struct ifpppstatsreq ppp_stats_req;
|
|
#endif
|
|
|
|
#define ACCOUNT_IN_FOUND 1
|
|
#define ACCOUNT_OUT_FOUND 2
|
|
|
|
extern char buffer[256];
|
|
extern char *in_rule_string, *out_rule_string, *device;
|
|
extern unsigned long totalbytes_in, totalbytes_out, lastbytes_in, lastbytes_out;
|
|
extern unsigned long totalpackets_in, totalpackets_out, lastpackets_in, lastpackets_out;
|
|
extern unsigned int diffpackets_in, diffpackets_out, diffbytes_in, diffbytes_out;
|
|
extern unsigned int out_rule, in_rule; /* number of rule in /proc/net/ip_acct to use */
|
|
extern Bool current_tx, current_rx, rx, tx;
|
|
|
|
|
|
|
|
char *available_drivers(void);
|
|
|
|
#ifdef USE_IPFWADM
|
|
int updateStats_ipfwadm(void);
|
|
int ipfwadm_test(void);
|
|
#endif
|
|
#ifdef USE_IPCHAINS
|
|
int updateStats_ipchains(void);
|
|
int ipchains_test(void);
|
|
#endif
|
|
#ifdef USE_2_1_DEV
|
|
int updateStats_dev(void);
|
|
int dev_test(void);
|
|
#endif
|
|
|
|
|
|
typedef int (*parser_func)(void);
|
|
static struct drivers_struct {
|
|
char * name;
|
|
parser_func function;
|
|
parser_func test;
|
|
} drivers[] = {
|
|
#ifdef USE_2_1_DEV
|
|
{"devstats", updateStats_dev, dev_test},
|
|
#endif
|
|
#ifdef USE_IPFWADM
|
|
{"ipfwadm", updateStats_ipfwadm, ipfwadm_test},
|
|
#endif
|
|
#ifdef USE_IPCHAINS
|
|
{"ipchains", updateStats_ipchains, ipchains_test},
|
|
#endif
|
|
#ifdef USE_LINUX_PPP
|
|
{"pppstats",updateStats_ppp, ppp_test},
|
|
#endif
|
|
#ifdef USE_KVM
|
|
{"kmem",kvm_updateStats, kvm_test},
|
|
#endif
|
|
{NULL, NULL}
|
|
};
|
|
|
|
char* available_drivers(void) {
|
|
int ind = 0;
|
|
int len = 0;
|
|
char *string, *ptr;
|
|
while(drivers[ind].name != NULL) {
|
|
len += strlen(drivers[ind].name) + 1;
|
|
ind++;
|
|
}
|
|
ptr = string = (char *)malloc(len);
|
|
*string = '\0';
|
|
ind = 0;
|
|
while(drivers[ind].name != NULL) {
|
|
strcpy(string, drivers[ind].name);
|
|
string += strlen(drivers[ind].name);
|
|
*string++ = ',';
|
|
ind++;
|
|
}
|
|
*(--string) = '\0';
|
|
return ptr;
|
|
}
|
|
|
|
|
|
|
|
parser_func find_driver(void) {
|
|
int ind = 0;
|
|
while(drivers[ind].name != NULL) {
|
|
if(drivers[ind].test()) {
|
|
return drivers[ind].function;
|
|
}
|
|
ind++;
|
|
}
|
|
fprintf(stderr, "wmnet: no appropriate stat driver found\n");
|
|
exit(30);
|
|
}
|
|
|
|
|
|
parser_func setup_driver(char * parser_name) {
|
|
int ind = 0;
|
|
if (parser_name == NULL) return find_driver();
|
|
while(drivers[ind].name != NULL) {
|
|
if(!strcmp(parser_name, drivers[ind].name)) {
|
|
if (drivers[ind].test()) return drivers[ind].function;
|
|
fprintf(stderr, "wmnet: driver %s not appropriate for this machine\n", parser_name);
|
|
exit(18);
|
|
}
|
|
ind++;
|
|
}
|
|
fprintf(stderr, "wmnet: no driver %s\n", parser_name);
|
|
exit(18);
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef linux
|
|
/* All the data gathering is done in here.
|
|
* Return True if no change to tx/rx.
|
|
* Return False if display will need to be updated.
|
|
*/
|
|
#ifdef USE_IPFWADM
|
|
int ipfwadm_test(void) {
|
|
if(open("/proc/net/ip_acct", O_RDONLY) == -1) return False;
|
|
fprintf(stderr, "wmnet: using ipfwadm driver to monitor accounting rules %d and %d\n", in_rule, out_rule);
|
|
return True;
|
|
}
|
|
|
|
int updateStats_ipfwadm(void) {
|
|
FILE *ip_acct;
|
|
unsigned int flag = 0, lineno = 0;
|
|
unsigned int offset = 37;
|
|
char *ptr;
|
|
rx = False;
|
|
tx = False;
|
|
|
|
|
|
if ((ip_acct = fopen("/proc/net/ip_acct", "r")) == NULL) {
|
|
fprintf(stderr, "wmnet: /proc/net/ip_acct unavailable\n"
|
|
"You either don't have IP accounting compiled in, or this isn't a 2.0 linux kernel.\n");
|
|
exit(4);
|
|
}
|
|
|
|
/* IP Accounting Rules for 2.0.x linux kernels*/
|
|
while(flag != (ACCOUNT_IN_FOUND|ACCOUNT_OUT_FOUND) && fgets(buffer, 256, ip_acct)) {
|
|
switch(lineno == out_rule ? ACCOUNT_OUT_FOUND : ( lineno == in_rule ? ACCOUNT_IN_FOUND : -1 ) ) {
|
|
case ACCOUNT_IN_FOUND:
|
|
/* accounting in */
|
|
flag |= ACCOUNT_IN_FOUND;
|
|
while(buffer[offset++] != ' ');
|
|
offset += 18;
|
|
totalpackets_in = strtoul(&buffer[offset], &ptr, 10);
|
|
if (totalpackets_in == lastpackets_in) break;
|
|
totalbytes_in = strtoul(ptr, NULL, 10);
|
|
diffpackets_in += totalpackets_in - lastpackets_in;
|
|
diffbytes_in += totalbytes_in - lastbytes_in;
|
|
lastpackets_in = totalpackets_in;
|
|
lastbytes_in = totalbytes_in;
|
|
rx = True;
|
|
break;
|
|
case ACCOUNT_OUT_FOUND:
|
|
/* accounting out */
|
|
flag |= ACCOUNT_OUT_FOUND;
|
|
while(buffer[offset++] != ' ');
|
|
offset += 18;
|
|
totalpackets_out = strtoul(&buffer[offset], &ptr, 10);
|
|
if (totalpackets_out == lastpackets_out) break;
|
|
totalbytes_out = strtoul(ptr, NULL, 10);
|
|
diffpackets_out += totalpackets_out - lastpackets_out;
|
|
diffbytes_out += totalbytes_out - lastbytes_out;
|
|
lastpackets_out = totalpackets_out;
|
|
lastbytes_out = totalbytes_out;
|
|
tx = True;
|
|
break;
|
|
}
|
|
lineno++;
|
|
offset = 37;
|
|
}
|
|
|
|
if(flag != (ACCOUNT_IN_FOUND|ACCOUNT_OUT_FOUND)) {
|
|
fprintf(stderr,"wmnet: couldn't find %s accounting rule to monitor in /proc/net/ip_acct\n",
|
|
(flag == ACCOUNT_IN_FOUND) ? "the TX" : ((flag == ACCOUNT_OUT_FOUND) ? "the RX" : "a single"));
|
|
exit(4);
|
|
}
|
|
fclose(ip_acct);
|
|
|
|
/* return True if no change to tx/rx
|
|
* return False if display will need to be updated
|
|
*/
|
|
return((rx == current_rx) && (tx == current_tx));
|
|
|
|
}
|
|
#endif /* USE_IPFWADM */
|
|
|
|
#ifdef USE_IPCHAINS
|
|
int ipchains_test(void) {
|
|
if (open("/proc/net/ip_fwchains",O_RDONLY) == -1) return False;
|
|
if (in_rule_string == NULL) in_rule_string = "acctin";
|
|
if (out_rule_string == NULL) out_rule_string = "acctout";
|
|
fprintf(stderr, "wmnet: using ipchains driver to monitor chains %s and %s\n", in_rule_string, out_rule_string);
|
|
return True;
|
|
}
|
|
|
|
/* ipchains parser mostly from Bjoern Kriews <bkr@cut.de> */
|
|
int updateStats_ipchains(void) {
|
|
FILE *ip_acct;
|
|
unsigned int flag = 0;
|
|
static char name[32];
|
|
unsigned long pack, bytes;
|
|
rx = False;
|
|
tx = False;
|
|
|
|
|
|
if ((ip_acct = fopen("/proc/net/ip_fwchains", "r")) == NULL) {
|
|
fprintf(stderr, "/proc/net/ip_fwchains does not exist?\n"
|
|
"Do you have IP accounting in your kernel?\n");
|
|
exit(4);
|
|
}
|
|
|
|
/* IP Chain Rules for Linux kernel 2_1.x */
|
|
while(flag != (ACCOUNT_IN_FOUND|ACCOUNT_OUT_FOUND) && fgets(buffer, 256, ip_acct)) {
|
|
*name = 0;
|
|
|
|
sscanf(buffer, "%30s %*s - %*d %*d %*d %*d %lu %*d %lu",
|
|
name, &pack, &bytes);
|
|
|
|
|
|
if(strcmp(name, in_rule_string) == 0) {
|
|
flag |= ACCOUNT_IN_FOUND;
|
|
|
|
totalpackets_in = pack;
|
|
if (totalpackets_in != lastpackets_in) {
|
|
totalbytes_in = bytes;
|
|
diffpackets_in += totalpackets_in - lastpackets_in;
|
|
diffbytes_in += totalbytes_in - lastbytes_in;
|
|
lastpackets_in = totalpackets_in;
|
|
lastbytes_in = totalbytes_in;
|
|
rx = True;
|
|
}
|
|
|
|
} else if (strcmp(name, out_rule_string) == 0) {
|
|
flag |= ACCOUNT_OUT_FOUND;
|
|
|
|
totalpackets_out = pack;
|
|
if (totalpackets_out != lastpackets_out) {
|
|
totalbytes_out = bytes;
|
|
diffpackets_out += totalpackets_out - lastpackets_out;
|
|
diffbytes_out += totalbytes_out - lastbytes_out;
|
|
lastpackets_out = totalpackets_out;
|
|
lastbytes_out = totalbytes_out;
|
|
tx = True;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(flag != (ACCOUNT_IN_FOUND|ACCOUNT_OUT_FOUND)) {
|
|
fprintf(stderr,"I couldn't find %s IP chain to monitor in /proc/net/ip_fwchains.\n",
|
|
(flag == ACCOUNT_IN_FOUND) ? "the TX" : ((flag == ACCOUNT_OUT_FOUND) ? "the RX" : "a single"));
|
|
exit(4);
|
|
}
|
|
fclose(ip_acct);
|
|
|
|
/* return True if no change to tx/rx
|
|
* return False if display will need to be updated
|
|
*/
|
|
return((rx == current_rx) && (tx == current_tx));
|
|
|
|
}
|
|
#endif /* USE_IPCHAINS */
|
|
|
|
|
|
#ifdef USE_2_1_DEV
|
|
|
|
int updateStats_dev(void) {
|
|
FILE *dev;
|
|
char *ptr;
|
|
unsigned int flag = 0;
|
|
char *name;
|
|
rx = False;
|
|
tx = False;
|
|
|
|
|
|
if ((dev = fopen("/proc/net/dev", "r")) == NULL) {
|
|
fprintf(stderr, "/proc/net/dev does not exist?\n"
|
|
"Perhaps we are not running Linux?\n");
|
|
exit(4);
|
|
}
|
|
/* the first two lines we can skip */
|
|
fgets(buffer, 256, dev);
|
|
fgets(buffer, 256, dev);
|
|
|
|
/* IP Chain Rules for Linux kernel 2_1.x */
|
|
while(flag != (ACCOUNT_IN_FOUND|ACCOUNT_OUT_FOUND) && fgets(buffer, 256, dev)) {
|
|
ptr = buffer;
|
|
while(*ptr == ' ') ptr++;
|
|
name = ptr;
|
|
while(*ptr != ':') ptr++;
|
|
*ptr = '\0';
|
|
|
|
if (!strcmp(name, device)) {
|
|
|
|
flag = (ACCOUNT_IN_FOUND|ACCOUNT_OUT_FOUND);
|
|
|
|
totalpackets_in = strtoul(&buffer[15], NULL, 10);
|
|
if (totalpackets_in != lastpackets_in) {
|
|
totalbytes_in = strtoul(&buffer[7], NULL, 10);
|
|
diffpackets_in += totalpackets_in - lastpackets_in;
|
|
diffbytes_in += totalbytes_in - lastbytes_in;
|
|
lastpackets_in = totalpackets_in;
|
|
lastbytes_in = totalbytes_in;
|
|
rx = True;
|
|
}
|
|
|
|
|
|
totalpackets_out = strtoul(&buffer[74], NULL, 10);
|
|
if (totalpackets_out != lastpackets_out) {
|
|
totalbytes_out = strtoul(&buffer[66], NULL, 10);
|
|
diffpackets_out += totalpackets_out - lastpackets_out;
|
|
diffbytes_out += totalbytes_out - lastbytes_out;
|
|
lastpackets_out = totalpackets_out;
|
|
lastbytes_out = totalbytes_out;
|
|
tx = True;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(dev);
|
|
|
|
/* return True if no change to tx/rx
|
|
* return False if display will need to be updated
|
|
*/
|
|
return((rx == current_rx) && (tx == current_tx));
|
|
}
|
|
|
|
int dev_test(void) {
|
|
int devfd;
|
|
if((devfd = open("/proc/net/dev", O_RDONLY)) == -1) return False;
|
|
read(devfd, buffer, 36);
|
|
if(buffer[35] == '|') return False;
|
|
if(device == NULL) device = "eth0";
|
|
fprintf(stderr, "wmnet: using devstats driver to monitor %s\n", device);
|
|
return True;
|
|
}
|
|
|
|
#endif /* USE_2_1_DEV */
|
|
|
|
#ifdef USE_LINUX_PPP
|
|
int ppp_test(void) {
|
|
pppfd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if(device == NULL) device = "ppp0";
|
|
strncpy(ppp_stats_req.ifr__name, device, 15);
|
|
ppp_stats_req.stats_ptr =(caddr_t) &ppp_stats_req.stats;
|
|
fprintf(stderr, "wmnet: using pppstats driver to monitor %s\n", device);
|
|
return True;
|
|
|
|
}
|
|
|
|
int updateStats_ppp(void) {
|
|
if(ioctl(pppfd, SIOCGPPPSTATS, &ppp_stats_req) != 0) {
|
|
return False;
|
|
}
|
|
totalpackets_in = ppp_stats_req.stats.p.ppp_ipackets;
|
|
if (totalpackets_in != lastpackets_in) {
|
|
totalbytes_in = ppp_stats_req.stats.p.ppp_ibytes;
|
|
diffpackets_in += totalpackets_in - lastpackets_in;
|
|
diffbytes_in += totalbytes_in - lastbytes_in;
|
|
lastpackets_in = totalpackets_in;
|
|
lastbytes_in = totalbytes_in;
|
|
rx = True;
|
|
}
|
|
|
|
|
|
totalpackets_out = ppp_stats_req.stats.p.ppp_opackets;
|
|
if (totalpackets_out != lastpackets_out) {
|
|
totalbytes_out =ppp_stats_req.stats.p.ppp_obytes;
|
|
diffpackets_out += totalpackets_out - lastpackets_out;
|
|
diffbytes_out += totalbytes_out - lastbytes_out;
|
|
lastpackets_out = totalpackets_out;
|
|
lastbytes_out = totalbytes_out;
|
|
tx = True;
|
|
}
|
|
|
|
/* return True if no change to tx/rx
|
|
* return False if display will need to be updated
|
|
*/
|
|
return((rx == current_rx) && (tx == current_tx));
|
|
|
|
}
|
|
|
|
|
|
#endif /* USE_LINUX_PPP */
|
|
|
|
|
|
#endif /* linux */
|
|
|
|
#ifdef USE_KVM
|
|
int kvm_test(void) {
|
|
if (((kvmfd = kvm_open(NULL, NULL, NULL, O_RDONLY, buffer)) == NULL) ||
|
|
(kvm_nlist(kvmfd, symbols) < 0) ||
|
|
kvm_read(kvmfd, (unsigned long)symbols[0].n_value, &ifnet_savedaddr, sizeof(unsigned long)) == -1 ) return False;
|
|
if(device == NULL) device = "ec0";
|
|
fprintf(stderr, "wmnet: using kmem driver to monitor %s\n", device);
|
|
return True;
|
|
}
|
|
|
|
int kvm_updateStats(void) {
|
|
struct ifnet * ifnet = (struct ifnet *)buffer;
|
|
unsigned long ifnet_addr = ifnet_savedaddr;
|
|
char devname[16];
|
|
int flag = 0;
|
|
while (ifnet_addr && flag != (ACCOUNT_IN_FOUND|ACCOUNT_OUT_FOUND)) {
|
|
kvm_read(kvmfd, ifnet_addr, buffer, sizeof(struct ifnet));
|
|
#ifdef __OpenBSD__
|
|
snprintf(devname, 15, "%s", ifnet->if_xname);
|
|
#else
|
|
kvm_read(kvmfd, (unsigned long)ifnet->if_name, devname, 15);
|
|
snprintf(devname, 15, "%s%d", devname, ifnet->if_unit);
|
|
#endif
|
|
if(!strncmp(devname, device, strlen(device))) { /* we found our struct */
|
|
totalpackets_in =ifnet->if_ipackets;
|
|
if (totalpackets_in != lastpackets_in) {
|
|
totalbytes_in = ifnet->if_ibytes;
|
|
diffpackets_in += totalpackets_in - lastpackets_in;
|
|
diffbytes_in += totalbytes_in - lastbytes_in;
|
|
lastpackets_in = totalpackets_in;
|
|
lastbytes_in = totalbytes_in;
|
|
rx = True;
|
|
}
|
|
|
|
|
|
totalpackets_out = ifnet->if_opackets;
|
|
if (totalpackets_out != lastpackets_out) {
|
|
totalbytes_out =ifnet->if_obytes;
|
|
diffpackets_out += totalpackets_out - lastpackets_out;
|
|
diffbytes_out += totalbytes_out - lastbytes_out;
|
|
lastpackets_out = totalpackets_out;
|
|
lastbytes_out = totalbytes_out;
|
|
tx = True;
|
|
}
|
|
flag = (ACCOUNT_IN_FOUND|ACCOUNT_OUT_FOUND);
|
|
|
|
} else {
|
|
#ifdef __OpenBSD__
|
|
ifnet_addr = (unsigned long)ifnet->if_list.tqe_next;
|
|
#else
|
|
ifnet_addr = (unsigned long)ifnet->if_next;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* return True if no change to tx/rx
|
|
* return False if display will need to be updated
|
|
*/
|
|
return((rx == current_rx) && (tx == current_tx));
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|