From: David Gwynne Subject: Re: [patch] change BPF filters without discarding buffered packets To: Matthew Luckie , tech@openbsd.org Date: Fri, 9 Aug 2024 14:45:34 +1000 This makes sense to me and reads well too. What are you using it for? On 24/07/2024 15:11, Matthew Luckie wrote: > Hi, > > BIOCSETF (change BPF read filter) and BIOCSETWF (change BPF write > filter) call bpf_resetd, 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 read > filter without resetting the hold buffer and without resetting the > packet rx counts, which is handy when the application wants to > dynamically adjust its filter program, without discarding whatever the > system might have buffered, yet to be delivered to userspace. > > I've also changed BIOCSETWF to map to the new function. I don't see > the rationale in having BIOCSETWF (which is about changing write > filters) resetting the receive stats and read hold buffer. > > Patch is against HEAD. I've tested it on 7.5. The patch includes > modifications to bpf(4) manual page. FreeBSD and MacOS have the same > ioctl. I initially did the work for FreeBSD. > > https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=118486 > > Matthew > > diff --git share/man/man4/bpf.4 share/man/man4/bpf.4 > index 46429f1cde5..7a884c727bf 100644 > --- share/man/man4/bpf.4 > +++ share/man/man4/bpf.4 > @@ -317,6 +317,7 @@ the age of packets in the kernel buffer does not make the buffer > readable. > .Pp > .It Dv BIOCSETF Fa "struct bpf_program *" > +.It Dv BIOCSETFNR Fa "struct bpf_program *" > Sets the filter program used by the kernel to discard uninteresting packets. > An array of instructions and its length are passed in using the following > structure: > @@ -334,9 +335,11 @@ field, while its length in units of > is given by the > .Fa bf_len > field. > -Also, the actions of > +If > +.Dv BIOCSETF > +is used, the actions of > .Dv BIOCFLUSH > -are performed. > +are also performed. > .Pp > See section > .Sx FILTER MACHINE > @@ -349,8 +352,6 @@ network. > See > .Dv BIOCSETF > for a description of the filter program. > -This ioctl also acts as > -.Dv BIOCFLUSH . > .Pp > Note that the filter operates on the packet data written to the descriptor. > If the > diff --git sys/net/bpf.c sys/net/bpf.c > index 51a1ed66ff4..8cd74c5bbec 100644 > --- sys/net/bpf.c > +++ sys/net/bpf.c > @@ -758,7 +758,8 @@ bpf_get_wtimeout(struct bpf_d *d, struct timeval *tv) > /* > * FIONREAD Check for read packet available. > * BIOCGBLEN Get buffer len [for read()]. > - * BIOCSETF Set ethernet read filter. > + * BIOCSETF Set read filter. > + * BIOCSETFNR Set read filter without resetting descriptor. > * BIOCFLUSH Flush read packet buffer. > * BIOCPROMISC Put interface into promiscuous mode. > * BIOCGDLTLIST Get supported link layer types. > @@ -863,17 +864,12 @@ bpfioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) > break; > > /* > - * Set link layer read filter. > + * Set link layer read/write filter. > */ > case BIOCSETF: > - error = bpf_setf(d, (struct bpf_program *)addr, 0); > - break; > - > - /* > - * Set link layer write filter. > - */ > + case BIOCSETFNR: > case BIOCSETWF: > - error = bpf_setf(d, (struct bpf_program *)addr, 1); > + error = bpf_setf(d, (struct bpf_program *)addr, cmd); > break; > > /* > @@ -1118,7 +1114,7 @@ bpfioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) > * free it and replace it. Returns EINVAL for bogus requests. > */ > int > -bpf_setf(struct bpf_d *d, struct bpf_program *fp, int wf) > +bpf_setf(struct bpf_d *d, struct bpf_program *fp, u_long cmd) > { > struct bpf_program_smr *bps, *old_bps; > struct bpf_insn *fcode; > @@ -1153,7 +1149,7 @@ bpf_setf(struct bpf_d *d, struct bpf_program *fp, int wf) > bps->bps_bf.bf_insns = fcode; > } > > - if (wf == 0) { > + if (cmd != BIOCSETWF) { > old_bps = SMR_PTR_GET_LOCKED(&d->bd_rfilter); > SMR_PTR_SET_LOCKED(&d->bd_rfilter, bps); > } else { > @@ -1161,9 +1157,12 @@ bpf_setf(struct bpf_d *d, struct bpf_program *fp, int wf) > SMR_PTR_SET_LOCKED(&d->bd_wfilter, bps); > } > > - mtx_enter(&d->bd_mtx); > - bpf_resetd(d); > - mtx_leave(&d->bd_mtx); > + if (cmd == BIOCSETF) { > + mtx_enter(&d->bd_mtx); > + bpf_resetd(d); > + mtx_leave(&d->bd_mtx); > + } > + > if (old_bps != NULL) > smr_call(&old_bps->bps_smr, bpf_prog_smr, old_bps); > > diff --git sys/net/bpf.h sys/net/bpf.h > index ab64061fb97..f35ab0d628b 100644 > --- sys/net/bpf.h > +++ sys/net/bpf.h > @@ -122,6 +122,7 @@ struct bpf_version { > #define BIOCSWTIMEOUT _IOW('B',126, struct timeval) > #define BIOCGWTIMEOUT _IOR('B',126, struct timeval) > #define BIOCDWTIMEOUT _IO('B',126) > +#define BIOCSETFNR _IOW('B',127, struct bpf_program) > > /* > * Direction filters for BIOCSDIRFILT/BIOCGDIRFILT > diff --git sys/net/bpfdesc.h sys/net/bpfdesc.h > index 76b75ff5a20..2f18d734e89 100644 > --- sys/net/bpfdesc.h > +++ sys/net/bpfdesc.h > @@ -123,6 +123,6 @@ struct bpf_if { > struct ifnet *bif_ifp; /* corresponding interface */ > }; > > -int bpf_setf(struct bpf_d *, struct bpf_program *, int); > +int bpf_setf(struct bpf_d *, struct bpf_program *, u_long); > #endif /* _KERNEL */ > #endif /* _NET_BPFDESC_H_ */ >