All of lore.kernel.org
 help / color / mirror / Atom feed
From: Philip Craig <philipc@snapgear.com>
To: "Timo Teräs" <timo.teras@iki.fi>
Cc: netdev@vger.kernel.org, bridge@lists.linux-foundation.org
Subject: Re: [Bridge] bridging with gre tunnel
Date: Mon, 14 Jul 2008 10:41:49 +1000	[thread overview]
Message-ID: <487AA0CD.9090600@snapgear.com> (raw)
In-Reply-To: <4876FDF0.1030807@iki.fi>

[-- Attachment #1: Type: text/plain, Size: 457 bytes --]

Timo Teräs wrote:
> Is there newer/better patches to achieve this? Any thoughts about
> doing bridging with gre tunnels?

Here's a patch I did against 2.6.17.

I didn't submit this to mainline though because the userspace ABI
is ugly.  The basic problem is that the struct used for the ioctl
isn't extensible.  The only way I can think of to fix it properly
is to add configuration of gre using netlink, but I never got around
to doing that.  


[-- Attachment #2: gre-bridge-6.patch --]
[-- Type: text/x-diff, Size: 11231 bytes --]

Subject: ethernet over GRE

- ip tunnel show doesn't work
- iph.id field isn't really appropriate

--- linux-2.6.x/net/ipv4/ip_gre.c	18 Jun 2006 23:30:56 -0000	1.1.1.33
+++ linux-2.6.x/net/ipv4/ip_gre.c	11 Aug 2006 04:10:04 -0000
@@ -30,6 +30,9 @@
 #include <linux/igmp.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/if_ether.h>
+#include <linux/if_bridge.h>
+#include <linux/etherdevice.h>
+#include <linux/llc.h>
 
 #include <net/sock.h>
 #include <net/ip.h>
@@ -119,6 +122,7 @@
 
 static int ipgre_tunnel_init(struct net_device *dev);
 static void ipgre_tunnel_setup(struct net_device *dev);
+static void ipgre_ether_tunnel_setup(struct net_device *dev);
 
 /* Fallback tunnel: no source, no destination, no key, no options */
 
@@ -274,7 +278,10 @@ static struct ip_tunnel * ipgre_tunnel_l
 			goto failed;
 	}
 
-	dev = alloc_netdev(sizeof(*t), name, ipgre_tunnel_setup);
+	if (parms->iph.id == htons(ETH_P_BRIDGE))
+		dev = alloc_netdev(sizeof(*t), name, ipgre_ether_tunnel_setup);
+	else
+		dev = alloc_netdev(sizeof(*t), name, ipgre_tunnel_setup);
 	if (!dev)
 	  return NULL;
 
@@ -550,6 +557,23 @@ ipgre_ecn_encapsulate(u8 tos, struct iph
 	return INET_ECN_encapsulate(tos, inner);
 }
 
+static __be16 ipgre_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	if (skb->protocol == htons(ETH_P_BRIDGE)) {
+		if (!pskb_may_pull(skb, ETH_HLEN))
+			return 0;
+		return eth_type_trans(skb, dev);
+	}
+#if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
+	else if (skb->protocol == htons(LLC_SAP_BSPAN)) {
+		br_stp_rcv_raw(skb, dev);
+		return 0;
+	}
+#endif
+
+	return 0;
+}
+
 static int ipgre_rcv(struct sk_buff *skb)
 {
 	struct iphdr *iph;
@@ -645,6 +669,13 @@ static int ipgre_rcv(struct sk_buff *skb
 			}
 			tunnel->i_seqno = seqno + 1;
 		}
+		if (tunnel->dev->type == ARPHRD_ETHER) {
+			skb->protocol = ipgre_type_trans(skb, tunnel->dev);
+			if (!skb->protocol) {
+				tunnel->stat.rx_errors++;
+				goto drop;
+			}
+		}
 		tunnel->stat.rx_packets++;
 		tunnel->stat.rx_bytes += skb->len;
 		skb->dev = tunnel->dev;
@@ -678,6 +709,7 @@ static int ipgre_tunnel_xmit(struct sk_b
 	struct iphdr  *iph;			/* Our new IP header */
 	int    max_headroom;			/* The extra header space needed */
 	int    gre_hlen;
+	int    push_hlen;
 	u32    dst;
 	int    mtu;
 
@@ -686,11 +718,18 @@ static int ipgre_tunnel_xmit(struct sk_b
 		goto tx_error;
 	}
 
-	if (dev->hard_header) {
-		gre_hlen = 0;
+	if (dev->type == ARPHRD_ETHER) {
+		skb->protocol = htons(ETH_P_BRIDGE);
+		gre_hlen = tunnel->hlen - ETH_HLEN;
+		push_hlen = gre_hlen;
+		tiph = &tunnel->parms.iph;
+	} else if (dev->hard_header) {
+		gre_hlen = tunnel->hlen;
+		push_hlen = 0;
 		tiph = (struct iphdr*)skb->data;
 	} else {
 		gre_hlen = tunnel->hlen;
+		push_hlen = gre_hlen;
 		tiph = &tunnel->parms.iph;
 	}
 
@@ -792,7 +831,8 @@ static int ipgre_tunnel_xmit(struct sk_b
 			}
 		}
 
-		if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) {
+		if (mtu >= IPV6_MIN_MTU &&
+		    mtu < skb->len - tunnel->hlen + push_hlen) {
 			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
 			ip_rt_put(rt);
 			goto tx_error;
@@ -809,7 +849,7 @@ static int ipgre_tunnel_xmit(struct sk_b
 			tunnel->err_count = 0;
 	}
 
-	max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen;
+	max_headroom = LL_RESERVED_SPACE(tdev) + push_hlen;
 
 	if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {
 		struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
@@ -828,7 +868,7 @@ static int ipgre_tunnel_xmit(struct sk_b
 	}
 
 	skb->h.raw = skb->nh.raw;
-	skb->nh.raw = skb_push(skb, gre_hlen);
+	skb->nh.raw = skb_push(skb, push_hlen);
 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
 			      IPSKB_REROUTED);
@@ -863,7 +903,7 @@ static int ipgre_tunnel_xmit(struct sk_b
 	((u16*)(iph+1))[1] = skb->protocol;
 
 	if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {
-		u32 *ptr = (u32*)(((u8*)iph) + tunnel->hlen - 4);
+		u32 *ptr = (u32*)(((u8*)iph) + gre_hlen - 4);
 
 		if (tunnel->parms.o_flags&GRE_SEQ) {
 			++tunnel->o_seqno;
@@ -935,6 +975,8 @@ ipgre_tunnel_ioctl (struct net_device *d
 		    p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)) ||
 		    ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING)))
 			goto done;
+		if (p.iph.id != 0 && p.iph.id != htons(ETH_P_BRIDGE))
+			goto done;
 		if (p.iph.ttl)
 			p.iph.frag_off |= htons(IP_DF);
 
@@ -956,7 +998,9 @@ ipgre_tunnel_ioctl (struct net_device *d
 
 				t = netdev_priv(dev);
 
-				if (MULTICAST(p.iph.daddr))
+				if (t->dev->type == ARPHRD_ETHER)
+					nflags = IFF_BROADCAST;
+				else if (MULTICAST(p.iph.daddr))
 					nflags = IFF_BROADCAST;
 				else if (p.iph.daddr)
 					nflags = IFF_POINTOPOINT;
@@ -1147,6 +1191,18 @@ static void ipgre_tunnel_setup(struct ne
 	dev->addr_len		= 4;
 }
 
+static void ipgre_ether_tunnel_setup(struct net_device *dev)
+{
+	ether_setup(dev);
+
+	SET_MODULE_OWNER(dev);
+	dev->uninit		= ipgre_tunnel_uninit;
+	dev->destructor 	= free_netdev;
+	dev->hard_start_xmit	= ipgre_tunnel_xmit;
+	dev->get_stats		= ipgre_tunnel_get_stats;
+	dev->do_ioctl		= ipgre_tunnel_ioctl;
+}
+
 static int ipgre_tunnel_init(struct net_device *dev)
 {
 	struct net_device *tdev = NULL;
@@ -1162,8 +1218,27 @@ static int ipgre_tunnel_init(struct net_
 	tunnel->dev = dev;
 	strcpy(tunnel->parms.name, dev->name);
 
-	memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
-	memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
+	if (dev->type == ARPHRD_ETHER)
+		random_ether_addr(dev->dev_addr);
+	else {
+		memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
+		memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
+	}
+
+	if (dev->type == ARPHRD_ETHER)
+		dev->flags |= IFF_BROADCAST;
+#ifdef CONFIG_NET_IPGRE_BROADCAST
+	else if (MULTICAST(iph->daddr)) {
+		if (!iph->saddr)
+			return -EINVAL;
+		dev->flags = IFF_BROADCAST;
+		dev->hard_header = ipgre_header;
+		dev->open = ipgre_open;
+		dev->stop = ipgre_close;
+	}
+#endif
+	else if (iph->daddr)
+		dev->flags |= IFF_POINTOPOINT;
 
 	/* Guess output device to choose reasonable mtu and hard_header_len */
 
@@ -1179,19 +1254,6 @@ static int ipgre_tunnel_init(struct net_
 			tdev = rt->u.dst.dev;
 			ip_rt_put(rt);
 		}
-
-		dev->flags |= IFF_POINTOPOINT;
-
-#ifdef CONFIG_NET_IPGRE_BROADCAST
-		if (MULTICAST(iph->daddr)) {
-			if (!iph->saddr)
-				return -EINVAL;
-			dev->flags = IFF_BROADCAST;
-			dev->hard_header = ipgre_header;
-			dev->open = ipgre_open;
-			dev->stop = ipgre_close;
-		}
-#endif
 	}
 
 	if (!tdev && tunnel->parms.link)
@@ -1212,6 +1274,8 @@ static int ipgre_tunnel_init(struct net_
 		if (tunnel->parms.o_flags&GRE_SEQ)
 			addend += 4;
 	}
+	if (dev->type == ARPHRD_ETHER)
+		addend += ETH_HLEN;
 	dev->hard_header_len = hlen + addend;
 	dev->mtu = mtu - addend;
 	tunnel->hlen = addend;
--- linux-2.6.x/net/bridge/br_netfilter.c	18 Jun 2006 23:30:55 -0000	1.1.1.25
+++ linux-2.6.x/net/bridge/br_netfilter.c	11 Aug 2006 04:10:04 -0000
@@ -765,14 +765,28 @@ out:
 	return NF_STOLEN;
 }
 
+/*
+ * We've finished passing through netfilter, so we can remove the fake dst.
+ * This is required by some lower layers, eg ip_gre
+ */
+static int br_nf_dev_queue_xmit_finish(struct sk_buff *skb)
+{
+	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
+		dst_release(skb->dst);
+		skb->dst = NULL;
+	}
+
+	return br_dev_queue_push_xmit(skb);
+}
+
 static int br_nf_dev_queue_xmit(struct sk_buff *skb)
 {
 	if (skb->protocol == htons(ETH_P_IP) &&
 	    skb->len > skb->dev->mtu &&
 	    !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size))
-		return ip_fragment(skb, br_dev_queue_push_xmit);
+		return ip_fragment(skb, br_nf_dev_queue_xmit_finish);
 	else
-		return br_dev_queue_push_xmit(skb);
+		return br_nf_dev_queue_xmit_finish(skb);
 }
 
 /* PF_BRIDGE/POST_ROUTING ********************************************/
--- linux-2.6.x/net/bridge/br_stp_bpdu.c	18 Jun 2006 23:30:55 -0000	1.1.1.9
+++ linux-2.6.x/net/bridge/br_stp_bpdu.c	11 Aug 2006 04:10:04 -0000
@@ -124,35 +124,23 @@ void br_send_tcn_bpdu(struct net_bridge_
 	br_send_bpdu(p, buf, 7);
 }
 
-/*
- * Called from llc.
- *
- * NO locks, but rcu_read_lock (preempt_disabled)
- */
-int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
-	       struct packet_type *pt, struct net_device *orig_dev)
+static void __br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
+			 const unsigned char *dest)
 {
-	const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
-	const unsigned char *dest = eth_hdr(skb)->h_dest;
 	struct net_bridge_port *p = rcu_dereference(dev->br_port);
 	struct net_bridge *br;
 	const unsigned char *buf;
 
 	if (!p)
-		goto err;
-
-	if (pdu->ssap != LLC_SAP_BSPAN
-	    || pdu->dsap != LLC_SAP_BSPAN
-	    || pdu->ctrl_1 != LLC_PDU_TYPE_U)
-		goto err;
+		return;
 
 	if (!pskb_may_pull(skb, 4))
-		goto err;
+		return;
 
 	/* compare of protocol id and version */
 	buf = skb->data;
 	if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
-		goto err;
+		return;
 
 	br = p->br;
 	spin_lock(&br->lock);
@@ -162,7 +150,7 @@ int br_stp_rcv(struct sk_buff *skb, stru
 	    || !(br->dev->flags & IFF_UP))
 		goto out;
 
-	if (compare_ether_addr(dest, br->group_addr) != 0)
+	if (dest && compare_ether_addr(dest, br->group_addr) != 0)
 		goto out;
 
 	buf = skb_pull(skb, 3);
@@ -213,7 +201,34 @@ int br_stp_rcv(struct sk_buff *skb, stru
 	}
  out:
 	spin_unlock(&br->lock);
+}
+
+/*
+ * Called from llc.
+ *
+ * NO locks, but rcu_read_lock (preempt_disabled)
+ */
+int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
+	       struct packet_type *pt, struct net_device *orig_dev)
+{
+	const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
+	const unsigned char *dest = eth_hdr(skb)->h_dest;
+
+	if (pdu->ssap != LLC_SAP_BSPAN
+	    || pdu->dsap != LLC_SAP_BSPAN
+	    || pdu->ctrl_1 != LLC_PDU_TYPE_U)
+		goto err;
+
+	__br_stp_rcv(skb, dev, dest);
+
  err:
 	kfree_skb(skb);
 	return 0;
 }
+
+void br_stp_rcv_raw(struct sk_buff *skb, struct net_device *dev)
+{
+	rcu_read_lock();
+	__br_stp_rcv(skb, dev, NULL);
+	rcu_read_unlock();
+}
--- linux-2.6.x/include/linux/if_bridge.h	19 Oct 2004 06:13:18 -0000	1.1.1.6
+++ linux-2.6.x/include/linux/if_bridge.h	11 Aug 2006 04:10:04 -0000
@@ -107,6 +107,7 @@ struct __fdb_entry
 extern void brioctl_set(int (*ioctl_hook)(unsigned int, void __user *));
 extern int (*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff **pskb);
 extern int (*br_should_route_hook)(struct sk_buff **pskb);
+extern void br_stp_rcv_raw(struct sk_buff *skb, struct net_device *dev);
 
 #endif
 
--- linux-2.6.x/include/linux/if_ether.h	18 Jun 2006 23:30:44 -0000	1.1.1.11
+++ linux-2.6.x/include/linux/if_ether.h	11 Aug 2006 04:10:04 -0000
@@ -55,6 +55,7 @@
 #define ETH_P_DIAG      0x6005          /* DEC Diagnostics              */
 #define ETH_P_CUST      0x6006          /* DEC Customer use             */
 #define ETH_P_SCA       0x6007          /* DEC Systems Comms Arch       */
+#define ETH_P_BRIDGE    0x6558          /* Transparent Ethernet Bridging */
 #define ETH_P_RARP      0x8035		/* Reverse Addr Res packet	*/
 #define ETH_P_ATALK	0x809B		/* Appletalk DDP		*/
 #define ETH_P_AARP	0x80F3		/* Appletalk AARP		*/

[-- Attachment #3: gre-bridge-iproute2.patch --]
[-- Type: text/x-diff, Size: 2805 bytes --]

Index: iproute2/ip/iptunnel.c
===================================================================
RCS file: /cvs/sw/new-wave/user/iproute2/ip/iptunnel.c,v
retrieving revision 1.4
diff -u -p -r1.4 iptunnel.c
--- iproute2/ip/iptunnel.c	17 Sep 2003 10:04:44 -0000	1.4
+++ iproute2/ip/iptunnel.c	11 Aug 2006 04:19:50 -0000
@@ -30,6 +30,7 @@
 #include <net/if_arp.h>
 #include <netinet/in.h>
 
+#include "../lib/if_ether.h"
 #include "if_tunnel.h"
 #include "rt_names.h"
 #include "utils.h"
@@ -186,11 +187,20 @@ static int parse_args(int argc, char **a
 				p->iph.protocol = IPPROTO_IPIP;
 			} else if (strcmp(*argv, "gre") == 0 ||
 				   strcmp(*argv, "gre/ip") == 0) {
-				if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
+				if (p->iph.protocol && (p->iph.protocol != IPPROTO_GRE || p->iph.id != 0)) {
 					fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
 					exit(-1);
 				}
 				p->iph.protocol = IPPROTO_GRE;
+			} else if (strcmp(*argv, "eogre") == 0 ||
+				   strcmp(*argv, "ether/gre") == 0 ||
+				   strcmp(*argv, "ether/gre/ip") == 0) {
+				if (p->iph.protocol && (p->iph.protocol != IPPROTO_GRE || p->iph.id != htons(ETH_P_BRIDGE))) {
+					fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
+					exit(-1);
+				}
+				p->iph.protocol = IPPROTO_GRE;
+				p->iph.id = htons(ETH_P_BRIDGE);
 			} else if (strcmp(*argv, "sit") == 0 ||
 				   strcmp(*argv, "ipv6/ip") == 0) {
 				if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
@@ -409,8 +419,9 @@ void print_tunnel(struct ip_tunnel_parm 
 	inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
 	inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
 
-	printf("%s: %s/ip  remote %s  local %s ",
+	printf("%s: %s%s/ip  remote %s  local %s ",
 	       p->name,
+	       p->iph.id == htons(ETH_P_BRIDGE) ? "ether/" : "",
 	       p->iph.protocol == IPPROTO_IPIP ? "ip" :
 	       (p->iph.protocol == IPPROTO_GRE ? "gre" :
 		(p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
Index: iproute2/lib/if_ether.h
===================================================================
RCS file: /cvs/sw/new-wave/user/iproute2/lib/if_ether.h,v
retrieving revision 1.1
diff -u -p -r1.1 if_ether.h
--- iproute2/lib/if_ether.h	10 Sep 2003 05:25:08 -0000	1.1
+++ iproute2/lib/if_ether.h	11 Aug 2006 04:19:50 -0000
@@ -53,6 +53,7 @@
 #define ETH_P_DIAG      0x6005          /* DEC Diagnostics              */
 #define ETH_P_CUST      0x6006          /* DEC Customer use             */
 #define ETH_P_SCA       0x6007          /* DEC Systems Comms Arch       */
+#define ETH_P_BRIDGE    0x6558          /* Transparent Ethernet Bridging */
 #define ETH_P_RARP      0x8035		/* Reverse Addr Res packet	*/
 #define ETH_P_ATALK	0x809B		/* Appletalk DDP		*/
 #define ETH_P_AARP	0x80F3		/* Appletalk AARP		*/

WARNING: multiple messages have this Message-ID (diff)
From: Philip Craig <philipc@snapgear.com>
To: "Timo Teräs" <timo.teras@iki.fi>
Cc: bridge@lists.linux-foundation.org, netdev@vger.kernel.org
Subject: Re: bridging with gre tunnel
Date: Mon, 14 Jul 2008 10:41:49 +1000	[thread overview]
Message-ID: <487AA0CD.9090600@snapgear.com> (raw)
In-Reply-To: <4876FDF0.1030807@iki.fi>

[-- Attachment #1: Type: text/plain, Size: 445 bytes --]

Timo Teräs wrote:
> Is there newer/better patches to achieve this? Any thoughts about
> doing bridging with gre tunnels?

Here's a patch I did against 2.6.17.

I didn't submit this to mainline though because the userspace ABI
is ugly.  The basic problem is that the struct used for the ioctl
isn't extensible.  The only way I can think of to fix it properly
is to add configuration of gre using netlink, but I never got around
to doing that.  


[-- Attachment #2: gre-bridge-6.patch --]
[-- Type: text/x-diff, Size: 11231 bytes --]

Subject: ethernet over GRE

- ip tunnel show doesn't work
- iph.id field isn't really appropriate

--- linux-2.6.x/net/ipv4/ip_gre.c	18 Jun 2006 23:30:56 -0000	1.1.1.33
+++ linux-2.6.x/net/ipv4/ip_gre.c	11 Aug 2006 04:10:04 -0000
@@ -30,6 +30,9 @@
 #include <linux/igmp.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/if_ether.h>
+#include <linux/if_bridge.h>
+#include <linux/etherdevice.h>
+#include <linux/llc.h>
 
 #include <net/sock.h>
 #include <net/ip.h>
@@ -119,6 +122,7 @@
 
 static int ipgre_tunnel_init(struct net_device *dev);
 static void ipgre_tunnel_setup(struct net_device *dev);
+static void ipgre_ether_tunnel_setup(struct net_device *dev);
 
 /* Fallback tunnel: no source, no destination, no key, no options */
 
@@ -274,7 +278,10 @@ static struct ip_tunnel * ipgre_tunnel_l
 			goto failed;
 	}
 
-	dev = alloc_netdev(sizeof(*t), name, ipgre_tunnel_setup);
+	if (parms->iph.id == htons(ETH_P_BRIDGE))
+		dev = alloc_netdev(sizeof(*t), name, ipgre_ether_tunnel_setup);
+	else
+		dev = alloc_netdev(sizeof(*t), name, ipgre_tunnel_setup);
 	if (!dev)
 	  return NULL;
 
@@ -550,6 +557,23 @@ ipgre_ecn_encapsulate(u8 tos, struct iph
 	return INET_ECN_encapsulate(tos, inner);
 }
 
+static __be16 ipgre_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	if (skb->protocol == htons(ETH_P_BRIDGE)) {
+		if (!pskb_may_pull(skb, ETH_HLEN))
+			return 0;
+		return eth_type_trans(skb, dev);
+	}
+#if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
+	else if (skb->protocol == htons(LLC_SAP_BSPAN)) {
+		br_stp_rcv_raw(skb, dev);
+		return 0;
+	}
+#endif
+
+	return 0;
+}
+
 static int ipgre_rcv(struct sk_buff *skb)
 {
 	struct iphdr *iph;
@@ -645,6 +669,13 @@ static int ipgre_rcv(struct sk_buff *skb
 			}
 			tunnel->i_seqno = seqno + 1;
 		}
+		if (tunnel->dev->type == ARPHRD_ETHER) {
+			skb->protocol = ipgre_type_trans(skb, tunnel->dev);
+			if (!skb->protocol) {
+				tunnel->stat.rx_errors++;
+				goto drop;
+			}
+		}
 		tunnel->stat.rx_packets++;
 		tunnel->stat.rx_bytes += skb->len;
 		skb->dev = tunnel->dev;
@@ -678,6 +709,7 @@ static int ipgre_tunnel_xmit(struct sk_b
 	struct iphdr  *iph;			/* Our new IP header */
 	int    max_headroom;			/* The extra header space needed */
 	int    gre_hlen;
+	int    push_hlen;
 	u32    dst;
 	int    mtu;
 
@@ -686,11 +718,18 @@ static int ipgre_tunnel_xmit(struct sk_b
 		goto tx_error;
 	}
 
-	if (dev->hard_header) {
-		gre_hlen = 0;
+	if (dev->type == ARPHRD_ETHER) {
+		skb->protocol = htons(ETH_P_BRIDGE);
+		gre_hlen = tunnel->hlen - ETH_HLEN;
+		push_hlen = gre_hlen;
+		tiph = &tunnel->parms.iph;
+	} else if (dev->hard_header) {
+		gre_hlen = tunnel->hlen;
+		push_hlen = 0;
 		tiph = (struct iphdr*)skb->data;
 	} else {
 		gre_hlen = tunnel->hlen;
+		push_hlen = gre_hlen;
 		tiph = &tunnel->parms.iph;
 	}
 
@@ -792,7 +831,8 @@ static int ipgre_tunnel_xmit(struct sk_b
 			}
 		}
 
-		if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) {
+		if (mtu >= IPV6_MIN_MTU &&
+		    mtu < skb->len - tunnel->hlen + push_hlen) {
 			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
 			ip_rt_put(rt);
 			goto tx_error;
@@ -809,7 +849,7 @@ static int ipgre_tunnel_xmit(struct sk_b
 			tunnel->err_count = 0;
 	}
 
-	max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen;
+	max_headroom = LL_RESERVED_SPACE(tdev) + push_hlen;
 
 	if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {
 		struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
@@ -828,7 +868,7 @@ static int ipgre_tunnel_xmit(struct sk_b
 	}
 
 	skb->h.raw = skb->nh.raw;
-	skb->nh.raw = skb_push(skb, gre_hlen);
+	skb->nh.raw = skb_push(skb, push_hlen);
 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
 			      IPSKB_REROUTED);
@@ -863,7 +903,7 @@ static int ipgre_tunnel_xmit(struct sk_b
 	((u16*)(iph+1))[1] = skb->protocol;
 
 	if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {
-		u32 *ptr = (u32*)(((u8*)iph) + tunnel->hlen - 4);
+		u32 *ptr = (u32*)(((u8*)iph) + gre_hlen - 4);
 
 		if (tunnel->parms.o_flags&GRE_SEQ) {
 			++tunnel->o_seqno;
@@ -935,6 +975,8 @@ ipgre_tunnel_ioctl (struct net_device *d
 		    p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)) ||
 		    ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING)))
 			goto done;
+		if (p.iph.id != 0 && p.iph.id != htons(ETH_P_BRIDGE))
+			goto done;
 		if (p.iph.ttl)
 			p.iph.frag_off |= htons(IP_DF);
 
@@ -956,7 +998,9 @@ ipgre_tunnel_ioctl (struct net_device *d
 
 				t = netdev_priv(dev);
 
-				if (MULTICAST(p.iph.daddr))
+				if (t->dev->type == ARPHRD_ETHER)
+					nflags = IFF_BROADCAST;
+				else if (MULTICAST(p.iph.daddr))
 					nflags = IFF_BROADCAST;
 				else if (p.iph.daddr)
 					nflags = IFF_POINTOPOINT;
@@ -1147,6 +1191,18 @@ static void ipgre_tunnel_setup(struct ne
 	dev->addr_len		= 4;
 }
 
+static void ipgre_ether_tunnel_setup(struct net_device *dev)
+{
+	ether_setup(dev);
+
+	SET_MODULE_OWNER(dev);
+	dev->uninit		= ipgre_tunnel_uninit;
+	dev->destructor 	= free_netdev;
+	dev->hard_start_xmit	= ipgre_tunnel_xmit;
+	dev->get_stats		= ipgre_tunnel_get_stats;
+	dev->do_ioctl		= ipgre_tunnel_ioctl;
+}
+
 static int ipgre_tunnel_init(struct net_device *dev)
 {
 	struct net_device *tdev = NULL;
@@ -1162,8 +1218,27 @@ static int ipgre_tunnel_init(struct net_
 	tunnel->dev = dev;
 	strcpy(tunnel->parms.name, dev->name);
 
-	memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
-	memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
+	if (dev->type == ARPHRD_ETHER)
+		random_ether_addr(dev->dev_addr);
+	else {
+		memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
+		memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
+	}
+
+	if (dev->type == ARPHRD_ETHER)
+		dev->flags |= IFF_BROADCAST;
+#ifdef CONFIG_NET_IPGRE_BROADCAST
+	else if (MULTICAST(iph->daddr)) {
+		if (!iph->saddr)
+			return -EINVAL;
+		dev->flags = IFF_BROADCAST;
+		dev->hard_header = ipgre_header;
+		dev->open = ipgre_open;
+		dev->stop = ipgre_close;
+	}
+#endif
+	else if (iph->daddr)
+		dev->flags |= IFF_POINTOPOINT;
 
 	/* Guess output device to choose reasonable mtu and hard_header_len */
 
@@ -1179,19 +1254,6 @@ static int ipgre_tunnel_init(struct net_
 			tdev = rt->u.dst.dev;
 			ip_rt_put(rt);
 		}
-
-		dev->flags |= IFF_POINTOPOINT;
-
-#ifdef CONFIG_NET_IPGRE_BROADCAST
-		if (MULTICAST(iph->daddr)) {
-			if (!iph->saddr)
-				return -EINVAL;
-			dev->flags = IFF_BROADCAST;
-			dev->hard_header = ipgre_header;
-			dev->open = ipgre_open;
-			dev->stop = ipgre_close;
-		}
-#endif
 	}
 
 	if (!tdev && tunnel->parms.link)
@@ -1212,6 +1274,8 @@ static int ipgre_tunnel_init(struct net_
 		if (tunnel->parms.o_flags&GRE_SEQ)
 			addend += 4;
 	}
+	if (dev->type == ARPHRD_ETHER)
+		addend += ETH_HLEN;
 	dev->hard_header_len = hlen + addend;
 	dev->mtu = mtu - addend;
 	tunnel->hlen = addend;
--- linux-2.6.x/net/bridge/br_netfilter.c	18 Jun 2006 23:30:55 -0000	1.1.1.25
+++ linux-2.6.x/net/bridge/br_netfilter.c	11 Aug 2006 04:10:04 -0000
@@ -765,14 +765,28 @@ out:
 	return NF_STOLEN;
 }
 
+/*
+ * We've finished passing through netfilter, so we can remove the fake dst.
+ * This is required by some lower layers, eg ip_gre
+ */
+static int br_nf_dev_queue_xmit_finish(struct sk_buff *skb)
+{
+	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
+		dst_release(skb->dst);
+		skb->dst = NULL;
+	}
+
+	return br_dev_queue_push_xmit(skb);
+}
+
 static int br_nf_dev_queue_xmit(struct sk_buff *skb)
 {
 	if (skb->protocol == htons(ETH_P_IP) &&
 	    skb->len > skb->dev->mtu &&
 	    !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size))
-		return ip_fragment(skb, br_dev_queue_push_xmit);
+		return ip_fragment(skb, br_nf_dev_queue_xmit_finish);
 	else
-		return br_dev_queue_push_xmit(skb);
+		return br_nf_dev_queue_xmit_finish(skb);
 }
 
 /* PF_BRIDGE/POST_ROUTING ********************************************/
--- linux-2.6.x/net/bridge/br_stp_bpdu.c	18 Jun 2006 23:30:55 -0000	1.1.1.9
+++ linux-2.6.x/net/bridge/br_stp_bpdu.c	11 Aug 2006 04:10:04 -0000
@@ -124,35 +124,23 @@ void br_send_tcn_bpdu(struct net_bridge_
 	br_send_bpdu(p, buf, 7);
 }
 
-/*
- * Called from llc.
- *
- * NO locks, but rcu_read_lock (preempt_disabled)
- */
-int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
-	       struct packet_type *pt, struct net_device *orig_dev)
+static void __br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
+			 const unsigned char *dest)
 {
-	const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
-	const unsigned char *dest = eth_hdr(skb)->h_dest;
 	struct net_bridge_port *p = rcu_dereference(dev->br_port);
 	struct net_bridge *br;
 	const unsigned char *buf;
 
 	if (!p)
-		goto err;
-
-	if (pdu->ssap != LLC_SAP_BSPAN
-	    || pdu->dsap != LLC_SAP_BSPAN
-	    || pdu->ctrl_1 != LLC_PDU_TYPE_U)
-		goto err;
+		return;
 
 	if (!pskb_may_pull(skb, 4))
-		goto err;
+		return;
 
 	/* compare of protocol id and version */
 	buf = skb->data;
 	if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
-		goto err;
+		return;
 
 	br = p->br;
 	spin_lock(&br->lock);
@@ -162,7 +150,7 @@ int br_stp_rcv(struct sk_buff *skb, stru
 	    || !(br->dev->flags & IFF_UP))
 		goto out;
 
-	if (compare_ether_addr(dest, br->group_addr) != 0)
+	if (dest && compare_ether_addr(dest, br->group_addr) != 0)
 		goto out;
 
 	buf = skb_pull(skb, 3);
@@ -213,7 +201,34 @@ int br_stp_rcv(struct sk_buff *skb, stru
 	}
  out:
 	spin_unlock(&br->lock);
+}
+
+/*
+ * Called from llc.
+ *
+ * NO locks, but rcu_read_lock (preempt_disabled)
+ */
+int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
+	       struct packet_type *pt, struct net_device *orig_dev)
+{
+	const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
+	const unsigned char *dest = eth_hdr(skb)->h_dest;
+
+	if (pdu->ssap != LLC_SAP_BSPAN
+	    || pdu->dsap != LLC_SAP_BSPAN
+	    || pdu->ctrl_1 != LLC_PDU_TYPE_U)
+		goto err;
+
+	__br_stp_rcv(skb, dev, dest);
+
  err:
 	kfree_skb(skb);
 	return 0;
 }
+
+void br_stp_rcv_raw(struct sk_buff *skb, struct net_device *dev)
+{
+	rcu_read_lock();
+	__br_stp_rcv(skb, dev, NULL);
+	rcu_read_unlock();
+}
--- linux-2.6.x/include/linux/if_bridge.h	19 Oct 2004 06:13:18 -0000	1.1.1.6
+++ linux-2.6.x/include/linux/if_bridge.h	11 Aug 2006 04:10:04 -0000
@@ -107,6 +107,7 @@ struct __fdb_entry
 extern void brioctl_set(int (*ioctl_hook)(unsigned int, void __user *));
 extern int (*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff **pskb);
 extern int (*br_should_route_hook)(struct sk_buff **pskb);
+extern void br_stp_rcv_raw(struct sk_buff *skb, struct net_device *dev);
 
 #endif
 
--- linux-2.6.x/include/linux/if_ether.h	18 Jun 2006 23:30:44 -0000	1.1.1.11
+++ linux-2.6.x/include/linux/if_ether.h	11 Aug 2006 04:10:04 -0000
@@ -55,6 +55,7 @@
 #define ETH_P_DIAG      0x6005          /* DEC Diagnostics              */
 #define ETH_P_CUST      0x6006          /* DEC Customer use             */
 #define ETH_P_SCA       0x6007          /* DEC Systems Comms Arch       */
+#define ETH_P_BRIDGE    0x6558          /* Transparent Ethernet Bridging */
 #define ETH_P_RARP      0x8035		/* Reverse Addr Res packet	*/
 #define ETH_P_ATALK	0x809B		/* Appletalk DDP		*/
 #define ETH_P_AARP	0x80F3		/* Appletalk AARP		*/

[-- Attachment #3: gre-bridge-iproute2.patch --]
[-- Type: text/x-diff, Size: 2805 bytes --]

Index: iproute2/ip/iptunnel.c
===================================================================
RCS file: /cvs/sw/new-wave/user/iproute2/ip/iptunnel.c,v
retrieving revision 1.4
diff -u -p -r1.4 iptunnel.c
--- iproute2/ip/iptunnel.c	17 Sep 2003 10:04:44 -0000	1.4
+++ iproute2/ip/iptunnel.c	11 Aug 2006 04:19:50 -0000
@@ -30,6 +30,7 @@
 #include <net/if_arp.h>
 #include <netinet/in.h>
 
+#include "../lib/if_ether.h"
 #include "if_tunnel.h"
 #include "rt_names.h"
 #include "utils.h"
@@ -186,11 +187,20 @@ static int parse_args(int argc, char **a
 				p->iph.protocol = IPPROTO_IPIP;
 			} else if (strcmp(*argv, "gre") == 0 ||
 				   strcmp(*argv, "gre/ip") == 0) {
-				if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
+				if (p->iph.protocol && (p->iph.protocol != IPPROTO_GRE || p->iph.id != 0)) {
 					fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
 					exit(-1);
 				}
 				p->iph.protocol = IPPROTO_GRE;
+			} else if (strcmp(*argv, "eogre") == 0 ||
+				   strcmp(*argv, "ether/gre") == 0 ||
+				   strcmp(*argv, "ether/gre/ip") == 0) {
+				if (p->iph.protocol && (p->iph.protocol != IPPROTO_GRE || p->iph.id != htons(ETH_P_BRIDGE))) {
+					fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
+					exit(-1);
+				}
+				p->iph.protocol = IPPROTO_GRE;
+				p->iph.id = htons(ETH_P_BRIDGE);
 			} else if (strcmp(*argv, "sit") == 0 ||
 				   strcmp(*argv, "ipv6/ip") == 0) {
 				if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
@@ -409,8 +419,9 @@ void print_tunnel(struct ip_tunnel_parm 
 	inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
 	inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
 
-	printf("%s: %s/ip  remote %s  local %s ",
+	printf("%s: %s%s/ip  remote %s  local %s ",
 	       p->name,
+	       p->iph.id == htons(ETH_P_BRIDGE) ? "ether/" : "",
 	       p->iph.protocol == IPPROTO_IPIP ? "ip" :
 	       (p->iph.protocol == IPPROTO_GRE ? "gre" :
 		(p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
Index: iproute2/lib/if_ether.h
===================================================================
RCS file: /cvs/sw/new-wave/user/iproute2/lib/if_ether.h,v
retrieving revision 1.1
diff -u -p -r1.1 if_ether.h
--- iproute2/lib/if_ether.h	10 Sep 2003 05:25:08 -0000	1.1
+++ iproute2/lib/if_ether.h	11 Aug 2006 04:19:50 -0000
@@ -53,6 +53,7 @@
 #define ETH_P_DIAG      0x6005          /* DEC Diagnostics              */
 #define ETH_P_CUST      0x6006          /* DEC Customer use             */
 #define ETH_P_SCA       0x6007          /* DEC Systems Comms Arch       */
+#define ETH_P_BRIDGE    0x6558          /* Transparent Ethernet Bridging */
 #define ETH_P_RARP      0x8035		/* Reverse Addr Res packet	*/
 #define ETH_P_ATALK	0x809B		/* Appletalk DDP		*/
 #define ETH_P_AARP	0x80F3		/* Appletalk AARP		*/

  parent reply	other threads:[~2008-07-14  0:41 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-07-11  6:30 [Bridge] bridging with gre tunnel Timo Teräs
2008-07-11  6:30 ` Timo Teräs
2008-07-11 19:07 ` [Bridge] " Stephen Hemminger
2008-07-11 19:07   ` Stephen Hemminger
2008-07-14  0:41 ` Philip Craig [this message]
2008-07-14  0:41   ` Philip Craig
2008-07-14  7:25   ` [Bridge] " Timo Teräs
2008-07-14  7:25     ` Timo Teräs
2008-07-14  8:44     ` [Bridge] " Philip Craig
2008-07-14  8:44       ` Philip Craig
2008-07-14  9:13       ` [Bridge] " James Chapman
2008-07-14  9:13         ` James Chapman
2008-07-14  9:44       ` [Bridge] " Timo Teräs
2008-07-14  9:44         ` Timo Teräs
2008-07-14 11:45       ` [Bridge] " Herbert Xu
2008-07-14 11:45         ` Herbert Xu
2008-07-14 12:15         ` [Bridge] " Patrick McHardy
2008-07-14 12:15           ` Patrick McHardy
2008-07-14 12:20           ` Herbert Xu
2008-07-14 12:20             ` Herbert Xu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=487AA0CD.9090600@snapgear.com \
    --to=philipc@snapgear.com \
    --cc=bridge@lists.linux-foundation.org \
    --cc=netdev@vger.kernel.org \
    --cc=timo.teras@iki.fi \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.