diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -4184,6 +4184,7 @@ net/bpf_jitter.c optional bpf_jitter net/bpf_filter.c optional bpf | netgraph_bpf net/bpf_zerocopy.c optional bpf +net/bpf_ifnet.c optional bpf net/bridgestp.c optional bridge | if_bridge net/dummymbuf.c optional dummymbuf net/ieee8023ad_lacp.c optional lagg diff --git a/sys/net/bpf.h b/sys/net/bpf.h --- a/sys/net/bpf.h +++ b/sys/net/bpf.h @@ -408,8 +408,12 @@ * bpf_peers_present() calls. */ struct bpf_if; +struct bif_methods; CK_LIST_HEAD(bpfd_list, bpf_d); +struct bpf_if * bpf_attach(const char *, u_int, u_int, + const struct bif_methods *, void *); +void bpf_detach(struct bpf_if *); void bpf_bufheld(struct bpf_d *d); int bpf_validate(const struct bpf_insn *, int); void bpf_tap(struct bpf_if *, u_char *, u_int); @@ -419,7 +423,6 @@ void bpf_mtap2(struct bpf_if *, void *, u_int, struct mbuf *); void bpf_mtap2_if(struct ifnet *, void *, u_int, struct mbuf *); void bpfattach(struct ifnet *, u_int, u_int); -void bpfattach2(struct ifnet *, u_int, u_int, struct bpf_if **); void bpfdetach(struct ifnet *); bool bpf_peers_present_if(struct ifnet *); #ifdef VIMAGE @@ -444,6 +447,26 @@ bpf_mtap_if((_ifp), (_m)) #define BPF_MTAP2(_ifp, _data, _dlen, _m) \ bpf_mtap2_if((_ifp), (_data), (_dlen), (_m)) + +typedef void bif_attachd_t(void *); +typedef void bif_detachd_t(void *); +typedef bool bif_chkdir_t(void *, const struct mbuf *, int); +typedef int bif_write_t(void *, struct mbuf *, struct mbuf *, int); +typedef uint32_t bif_wrsize_t(void *); +typedef int bif_promisc_t(void *, bool); +struct bif_methods { + bif_attachd_t *bif_attachd; + bif_detachd_t *bif_detachd; + bif_chkdir_t *bif_chkdir; + bif_promisc_t *bif_promisc; + /* Writable taps shall implement the below methods. */ + bif_write_t *bif_write; + bif_wrsize_t *bif_wrsize; +}; + +/* Ifnet methods implemented in bpf_ifnet.c are shared with net80211. */ +extern bif_wrsize_t bpf_ifnet_wrsize; +extern bif_promisc_t bpf_ifnet_promisc; #endif /* _KERNEL */ /* diff --git a/sys/net/bpf.c b/sys/net/bpf.c --- a/sys/net/bpf.c +++ b/sys/net/bpf.c @@ -37,12 +37,10 @@ #include #include "opt_bpf.h" -#include "opt_ddb.h" #include "opt_netgraph.h" #include #include -#include #include #include #include @@ -61,22 +59,16 @@ #include #include -#include #include #include #include #include -#ifdef DDB -#include -#endif - #include #include #include #include -#include #include #include #ifdef BPF_JITTER @@ -84,31 +76,25 @@ #endif #include #include -#include #include -#include -#include #include #include -#include - #include MALLOC_DEFINE(M_BPF, "BPF", "BPF data"); -static const struct bpfd_list dead_bpf_if = CK_LIST_HEAD_INITIALIZER(); - struct bpf_if { struct bpfd_list bif_dlist; /* list of all interfaces */ LIST_ENTRY(bpf_if) bif_next; /* descriptor list */ u_int bif_dlt; /* link layer type */ u_int bif_hdrlen; /* length of link header */ - struct bpfd_list bif_wlist; /* writer-only list */ - struct ifnet *bif_ifp; /* corresponding interface */ - struct bpf_if **bif_bpf; /* Pointer to pointer to us */ volatile u_int bif_refcnt; + struct bpfd_list bif_wlist; /* writer-only list */ + const struct bif_methods *bif_methods; + void *bif_softc; + const char *bif_name; struct epoch_context epoch_ctx; }; @@ -116,6 +102,49 @@ _Static_assert(offsetof(struct bpf_if, bif_dlist) == 0, "bpf_if shall start with bif_dlist"); +static inline void +bif_attachd(struct bpf_if *bp) +{ + if (bp->bif_methods->bif_attachd != NULL) + bp->bif_methods->bif_attachd(bp->bif_softc); +} + +static inline void +bif_detachd(struct bpf_if *bp) +{ + if (bp->bif_methods->bif_detachd != NULL) + bp->bif_methods->bif_detachd(bp->bif_softc); +} + +static inline uint32_t +bif_wrsize(struct bpf_if *bp) +{ + if (bp->bif_methods->bif_wrsize != NULL) + return (bp->bif_methods->bif_wrsize(bp->bif_softc)); + else + return (0); +} + +static inline int +bif_promisc(struct bpf_if *bp, bool on) +{ + if (bp->bif_methods->bif_promisc != NULL) + return (bp->bif_methods->bif_promisc(bp->bif_softc, on)); + else + return (0); +} + +/* + * XXXGL: Once we migrate to tapping KPI that would specify packet direction + * we no longer need bif_chkdir method. + */ +static inline bool +bpf_chkdir(struct bpf_d *d, struct mbuf *m) +{ + return (d->bd_bif->bif_methods->bif_chkdir(d->bd_bif->bif_softc, m, + d->bd_direction)); +} + struct bpf_program_buffer { struct epoch_context epoch_ctx; #ifdef BPF_JITTER @@ -189,12 +218,9 @@ static void bpfd_ref(struct bpf_d *); static void bpfd_rele(struct bpf_d *); -static void bpf_attachd(struct bpf_d *, struct bpf_if *); +static int bpf_attachd(struct bpf_d *d, struct bpf_if *); static void bpf_detachd(struct bpf_d *, bool); static void bpfd_free(epoch_context_t); -static int bpf_movein(struct uio *, int, struct ifnet *, struct mbuf **, - struct sockaddr *, int *, struct bpf_d *); -static int bpf_setif(struct bpf_d *, struct ifreq *); static void bpf_timed_out(void *); static __inline void bpf_wakeup(struct bpf_d *); @@ -303,7 +329,6 @@ struct bpf_if *bp; bp = __containerof(ctx, struct bpf_if, epoch_ctx); - if_rele(bp->bif_ifp); free(bp, M_BPF); } @@ -557,221 +582,6 @@ return (bpf_zerocopy_ioctl_setzbuf(td, d, bz)); } -/* - * General BPF functions. - */ -static int -bpf_movein(struct uio *uio, int linktype, struct ifnet *ifp, struct mbuf **mp, - struct sockaddr *sockp, int *hdrlen, struct bpf_d *d) -{ - const struct ieee80211_bpf_params *p; - struct ether_header *eh; - struct mbuf *m; - int error; - int len; - int hlen; - int slen; - - /* - * Build a sockaddr based on the data link layer type. - * We do this at this level because the ethernet header - * is copied directly into the data field of the sockaddr. - * In the case of SLIP, there is no header and the packet - * is forwarded as is. - * Also, we are careful to leave room at the front of the mbuf - * for the link level header. - */ - switch (linktype) { - case DLT_SLIP: - sockp->sa_family = AF_INET; - hlen = 0; - break; - - case DLT_EN10MB: - sockp->sa_family = AF_UNSPEC; - /* XXX Would MAXLINKHDR be better? */ - hlen = ETHER_HDR_LEN; - break; - - case DLT_FDDI: - sockp->sa_family = AF_IMPLINK; - hlen = 0; - break; - - case DLT_RAW: - sockp->sa_family = AF_UNSPEC; - hlen = 0; - break; - - case DLT_NULL: - /* - * null interface types require a 4 byte pseudo header which - * corresponds to the address family of the packet. - */ - sockp->sa_family = AF_UNSPEC; - hlen = 4; - break; - - case DLT_ATM_RFC1483: - /* - * en atm driver requires 4-byte atm pseudo header. - * though it isn't standard, vpi:vci needs to be - * specified anyway. - */ - sockp->sa_family = AF_UNSPEC; - hlen = 12; /* XXX 4(ATM_PH) + 3(LLC) + 5(SNAP) */ - break; - - case DLT_PPP: - sockp->sa_family = AF_UNSPEC; - hlen = 4; /* This should match PPP_HDRLEN */ - break; - - case DLT_IEEE802_11: /* IEEE 802.11 wireless */ - sockp->sa_family = AF_IEEE80211; - hlen = 0; - break; - - case DLT_IEEE802_11_RADIO: /* IEEE 802.11 wireless w/ phy params */ - sockp->sa_family = AF_IEEE80211; - sockp->sa_len = 12; /* XXX != 0 */ - hlen = sizeof(struct ieee80211_bpf_params); - break; - - default: - return (EIO); - } - - len = uio->uio_resid; - if (len < hlen || len - hlen > ifp->if_mtu) - return (EMSGSIZE); - - /* Allocate a mbuf, up to MJUM16BYTES bytes, for our write. */ - m = m_get3(len, M_WAITOK, MT_DATA, M_PKTHDR); - if (m == NULL) - return (EIO); - m->m_pkthdr.len = m->m_len = len; - *mp = m; - - error = uiomove(mtod(m, u_char *), len, uio); - if (error) - goto bad; - - slen = bpf_filter(d->bd_wfilter, mtod(m, u_char *), len, len); - if (slen == 0) { - error = EPERM; - goto bad; - } - - /* Check for multicast destination */ - switch (linktype) { - case DLT_EN10MB: - eh = mtod(m, struct ether_header *); - if (ETHER_IS_MULTICAST(eh->ether_dhost)) { - if (bcmp(ifp->if_broadcastaddr, eh->ether_dhost, - ETHER_ADDR_LEN) == 0) - m->m_flags |= M_BCAST; - else - m->m_flags |= M_MCAST; - } - if (!(d->bd_flags & BPFD_HDRCMPLT)) { - memcpy(eh->ether_shost, IF_LLADDR(ifp), - sizeof(eh->ether_shost)); - } - break; - } - - /* - * Make room for link header, and copy it to sockaddr - */ - if (hlen != 0) { - if (sockp->sa_family == AF_IEEE80211) { - /* - * Collect true length from the parameter header - * NB: sockp is known to be zero'd so if we do a - * short copy unspecified parameters will be - * zero. - * NB: packet may not be aligned after stripping - * bpf params - * XXX check ibp_vers - */ - p = mtod(m, const struct ieee80211_bpf_params *); - hlen = p->ibp_len; - if (hlen > sizeof(sockp->sa_data)) { - error = EINVAL; - goto bad; - } - } - bcopy(mtod(m, const void *), sockp->sa_data, hlen); - } - *hdrlen = hlen; - - return (0); -bad: - m_freem(m); - return (error); -} - -/* - * Attach descriptor to the bpf interface, i.e. make d listen on bp, - * then reset its buffers and counters with reset_d(). - */ -static void -bpf_attachd(struct bpf_d *d, struct bpf_if *bp) -{ - int op_w; - - BPF_LOCK_ASSERT(); - - /* - * Save sysctl value to protect from sysctl change - * between reads - */ - op_w = V_bpf_optimize_writers || d->bd_writer; - - if (d->bd_bif != NULL) - bpf_detachd(d, false); - /* - * Point d at bp, and add d to the interface's list. - * Since there are many applications using BPF for - * sending raw packets only (dhcpd, cdpd are good examples) - * we can delay adding d to the list of active listeners until - * some filter is configured. - */ - - BPFD_LOCK(d); - /* - * Hold reference to bpif while descriptor uses this interface. - */ - bpfif_ref(bp); - d->bd_bif = bp; - if (op_w != 0) { - /* Add to writers-only list */ - CK_LIST_INSERT_HEAD(&bp->bif_wlist, d, bd_next); - /* - * We decrement bd_writer on every filter set operation. - * First BIOCSETF is done by pcap_open_live() to set up - * snap length. After that appliation usually sets its own - * filter. - */ - d->bd_writer = 2; - } else - CK_LIST_INSERT_HEAD(&bp->bif_dlist, d, bd_next); - - reset_d(d); - - /* Trigger EVFILT_WRITE events. */ - bpf_wakeup(d); - - BPFD_UNLOCK(d); - - CTR3(KTR_NET, "%s: bpf_attach called by pid %d, adding to %s list", - __func__, d->bd_pid, d->bd_writer ? "writer" : "active"); - - if (op_w == 0) - EVENTHANDLER_INVOKE(bpf_track, bp->bif_ifp, bp->bif_dlt, 1); -} - /* * Check if we need to upgrade our descriptor @d from write-only mode. */ @@ -842,8 +652,7 @@ bpf_detachd(struct bpf_d *d, bool detached_ifp) { struct bpf_if *bp; - struct ifnet *ifp; - int error; + bool writer; BPF_LOCK_ASSERT(); CTR2(KTR_NET, "%s: detach required by pid %d", __func__, d->bd_pid); @@ -853,11 +662,8 @@ return; BPFD_LOCK(d); - /* Remove d from the interface's descriptor list. */ CK_LIST_REMOVE(d, bd_next); - /* Save bd_writer value */ - error = d->bd_writer; - ifp = bp->bif_ifp; + writer = (d->bd_writer > 0); d->bd_bif = NULL; if (detached_ifp) { /* @@ -868,30 +674,14 @@ } BPFD_UNLOCK(d); - /* Call event handler iff d is attached */ - if (error == 0) - EVENTHANDLER_INVOKE(bpf_track, ifp, bp->bif_dlt, 0); + if (!writer) + bif_detachd(bp); - /* - * Check if this descriptor had requested promiscuous mode. - * If so and ifnet is not detached, turn it off. - */ if ((d->bd_flags & BPFD_PROMISC) && !detached_ifp) { d->bd_flags &= ~BPFD_PROMISC; - CURVNET_SET(ifp->if_vnet); - error = ifpromisc(ifp, 0); - CURVNET_RESTORE(); - if (error != 0 && error != ENXIO) { - /* - * ENXIO can happen if a pccard is unplugged - * Something is really wrong if we were able to put - * the driver into promiscuous mode, but can't - * take it out. - */ - if_printf(bp->bif_ifp, - "bpf_detach: ifpromisc failed (%d)\n", error); - } + (void)bif_promisc(bp, false); } + bpfif_rele(bp); } @@ -1169,128 +959,97 @@ static int bpfwrite(struct cdev *dev, struct uio *uio, int ioflag) { - struct route ro; - struct sockaddr dst; struct epoch_tracker et; struct bpf_if *bp; struct bpf_d *d; - struct ifnet *ifp; struct mbuf *m, *mc; - int error, hlen; + ssize_t len; + int error; error = devfs_get_cdevpriv((void **)&d); if (error != 0) return (error); - NET_EPOCH_ENTER(et); + if (uio->uio_resid == 0) + return (0); + BPFD_LOCK(d); - BPF_PID_REFRESH_CUR(d); - counter_u64_add(d->bd_wcount, 1); - if ((bp = d->bd_bif) == NULL) { + if ((bp = d->bd_bif) == NULL) error = ENXIO; - goto out_locked; - } - - ifp = bp->bif_ifp; - if ((ifp->if_flags & IFF_UP) == 0) { - error = ENETDOWN; - goto out_locked; + else if (bp->bif_methods->bif_write == NULL) + error = EOPNOTSUPP; + if (error) { + BPFD_UNLOCK(d); + counter_u64_add(d->bd_wdcount, 1); + return (error); } - - if (uio->uio_resid == 0) - goto out_locked; - - bzero(&dst, sizeof(dst)); - m = NULL; - hlen = 0; - - /* - * Take extra reference, unlock d and exit from epoch section, - * since bpf_movein() can sleep. - */ bpfd_ref(d); - NET_EPOCH_EXIT(et); BPFD_UNLOCK(d); - error = bpf_movein(uio, (int)bp->bif_dlt, ifp, - &m, &dst, &hlen, d); - - if (error != 0) { - counter_u64_add(d->bd_wdcount, 1); - bpfd_rele(d); - return (error); + len = uio->uio_resid; + /* Allocate a mbuf, up to MJUM16BYTES bytes, for our write. */ + m = m_get3(len, M_WAITOK, MT_DATA, M_PKTHDR); + if (m == NULL) { + error = ENOMEM; + goto fail_wref; } + m->m_pkthdr.len = m->m_len = len; - BPFD_LOCK(d); - /* - * Check that descriptor is still attached to the interface. - * This can happen on bpfdetach(). To avoid access to detached - * ifnet, free mbuf and return ENXIO. - */ - if (d->bd_bif == NULL) { - counter_u64_add(d->bd_wdcount, 1); - BPFD_UNLOCK(d); - bpfd_rele(d); - m_freem(m); - return (ENXIO); + error = uiomove(mtod(m, u_char *), len, uio); + if (error) + goto fail_wref; + + if (bpf_filter(d->bd_wfilter, mtod(m, u_char *), len, len) == 0) { + error = EPERM; + goto fail_wref; } - counter_u64_add(d->bd_wfcount, 1); - if (d->bd_flags & BPFD_HDRCMPLT) - dst.sa_family = pseudo_AF_HDRCMPLT; if (d->bd_flags & BPFD_FEEDBACK) { - mc = m_dup(m, M_NOWAIT); - if (mc != NULL) - mc->m_pkthdr.rcvif = ifp; + mc = m_dup(m, M_WAITOK); /* Set M_PROMISC for outgoing packets to be discarded. */ if (d->bd_direction == BPF_D_INOUT) m->m_flags |= M_PROMISC; } else mc = NULL; - m->m_pkthdr.len -= hlen; - m->m_len -= hlen; - m->m_data += hlen; /* XXX */ + /* XXXGL: should belong to bpf_ifnet.c */ + if (d->bd_pcp != 0) + (void)vlan_set_pcp(m, d->bd_pcp); - CURVNET_SET(ifp->if_vnet); #ifdef MAC mac_bpfdesc_create_mbuf(d, m); if (mc != NULL) mac_bpfdesc_create_mbuf(d, mc); #endif - bzero(&ro, sizeof(ro)); - if (hlen != 0) { - ro.ro_prepend = (u_char *)&dst.sa_data; - ro.ro_plen = hlen; - ro.ro_flags = RT_HAS_HEADER; + BPFD_LOCK(d); + /* + * Check that descriptor is still attached to the interface. + * This can happen on bpfdetach() or if other thread did BIOCSDLT. + */ + if (__predict_false(d->bd_bif != bp)) { + BPFD_UNLOCK(d); + m_freem(mc); + error = ENXIO; + goto fail_wref; } + BPFD_UNLOCK(d); - if (d->bd_pcp != 0) - vlan_set_pcp(m, d->bd_pcp); - - /* Avoid possible recursion on BPFD_LOCK(). */ NET_EPOCH_ENTER(et); - BPFD_UNLOCK(d); - error = (*ifp->if_output)(ifp, m, &dst, &ro); + error = bp->bif_methods->bif_write(bp->bif_softc, m, mc, d->bd_flags); + NET_EPOCH_EXIT(et); if (error) counter_u64_add(d->bd_wdcount, 1); - - if (mc != NULL) { - if (error == 0) - (*ifp->if_input)(ifp, mc); - else - m_freem(mc); - } - NET_EPOCH_EXIT(et); - CURVNET_RESTORE(); + else + counter_u64_add(d->bd_wfcount, 1); bpfd_rele(d); + return (error); -out_locked: +fail_wref: counter_u64_add(d->bd_wdcount, 1); - NET_EPOCH_EXIT(et); - BPFD_UNLOCK(d); + bpfd_rele(d); + m_freem(m); return (error); } @@ -1511,8 +1270,9 @@ */ error = EINVAL; } else if (!(d->bd_flags & BPFD_PROMISC)) { - error = ifpromisc(d->bd_bif->bif_ifp, 1); - if (error == 0) + struct bpf_if *bp = d->bd_bif; + + if ((error = bif_promisc(bp, true)) == 0) d->bd_flags |= BPFD_PROMISC; } BPF_UNLOCK(); @@ -1584,10 +1344,10 @@ if (d->bd_bif == NULL) error = EINVAL; else { - struct ifnet *const ifp = d->bd_bif->bif_ifp; + struct bpf_if *const bp = d->bd_bif; struct ifreq *const ifr = (struct ifreq *)addr; - strlcpy(ifr->ifr_name, ifp->if_xname, + strlcpy(ifr->ifr_name, bp->bif_name, sizeof(ifr->ifr_name)); } BPF_UNLOCK(); @@ -1596,7 +1356,10 @@ /* * Set interface. */ - case BIOCSETIF: + case BIOCSETIF: { + struct ifreq *const ifr = (struct ifreq *)addr; + struct bpf_if *bp; + /* * Behavior here depends on the buffering model. If we're * using kernel memory buffers, then we can allocate them here. @@ -1616,10 +1379,21 @@ } else BPFD_UNLOCK(d); BPF_LOCK(); - error = bpf_setif(d, (struct ifreq *)addr); + /* + * Look through attached interfaces for the named one. + */ + LIST_FOREACH(bp, &bpf_iflist, bif_next) { + if (strncmp(ifr->ifr_name, bp->bif_name, + sizeof(ifr->ifr_name)) == 0) + break; + } + if (bp != NULL) + error = bpf_attachd(d, bp); + else + error = ENXIO; BPF_UNLOCK(); break; - + } /* * Set read timeout. */ @@ -2056,41 +1830,24 @@ NET_EPOCH_CALL(bpf_program_buffer_free, &fcode->epoch_ctx); if (track_event) - EVENTHANDLER_INVOKE(bpf_track, - d->bd_bif->bif_ifp, d->bd_bif->bif_dlt, 1); + bif_attachd(d->bd_bif); BPF_UNLOCK(); return (0); } /* - * Detach a file from its current interface (if attached at all) and attach - * to the interface indicated by the name stored in ifr. - * Return an errno or 0. + * Attach descriptor to a tap point, possibly detaching from the old one, + * reset the counters. + * XXXGL: this KPI is subject to change */ static int -bpf_setif(struct bpf_d *d, struct ifreq *ifr) +bpf_attachd(struct bpf_d *d, struct bpf_if *bp) { - struct bpf_if *bp; - struct ifnet *theywant; + bool writer; BPF_LOCK_ASSERT(); - theywant = ifunit(ifr->ifr_name); - if (theywant == NULL) - return (ENXIO); - /* - * Look through attached interfaces for the named one. - */ - LIST_FOREACH(bp, &bpf_iflist, bif_next) { - if (bp->bif_ifp == theywant && - bp->bif_bpf == &theywant->if_bpf) - break; - } - if (bp == NULL) - return (ENXIO); - - MPASS(bp == theywant->if_bpf); /* * At this point, we expect the buffer is already allocated. If not, * return an error. @@ -2103,15 +1860,61 @@ break; default: - panic("bpf_setif: bufmode %d", d->bd_bufmode); + panic("%s: bufmode %d", __func__, d->bd_bufmode); } - if (bp != d->bd_bif) - bpf_attachd(d, bp); - else { + + if (bp == d->bd_bif) { BPFD_LOCK(d); reset_d(d); BPFD_UNLOCK(d); - } + return (0); + } else if (d->bd_bif != NULL) + bpf_detachd(d, false); + + /* + * Save sysctl value to protect from sysctl change between reads. + */ + writer = V_bpf_optimize_writers || (d->bd_writer > 0); + + /* + * Point d at bp, and add d to the interface's list. + * Since there are many applications using BPF for + * sending raw packets only (dhcpd, cdpd are good examples) + * we can delay adding d to the list of active listeners until + * some filter is configured. + */ + BPFD_LOCK(d); + /* + * Hold reference to bpif while descriptor uses this interface. + */ + bpfif_ref(bp); + d->bd_bif = bp; + if (writer) { + /* Add to writers-only list */ + CK_LIST_INSERT_HEAD(&bp->bif_wlist, d, bd_next); + /* + * We decrement bd_writer on every filter set operation. + * First BIOCSETF is done by pcap_open_live() to set up + * snap length. After that application usually sets its own + * filter. + */ + d->bd_writer = 2; + } else + CK_LIST_INSERT_HEAD(&bp->bif_dlist, d, bd_next); + + reset_d(d); + + /* Trigger EVFILT_WRITE events. */ + bpf_wakeup(d); + + BPFD_UNLOCK(d); + + CTR3(KTR_NET, "%s: called by pid %d, adding to %s list", + __func__, d->bd_pid, d->bd_writer ? "writer" : "active"); + + if (!writer) + bif_attachd(bp); + return (0); } @@ -2234,7 +2037,7 @@ kn->kn_data = 0; return (0); } else { - kn->kn_data = d->bd_bif->bif_ifp->if_mtu; + kn->kn_data = bif_wrsize(d->bd_bif); return (1); } } @@ -2310,7 +2113,7 @@ CK_LIST_FOREACH(d, &bp->bif_dlist, bd_next) { counter_u64_add(d->bd_rcount, 1); /* - * NB: We dont call BPF_CHECK_DIRECTION() here since there + * NB: We don't check the direction here since there * is no way for the caller to indiciate to us whether this * packet is inbound or outbound. In the bpf_mtap() routines, * we use the interface pointers on the mbuf to figure it out. @@ -2348,10 +2151,6 @@ bpf_tap(ifp->if_bpf, pkt, pktlen); } -#define BPF_CHECK_DIRECTION(d, r, i) \ - (((d)->bd_direction == BPF_D_IN && (r) != (i)) || \ - ((d)->bd_direction == BPF_D_OUT && (r) == (i))) - /* * Incoming linkage from device drivers, when packet is in an mbuf chain. * Locking model is explained in bpf_tap(). @@ -2379,7 +2178,7 @@ gottime = BPF_TSTAMP_NONE; CK_LIST_FOREACH(d, &bp->bif_dlist, bd_next) { - if (BPF_CHECK_DIRECTION(d, m_rcvif(m), bp->bif_ifp)) + if (bpf_chkdir(d, m)) continue; counter_u64_add(d->bd_rcount, 1); #ifdef BPF_JITTER @@ -2452,7 +2251,7 @@ gottime = BPF_TSTAMP_NONE; CK_LIST_FOREACH(d, &bp->bif_dlist, bd_next) { - if (BPF_CHECK_DIRECTION(d, m->m_pkthdr.rcvif, bp->bif_ifp)) + if (bpf_chkdir(d, m)) continue; counter_u64_add(d->bd_rcount, 1); slen = bpf_filter(d->bd_rfilter, (u_char *)&mb, pktlen, 0); @@ -2481,7 +2280,6 @@ } } -#undef BPF_CHECK_DIRECTION #undef BPF_TSTAMP_NONE #undef BPF_TSTAMP_FAST #undef BPF_TSTAMP_NORMAL @@ -2761,57 +2559,36 @@ } /* - * Attach an interface to bpf. dlt is the link layer type; hdrlen is the - * fixed size of the link header (variable length headers not yet supported). - */ -void -bpfattach(struct ifnet *ifp, u_int dlt, u_int hdrlen) -{ - - bpfattach2(ifp, dlt, hdrlen, &ifp->if_bpf); -} - -/* - * Attach an interface to bpf. ifp is a pointer to the structure - * defining the interface to be attached, dlt is the link layer type, - * and hdrlen is the fixed size of the link header (variable length - * headers are not yet supporrted). + * Attach a tap point to bpf. + * XXX: with current KPI it is consumer's responsibility to avoid duplicates. */ -void -bpfattach2(struct ifnet *ifp, u_int dlt, u_int hdrlen, - struct bpf_if **driverp) +struct bpf_if * +bpf_attach(const char *name, u_int dlt, u_int hdrlen, + const struct bif_methods *methods, void *sc) { struct bpf_if *bp; - KASSERT(*driverp == NULL, - ("bpfattach2: driverp already initialized")); - bp = malloc(sizeof(*bp), M_BPF, M_WAITOK | M_ZERO); CK_LIST_INIT(&bp->bif_dlist); CK_LIST_INIT(&bp->bif_wlist); - bp->bif_ifp = ifp; bp->bif_dlt = dlt; bp->bif_hdrlen = hdrlen; - bp->bif_bpf = driverp; + bp->bif_softc = sc; + bp->bif_name = name; + bp->bif_methods = methods; refcount_init(&bp->bif_refcnt, 1); - *driverp = bp; - /* - * Reference ifnet pointer, so it won't freed until - * we release it. - */ - if_ref(ifp); BPF_LOCK(); LIST_INSERT_HEAD(&bpf_iflist, bp, bif_next); BPF_UNLOCK(); - if (bootverbose && IS_DEFAULT_VNET(curvnet)) - if_printf(ifp, "bpf attached\n"); + return (bp); } #ifdef VIMAGE /* * Detach descriptors on interface's vmove event. + * XXXGL: shouldn't be a special case, but a full detach. */ void bpf_ifdetach(struct ifnet *ifp) @@ -2821,7 +2598,8 @@ BPF_LOCK(); LIST_FOREACH(bp, &bpf_iflist, bif_next) { - if (bp->bif_ifp != ifp) + /* XXXGL: assuming softc is ifnet here */ + if (bp->bif_softc != ifp) continue; /* Detach common descriptors */ @@ -2839,40 +2617,31 @@ #endif /* - * Detach bpf from an interface. This involves detaching each descriptor - * associated with the interface. Notify each descriptor as it's detached - * so that any sleepers wake up and get ENXIO. + * Detach bpf tap point. This involves detaching each descriptor associated + * with the interface. Notify each descriptor as it's detached so that any + * sleepers wake up and get ENXIO. */ void -bpfdetach(struct ifnet *ifp) +bpf_detach(struct bpf_if *bp) { - struct bpf_if *bp, *bp_temp; struct bpf_d *d; BPF_LOCK(); - /* Find all bpf_if struct's which reference ifp and detach them. */ - LIST_FOREACH_SAFE(bp, &bpf_iflist, bif_next, bp_temp) { - if (ifp != bp->bif_ifp) - continue; - - LIST_REMOVE(bp, bif_next); - *bp->bif_bpf = __DECONST(struct bpf_if *, &dead_bpf_if); + LIST_REMOVE(bp, bif_next); - CTR4(KTR_NET, - "%s: sheduling free for encap %d (%p) for if %p", - __func__, bp->bif_dlt, bp, ifp); + CTR4(KTR_NET, "%s: sheduling free for encap %d (%p) for if %p", + __func__, bp->bif_dlt, bp, ifp); - /* Detach common descriptors */ - while ((d = CK_LIST_FIRST(&bp->bif_dlist)) != NULL) { - bpf_detachd(d, true); - } + /* Detach common descriptors */ + while ((d = CK_LIST_FIRST(&bp->bif_dlist)) != NULL) { + bpf_detachd(d, true); + } - /* Detach writer-only descriptors */ - while ((d = CK_LIST_FIRST(&bp->bif_wlist)) != NULL) { - bpf_detachd(d, true); - } - bpfif_rele(bp); + /* Detach writer-only descriptors */ + while ((d = CK_LIST_FIRST(&bp->bif_wlist)) != NULL) { + bpf_detachd(d, true); } + bpfif_rele(bp); BPF_UNLOCK(); } @@ -2883,22 +2652,24 @@ } /* - * Get a list of available data link type of the interface. + * Get a list of available data link type of the tap point. If a tap point + * attaches more than one time, it is supposed to attach with different DLTs + * but with the same name pointer. */ static int bpf_getdltlist(struct bpf_d *d, struct bpf_dltlist *bfl) { - struct ifnet *ifp; + const char *name; struct bpf_if *bp; u_int *lst; int error, n, n1; BPF_LOCK_ASSERT(); - ifp = d->bd_bif->bif_ifp; + name = d->bd_bif->bif_name; n1 = 0; LIST_FOREACH(bp, &bpf_iflist, bif_next) { - if (bp->bif_ifp == ifp) + if (bp->bif_name == name) n1++; } if (bfl->bfl_list == NULL) { @@ -2911,7 +2682,7 @@ lst = malloc(n1 * sizeof(u_int), M_TEMP, M_WAITOK); n = 0; LIST_FOREACH(bp, &bpf_iflist, bif_next) { - if (bp->bif_ifp != ifp) + if (bp->bif_name != name) continue; lst[n++] = bp->bif_dlt; } @@ -2922,12 +2693,19 @@ } /* - * Set the data link type of a BPF instance. + * Set the data link type of a BPF descriptor. The convention is that + * application first do BIOCSETIF and then BIOCSETDLT, thus the descriptor + * is supposed to be already attached. Only one kernel facility provides + * tapping points with same name but different DLT - ieee80211_radiotap. + * + * XXXGL: this function definitely looks suspicious, e.g. it clearly doesn't + * clear promisc on the old bpf_if. The convention about reference counting + * is also unclear. */ static int bpf_setdlt(struct bpf_d *d, u_int dlt) { - struct ifnet *ifp; + const char *name; struct bpf_if *bp; int error; bool opromisc; @@ -2942,21 +2720,22 @@ if (d->bd_bif->bif_dlt == dlt) return (0); - ifp = d->bd_bif->bif_ifp; + name = d->bd_bif->bif_name; LIST_FOREACH(bp, &bpf_iflist, bif_next) { - if (bp->bif_ifp == ifp && bp->bif_dlt == dlt) + if (bp->bif_name == name && bp->bif_dlt == dlt) break; } if (bp == NULL) return (EINVAL); opromisc = d->bd_flags & BPFD_PROMISC; + bpf_detachd(d, false); bpf_attachd(d, bp); if (opromisc) { - error = ifpromisc(bp->bif_ifp, 1); + error = bp->bif_methods->bif_promisc(bp->bif_softc, true); if (error) - if_printf(bp->bif_ifp, "%s: ifpromisc failed (%d)\n", - __func__, error); + printf("%s: bif_promisc on %s failed (%d)\n", + __func__, bp->bif_name, error); else d->bd_flags |= BPFD_PROMISC; } @@ -3027,8 +2806,7 @@ d->bd_hlen = bd->bd_hlen; d->bd_bufsize = bd->bd_bufsize; d->bd_pid = bd->bd_pid; - strlcpy(d->bd_ifname, - bd->bd_bif->bif_ifp->if_xname, IFNAMSIZ); + strlcpy(d->bd_ifname, bd->bd_bif->bif_name, sizeof(d->bd_ifname)); d->bd_locked = bd->bd_flags & BPFD_LOCKED ? 1 : 0; d->bd_wcount = counter_u64_fetch(bd->bd_wcount); d->bd_wdcount = counter_u64_fetch(bd->bd_wdcount); @@ -3156,15 +2934,9 @@ void bpfattach(struct ifnet *ifp, u_int dlt, u_int hdrlen) { + static const struct bpfd_list dead_bpf_if = CK_LIST_HEAD_INITIALIZER(); - bpfattach2(ifp, dlt, hdrlen, &ifp->if_bpf); -} - -void -bpfattach2(struct ifnet *ifp, u_int dlt, u_int hdrlen, struct bpf_if **driverp) -{ - - *driverp = __DECONST(struct bpf_if *, &dead_bpf_if); + ifp->if_bpf = __DECONST(struct bpf_if *, &dead_bpf_if); } void @@ -3191,35 +2963,3 @@ } #endif /* !DEV_BPF && !NETGRAPH_BPF */ - -#ifdef DDB -static void -bpf_show_bpf_if(struct bpf_if *bpf_if) -{ - - if (bpf_if == NULL) - return; - db_printf("%p:\n", bpf_if); -#define BPF_DB_PRINTF(f, e) db_printf(" %s = " f "\n", #e, bpf_if->e); -#define BPF_DB_PRINTF_RAW(f, e) db_printf(" %s = " f "\n", #e, e); - /* bif_ext.bif_next */ - /* bif_ext.bif_dlist */ - BPF_DB_PRINTF("%#x", bif_dlt); - BPF_DB_PRINTF("%u", bif_hdrlen); - /* bif_wlist */ - BPF_DB_PRINTF("%p", bif_ifp); - BPF_DB_PRINTF("%p", bif_bpf); - BPF_DB_PRINTF_RAW("%u", refcount_load(&bpf_if->bif_refcnt)); -} - -DB_SHOW_COMMAND(bpf_if, db_show_bpf_if) -{ - - if (!have_addr) { - db_printf("usage: show bpf_if \n"); - return; - } - - bpf_show_bpf_if((struct bpf_if *)addr); -} -#endif diff --git a/sys/net/bpf_ifnet.c b/sys/net/bpf_ifnet.c new file mode 100644 --- /dev/null +++ b/sys/net/bpf_ifnet.c @@ -0,0 +1,241 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2019 Andrey V. Elsukov + * Copyright (c) 2025 Gleb Smirnoff + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We need to know all the ifnets we support. */ +#include +#include +#include +#include +#include + +static int +bpf_ifnet_write(void *arg, struct mbuf *m, struct mbuf *mc, int flags) +{ + struct ifnet *ifp = arg; + struct route ro = {}; + struct sockaddr dst = { + .sa_family = AF_UNSPEC, + }; + u_int hlen; + int error; + + NET_EPOCH_ASSERT(); + + if (__predict_false((ifp->if_flags & IFF_UP) == 0)) { + m_freem(m); + m_freem(mc); + return (ENETDOWN); + } + + switch (ifp->if_type) { + /* DLT_RAW */ + case IFT_MBIM: /* umb(4) */ + case IFT_OTHER: /* uhso(4), usie */ + hlen = 0; + break; + + /* DLT_ENC */ + case IFT_ENC: + hlen = 12; /* XXXGL: sizeof(struct enchdr); */ + break; + + /* DLT_EN10MB */ + case IFT_ETHER: /* if_ethersubr.c */ + case IFT_L2VLAN: /* vlan(4) */ + case IFT_IEEE8023ADLAG: /* lagg(4) */ + case IFT_INFINIBAND: /* if_infiniband.c */ + { + struct ether_header *eh; + + eh = mtod(m, struct ether_header *); + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + if (bcmp(ifp->if_broadcastaddr, eh->ether_dhost, + ETHER_ADDR_LEN) == 0) + m->m_flags |= M_BCAST; + else + m->m_flags |= M_MCAST; + } + if (!(flags & BPFD_HDRCMPLT)) { + memcpy(eh->ether_shost, IF_LLADDR(ifp), + sizeof(eh->ether_shost)); + } + hlen = ETHER_HDR_LEN; + break; + } + /* DLT_APPLE_IP_OVER_IEEE1394 */ + case IFT_IEEE1394: /* fwip(4) */ + hlen = sizeof(struct fw_hwaddr); + break; + + /* DLT_NULL */ + case IFT_GIF: /* gif(4) */ + case IFT_LOOP: /* lo(4), disc(4) */ + case IFT_PARA: /* plip(4), iic */ + case IFT_PPP: /* tun(4) */ + case IFT_PROPVIRTUAL: /* ng_iface(4) */ + case IFT_WIREGUARD: /* wg(4) */ + case IFT_STF: /* stf(4) */ + case IFT_TUNNEL: /* ipsec(4), me(4), gre(4), ovpn(4) */ + hlen = sizeof(uint32_t); + break; + + /* DLT_PFLOG */ + case IFT_PFLOG: + hlen = PFLOG_HDRLEN; + break; + + /* DLT_PFSYNC */ + case IFT_PFSYNC: + hlen = PFSYNC_HDRLEN; + break; + + default: + KASSERT(0, ("%s: ifp %p type %u not supported", __func__, + ifp, ifp->if_type)); + } + + if (__predict_false(hlen > m->m_len)) { + m_freem(m); + m_freem(mc); + return (EMSGSIZE); + }; + + if (hlen != 0) { + bcopy(mtod(m, const void *), &dst.sa_data, hlen); + ro.ro_prepend = (char *)&dst.sa_data; + ro.ro_plen = hlen; + ro.ro_flags = RT_HAS_HEADER; + m->m_pkthdr.len -= hlen; + m->m_len -= hlen; + m->m_data += hlen; + }; + + CURVNET_SET(ifp->if_vnet); + error = ifp->if_output(ifp, m, &dst, &ro); + if (error != 0) { + m_freem(mc); + } else if (mc != NULL) { + mc->m_pkthdr.rcvif = ifp; + (void)ifp->if_input(ifp, mc); + } + CURVNET_RESTORE(); + + return (error); +} + +static bool +bpf_ifnet_chkdir(void *arg, const struct mbuf *m, int dir) +{ + struct ifnet *ifp = arg; + struct ifnet *rcvif = m_rcvif(m); + + return ((dir == BPF_D_IN && ifp != rcvif) || + (dir == BPF_D_OUT && ifp == rcvif)); +} + +uint32_t +bpf_ifnet_wrsize(void *arg) +{ + struct ifnet *ifp = arg; + + return (ifp->if_mtu); +} + +int +bpf_ifnet_promisc(void *arg, bool on) +{ + struct ifnet *ifp = arg; + int error; + + CURVNET_SET(ifp->if_vnet); + if ((error = ifpromisc(ifp, on ? 1 : 0)) != 0) + if_printf(ifp, "%s: ifpromisc failed (%d)\n", __func__, error); + CURVNET_RESTORE(); + + return (error); +} + +static const struct bif_methods bpf_ifnet_methods = { + .bif_chkdir = bpf_ifnet_chkdir, + .bif_promisc = bpf_ifnet_promisc, + .bif_wrsize = bpf_ifnet_wrsize, + .bif_write = bpf_ifnet_write, +}; + +/* + * Attach an interface to bpf. dlt is the link layer type; hdrlen is the + * fixed size of the link header (variable length headers not yet supported). + * Legacy KPI to be obsoleted soon. + */ +void +bpfattach(struct ifnet *ifp, u_int dlt, u_int hdrlen) +{ + + ifp->if_bpf = bpf_attach(ifp->if_xname, dlt, hdrlen, + &bpf_ifnet_methods, ifp); + if_ref(ifp); + if (bootverbose && IS_DEFAULT_VNET(curvnet)) + if_printf(ifp, "bpf attached\n"); +} + +/* + * The dead_bpf_if is an ugly plug against races at ifnet destroy time that + * still exist and are not properly covered by epoch(9). + * Legacy KPI to be obsoleted soon. + */ +void +bpfdetach(struct ifnet *ifp) +{ + static const struct bpfd_list dead_bpf_if = CK_LIST_HEAD_INITIALIZER(); + struct bpf_if *bif; + + bif = ifp->if_bpf; + ifp->if_bpf = __DECONST(struct bpf_if *, &dead_bpf_if); + bpf_detach(bif); + if_rele(ifp); +} diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c --- a/sys/net80211/ieee80211_freebsd.c +++ b/sys/net80211/ieee80211_freebsd.c @@ -1088,35 +1088,7 @@ #endif } -static eventhandler_tag wlan_bpfevent; static eventhandler_tag wlan_ifllevent; - -static void -bpf_track(void *arg, struct ifnet *ifp, int dlt, int attach) -{ - /* NB: identify vap's by if_init */ - if (dlt == DLT_IEEE802_11_RADIO && - ifp->if_init == ieee80211_init) { - struct ieee80211vap *vap = ifp->if_softc; - /* - * Track bpf radiotap listener state. We mark the vap - * to indicate if any listener is present and the com - * to indicate if any listener exists on any associated - * vap. This flag is used by drivers to prepare radiotap - * state only when needed. - */ - if (attach) { - ieee80211_syncflag_ext(vap, IEEE80211_FEXT_BPF); - if (vap->iv_opmode == IEEE80211_M_MONITOR) - atomic_add_int(&vap->iv_ic->ic_montaps, 1); - } else if (!bpf_peers_present(vap->iv_rawbpf)) { - ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_BPF); - if (vap->iv_opmode == IEEE80211_M_MONITOR) - atomic_subtract_int(&vap->iv_ic->ic_montaps, 1); - } - } -} - /* * Change MAC address on the vap (if was not started). */ @@ -1385,8 +1357,6 @@ case MOD_LOAD: if (bootverbose) printf("wlan: <802.11 Link Layer>\n"); - wlan_bpfevent = EVENTHANDLER_REGISTER(bpf_track, - bpf_track, 0, EVENTHANDLER_PRI_ANY); wlan_ifllevent = EVENTHANDLER_REGISTER(iflladdr_event, wlan_iflladdr, NULL, EVENTHANDLER_PRI_ANY); struct if_clone_addreq req = { @@ -1398,7 +1368,6 @@ return 0; case MOD_UNLOAD: ifc_detach_cloner(wlan_cloner); - EVENTHANDLER_DEREGISTER(bpf_track, wlan_bpfevent); EVENTHANDLER_DEREGISTER(iflladdr_event, wlan_ifllevent); return 0; } diff --git a/sys/net80211/ieee80211_radiotap.c b/sys/net80211/ieee80211_radiotap.c --- a/sys/net80211/ieee80211_radiotap.c +++ b/sys/net80211/ieee80211_radiotap.c @@ -43,8 +43,10 @@ #include #include #include +#include #include #include +#include #include @@ -108,20 +110,6 @@ { } -void -ieee80211_radiotap_vattach(struct ieee80211vap *vap) -{ - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_radiotap_header *th = ic->ic_th; - - if (th != NULL && ic->ic_rh != NULL) { - /* radiotap DLT for raw 802.11 frames */ - bpfattach2(vap->iv_ifp, DLT_IEEE802_11_RADIO, - sizeof(struct ieee80211_frame) + le16toh(th->it_len), - &vap->iv_rawbpf); - } -} - void ieee80211_radiotap_vdetach(struct ieee80211vap *vap) { @@ -372,3 +360,104 @@ } return -1; } + +static void +bpf_ieee80211_attach(void *sc) +{ + struct ieee80211vap *vap = if_getsoftc((if_t)sc); + + /* + * Track bpf radiotap listener state. We mark the vap + * to indicate if any listener is present and the com + * to indicate if any listener exists on any associated + * vap. This flag is used by drivers to prepare radiotap + * state only when needed. + */ + ieee80211_syncflag_ext(vap, IEEE80211_FEXT_BPF); + if (vap->iv_opmode == IEEE80211_M_MONITOR) + atomic_add_int(&vap->iv_ic->ic_montaps, 1); +} + +static void +bpf_ieee80211_detach(void *sc) +{ + struct ieee80211vap *vap = if_getsoftc((if_t)sc); + + if (!bpf_peers_present(vap->iv_rawbpf)) { + ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_BPF); + if (vap->iv_opmode == IEEE80211_M_MONITOR) + atomic_subtract_int(&vap->iv_ic->ic_montaps, 1); + } +} + +static int +bpf_ieee80211_write(void *arg, struct mbuf *m, struct mbuf *mc, int flags) +{ + struct ifnet *ifp = arg; + struct sockaddr dst = { + .sa_family = AF_IEEE80211, + /* XXXGL: value and XXX comment from 246b5467621a */ + .sa_len = 12, /* XXX != 0 */ + }; + struct route ro = { + .ro_prepend = (char *)&dst.sa_data, + .ro_flags = RT_HAS_HEADER, + }; + const struct ieee80211_bpf_params *p; + u_int hlen; + int error; + + NET_EPOCH_ASSERT(); + + /* + * Collect true length from the parameter header. + * XXX check ibp_vers + */ + p = mtod(m, const struct ieee80211_bpf_params *); + if (p->ibp_len > sizeof(dst.sa_data)) { + m_freem(m); + m_freem(mc); + return (EMSGSIZE); + } + hlen = ro.ro_plen = p->ibp_len; + bcopy(mtod(m, const void *), &dst.sa_data, hlen); + m->m_pkthdr.len -= hlen; + m->m_len -= hlen; + m->m_data += hlen; + + CURVNET_SET(ifp->if_vnet); + error = ifp->if_output(ifp, m, &dst, &ro); + if (error != 0) { + m_freem(mc); + } else if (mc != NULL) { + mc->m_pkthdr.rcvif = ifp; + (void)ifp->if_input(ifp, mc); + } + CURVNET_RESTORE(); + + return (error); +} + +static const struct bif_methods bpf_ieee80211_methods = { + .bif_attachd = bpf_ieee80211_attach, + .bif_detachd = bpf_ieee80211_detach, + .bif_promisc = bpf_ifnet_promisc, + .bif_wrsize = bpf_ifnet_wrsize, + .bif_write = bpf_ieee80211_write, +}; + +void +ieee80211_radiotap_vattach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_radiotap_header *th = ic->ic_th; + + if (th != NULL && ic->ic_rh != NULL) { + /* radiotap DLT for raw 802.11 frames */ + vap->iv_rawbpf = bpf_attach(vap->iv_ifp->if_xname, + DLT_IEEE802_11_RADIO, + sizeof(struct ieee80211_frame) + le16toh(th->it_len), + &bpf_ieee80211_methods, vap->iv_ifp); + if_ref(vap->iv_ifp); + } +}