diff options
author | bms <bms@FreeBSD.org> | 2007-02-03 02:57:45 +0000 |
---|---|---|
committer | bms <bms@FreeBSD.org> | 2007-02-03 02:57:45 +0000 |
commit | cb84e5a9bd809b0fc9ae4a9601889aa89770481d (patch) | |
tree | 16921ed174ef8881a2165e678af900074228fa19 /sys/net/if_tap.c | |
parent | 3910945fa0e5f58974ad6be73b36658186af7759 (diff) | |
download | FreeBSD-src-cb84e5a9bd809b0fc9ae4a9601889aa89770481d.zip FreeBSD-src-cb84e5a9bd809b0fc9ae4a9601889aa89770481d.tar.gz |
Drop unicast Ethernet frames not destined for the configured address
of a tap(4) instance, if IFF_PROMISC is not set.
In tap(4), we should emulate the effect IFF_PROMISC would have on
hardware, otherwise we risk introducing layer 2 loops if tap(4) is
used with bridges. This means not even bpf(4) gets to see them.
This patch has been tested in a variety of situations. Multicast and
broadcast frames are correctly allowed through. I have observed this
behaviour causing problems with multiple QEMU instances hosted on
the same FreeBSD machine.
The checks in in ether_demux() [if_ethersubr.c, rev 1.222, line 638]
are insufficient to prevent this bug from occurring, as ifp->if_vlantrunk
will always be NULL for the non-vlan case.
MFC after: 3 weeks
PR: 86429
Submitted by: Pieter de Boer (with changes)
Diffstat (limited to 'sys/net/if_tap.c')
-rw-r--r-- | sys/net/if_tap.c | 18 |
1 files changed, 18 insertions, 0 deletions
diff --git a/sys/net/if_tap.c b/sys/net/if_tap.c index 1b51647..653d618 100644 --- a/sys/net/if_tap.c +++ b/sys/net/if_tap.c @@ -813,6 +813,7 @@ tapread(struct cdev *dev, struct uio *uio, int flag) static int tapwrite(struct cdev *dev, struct uio *uio, int flag) { + struct ether_header *eh; struct tap_softc *tp = dev->si_drv1; struct ifnet *ifp = tp->tap_ifp; struct mbuf *m; @@ -838,6 +839,23 @@ tapwrite(struct cdev *dev, struct uio *uio, int flag) m->m_pkthdr.rcvif = ifp; + /* + * Only pass a unicast frame to ether_input(), if it would actually + * have been received by non-virtual hardware. + */ + if (m->m_len < sizeof(struct ether_header)) { + m_freem(m); + return (0); + } + eh = mtod(m, struct ether_header *); + + if (eh && (ifp->if_flags & IFF_PROMISC) == 0 && + !ETHER_IS_MULTICAST(eh->ether_dhost) && + bcmp(eh->ether_dhost, IF_LLADDR(ifp), ETHER_ADDR_LEN) != 0) { + m_freem(m); + return (0); + } + /* Pass packet up to parent. */ (*ifp->if_input)(ifp, m); ifp->if_ipackets ++; /* ibytes are counted in parent */ |