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