Subject: EtherIP fix for 4.0_BETA
To: None <tech-net@netbsd.org>
From: Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
List: tech-net
Date: 08/18/2006 18:40:05
--pWyiEgJYm5f9v55/
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hello,

the current EtherIP implementation has a bug that makes it impossible to
send broadcast or multicast packets through a tunnel, the M_BCAST and
M_MCAST flags are not correctly reset when encapsulating those packets.

The attached patch fixes this, attaches gif to bpf a second time with
ethernet data link type, adds collection of gif interface statistics for
EtherIP and makes it possible to use EtherIP over IPv6.  

I would really like to have that in 4.0.

Hans
-- 
%SYSTEM-F-ANARCHISM, The operating system has been overthrown

--pWyiEgJYm5f9v55/
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="etherip.diff"

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	17 Aug 2006 13:09:57 -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	17 Aug 2006 13:09:57 -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
 }
@@ -399,31 +402,41 @@
 		if (m == NULL)
 			break;
 
+		/* 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 +484,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 +534,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 +552,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	17 Aug 2006 13:09:58 -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.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_gif.c,v
retrieving revision 1.50
diff -u -r1.50 in_gif.c
--- sys/netinet/in_gif.c	28 Jul 2006 17:34:13 -0000	1.50
+++ sys/netinet/in_gif.c	17 Aug 2006 13:09:58 -0000
@@ -110,6 +110,13 @@
 		return EAFNOSUPPORT;
 	}
 
+	/*
+	 * Reset broadcast/multicast flag. This has already been done by
+	 * gif_output, but a test showed that these were set again
+	 * somewhere.
+	 */
+	m->m_flags &= ~(M_BCAST|M_MCAST);
+
 	switch (family) {
 #ifdef INET
 	case AF_INET:
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	17 Aug 2006 13:09:58 -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	17 Aug 2006 13:09:59 -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;
 
@@ -106,6 +113,13 @@
 		return EAFNOSUPPORT;
 	}
 
+	/*
+	 * Reset broadcast/multicast flag. This has already been done by
+	 * gif_output, but a test showed that these were set again
+	 * somewhere.
+	 */
+	m->m_flags &= ~(M_BCAST|M_MCAST);
+
 	switch (family) {
 #ifdef INET
 	case AF_INET:
@@ -143,6 +157,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 +330,11 @@
 #ifdef ISO
 	case IPPROTO_EON:
 		af = AF_ISO;
+		break;
+#endif
+#if NBRIDGE > 0
+	case IPPROTO_ETHERIP:
+		af = AF_LINK;
 		break;
 #endif
 	default:

--pWyiEgJYm5f9v55/--