Module Name: src
Committed By: roy
Date: Thu Jun 11 13:36:20 UTC 2020
Modified Files:
src/share/man/man4: bpf.4
src/sys/net: bpf.c bpf.h bpfdesc.h
Log Message:
bpf(4): Add ioctls BIOCSETWF and BIOCLOCK
Once BIOCLOCK is executed, the device becomes locked which prevents the
execution of ioctl(2) commands which can change the underlying parameters
of the bpf(4) device. An example might be the setting of bpf(4) filter
programs or attaching to different network interfaces.
BIOCSETWF can be used to set write filters for outgoing packets.
Currently if a bpf(4) consumer is compromised, the bpf(4) descriptor can
essentially be used as a raw socket, regardless of consumer's UID.
Write filters give users the ability to constrain which packets can be sent
through the bpf(4) descriptor.
Taken from OpenBSD.
To generate a diff of this commit:
cvs rdiff -u -r1.61 -r1.62 src/share/man/man4/bpf.4
cvs rdiff -u -r1.236 -r1.237 src/sys/net/bpf.c
cvs rdiff -u -r1.74 -r1.75 src/sys/net/bpf.h
cvs rdiff -u -r1.46 -r1.47 src/sys/net/bpfdesc.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/share/man/man4/bpf.4
diff -u src/share/man/man4/bpf.4:1.61 src/share/man/man4/bpf.4:1.62
--- src/share/man/man4/bpf.4:1.61 Tue Jun 26 06:47:57 2018
+++ src/share/man/man4/bpf.4 Thu Jun 11 13:36:20 2020
@@ -1,6 +1,6 @@
.\" -*- nroff -*-
.\"
-.\" $NetBSD: bpf.4,v 1.61 2018/06/26 06:47:57 msaitoh Exp $
+.\" $NetBSD: bpf.4,v 1.62 2020/06/11 13:36:20 roy Exp $
.\"
.\" Copyright (c) 1990, 1991, 1992, 1993, 1994
.\" The Regents of the University of California. All rights reserved.
@@ -24,7 +24,7 @@
.\" This document is derived in part from the enet man page (enet.4)
.\" distributed with 4.3BSD Unix.
.\"
-.Dd June 22, 2018
+.Dd June 11, 2020
.Dt BPF 4
.Os
.Sh NAME
@@ -231,6 +231,10 @@ This is useful for programs like
.Xr rarpd 8 ,
which must respond to messages in real time.
The default for a new file is off.
+.Dv BIOCLOCK
+Set the locked flag on the bpf descriptor.
+This prevents the execution of ioctl commands which could change the
+underlying operating parameters of the device.
.It Dv BIOCSETF ( struct bpf_program )
Sets the filter program used by the kernel to discard uninteresting
packets.
@@ -256,6 +260,10 @@ are performed.
See section
.Sy FILTER MACHINE
for an explanation of the filter language.
+.It Dv BIOCSETWF ( struct bpf_program )
+Sets the write filter program used by the kernel to control what type
+of packets can be written to the interface.
+See the BIOCSETF command for more information on the bpf filter program.
.It Dv BIOCVERSION ( struct bpf_version )
Returns the major and minor version numbers of the filter language currently
recognized by the kernel.
Index: src/sys/net/bpf.c
diff -u src/sys/net/bpf.c:1.236 src/sys/net/bpf.c:1.237
--- src/sys/net/bpf.c:1.236 Mon Mar 16 21:20:11 2020
+++ src/sys/net/bpf.c Thu Jun 11 13:36:20 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: bpf.c,v 1.236 2020/03/16 21:20:11 pgoyette Exp $ */
+/* $NetBSD: bpf.c,v 1.237 2020/06/11 13:36:20 roy Exp $ */
/*
* Copyright (c) 1990, 1991, 1993
@@ -39,7 +39,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.236 2020/03/16 21:20:11 pgoyette Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.237 2020/06/11 13:36:20 roy Exp $");
#if defined(_KERNEL_OPT)
#include "opt_bpf.h"
@@ -236,6 +236,7 @@ static struct pslist_head bpf_dlist;
PSLIST_ENTRY_DESTROY((__d), bd_bif_dlist_entry)
static int bpf_allocbufs(struct bpf_d *);
+static u_int bpf_xfilter(struct bpf_filter **, void *, u_int, u_int);
static void bpf_deliver(struct bpf_if *,
void *(*cpfn)(void *, const void *, size_t),
void *, u_int, u_int, const u_int);
@@ -244,11 +245,12 @@ static void bpf_free_filter(struct bpf_f
static void bpf_ifname(struct ifnet *, struct ifreq *);
static void *bpf_mcpy(void *, const void *, size_t);
static int bpf_movein(struct uio *, int, uint64_t,
- struct mbuf **, struct sockaddr *);
+ struct mbuf **, struct sockaddr *,
+ struct bpf_filter **);
static void bpf_attachd(struct bpf_d *, struct bpf_if *);
static void bpf_detachd(struct bpf_d *);
static int bpf_setif(struct bpf_d *, struct ifreq *);
-static int bpf_setf(struct bpf_d *, struct bpf_program *);
+static int bpf_setf(struct bpf_d *, struct bpf_program *, u_long);
static void bpf_timed_out(void *);
static inline void
bpf_wakeup(struct bpf_d *);
@@ -322,13 +324,14 @@ bpf_jit_freecode(bpfjit_func_t jcode)
static int
bpf_movein(struct uio *uio, int linktype, uint64_t mtu, struct mbuf **mp,
- struct sockaddr *sockp)
+ struct sockaddr *sockp, struct bpf_filter **wfilter)
{
struct mbuf *m, *m0, *n;
int error;
size_t len;
size_t hlen;
size_t align;
+ u_int slen;
/*
* Build a sockaddr based on the data link layer type.
@@ -431,6 +434,12 @@ bpf_movein(struct uio *uio, int linktype
m = n;
}
+ slen = bpf_xfilter(wfilter, mtod(m, u_char *), len, len);
+ if (slen == 0) {
+ error = EPERM;
+ goto bad;
+ }
+
if (hlen != 0) {
/* move link level header in the top of mbuf to sa_data */
memcpy(sockp->sa_data, mtod(m0, void *), hlen);
@@ -572,7 +581,9 @@ bpfopen(dev_t dev, int flag, int mode, s
callout_init(&d->bd_callout, CALLOUT_MPSAFE);
selinit(&d->bd_sel);
d->bd_jitcode = NULL;
- d->bd_filter = NULL;
+ d->bd_rfilter = NULL;
+ d->bd_wfilter = NULL;
+ d->bd_locked = 0;
BPF_DLIST_ENTRY_INIT(d);
BPFIF_DLIST_ENTRY_INIT(d);
d->bd_mtx = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET);
@@ -820,7 +831,7 @@ bpf_write(struct file *fp, off_t *offp,
}
error = bpf_movein(uio, (int)bp->bif_dlt, ifp->if_mtu, &m,
- (struct sockaddr *) &dst);
+ (struct sockaddr *) &dst, &d->bd_wfilter);
if (error)
goto out;
@@ -934,6 +945,28 @@ bpf_ioctl(struct file *fp, u_long cmd, v
d->bd_state = BPF_IDLE;
mutex_exit(d->bd_mtx);
+ if (d->bd_locked) {
+ switch (cmd) {
+ case BIOCGBLEN: /* FALLTHROUGH */
+ case BIOCFLUSH: /* FALLTHROUGH */
+ case BIOCGDLT: /* FALLTHROUGH */
+ case BIOCGDLTLIST: /* FALLTHROUGH */
+ case BIOCGETIF: /* FALLTHROUGH */
+ case BIOCGRTIMEOUT: /* FALLTHROUGH */
+ case BIOCGSTATS: /* FALLTHROUGH */
+ case BIOCVERSION: /* FALLTHROUGH */
+ case BIOCGHDRCMPLT: /* FALLTHROUGH */
+ case FIONREAD: /* FALLTHROUGH */
+ case BIOCLOCK: /* FALLTHROUGH */
+ case BIOCSRTIMEOUT: /* FALLTHROUGH */
+ case BIOCIMMEDIATE: /* FALLTHROUGH */
+ case TIOCGPGRP:
+ break;
+ default:
+ return EPERM;
+ }
+ }
+
switch (cmd) {
default:
@@ -992,8 +1025,13 @@ bpf_ioctl(struct file *fp, u_long cmd, v
/*
* Set link layer read filter.
*/
- case BIOCSETF:
- error = bpf_setf(d, addr);
+ case BIOCSETF: /* FALLTHROUGH */
+ case BIOCSETWF:
+ error = bpf_setf(d, addr, cmd);
+ break;
+
+ case BIOCLOCK:
+ d->bd_locked = 1;
break;
/*
@@ -1267,12 +1305,12 @@ bpf_ioctl(struct file *fp, u_long cmd, v
* free it and replace it. Returns EINVAL for bogus requests.
*/
static int
-bpf_setf(struct bpf_d *d, struct bpf_program *fp)
+bpf_setf(struct bpf_d *d, struct bpf_program *fp, u_long cmd)
{
struct bpf_insn *fcode;
bpfjit_func_t jcode;
size_t flen, size = 0;
- struct bpf_filter *oldf, *newf;
+ struct bpf_filter *oldf, *newf, **storef;
jcode = NULL;
flen = fp->bf_len;
@@ -1303,13 +1341,20 @@ bpf_setf(struct bpf_d *d, struct bpf_pro
newf->bf_insn = fcode;
newf->bf_size = size;
newf->bf_jitcode = jcode;
- d->bd_jitcode = jcode; /* XXX just for kvm(3) users */
+ if (cmd == BIOCSETF)
+ d->bd_jitcode = jcode; /* XXX just for kvm(3) users */
/* Need to hold bpf_mtx for pserialize_perform */
mutex_enter(&bpf_mtx);
mutex_enter(d->bd_mtx);
- oldf = d->bd_filter;
- atomic_store_release(&d->bd_filter, newf);
+ if (cmd == BIOCSETWF) {
+ oldf = d->bd_wfilter;
+ storef = &d->bd_wfilter;
+ } else {
+ oldf = d->bd_rfilter;
+ storef = &d->bd_rfilter;
+ }
+ atomic_store_release(storef, newf);
reset_d(d);
pserialize_perform(bpf_psz);
mutex_exit(d->bd_mtx);
@@ -1560,6 +1605,31 @@ bpf_mcpy(void *dst_arg, const void *src_
return dst_arg;
}
+static inline u_int
+bpf_xfilter(struct bpf_filter **filter, void *pkt, u_int pktlen, u_int buflen)
+{
+ struct bpf_filter *filt;
+ uint32_t mem[BPF_MEMWORDS];
+ bpf_args_t args = {
+ .pkt = (const uint8_t *)pkt,
+ .wirelen = pktlen,
+ .buflen = buflen,
+ .mem = mem,
+ .arg = NULL
+ };
+ u_int slen;
+
+ filt = atomic_load_consume(filter);
+ if (filt == NULL) /* No filter means accept all. */
+ return (u_int)-1;
+
+ if (filt->bf_jitcode != NULL)
+ slen = filt->bf_jitcode(NULL, &args);
+ else
+ slen = bpf_filter_ext(NULL, filt->bf_insn, &args);
+ return slen;
+}
+
/*
* Dispatch a packet to all the listeners on interface bp.
*
@@ -1573,18 +1643,11 @@ static inline void
bpf_deliver(struct bpf_if *bp, void *(*cpfn)(void *, const void *, size_t),
void *pkt, u_int pktlen, u_int buflen, const u_int direction)
{
- uint32_t mem[BPF_MEMWORDS];
- bpf_args_t args = {
- .pkt = (const uint8_t *)pkt,
- .wirelen = pktlen,
- .buflen = buflen,
- .mem = mem,
- .arg = NULL
- };
bool gottime = false;
struct timespec ts;
struct bpf_d *d;
int s;
+ u_int slen;
KASSERT(!cpu_intr_p());
@@ -1595,9 +1658,6 @@ bpf_deliver(struct bpf_if *bp, void *(*c
*/
s = pserialize_read_enter();
BPFIF_DLIST_READER_FOREACH(d, bp) {
- u_int slen = 0;
- struct bpf_filter *filter;
-
if (direction == BPF_D_IN) {
if (d->bd_direction == BPF_D_OUT)
continue;
@@ -1609,18 +1669,10 @@ bpf_deliver(struct bpf_if *bp, void *(*c
atomic_inc_ulong(&d->bd_rcount);
BPF_STATINC(recv);
- filter = atomic_load_consume(&d->bd_filter);
- if (filter != NULL) {
- if (filter->bf_jitcode != NULL)
- slen = filter->bf_jitcode(NULL, &args);
- else
- slen = bpf_filter_ext(NULL, filter->bf_insn,
- &args);
- }
-
- if (!slen) {
+ slen = bpf_xfilter(&d->bd_rfilter, pkt, pktlen, buflen);
+ if (slen == 0)
continue;
- }
+
if (!gottime) {
gottime = true;
nanotime(&ts);
@@ -2044,9 +2096,13 @@ bpf_freed(struct bpf_d *d)
if (d->bd_fbuf != NULL)
kmem_free(d->bd_fbuf, d->bd_bufsize);
}
- if (d->bd_filter != NULL) {
- bpf_free_filter(d->bd_filter);
- d->bd_filter = NULL;
+ if (d->bd_rfilter != NULL) {
+ bpf_free_filter(d->bd_rfilter);
+ d->bd_rfilter = NULL;
+ }
+ if (d->bd_wfilter != NULL) {
+ bpf_free_filter(d->bd_wfilter);
+ d->bd_wfilter = NULL;
}
d->bd_jitcode = NULL;
}
@@ -2371,6 +2427,7 @@ sysctl_net_bpf_peers(SYSCTLFN_ARGS)
IFNAMSIZ - 1);
else
dpe.bde_ifname[0] = '\0';
+ dpe.bde_locked = dp->bd_locked;
mutex_exit(dp->bd_mtx);
error = copyout(&dpe, sp, out_size);
Index: src/sys/net/bpf.h
diff -u src/sys/net/bpf.h:1.74 src/sys/net/bpf.h:1.75
--- src/sys/net/bpf.h:1.74 Tue Feb 26 10:30:28 2019
+++ src/sys/net/bpf.h Thu Jun 11 13:36:20 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: bpf.h,v 1.74 2019/02/26 10:30:28 msaitoh Exp $ */
+/* $NetBSD: bpf.h,v 1.75 2020/06/11 13:36:20 roy Exp $ */
/*
* Copyright (c) 1990, 1991, 1993
@@ -151,6 +151,8 @@ struct bpf_version {
#define BIOCGFEEDBACK _IOR('B', 124, u_int)
#define BIOCSFEEDBACK _IOW('B', 125, u_int)
#define BIOCFEEDBACK BIOCSFEEDBACK /* FreeBSD name */
+#define BIOCLOCK _IO('B', 126)
+#define BIOCSETWF _IOW('B', 127, struct bpf_program)
/* Obsolete */
#define BIOCGSEESENT BIOCGDIRECTION
Index: src/sys/net/bpfdesc.h
diff -u src/sys/net/bpfdesc.h:1.46 src/sys/net/bpfdesc.h:1.47
--- src/sys/net/bpfdesc.h:1.46 Tue Jun 26 06:48:02 2018
+++ src/sys/net/bpfdesc.h Thu Jun 11 13:36:20 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: bpfdesc.h,v 1.46 2018/06/26 06:48:02 msaitoh Exp $ */
+/* $NetBSD: bpfdesc.h,v 1.47 2020/06/11 13:36:20 roy Exp $ */
/*
* Copyright (c) 1990, 1991, 1993
@@ -123,7 +123,9 @@ struct bpf_d {
#endif
/* DEPRECATED. Keep it to avoid breaking kvm(3) users */
bpfjit_func_t bd_jitcode; /* compiled filter program */
- struct bpf_filter *bd_filter;
+ struct bpf_filter *bd_rfilter;
+ struct bpf_filter *bd_wfilter;
+ int bd_locked;
#ifdef _KERNEL
struct pslist_entry bd_bif_dlist_entry; /* For bpf_if */
struct pslist_entry bd_bpf_dlist_entry; /* For the global list */
@@ -155,6 +157,7 @@ struct bpf_d_ext {
uint64_t bde_dcount; /* number of packets dropped */
uint64_t bde_ccount; /* number of packets captured */
char bde_ifname[IFNAMSIZ];
+ int bde_locked;
};