kern/86429: if_tap doesn't filter frames not destined for itself
Pieter de Boer
pieter at thedarkside.nl
Wed Sep 21 14:20:09 PDT 2005
>Number: 86429
>Category: kern
>Synopsis: if_tap doesn't filter frames not destined for itself
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Wed Sep 21 21:20:06 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator: Pieter de Boer
>Release: 6.0-BETA4
>Organization:
>Environment:
FreeBSD edinburgh 6.0-BETA4 FreeBSD 6.0-BETA4 #2: Sat Sep 17 18:01:08 CEST 2005 pieter at edinburgh:/usr/obj/usr/src/sys/kernel-31-07-2005 i386
>Description:
The tap-driver doesn't filter incoming ethernet frames with a different unicast destination mac address than it's own mac address. The frames are sent up to the IP-stack, which may route them when forwarding is turned on. In my case leading to the system doing arp who-has on the destination IP-address in the ethernet frame, because the kernel wants to route the packet. On a virtual internet-exchange (www.virt-ix.net) using tap-tunnels this leads to a packet storm.
>How-To-Repeat:
Set up a ethernet tunnel using vtund/openvpn, configure two ethernet-addresses on both ends. On one system (A), turn on IP forwarding. On system B, create a static ARP entry for an address in the subnet, with a non-existent MAC address (like de:ad:ba:be:ca:fe). Run tcpdump on system B(!) and ping from system B to the IP address with the fake MAC address. See how system A does an arp request for the destination IP address because it wants to route it. This indicates
the frame was wrongly pushed to the IP stack.
>Fix:
Adding an extra check to if_tap.c fixes this bug. The following patch works
on my system.
--- if_tap.c.orig Wed Sep 7 21:12:10 2005
+++ if_tap.c Thu Sep 8 22:05:46 2005
@@ -801,6 +801,7 @@
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;
@@ -825,6 +826,21 @@
}
m->m_pkthdr.rcvif = ifp;
+
+ /* Check length first */
+ if (m->m_len < sizeof(struct ether_header)) {
+ m_freem(m);
+ return(0);
+ }
+
+ /* Only pass it up to the ether_input() layer if it's meant for us. */
+ eh = mtod(m, struct ether_header *);
+ if (eh && (ifp->if_flags & IFF_PROMISC) == 0 && bcmp(eh->ether_dhost,
+ IFP2ENADDR(ifp), ETHER_ADDR_LEN) &&
+ (eh->ether_dhost[0] & 1) == 0) {
+ m_freem(m);
+ return(0);
+ }
/* Pass packet up to parent. */
(*ifp->if_input)(ifp, m);
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list