Subject: kern/34268: multiple EtherIP problems
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <rosenfeld@grumpf.hope-2000.org>
List: netbsd-bugs
Date: 08/23/2006 14:55:00
>Number:         34268
>Category:       kern
>Synopsis:       multiple EtherIP problems
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Aug 23 14:55:00 +0000 2006
>Originator:     Hans Rosenfeld
>Release:        NetBSD 4.0_BETA
>Organization:
>Environment:
System: NetBSD panic 4.0_BETA NetBSD 4.0_BETA (PANIC) #8: Fri Aug 11 12:58:37 CEST 2006 woodstoc@panic:/usr/obj/sys/arch/i386/compile.i386/PANIC i386

Architecture: i386
Machine: i386
>Description:
EtherIP packets containing broadcast or multicast packets are sent to
a wrong destination because the M_BCAST and M_MCAST mbuf flags are not
reset while encapsulating those packets.

It is not possible to use EtherIP over IPv6, interface statistics are
not properly collected and it is not possible to dump EtherIP packets
through bpf.
>How-To-Repeat:
Try to send broadcast or multicast packets through an EtherIP tunnel.
>Fix:
The M_BCAST and M_MCAST mbuf flags are reset in gif_output() before a
packet is put into the interface output queue. Since all EtherIP packets
come from a bridge and the bridge code puts the packets into the output
queues of its interfaces directly, gif_output() is never called for
EtherIP packets and the mbuf flags are not reset. Resetting those flags
in gifintr() instead of gif_output() will fix this.

The patch below fixes this and the other mentioned problems.

Index: sys/net/if_bridge.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_bridge.c,v
retrieving revision 1.40
diff -u -r1.40 if_bridge.c
--- sys/net/if_bridge.c	23 Jul 2006 22:06:12 -0000	1.40
+++ sys/net/if_bridge.c	21 Aug 2006 18:47:14 -0000
@@ -1307,6 +1307,16 @@
 		return (0);
 	}
 
+#if (NGIF > 0) && (NBPFILTER > 0)
+	/*
+	 * If the destination is a gif interface, give the packet to BPF
+	 * pretending it came from the source ethernet interface
+	 */
+	if ((dst_if->if_type == IFT_GIF) && ifp->if_bpf)
+		bpf_mtap(ifp->if_bpf, m);
+#endif
+
+
 	bridge_enqueue(sc, dst_if, m, 0);
 
 	splx(s);
@@ -1552,9 +1562,25 @@
 				    eh->ether_shost, ifp, 0, IFBAF_DYNAMIC);
 			m->m_pkthdr.rcvif = bif->bif_ifp;
 #if NGIF > 0
+			/*
+			 * Ethernet frames coming from gif interfaces
+			 * are not processed here but inserted into the
+			 * input routine of a bridged ethernet interface
+			 * to be processed there. The M_PROTO1 flag is
+			 * set to prevent that these packets entering
+			 * the bridge again.
+			 */
 			if (ifp->if_type == IFT_GIF) {
 				m->m_flags |= M_PROTO1;
 				m->m_pkthdr.rcvif = bif->bif_ifp;
+#if NBPFILTER > 0
+				/*
+				 * Give the packet to BPF, pretending it
+				 * is coming from the ethernet interface
+				 */
+				if (bif->bif_ifp->if_bpf)
+					bpf_mtap(bif->bif_ifp->if_bpf, m);
+#endif
 				(*bif->bif_ifp->if_input)(bif->bif_ifp, m);
 				m = NULL;
 			}
Index: sys/net/if_gif.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_gif.c,v
retrieving revision 1.60
diff -u -r1.60 if_gif.c
--- sys/net/if_gif.c	23 Jul 2006 22:06:12 -0000	1.60
+++ sys/net/if_gif.c	21 Aug 2006 18:47:15 -0000
@@ -172,6 +172,9 @@
 	if_attach(&sc->gif_if);
 	if_alloc_sadl(&sc->gif_if);
 #if NBPFILTER > 0
+#if NBRIDGE > 0
+	bpfattach(&sc->gif_if, DLT_EN10MB, sizeof(struct ether_header));
+#endif
 	bpfattach(&sc->gif_if, DLT_NULL, sizeof(u_int));
 #endif
 }
@@ -308,7 +311,6 @@
 		goto end;
 	}
 
-	m->m_flags &= ~(M_BCAST|M_MCAST);
 	if (!(ifp->if_flags & IFF_UP) ||
 	    sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
 		m_freem(m);
@@ -399,31 +401,43 @@
 		if (m == NULL)
 			break;
 
+		m->m_flags &= ~(M_BCAST|M_MCAST);
+
+		/* The M_PROTO1 flag indicates that this is a bridged
+ 		 * packet, set address family to AF_LINK but don't
+ 		 * prepend it for BPF. Clear the flag, it is not needed
+ 		 * once the packet was here.
+		 * Non-bridged packets will have an int containing the
+		 * address family before the actual packet data, use
+		 * this information and remove it _after_ BPF has
+		 * processed it.
+		 */ 
 #if NBRIDGE > 0
-		if(m->m_flags & M_PROTO1) {
-			M_PREPEND(m, sizeof(int), M_DONTWAIT);
-			if (!m) {
-				ifp->if_oerrors++;
-				continue;
+		if (m->m_flags & M_PROTO1) {
+			m->m_flags &= ~M_PROTO1;
+			family = AF_LINK;
+		} else 
+#endif
+		{
+			/* grab and chop off inner af type */
+			if (sizeof(int) > m->m_len) {
+				m = m_pullup(m, sizeof(int));
+				if (!m) {
+					ifp->if_oerrors++;
+					continue;
+				}
 			}
-			*mtod(m, int *) = AF_LINK;
+			family = *mtod(m, int *);
 		}
 
-#endif
-		/* grab and chop off inner af type */
-		if (sizeof(int) > m->m_len) {
-			m = m_pullup(m, sizeof(int));
-			if (!m) {
-				ifp->if_oerrors++;
-				continue;
-			}
-		}
-		family = *mtod(m, int *);
 #if NBPFILTER > 0
 		if (ifp->if_bpf)
 			bpf_mtap(ifp->if_bpf, m);
 #endif
-		m_adj(m, sizeof(int));
+#if NBRIDGE > 0
+		if (family != AF_LINK)
+#endif
+			m_adj(m, sizeof(int));
 
 		len = m->m_pkthdr.len;
 
@@ -471,6 +485,16 @@
 
 	m->m_pkthdr.rcvif = ifp;
 
+#if NBRIDGE > 0
+	/* strip EtherIP header before handing the packet to BPF */
+	if (af == AF_LINK) {
+		m_adj(m, sizeof(struct etherip_header));
+#if NBPFILTER > 0
+		if (ifp->if_bpf)
+			bpf_mtap(ifp->if_bpf, m);
+#endif
+	} else
+#endif
 #if NBPFILTER > 0
 	if (ifp->if_bpf)
 		bpf_mtap_af(ifp->if_bpf, af, m);
@@ -511,7 +535,6 @@
 #endif
 #if NBRIDGE > 0
 	case AF_LINK:
-		m_adj(m, sizeof(struct etherip_header));
 		if (sizeof(struct ether_header) > m->m_len) {
 			m = m_pullup(m, sizeof(struct ether_header));
 			if (!m) {
@@ -530,14 +553,20 @@
 		}
 		m->m_pkthdr.rcvif = ifp;
 		if(ifp->if_bridge) {
+			int len = m->m_pkthdr.len;
 			if (m->m_flags & (M_BCAST|M_MCAST))
 				ifp->if_imcasts++;
 
 			s = splnet();
 			m = bridge_input(ifp, m);
 			splx(s);
-			if (m == NULL)
+			if (m == NULL) {
+				s = splnet();
+				ifp->if_ipackets++;
+				ifp->if_ibytes += len;
+				splx(s);
 				return;
+			}
 		}
 #endif
 	default:
Index: sys/net/if_gif.h
===================================================================
RCS file: /cvsroot/src/sys/net/if_gif.h,v
retrieving revision 1.13
diff -u -r1.13 if_gif.h
--- sys/net/if_gif.h	11 Dec 2005 23:05:25 -0000	1.13
+++ sys/net/if_gif.h	21 Aug 2006 18:47:15 -0000
@@ -41,6 +41,7 @@
 
 #if defined(_KERNEL) && !defined(_LKM)
 #include "opt_inet.h"
+#include "bridge.h"
 #endif
 
 #include <netinet/in.h>
@@ -88,6 +89,17 @@
 void	gif_delete_tunnel(struct ifnet *);
 #ifdef GIF_ENCAPCHECK
 int	gif_encapcheck(struct mbuf *, int, int, void *);
+#endif
+
+#if NBRIDGE > 0
+struct etherip_header {
+	u_int8_t eip_ver;	/* version/reserved */
+	u_int8_t eip_pad;	/* required padding byte */
+};
+
+#define ETHERIP_VER_VERS_MASK	0x0f
+#define ETHERIP_VER_RSVD_MASK	0xf0
+#define ETHERIP_VERSION		0x03
 #endif
 
 #endif /* !_NET_IF_GIF_H_ */
Index: sys/netinet/in_gif.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_gif.h,v
retrieving revision 1.13
diff -u -r1.13 in_gif.h
--- sys/netinet/in_gif.h	10 Dec 2005 23:36:23 -0000	1.13
+++ sys/netinet/in_gif.h	21 Aug 2006 18:47:15 -0000
@@ -46,12 +46,4 @@
 int in_gif_attach(struct gif_softc *);
 int in_gif_detach(struct gif_softc *);
 
-struct etherip_header {
-        u_int8_t eip_ver;        /* version/reserved */
-        u_int8_t eip_pad;        /* required padding byte */
-};
-#define ETHERIP_VER_VERS_MASK   0x0f
-#define ETHERIP_VER_RSVD_MASK   0xf0
-#define ETHERIP_VERSION         0x03
-
 #endif /* !_NETINET_IN_GIF_H_ */
Index: sys/netinet6/in6_gif.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/in6_gif.c,v
retrieving revision 1.45
diff -u -r1.45 in6_gif.c
--- sys/netinet6/in6_gif.c	7 Jun 2006 22:34:03 -0000	1.45
+++ sys/netinet6/in6_gif.c	21 Aug 2006 18:47:15 -0000
@@ -68,6 +68,10 @@
 
 #include <net/if_gif.h>
 
+#include "gif.h"
+#include "bridge.h"
+#include <net/if_ether.h>
+
 #include <net/net_osdep.h>
 
 static int gif_validate6 __P((const struct ip6_hdr *, struct gif_softc *,
@@ -96,6 +100,9 @@
 	struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
 	struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
 	struct ip6_hdr *ip6;
+#if NBRIDGE > 0
+	struct etherip_header eiphdr;
+#endif
 	int proto, error;
 	u_int8_t itos, otos;
 
@@ -143,6 +150,21 @@
 		itos = 0;
 		break;
 #endif
+#if NBRIDGE > 0
+	case AF_LINK:
+		proto = IPPROTO_ETHERIP;
+		eiphdr.eip_ver = ETHERIP_VERSION & ETHERIP_VER_VERS_MASK;
+		eiphdr.eip_pad = 0;
+		/* prepend Ethernet-in-IP header */
+		M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT);
+		if (m && m->m_len < sizeof(struct etherip_header))
+			m = m_pullup(m, sizeof(struct etherip_header));
+		if (m == NULL)
+			return ENOBUFS;
+		bcopy(&eiphdr, mtod(m, struct etherip_header *),
+		      sizeof(struct etherip_header));
+		break;
+#endif
 	default:
 #ifdef DEBUG
 		printf("in6_gif_output: warning: unknown family %d passed\n",
@@ -301,6 +323,11 @@
 #ifdef ISO
 	case IPPROTO_EON:
 		af = AF_ISO;
+		break;
+#endif
+#if NBRIDGE > 0
+	case IPPROTO_ETHERIP:
+		af = AF_LINK;
 		break;
 #endif
 	default: