#include <sys/mbuf.h>
#include <sys/socket.h>
#include <net/bpf.h>
#include <net/bpfdesc.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_private.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <net/ethernet.h>
#include <net/firewire.h>
#include <net/if_pflog.h>
#include <net/if_pfsync.h>
#include <security/mac/mac_framework.h>
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) {
case IFT_MBIM:
case IFT_OTHER:
hlen = 0;
break;
case IFT_ENC:
hlen = 12;
break;
case IFT_ETHER:
case IFT_L2VLAN:
case IFT_BRIDGE:
case IFT_IEEE8023ADLAG:
case IFT_INFINIBAND:
{
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;
}
case IFT_IEEE1394:
hlen = sizeof(struct fw_hwaddr);
break;
case IFT_GIF:
case IFT_LOOP:
case IFT_PARA:
case IFT_PPP:
case IFT_PROPVIRTUAL:
case IFT_WIREGUARD:
case IFT_STF:
case IFT_TUNNEL:
hlen = sizeof(uint32_t);
break;
case IFT_PFLOG:
hlen = PFLOG_HDRLEN;
break;
case IFT_PFSYNC:
hlen = PFSYNC_HDRLEN;
break;
default:
hlen = 0;
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);
}
#ifdef MAC
static int
bpf_ifnet_mac_check_receive(void *arg, struct bpf_d *d)
{
struct ifnet *ifp = arg;
return (mac_bpfdesc_check_receive(d, ifp));
}
#endif
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,
#ifdef MAC
.bif_mac_check_receive = bpf_ifnet_mac_check_receive,
#endif
};
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");
}
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);
}