
#ifdef linux

#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>

#include <linux/if_ether.h>

/* Based on if_getstats from ifconfig.c */

int get_rx_packets(char *ifname, struct enet_statistics *stats)
{
  FILE *fp;
  int len;
  char buf[256],*bp;

  fp=fopen("/proc/net/dev","r");
  if (fp==NULL) return(-1);
  len=strlen(ifname);

  while(fgets(buf,255,fp))
  {
  	bp=buf;
  	while(*bp&&isspace(*bp)) bp++;
  	if(strncmp(bp,ifname,len)==0 && bp[len]==':')
  	{
 		bp=strchr(bp,':');
 		bp++;
        sscanf(bp,"%d %d %d %d %d %d %d %d %d %d %d",
            &stats->rx_packets,
            &stats->rx_errors,
            &stats->rx_dropped,
            &stats->rx_fifo_errors,
            &stats->rx_frame_errors,

            &stats->tx_packets,
            &stats->tx_errors,
            &stats->tx_dropped,
            &stats->tx_fifo_errors,
            &stats->collisions,

            &stats->tx_carrier_errors
        );
 		fclose(fp);
 		return(stats->rx_packets);
  	}
  }
  fclose(fp);
  return(-2);
}

#elif __FreeBSD__

/* Based on 'netstat' source, FreeBSD 1.5 & 2.0.5 */

#include <sys/param.h>
#if (BSD < 199300)
#define FreeBSD 1
#else
#define FreeBSD 2
#endif

#include <sys/socket.h>
#include <sys/file.h>
#include <errno.h>
#include <netdb.h>
#include <nlist.h>
#include <kvm.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <paths.h>
#include <limits.h>

#include <sys/mbuf.h>

#include <net/if.h>
#if FreeBSD >= 2
#include <net/if_dl.h>
#include <net/if_types.h>
#endif
#define  KERNEL
#include <net/route.h>
#undef KERNEL
#include <netinet/in.h>

#if (FreeBSD < 2)
#include <sys/kinfo.h>
#else
#include <sys/sysctl.h>
kvm_t *kvmd = NULL;
#endif

#ifdef NS
#include <netns/ns.h>
#endif

#include <netdb.h>
/* #include <sys/kinfo.h> */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>

kread(addr, buf, size)
        u_long addr;
        char *buf;
        int size;
{
#if FreeBSD < 2  
    if (kvm_read(addr, buf, size) != size)
    { warnx("%s", kvm_geterr()); return (-1); }
#else
    if (kvm_read(kvmd, addr, buf, size) != size)
    { warnx("%s", kvm_geterr(kvmd)); return (-1); }
#endif
    return (0);
}

#define kget(p, d) (kread((u_long)(p), (char *)&(d), sizeof (d)))

struct nlist nl[] = {
#if (FreeBSD < 2)
#define	N_MBSTAT	0
	{ "_mbstat" },
#define	N_IPSTAT	1
	{ "_ipstat" },
#define	N_TCB		2
	{ "_tcb" },
#define	N_TCPSTAT	3
	{ "_tcpstat" },
#define	N_UDB		4
	{ "_udb" },
#define	N_UDPSTAT	5
	{ "_udpstat" },
#define	N_IFNET		6
	{ "_ifnet" },
#define	N_IMP		7
	{ "_imp_softc" },
#define	N_RTHOST	8
	{ "_rthost" },
#define	N_RTNET		9
	{ "_rtnet" },
#define	N_ICMPSTAT	10
	{ "_icmpstat" },
#define	N_RTSTAT	11
	{ "_rtstat" },
#define	N_FILEHEAD	12
	{ "_filehead" },
#define	N_FILES		13
	{ "_nfiles" },
#define	N_UNIXSW	14
	{ "_unixsw" },
#define N_RTHASHSIZE	15
	{ "_rthashsize" },
#define N_IDP		16
	{ "_nspcb"},
#define N_IDPSTAT	17
	{ "_idpstat"},
#define N_SPPSTAT	18
	{ "_spp_istat"},
#define N_NSERR		19
	{ "_ns_errstat"},
#define	N_CLNPSTAT	20
	{ "_clnp_stat"},
#define	IN_TP		21
	{ "_tp_inpcb" },
#define	ISO_TP		22
	{ "_tp_isopcb" },
#define	N_TPSTAT	23
	{ "_tp_stat" },
#define	N_ESISSTAT	24
	{ "_esis_stat"},
#define N_NIMP		25
	{ "_nimp"},
#define N_RTREE		26
	{ "_radix_node_head"},
#define N_CLTP		27
	{ "_cltb"},
#define N_CLTPSTAT	28
	{ "_cltpstat"},
#define	N_IGMPSTAT	29
	{ "_igmpstat" },
#define	N_MRTPROTO	30
	{ "_ip_mrtproto" },
#define	N_MRTSTAT	31
	{ "_mrtstat" },
#define	N_MRTTABLE	32
	{ "_mrttable" },
#define	N_VIFTABLE	33
	{ "_viftable" },
#else
#define N_MBSTAT        0
        { "_mbstat" },
#define N_IPSTAT        1
        { "_ipstat" },
#define N_TCB           2
        { "_tcb" },
#define N_TCPSTAT       3
        { "_tcpstat" },
#define N_UDB           4
        { "_udb" },
#define N_UDPSTAT       5
        { "_udpstat" },
#define N_IFNET         6
        { "_ifnet" },
#define N_IMP           7
        { "_imp_softc" },
#define N_ICMPSTAT      8
        { "_icmpstat" },
#define N_RTSTAT        9
        { "_rtstat" },
#define N_UNIXSW        10
        { "_unixsw" },
#define N_IDP           11
        { "_nspcb"},
#define N_IDPSTAT       12
        { "_idpstat"},
#define N_SPPSTAT       13
        { "_spp_istat"},
#define N_NSERR         14
        { "_ns_errstat"},
#define N_CLNPSTAT      15
        { "_clnp_stat"},
#define IN_NOTUSED      16
        { "_tp_inpcb" },
#define ISO_TP          17
        { "_tp_refinfo" },
#define N_TPSTAT        18
        { "_tp_stat" },
#define N_ESISSTAT      19
        { "_esis_stat"},
#define N_NIMP          20
        { "_nimp"},
#define N_RTREE         21
        { "_rt_tables"},
#define N_CLTP          22
        { "_cltb"},
#define N_CLTPSTAT      23
        { "_cltpstat"},
#define N_NFILE         24
        { "_nfile" },
#define N_FILE          25
        { "_file" },
#define N_IGMPSTAT      26
        { "_igmpstat" },
#define N_MRTPROTO      27
        { "_ip_mrtproto" },
#define N_MRTSTAT       28
        { "_mrtstat" },
#define N_MFCTABLE      29
        { "_mfctable" },
#define N_VIFTABLE      30
        { "_viftable" },
#endif
        "",
};

addUsage (char* ifname, struct radix_node *rn, int* usage)
{
    struct radix_node rnode;
    
 again:
    kget (rn, rnode);
    if (rnode.rn_b < 0)
    {
	if (!(rnode.rn_flags & RNF_ROOT))
	{	    
	    struct rtentry rt;
	    struct ifnet ifnet;
	    char name[16];
	    int use;
	    
	    kget(rn, rt);
	    kget(rt.rt_ifp, ifnet);
	    kget(ifnet.if_name, name);

	    /* add all transmitted packets for all routes of interface */
	    sprintf (name+strlen(name), "%d", ifnet.if_unit);
	    if (strcmp(name, ifname) == 0)
	      *usage += rt.rt_use;
	}
	if (rn = rnode.rn_dupedkey)
	  goto again;
    } else {
	rn = rnode.rn_r;
	addUsage(ifname, rnode.rn_l, usage);
	addUsage(ifname, rn, usage);
    }
}

int get_usage (char* ifname, int* usage)
{
    char* vmunix = _PATH_UNIX;
    char* kmemf  = NULL;
    char* nlistf = NULL;
    char buf[_POSIX2_LINE_MAX];
    off_t rtree;
    int i;
    struct radix_node_head *rnh, head;
    int euid = geteuid();

    *usage=0;
    setuid(0); /* so we can read /dev/[k]mem */
#if FreeBSD < 2
    if (kvm_openfiles(vmunix, kmemf, NULL) == -1) {
	syslog(LOG_ERR, "netstat: kvm_openfiles: %s\n", kvm_geterr());
	seteuid(euid); return -1;
    }
    if (kvm_nlist(nl) < 0 || nl[0].n_type == 0) {
	syslog(LOG_ERR, "%s: no namelist\n", vmunix);
	seteuid(euid); return -1;
    }
#else
    kvmd = kvm_openfiles(nlistf, kmemf, NULL, O_RDONLY, buf);
    if (kvmd == NULL) {
	syslog (LOG_ERR, "kvm_open: %s", buf);
	seteuid(euid); return -1;
    }
    if (kvm_nlist(kvmd, nl) < 0) {
	if(nlistf)
	  syslog (LOG_ERR, "%s: kvm_nlist: %s", nlistf, kvm_geterr(kvmd));
	else
	  syslog (LOG_ERR, "kvm_nlist: %s", kvm_geterr(kvmd));
	seteuid(euid); return -1;
    }
#endif
    
    /*
     * Keep file descriptors open to avoid overhead
     * of open/close on each call to get* routines.
     */
    sethostent(1);
    setnetent(1);

    rtree = (off_t)nl[N_RTREE].n_value;
#if (FreeBSD < 2)
    for (kget(rtree, rnh); rnh; rnh = head.rnh_next) {
	kget(rnh, head);
	if (head.rnh_af != 0)
	  addUsage(ifname, head.rnh_treetop, usage);
    }
#else
    kget (rtree, rt_tables);
    for (i = 0; i <= AF_MAX; i++)
    {
	if ((rnh = rt_tables[i]) == 0)
	  continue;
	kget(rnh, head);
	if (i != AF_UNSPEC)
	  addUsage(ifname, head.rnh_treetop, usage);
    }
#endif
    seteuid(euid);
    return usage;
}

#endif /* __FreeBSD__ */
