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/--