Use BSD libc API to fetch data about interface statistics instead

of nosing in kernel memory. This API should work on all versions
of FreeBSD/NetBSD/OpenBSD/Dragonfly.

This fixes compilation on FreeBSD 11, where kernel structures
have changed and protects against future breakages. It also
make the file much simplier.

Tested by:	Daniel Engberg <daniel.engberg.lists pyret.net>
This commit is contained in:
Gleb Smirnoff 2014-09-14 13:41:49 +04:00
parent 0436d03757
commit 7f6cf3680e
1 changed files with 39 additions and 100 deletions
miniupnpd/bsd

View File

@ -1,61 +1,30 @@
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* author: Ryan Wagoner and Thomas Bernard * author: Gleb Smirnoff <glebius@FreeBSD.org>
* (c) 2006 Ryan Wagoner * (c) 2006 Ryan Wagoner
* (c) 2014 Gleb Smirnoff
* This software is subject to the conditions detailed * This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */ * in the LICENCE file provided within the distribution */
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h> #include <net/if.h>
#include <netinet/in.h> #include <errno.h>
#include <arpa/inet.h> #include <ifaddrs.h>
#if defined(__FreeBSD__) || defined(__DragonFly__)
#ifdef __DragonFly__
#define _KERNEL_STRUCTURES
#endif
#include <net/if_var.h>
#endif
#if defined(__DragonFly__)
#include <net/pf/pfvar.h>
#else
#include <net/pfvar.h>
#endif
#include <kvm.h>
#include <fcntl.h>
#include <nlist.h>
#include <sys/queue.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <limits.h> #include <syslog.h>
#ifdef ENABLE_GETIFSTATS_CACHING
#include <time.h>
#endif
#include "../getifstats.h" #include "../getifstats.h"
#include "../config.h" #include "../config.h"
static struct nlist list[] = {
{"_ifnet", 0, 0, 0, 0},
{NULL,0, 0, 0, 0}
};
int int
getifstats(const char * ifname, struct ifdata * data) getifstats(const char *ifname, struct ifdata *data)
{ {
#if defined(__FreeBSD__) || defined(__DragonFly__) static struct ifaddrs *ifap, *ifa;
struct ifnethead ifh;
#elif defined(__OpenBSD__) || defined(__NetBSD__)
struct ifnet_head ifh;
#else
#error "Dont know if I should use struct ifnethead or struct ifnet_head"
#endif
struct ifnet ifc;
struct ifnet *ifp;
kvm_t *kd;
ssize_t n;
char errstr[_POSIX2_LINE_MAX];
#ifdef ENABLE_GETIFSTATS_CACHING #ifdef ENABLE_GETIFSTATS_CACHING
static time_t cache_timestamp = 0; static time_t cache_timestamp;
static struct ifdata cache_data;
time_t current_time; time_t current_time;
#endif #endif
if(!data) if(!data)
@ -67,70 +36,40 @@ getifstats(const char * ifname, struct ifdata * data)
data->ibytes = 0; data->ibytes = 0;
if(!ifname || ifname[0]=='\0') if(!ifname || ifname[0]=='\0')
return -1; return -1;
#ifdef ENABLE_GETIFSTATS_CACHING #ifdef ENABLE_GETIFSTATS_CACHING
current_time = time(NULL); current_time = time(NULL);
if(current_time == ((time_t)-1)) { if (ifap != NULL &&
syslog(LOG_ERR, "getifstats() : time() error : %m"); current_time < cache_timestamp + GETIFSTATS_CACHING_DURATION)
} else { goto copy;
if(current_time < cache_timestamp + GETIFSTATS_CACHING_DURATION) {
memcpy(data, &cache_data, sizeof(struct ifdata));
return 0;
}
}
#endif #endif
/*kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);*/ if (ifap != NULL) {
kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errstr); freeifaddrs(ifap);
if(!kd) ifap = NULL;
{
syslog (LOG_ERR, "getifstats() : kvm_open(): %s", errstr);
return -1;
} }
if(kvm_nlist(kd, list) < 0)
{
syslog(LOG_ERR, "getifstats() : kvm_nlist(): FAILED");
goto error;
}
if(!list[0].n_value)
{
syslog(LOG_ERR, "getifstats() : n_value(): FAILED");
goto error;
}
n = kvm_read(kd, list[0].n_value, &ifh, sizeof(ifh));
if(n<0)
{
syslog(LOG_ERR, "getifstats() : kvm_read(head): %s", kvm_geterr(kd));
goto error;
}
for(ifp = TAILQ_FIRST(&ifh); ifp; ifp = TAILQ_NEXT(&ifc, if_list))
{
n = kvm_read(kd, (u_long)ifp, &ifc, sizeof(ifc));
if(n<0)
{
syslog(LOG_ERR, "getifstats() : kvm_read(element): %s", kvm_geterr(kd));
goto error;
}
if(strcmp(ifname, ifc.if_xname) == 0)
{
/* found the right interface */
data->opackets = ifc.if_data.ifi_opackets;
data->ipackets = ifc.if_data.ifi_ipackets;
data->obytes = ifc.if_data.ifi_obytes;
data->ibytes = ifc.if_data.ifi_ibytes;
data->baudrate = ifc.if_data.ifi_baudrate;
kvm_close(kd);
if (getifaddrs(&ifap) != 0) {
syslog (LOG_ERR, "getifstats() : getifaddrs(): %s",
strerror(errno));
return (-1);
}
for (ifa = ifap; ifa; ifa = ifa->ifa_next)
if (ifa->ifa_addr->sa_family == AF_LINK &&
strcmp(ifa->ifa_name, ifname) == 0) {
#ifdef ENABLE_GETIFSTATS_CACHING #ifdef ENABLE_GETIFSTATS_CACHING
if(current_time!=((time_t)-1)) { cache_timestamp = current_time;
cache_timestamp = current_time; copy:
memcpy(&cache_data, data, sizeof(struct ifdata));
}
#endif #endif
return 0; /* ok */ #define IFA_STAT(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s)
data->opackets = IFA_STAT(opackets);
data->ipackets = IFA_STAT(ipackets);
data->obytes = IFA_STAT(obytes);
data->ibytes = IFA_STAT(ibytes);
data->baudrate = IFA_STAT(baudrate);
return (0);
} }
}
error:
kvm_close(kd);
return -1; /* not found or error */
}
return (-1);
}