From mluckie@cs.waikato.ac.nz  Fri Dec  7 21:07:41 2007
Return-Path: <mluckie@cs.waikato.ac.nz>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 682FE16A46C
	for <FreeBSD-gnats-submit@freebsd.org>; Fri,  7 Dec 2007 21:07:41 +0000 (UTC)
	(envelope-from mluckie@cs.waikato.ac.nz)
Received: from zombie.scms.waikato.ac.nz (mail.scms.waikato.ac.nz [130.217.241.36])
	by mx1.freebsd.org (Postfix) with ESMTP id F273513C467
	for <FreeBSD-gnats-submit@freebsd.org>; Fri,  7 Dec 2007 21:07:40 +0000 (UTC)
	(envelope-from mluckie@cs.waikato.ac.nz)
Received: from sorcerer.cs.waikato.ac.nz ([130.217.250.39])
	by zombie.scms.waikato.ac.nz with esmtps (TLSv1:AES256-SHA:256)
	(Exim 4.52)
	id 1J0jmO-0001fx-C1
	for FreeBSD-gnats-submit@freebsd.org; Sat, 08 Dec 2007 09:26:36 +1300
Received: from mluckie by sorcerer.cs.waikato.ac.nz with local (Exim 4.68 (FreeBSD))
	(envelope-from <mluckie@sorcerer.cs.waikato.ac.nz>)
	id 1J0jmN-000GpX-OS
	for FreeBSD-gnats-submit@freebsd.org; Sat, 08 Dec 2007 09:26:35 +1300
Message-Id: <E1J0jmN-000GpX-OS@sorcerer.cs.waikato.ac.nz>
Date: Sat, 08 Dec 2007 09:26:35 +1300
From: Matthew Luckie <mluckie@cs.waikato.ac.nz>
Sender: Matthew Luckie <mluckie@cs.waikato.ac.nz>
Reply-To: Matthew Luckie <mluckie@cs.waikato.ac.nz>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: [patch][bpf] change BPF filters without discarding buffered packets
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         118486
>Category:       kern
>Synopsis:       [bpf] [patch] change BPF filters without discarding buffered packets
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Dec 07 21:10:01 UTC 2007
>Closed-Date:    Sat Aug 16 12:14:13 UTC 2008
>Last-Modified:  Tue Jul 10 03:39:49 UTC 2012
>Originator:     Matthew Luckie
>Release:        FreeBSD 6.2-STABLE i386
>Organization:
>Environment:
System: FreeBSD sorcerer.cs.waikato.ac.nz 6.2-STABLE FreeBSD 6.2-STABLE #0: Mon May 14 13:25:05 NZST 2007 root@sorcerer.cs.waikato.ac.nz:/usr/obj/usr/src/sys/SMP i386


	
>Description:
BIOCSETF and BIOCSETWF call reset_d which discards any packets
currently in the hold buffer and resets the packet rx count.

The patch below adds BIOCSETFNR, an ioctl that swaps the BPF filter
without resetting the hold buffer and without resetting the packet rx
counts, which is handy when the application wants to adjust its filter
program but without discarding whatever the system might have
buffered.

I've also changed BIOCSETWF to map to the new function.  I don't see
the rationale in having BIOCSETWF mucking with the receive stats and
hold buffer.
>How-To-Repeat:
	
>Fix:
Patch against -HEAD below.
--- bpf-setfnr.patch begins here ---
--- sys/net/bpf.c.orig	Mon Nov 19 16:09:09 2007
+++ sys/net/bpf.c	Sat Nov 24 12:14:23 2007
@@ -112,7 +112,8 @@
 		    u_int, void (*)(const void *, void *, size_t),
 		    struct timeval *);
 static void	reset_d(struct bpf_d *);
-static int	 bpf_setf(struct bpf_d *, struct bpf_program *, u_long cmd);
+static int	bpf_setf(struct bpf_d *, struct bpf_program *);
+static int	bpf_setfnr(struct bpf_d *, struct bpf_program *, u_long);
 static int	bpf_getdltlist(struct bpf_d *, struct bpf_dltlist *);
 static int	bpf_setdlt(struct bpf_d *, u_int);
 static void	filt_bpfdetach(struct knote *);
@@ -681,7 +682,6 @@
 static void
 reset_d(struct bpf_d *d)
 {
-
 	mtx_assert(&d->bd_mtx, MA_OWNED);
 	if (d->bd_hbuf) {
 		/* Free the hold buffer. */
@@ -699,8 +699,9 @@
  *  FIONREAD		Check for read packet available.
  *  SIOCGIFADDR		Get interface address - convenient hook to driver.
  *  BIOCGBLEN		Get buffer len [for read()].
- *  BIOCSETF		Set ethernet read filter.
- *  BIOCSETWF		Set ethernet write filter.
+ *  BIOCSETF		Set read filter.
+ *  BIOCSETFNR		Set read filter without resetting descriptor.
+ *  BIOCSETWF		Set write filter.
  *  BIOCFLUSH		Flush read packet buffer.
  *  BIOCPROMISC		Put interface into promiscuous mode.
  *  BIOCGDLT		Get link layer type.
@@ -823,8 +824,12 @@
 	 * Set link layer read filter.
 	 */
 	case BIOCSETF:
+		error = bpf_setf(d, (struct bpf_program *)addr);
+		break;
+
+	case BIOCSETFNR:
 	case BIOCSETWF:
-		error = bpf_setf(d, (struct bpf_program *)addr, cmd);
+		error = bpf_setfnr(d, (struct bpf_program *)addr, cmd);
 		break;
 
 	/*
@@ -1060,39 +1065,24 @@
  * free it and replace it.  Returns EINVAL for bogus requests.
  */
 static int
-bpf_setf(struct bpf_d *d, struct bpf_program *fp, u_long cmd)
+bpf_setf(struct bpf_d *d, struct bpf_program *fp)
 {
 	struct bpf_insn *fcode, *old;
-	u_int wfilter, flen, size;
+	u_int flen, size;
 #ifdef BPF_JITTER
-	bpf_jit_filter *ofunc;
+	bpf_jit_filter *ofunc, *nfunc;
 #endif
 
-	if (cmd == BIOCSETWF) {
-		old = d->bd_wfilter;
-		wfilter = 1;
-#ifdef BPF_JITTER
-		ofunc = NULL;
-#endif
-	} else {
-		wfilter = 0;
-		old = d->bd_rfilter;
-#ifdef BPF_JITTER
-		ofunc = d->bd_bfilter;
-#endif
-	}
 	if (fp->bf_insns == NULL) {
 		if (fp->bf_len != 0)
 			return (EINVAL);
 		BPFD_LOCK(d);
-		if (wfilter)
-			d->bd_wfilter = NULL;
-		else {
-			d->bd_rfilter = NULL;
+		old = d->bd_rfilter;
+		d->bd_rfilter = NULL;
 #ifdef BPF_JITTER
-			d->bd_bfilter = NULL;
+		ofunc = d->bd_bfilter;
+		d->bd_bfilter = NULL;
 #endif
-		}
 		reset_d(d);
 		BPFD_UNLOCK(d);
 		if (old != NULL)
@@ -1111,15 +1101,16 @@
 	fcode = (struct bpf_insn *)malloc(size, M_BPF, M_WAITOK);
 	if (copyin((caddr_t)fp->bf_insns, (caddr_t)fcode, size) == 0 &&
 	    bpf_validate(fcode, (int)flen)) {
+#ifdef BPF_JITTER
+		nfunc = bpf_jitter(fcode, flen);
+#endif
 		BPFD_LOCK(d);
-		if (wfilter)
-			d->bd_wfilter = fcode;
-		else {
-			d->bd_rfilter = fcode;
+		old = d->bd_rfilter;
+		d->bd_rfilter = fcode;
 #ifdef BPF_JITTER
-			d->bd_bfilter = bpf_jitter(fcode, flen);
+		ofunc = d->bd_bfilter;
+		d->bd_bfilter = nfunc;
 #endif
-		}
 		reset_d(d);
 		BPFD_UNLOCK(d);
 		if (old != NULL)
@@ -1128,11 +1119,70 @@
 		if (ofunc != NULL)
 			bpf_destroy_jit_filter(ofunc);
 #endif
-
 		return (0);
 	}
 	free((caddr_t)fcode, M_BPF);
 	return (EINVAL);
+}
+
+static int
+bpf_setfnr(struct bpf_d *d, struct bpf_program *fp, u_long cmd)
+{
+	struct bpf_insn *fcode, *old;
+	u_int flen, size;
+#ifdef BPF_JITTER
+	bpf_jit_filter *ofunc, *nfunc;
+#endif
+
+	if (fp->bf_insns != NULL) {
+		flen = fp->bf_len;
+		if (flen > bpf_maxinsns)
+			return (EINVAL);
+
+		size = flen * sizeof(*fp->bf_insns);
+		fcode = (struct bpf_insn *)malloc(size, M_BPF, M_WAITOK);
+		if (copyin((caddr_t)fp->bf_insns, (caddr_t)fcode, size) != 0 ||
+		    bpf_validate(fcode, (int)flen) == 0) {
+			free((caddr_t)fcode, M_BPF);
+			return (EINVAL);
+		}
+#ifdef BPF_JITTER
+		if (cmd == BIOCSETFNR)
+			nfunc = bpf_jitter(fcode, flen);
+		else
+			nfunc = NULL;
+#endif
+	} else {
+		if (fp->bf_len != 0)
+			return (EINVAL);
+		fcode = NULL;
+#ifdef BPF_JITTER
+		nfunc = NULL;
+#endif
+	}
+
+	BPFD_LOCK(d);
+	if (cmd == BIOCSETFNR) {
+		old = d->bd_rfilter;
+		d->bd_rfilter = fcode;
+#ifdef BPF_JITTER
+		ofunc = d->bd_bfilter;
+		d->bd_bfilter = nfunc;
+#endif
+	} else {
+		old = d->bd_wfilter;
+		d->bd_wfilter = fcode;
+	}
+	BPFD_UNLOCK(d);
+
+	if (old != NULL)
+		free((caddr_t)old, M_BPF);
+#ifdef BPF_JITTER
+	if (ofunc != NULL)
+		bpf_destroy_jit_filter(ofunc);
+#endif
+
+	return (0);
 }
 
 /*
--- sys/net/bpf.h.orig	Mon Nov 19 16:09:17 2007
+++ sys/net/bpf.h	Mon Nov 19 16:09:58 2007
@@ -116,6 +116,7 @@
 #define	BIOCLOCK	_IO('B', 122)
 #define	BIOCSETWF	_IOW('B',123, struct bpf_program)
 #define	BIOCFEEDBACK	_IOW('B',124, u_int)
+#define BIOCSETFNR	_IOW('B',125, struct bpf_program)
 
 /* Obsolete */
 #define	BIOCGSEESENT	BIOCGDIRECTION
--- share/man/man4/bpf.4.orig	Fri Nov 23 09:39:01 2007
+++ share/man/man4/bpf.4	Fri Nov 23 09:57:33 2007
@@ -230,6 +230,7 @@
 which must respond to messages in real time.
 The default for a new file is off.
 .It Dv BIOCSETF
+.It Dv BIOCSETFNR
 .Pq Li "struct bpf_program"
 Sets the read filter program used by the kernel to discard uninteresting
 packets.
@@ -249,12 +250,21 @@
 is given by the
 .Li bf_len
 field.
-Also, the actions of
-.Dv BIOCFLUSH
-are performed.
 See section
 .Sx "FILTER MACHINE"
 for an explanation of the filter language.
+.Pp
+The only difference between
+.Dv BIOCSETF
+and
+.Dv BIOCSETFNR
+is
+.Dv BIOCSETF
+performs the actions of
+.Dv BIOCFLUSH
+while
+.Dv BIOCSETFNR
+does not.
 .It Dv BIOCSETWF
 .Pq Li "struct bpf_program"
 Sets the write filter program used by the kernel to control what type of
--- bpf-setfnr.patch ends here ---


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->dwmalone 
Responsible-Changed-By: dwmalone 
Responsible-Changed-When: Sun Dec 9 15:36:25 UTC 2007 
Responsible-Changed-Why:  
I'm familar with the problem here - so I'll take a look. If anyone 
else would prefer to take a look, feel free to grab the PR. 

David. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=118486 
State-Changed-From-To: open->analyzed 
State-Changed-By: rwatson 
State-Changed-When: Tue May 20 10:10:09 UTC 2008 
State-Changed-Why:  
This sounds like a reasonable idea, and I agree about not resetting stats 
and the buffer when changing the write filter.  My primary comment is that 
I think I'd rather not see bpf_setf() duplicated, as that will encourage 
bugs -- what do you think of the idea if passing an "int reset" flag as an 
argument to bpf_setf() and keying reset activities to the flag instead? 



Responsible-Changed-From-To: dwmalone->rwatson 
Responsible-Changed-By: rwatson 
Responsible-Changed-When: Tue May 20 10:10:09 UTC 2008 
Responsible-Changed-Why:  
Grab ownership as Matthew pinged me about this. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=118486 

From: Matthew Luckie <mluckie@cs.waikato.ac.nz>
To: rwatson@FreeBSD.org
Cc: dwmalone@FreeBSD.org, bug-followup@freebsd.org
Subject: Re: kern/118486: [bpf] [patch] change BPF filters without discarding buffered packets
Date: Thu, 22 May 2008 22:28:40 +1200

 On Tue, May 20, 2008 at 10:12:41AM +0000, rwatson@FreeBSD.org wrote:
 > This sounds like a reasonable idea, and I agree about not resetting stats
 > and the buffer when changing the write filter.  My primary comment is that
 > I think I'd rather not see bpf_setf() duplicated, as that will encourage
 > bugs -- what do you think of the idea if passing an "int reset" flag as an
 > argument to bpf_setf() and keying reset activities to the flag instead?
 
 I've redone the patch as requested.  a reset flag to bpf_setf is
 unnecessary as the ioctl cmd is already passed.
 
 Patch against RELENG_7
 
 --- sys/net/bpf.c.orig	2008-05-22 21:51:46.000000000 +1200
 +++ sys/net/bpf.c	2008-05-22 22:05:58.000000000 +1200
 @@ -681,7 +681,6 @@
  static void
  reset_d(struct bpf_d *d)
  {
 -
  	mtx_assert(&d->bd_mtx, MA_OWNED);
  	if (d->bd_hbuf) {
  		/* Free the hold buffer. */
 @@ -699,8 +698,9 @@
   *  FIONREAD		Check for read packet available.
   *  SIOCGIFADDR		Get interface address - convenient hook to driver.
   *  BIOCGBLEN		Get buffer len [for read()].
 - *  BIOCSETF		Set ethernet read filter.
 - *  BIOCSETWF		Set ethernet write filter.
 + *  BIOCSETF		Set read filter.
 + *  BIOCSETFNR		Set read filter without resetting descriptor.
 + *  BIOCSETWF		Set write filter.
   *  BIOCFLUSH		Flush read packet buffer.
   *  BIOCPROMISC		Put interface into promiscuous mode.
   *  BIOCGDLT		Get link layer type.
 @@ -823,6 +823,7 @@
  	 * Set link layer read filter.
  	 */
  	case BIOCSETF:
 +	case BIOCSETFNR:
  	case BIOCSETWF:
  		error = bpf_setf(d, (struct bpf_program *)addr, cmd);
  		break;
 @@ -1092,8 +1093,9 @@
  #ifdef BPF_JITTER
  			d->bd_bfilter = NULL;
  #endif
 +			if (cmd == BIOCSETF)
 +				reset_d(d);
  		}
 -		reset_d(d);
  		BPFD_UNLOCK(d);
  		if (old != NULL)
  			free((caddr_t)old, M_BPF);
 @@ -1119,8 +1121,9 @@
  #ifdef BPF_JITTER
  			d->bd_bfilter = bpf_jitter(fcode, flen);
  #endif
 +			if (cmd == BIOCSETF)
 +				reset_d(d);
  		}
 -		reset_d(d);
  		BPFD_UNLOCK(d);
  		if (old != NULL)
  			free((caddr_t)old, M_BPF);
 --- sys/net/bpf.h.orig	2008-05-22 21:58:39.000000000 +1200
 +++ sys/net/bpf.h	2008-05-22 21:59:16.000000000 +1200
 @@ -116,6 +116,7 @@
  #define	BIOCLOCK	_IO('B', 122)
  #define	BIOCSETWF	_IOW('B',123, struct bpf_program)
  #define	BIOCFEEDBACK	_IOW('B',124, u_int)
 +#define BIOCSETFNR	_IOW('B',125, struct bpf_program)
  
  /* Obsolete */
  #define	BIOCGSEESENT	BIOCGDIRECTION
 --- share/man/man4/bpf.4.orig	2008-05-22 21:59:56.000000000 +1200
 +++ share/man/man4/bpf.4	2008-05-22 22:01:21.000000000 +1200
 @@ -230,6 +230,7 @@
  which must respond to messages in real time.
  The default for a new file is off.
  .It Dv BIOCSETF
 +.It Dv BIOCSETFNR
  .Pq Li "struct bpf_program"
  Sets the read filter program used by the kernel to discard uninteresting
  packets.
 @@ -249,12 +250,20 @@
  is given by the
  .Li bf_len
  field.
 -Also, the actions of
 -.Dv BIOCFLUSH
 -are performed.
  See section
  .Sx "FILTER MACHINE"
  for an explanation of the filter language.
 +The only difference between
 +.Dv BIOCSETF
 +and
 +.Dv BIOCSETFNR
 +is
 +.Dv BIOCSETF
 +performs the actions of
 +.Dv BIOCFLUSH
 +while
 +.Dv BIOCSETFNR
 +does not.
  .It Dv BIOCSETWF
  .Pq Li "struct bpf_program"
  Sets the write filter program used by the kernel to control what type of
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/118486: commit references a PR
Date: Mon,  7 Jul 2008 09:26:16 +0000 (UTC)

 dwmalone    2008-07-07 09:25:49 UTC
 
   FreeBSD src repository
 
   Modified files:
     sys/net              bpf.c bpf.h 
   Log:
   SVN rev 180337 on 2008-07-07 09:25:49Z by dwmalone
   
   Add a new ioctl for changing the read filter (BIOCSETFNR). This is
   just like BIOCSETF but it doesn't drop all the packets buffered on
   the discriptor and reset the statistics.
   
   Also, when setting the write filter, don't drop packets waiting to
   be read or reset the statistics.
   
   PR:             118486
   Submitted by:   Matthew Luckie <mluckie@cs.waikato.ac.nz>
   MFC after:      1 month
   
   Revision  Changes    Path
   1.197     +8 -4      src/sys/net/bpf.c
   1.51      +1 -0      src/sys/net/bpf.h
 _______________________________________________
 cvs-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/cvs-all
 To unsubscribe, send any mail to "cvs-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: analyzed->patched 
State-Changed-By: rwatson 
State-Changed-When: Sun Aug 3 16:18:37 UTC 2008 
State-Changed-Why:  
Transition to patched, as has been committed to HEAD.  Will close 
following an MFC. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=118486 
Responsible-Changed-From-To: rwatson->dwmalone 
Responsible-Changed-By: rwatson 
Responsible-Changed-When: Sun Aug 3 16:19:02 UTC 2008 
Responsible-Changed-Why:  
Assign to dwmalone as he has committed this. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=118486 
State-Changed-From-To: patched->closed 
State-Changed-By: dwmalone 
State-Changed-When: Sat Aug 16 12:13:40 UTC 2008 
State-Changed-Why:  
Committed to -current and RELENG_7. Thanks for the patch. 

David. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=118486 
Responsible-Changed-From-To: dwmalone->freebsd-bugs 
Responsible-Changed-By: eadler 
Responsible-Changed-When: Tue Jul 10 03:39:48 UTC 2012 
Responsible-Changed-Why:  
over to the pool (approved by bugmeister) 

http://www.freebsd.org/cgi/query-pr.cgi?pr=118486 
>Unformatted:
