Implemented the kernel part of BPF statistics and BPF peers, net.bpf.stats

and net.bpf.peers sysctls respectively.

A new structure was added to describe the external (user viewable)
representation of a BPF file; a new entry was added to the bpf_d
structure to store the PID of the calling process; a simple_lock was added
to protect the insert/removal from the net.bpf.peers sysctl handler.

This idea came from FreeBSD (Christian S.J. Peron) but while it is
implemented with sysctl's it differs a bit.

Reviewed by: christos@ and atatat@ (who gave me the tip for the net.bpf.peers
sysctl helper function).
This commit is contained in:
rpaulo 2005-08-04 19:30:47 +00:00
parent ff4290fdee
commit 2fcfc4c276
3 changed files with 156 additions and 7 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: bpf.c,v 1.109 2005/06/22 10:36:16 peter Exp $ */ /* $NetBSD: bpf.c,v 1.110 2005/08/04 19:30:47 rpaulo Exp $ */
/* /*
* Copyright (c) 1990, 1991, 1993 * Copyright (c) 1990, 1991, 1993
@ -39,7 +39,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.109 2005/06/22 10:36:16 peter Exp $"); __KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.110 2005/08/04 19:30:47 rpaulo Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -101,6 +101,18 @@ __KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.109 2005/06/22 10:36:16 peter Exp $");
int bpf_bufsize = BPF_BUFSIZE; int bpf_bufsize = BPF_BUFSIZE;
int bpf_maxbufsize = BPF_DFLTBUFSIZE; /* XXX set dynamically, see above */ int bpf_maxbufsize = BPF_DFLTBUFSIZE; /* XXX set dynamically, see above */
/*
* Global BPF statistics returned by net.bpf.stats sysctl.
*/
struct bpf_stat bpf_gstats;
/*
* Use a mutex to avoid a race condition between gathering the stats/peers
* and opening/closing the device.
*/
struct simplelock bpf_slock;
/* /*
* bpf_iflist is the list of interfaces; each corresponds to an ifnet * bpf_iflist is the list of interfaces; each corresponds to an ifnet
* bpf_dtab holds the descriptors, indexed by minor device # * bpf_dtab holds the descriptors, indexed by minor device #
@ -362,7 +374,15 @@ void
bpfilterattach(n) bpfilterattach(n)
int n; int n;
{ {
simple_lock_init(&bpf_slock);
simple_lock(&bpf_slock);
LIST_INIT(&bpf_list); LIST_INIT(&bpf_list);
simple_unlock(&bpf_slock);
bpf_gstats.bs_recv = 0;
bpf_gstats.bs_drop = 0;
bpf_gstats.bs_capt = 0;
} }
/* /*
@ -388,9 +408,12 @@ bpfopen(dev, flag, mode, p)
(void)memset(d, 0, sizeof(*d)); (void)memset(d, 0, sizeof(*d));
d->bd_bufsize = bpf_bufsize; d->bd_bufsize = bpf_bufsize;
d->bd_seesent = 1; d->bd_seesent = 1;
d->bd_pid = p->p_pid;
callout_init(&d->bd_callout); callout_init(&d->bd_callout);
simple_lock(&bpf_slock);
LIST_INSERT_HEAD(&bpf_list, d, bd_list); LIST_INSERT_HEAD(&bpf_list, d, bd_list);
simple_unlock(&bpf_slock);
return fdclone(p, fp, fd, flag, &bpf_fileops, d); return fdclone(p, fp, fd, flag, &bpf_fileops, d);
} }
@ -406,6 +429,11 @@ bpf_close(struct file *fp, struct proc *p)
struct bpf_d *d = fp->f_data; struct bpf_d *d = fp->f_data;
int s; int s;
/*
* Refresh the PID associated with this bpf file.
*/
d->bd_pid = p->p_pid;
s = splnet(); s = splnet();
if (d->bd_state == BPF_WAITING) if (d->bd_state == BPF_WAITING)
callout_stop(&d->bd_callout); callout_stop(&d->bd_callout);
@ -414,7 +442,9 @@ bpf_close(struct file *fp, struct proc *p)
bpf_detachd(d); bpf_detachd(d);
splx(s); splx(s);
bpf_freed(d); bpf_freed(d);
simple_lock(&bpf_slock);
LIST_REMOVE(d, bd_list); LIST_REMOVE(d, bd_list);
simple_unlock(&bpf_slock);
free(d, M_DEVBUF); free(d, M_DEVBUF);
fp->f_data = NULL; fp->f_data = NULL;
@ -658,6 +688,11 @@ bpf_ioctl(struct file *fp, u_long cmd, void *addr, struct proc *p)
struct bpf_insn **p; struct bpf_insn **p;
#endif #endif
/*
* Refresh the PID associated with this bpf file.
*/
d->bd_pid = p->p_pid;
s = splnet(); s = splnet();
if (d->bd_state == BPF_WAITING) if (d->bd_state == BPF_WAITING)
callout_stop(&d->bd_callout); callout_stop(&d->bd_callout);
@ -1079,6 +1114,11 @@ bpf_poll(struct file *fp, int events, struct proc *p)
int s = splnet(); int s = splnet();
int revents; int revents;
/*
* Refresh the PID associated with this bpf file.
*/
d->bd_pid = p->p_pid;
revents = events & (POLLOUT | POLLWRNORM); revents = events & (POLLOUT | POLLWRNORM);
if (events & (POLLIN | POLLRDNORM)) { if (events & (POLLIN | POLLRDNORM)) {
/* /*
@ -1178,6 +1218,7 @@ bpf_tap(void *arg, u_char *pkt, u_int pktlen)
bp = arg; bp = arg;
for (d = bp->bif_dlist; d != 0; d = d->bd_next) { for (d = bp->bif_dlist; d != 0; d = d->bd_next) {
++d->bd_rcount; ++d->bd_rcount;
++bpf_gstats.bs_recv;
slen = bpf_filter(d->bd_filter, pkt, pktlen, pktlen); slen = bpf_filter(d->bd_filter, pkt, pktlen, pktlen);
if (slen != 0) if (slen != 0)
catchpacket(d, pkt, pktlen, slen, memcpy); catchpacket(d, pkt, pktlen, slen, memcpy);
@ -1229,6 +1270,7 @@ bpf_deliver(struct bpf_if *bp, void *(*cpfn)(void *, const void *, size_t),
if (!d->bd_seesent && (rcvif == NULL)) if (!d->bd_seesent && (rcvif == NULL))
continue; continue;
++d->bd_rcount; ++d->bd_rcount;
++bpf_gstats.bs_recv;
slen = bpf_filter(d->bd_filter, marg, pktlen, buflen); slen = bpf_filter(d->bd_filter, marg, pktlen, buflen);
if (slen != 0) if (slen != 0)
catchpacket(d, marg, pktlen, slen, cpfn); catchpacket(d, marg, pktlen, slen, cpfn);
@ -1399,6 +1441,7 @@ catchpacket(struct bpf_d *d, u_char *pkt, u_int pktlen, u_int snaplen,
int hdrlen = d->bd_bif->bif_hdrlen; int hdrlen = d->bd_bif->bif_hdrlen;
++d->bd_ccount; ++d->bd_ccount;
++bpf_gstats.bs_capt;
/* /*
* Figure out how many bytes to move. If the packet is * Figure out how many bytes to move. If the packet is
* greater or equal to the snapshot length, transfer that * greater or equal to the snapshot length, transfer that
@ -1425,6 +1468,7 @@ catchpacket(struct bpf_d *d, u_char *pkt, u_int pktlen, u_int snaplen,
* so drop the packet. * so drop the packet.
*/ */
++d->bd_dcount; ++d->bd_dcount;
++bpf_gstats.bs_drop;
return; return;
} }
ROTATE_BUFFERS(d); ROTATE_BUFFERS(d);
@ -1694,7 +1738,77 @@ sysctl_net_bpf_maxbufsize(SYSCTLFN_ARGS)
return (0); return (0);
} }
SYSCTL_SETUP(sysctl_net_bfp_setup, "sysctl net.bpf subtree setup") static int
sysctl_net_bpf_peers(SYSCTLFN_ARGS)
{
int error, elem_count;
struct bpf_d *dp;
struct bpf_d_ext dpe;
size_t len, needed, elem_size, out_size;
char *sp;
if (namelen == 1 && name[0] == CTL_QUERY)
return (sysctl_query(SYSCTLFN_CALL(rnode)));
if (namelen != 2)
return (EINVAL);
if ((error = suser(l->l_proc->p_ucred, &l->l_proc->p_acflag)))
return (error);
len = (oldp != NULL) ? *oldlenp : 0;
sp = oldp;
elem_size = name[0];
elem_count = name[1];
out_size = MIN(sizeof(dpe), elem_size);
needed = 0;
if (elem_size < 1 || elem_count < 0)
return (EINVAL);
simple_lock(&bpf_slock);
LIST_FOREACH(dp, &bpf_list, bd_list) {
if (len >= elem_size && elem_count > 0) {
#define BPF_EXT(field) dpe.bde_ ## field = dp->bd_ ## field
BPF_EXT(bufsize);
BPF_EXT(promisc);
BPF_EXT(promisc);
BPF_EXT(state);
BPF_EXT(immediate);
BPF_EXT(hdrcmplt);
BPF_EXT(seesent);
BPF_EXT(pid);
BPF_EXT(rcount);
BPF_EXT(dcount);
BPF_EXT(ccount);
#undef BPF_EXT
if (dp->bd_bif)
(void)strlcpy(dpe.bde_ifname,
dp->bd_bif->bif_ifp->if_xname,
IFNAMSIZ - 1);
else
dpe.bde_ifname[0] = '\0';
error = copyout(&dpe, sp, out_size);
if (error)
break;
sp += elem_size;
len -= elem_size;
}
if (elem_count > 0) {
needed += elem_size;
if (elem_count != INT_MAX)
elem_count--;
}
}
simple_unlock(&bpf_slock);
*oldlenp = needed;
return (error);
}
SYSCTL_SETUP(sysctl_net_bpf_setup, "sysctl net.bpf subtree setup")
{ {
const struct sysctlnode *node; const struct sysctlnode *node;
@ -1711,11 +1825,25 @@ SYSCTL_SETUP(sysctl_net_bfp_setup, "sysctl net.bpf subtree setup")
SYSCTL_DESCR("BPF options"), SYSCTL_DESCR("BPF options"),
NULL, 0, NULL, 0, NULL, 0, NULL, 0,
CTL_NET, CTL_CREATE, CTL_EOL); CTL_NET, CTL_CREATE, CTL_EOL);
if (node != NULL) if (node != NULL) {
sysctl_createv(clog, 0, NULL, NULL, sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
CTLTYPE_INT, "maxbufsize", CTLTYPE_INT, "maxbufsize",
SYSCTL_DESCR("Maximum size for data capture buffer"), SYSCTL_DESCR("Maximum size for data capture buffer"),
sysctl_net_bpf_maxbufsize, 0, &bpf_maxbufsize, 0, sysctl_net_bpf_maxbufsize, 0, &bpf_maxbufsize, 0,
CTL_NET, node->sysctl_num, CTL_CREATE, CTL_EOL); CTL_NET, node->sysctl_num, CTL_CREATE, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRUCT, "stats",
SYSCTL_DESCR("BPF stats"),
NULL, 0, &bpf_gstats, sizeof(bpf_gstats),
CTL_NET, node->sysctl_num, CTL_CREATE, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRUCT, "peers",
SYSCTL_DESCR("BPF peers"),
sysctl_net_bpf_peers, 0, NULL, 0,
CTL_NET, node->sysctl_num, CTL_CREATE, CTL_EOL);
}
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: bpf.h,v 1.42 2005/02/26 22:45:09 perry Exp $ */ /* $NetBSD: bpf.h,v 1.43 2005/08/04 19:30:47 rpaulo Exp $ */
/* /*
* Copyright (c) 1990, 1991, 1993 * Copyright (c) 1990, 1991, 1993
@ -69,7 +69,7 @@ struct bpf_program {
}; };
/* /*
* Struct returned by BIOCGSTATS. * Struct returned by BIOCGSTATS and net.bpf.stats sysctl.
*/ */
struct bpf_stat { struct bpf_stat {
u_int64_t bs_recv; /* number of packets received */ u_int64_t bs_recv; /* number of packets received */

View File

@ -1,4 +1,4 @@
/* $NetBSD: bpfdesc.h,v 1.22 2005/03/17 20:39:17 kleink Exp $ */ /* $NetBSD: bpfdesc.h,v 1.23 2005/08/04 19:30:47 rpaulo Exp $ */
/* /*
* Copyright (c) 1990, 1991, 1993 * Copyright (c) 1990, 1991, 1993
@ -43,6 +43,7 @@
#include <sys/callout.h> #include <sys/callout.h>
#include <sys/selinfo.h> /* for struct selinfo */ #include <sys/selinfo.h> /* for struct selinfo */
#include <net/if.h> /* for IFNAMSIZ */
/* /*
* Descriptor associated with each open bpf file. * Descriptor associated with each open bpf file.
@ -89,6 +90,7 @@ struct bpf_d {
struct selinfo bd_sel; /* bsd select info */ struct selinfo bd_sel; /* bsd select info */
#endif #endif
struct callout bd_callout; /* for BPF timeouts with select */ struct callout bd_callout; /* for BPF timeouts with select */
pid_t bd_pid; /* corresponding PID */
LIST_ENTRY(bpf_d) bd_list; /* list of all BPF's */ LIST_ENTRY(bpf_d) bd_list; /* list of all BPF's */
}; };
@ -98,6 +100,25 @@ struct bpf_d {
#define BPF_WAITING 1 /* waiting for read timeout in select */ #define BPF_WAITING 1 /* waiting for read timeout in select */
#define BPF_TIMED_OUT 2 /* read timeout has expired in select */ #define BPF_TIMED_OUT 2 /* read timeout has expired in select */
/*
* Description associated with the external representation of each
* open bpf file.
*/
struct bpf_d_ext {
int32_t bde_bufsize;
u_int8_t bde_promisc;
u_int8_t bde_state;
u_int8_t bde_immediate;
int32_t bde_hdrcmplt;
int32_t bde_seesent;
pid_t bde_pid;
u_int64_t bde_rcount; /* number of packets received */
u_int64_t bde_dcount; /* number of packets dropped */
u_int64_t bde_ccount; /* number of packets captured */
char bde_ifname[IFNAMSIZ];
};
/* /*
* Descriptor associated with each attached hardware interface. * Descriptor associated with each attached hardware interface.
*/ */