PATCH : [Re: [Fwd: broadcast over gre tunnel?]]

Linux Advanced Routing and Traffic Control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Guys,

Here is our patch to allow broadcast packets over a GRE tunnel.
Hopefully it might be accepted into the source someday.

You need to enabled bridging and GRE tunnels in your kernel.  No other
options are required.  The gre patch determines what type of protocol
type to put in the GRE header based on the whether the packet is
forwarded from a bridge or not.

To use the patch:

# Create your GRE tunnel
ip tunnel add gre1 mode gre remote 10.4.4.1 local 10.4.4.2
ifconfig gre1 up

# Bring the ethernet device up
ifconfig eth1 up

#create the bridge and add the devices:
brctl addbr br0
brctl addif br0 gre1
brctl addif br0 eth1

ifconfig br0 10.4.1.1 netmask 255.255.255.0 broadcast 10.4.1.255


regards



-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Damion de Soto - Software Engineer email: damion@xxxxxxxxxxxx SnapGear --- ph: +61 7 3435 2809 | Custom Embedded Solutions fax: +61 7 3891 3630 | and Security Appliances web: http://www.snapgear.com ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- Free Embedded Linux Distro at http://www.snapgear.org ---
diff -ru linux-2.4.22/include/linux/if_tunnel.h linux-2.4.22.patched/include/linux/if_tunnel.h
--- linux-2.4.22/include/linux/if_tunnel.h	Mon Dec  1 08:00:38 1997
+++ linux-2.4.22.patched/include/linux/if_tunnel.h	Wed Oct  8 15:26:00 2003
@@ -15,6 +15,8 @@
 #define GRE_FLAGS	__constant_htons(0x00F8)
 #define GRE_VERSION	__constant_htons(0x0007)
 
+#define GRE_P_ETH_BR	__constant_htons(0x6558)
+
 struct ip_tunnel_parm
 {
 	char			name[IFNAMSIZ];
diff -ru linux-2.4.22/net/bridge/br_if.c linux-2.4.22.patched/net/bridge/br_if.c
--- linux-2.4.22/net/bridge/br_if.c	Mon Aug 25 21:44:44 2003
+++ linux-2.4.22.patched/net/bridge/br_if.c	Wed Oct  8 14:45:46 2003
@@ -226,8 +226,10 @@
 	if (dev->br_port != NULL)
 		return -EBUSY;
 
+#if 0
 	if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)
 		return -EINVAL;
+#endif
 
 	if (dev->hard_start_xmit == br_dev_xmit)
 		return -ELOOP;
diff -ru linux-2.4.22/net/ipv4/ip_gre.c linux-2.4.22.patched/net/ipv4/ip_gre.c
--- linux-2.4.22/net/ipv4/ip_gre.c	Mon Aug 25 21:44:44 2003
+++ linux-2.4.22.patched/net/ipv4/ip_gre.c	Wed Oct  8 14:45:46 2003
@@ -18,6 +18,7 @@
 #include <asm/uaccess.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include <linux/in.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
@@ -119,6 +120,14 @@
 
 static int ipgre_fb_tunnel_init(struct net_device *dev);
 
+
+/*
+ * we need a special function to be able to be able to pull the ethernet
+ * buffer out.  It is nearly the same as the eth_type_trans structure except
+ * the header size is adjusted.
+ */
+unsigned short gre_eth_type_trans(struct sk_buff *skb, struct net_device *dev);
+
 static struct net_device ipgre_fb_tunnel_dev = {
 	"gre0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, ipgre_fb_tunnel_init,
 };
@@ -566,6 +575,7 @@
 	u32    seqno = 0;
 	struct ip_tunnel *tunnel;
 	int    offset = 4;
+	unsigned short 	proto;
 
 	if (!pskb_may_pull(skb, 16))
 		goto drop_nolock;
@@ -573,6 +583,7 @@
 	iph = skb->nh.iph;
 	h = skb->data;
 	flags = *(u16*)h;
+	proto = *(u16*)(h+2);
 
 	if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) {
 		/* - Version must be 0.
@@ -606,23 +617,6 @@
 
 	read_lock(&ipgre_lock);
 	if ((tunnel = ipgre_tunnel_lookup(iph->saddr, iph->daddr, key)) != NULL) {
-		skb->mac.raw = skb->nh.raw;
-		skb->nh.raw = __pskb_pull(skb, offset);
-		memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
-		if (skb->ip_summed == CHECKSUM_HW)
-			skb->csum = csum_sub(skb->csum,
-					     csum_partial(skb->mac.raw, skb->nh.raw-skb->mac.raw, 0));
-		skb->protocol = *(u16*)(h + 2);
-		skb->pkt_type = PACKET_HOST;
-#ifdef CONFIG_NET_IPGRE_BROADCAST
-		if (MULTICAST(iph->daddr)) {
-			/* Looped back packet, drop it! */
-			if (((struct rtable*)skb->dst)->key.iif == 0)
-				goto drop;
-			tunnel->stat.multicast++;
-			skb->pkt_type = PACKET_BROADCAST;
-		}
-#endif
 
 		if (((flags&GRE_CSUM) && csum) ||
 		    (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) {
@@ -639,6 +633,70 @@
 			}
 			tunnel->i_seqno = seqno + 1;
 		}
+
+		if (proto == GRE_P_ETH_BR) {
+			struct sk_buff *skb2;
+
+			/* Pull off the offset. */
+			skb->mac.raw = __pskb_pull(skb, offset);
+//#define OLD_WAY
+#ifndef OLD_WAY
+			/* ensure it is linear so we can simply copy the data out */
+			skb_linearize(skb, GFP_ATOMIC);
+
+			skb2 = dev_alloc_skb(skb->len+2);
+			if (!skb2) {
+				printk(KERN_ERR "Memory squeeze.\n");
+				goto drop;
+			}
+
+			skb2->dev = tunnel->dev;
+
+			/* Packet allignment apparently.  */
+			skb_reserve(skb2, 2);
+
+			/* copy data and then set length */
+			memcpy(skb2->data, skb->data, skb->len);
+			skb_put(skb2, skb->len);
+
+			/* setup protocol */
+			skb2->protocol = gre_eth_type_trans(skb2, tunnel->dev);
+
+			/* update counters */
+			tunnel->stat.rx_packets++;
+			tunnel->stat.rx_bytes += skb->len;
+
+#ifdef CONFIG_NETFILTER
+			nf_conntrack_put(skb->nfct);
+#endif
+			kfree_skb(skb);
+
+			netif_rx(skb2);
+
+			read_unlock(&ipgre_lock);
+			return(0);
+#else
+			skb->protocol = gre_eth_type_trans(skb, tunnel->dev);
+#endif
+		} else {
+			skb->mac.raw = skb->nh.raw;
+			skb->nh.raw = __pskb_pull(skb, offset);
+			memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+			if (skb->ip_summed == CHECKSUM_HW)
+				skb->csum = csum_sub(skb->csum,
+							 csum_partial(skb->mac.raw, skb->nh.raw-skb->mac.raw, 0));
+			skb->protocol = *(u16*)(h + 2);
+			skb->pkt_type = PACKET_HOST;
+#ifdef CONFIG_NET_IPGRE_BROADCAST
+			if (MULTICAST(iph->daddr)) {
+				/* Looped back packet, drop it! */
+				if (((struct rtable*)skb->dst)->key.iif == 0)
+					goto drop;
+				tunnel->stat.multicast++;
+				skb->pkt_type = PACKET_BROADCAST;
+			}
+#endif
+		}
 		tunnel->stat.rx_packets++;
 		tunnel->stat.rx_bytes += skb->len;
 		skb->dev = tunnel->dev;
@@ -687,6 +745,8 @@
 	u32    dst;
 	int    mtu;
 
+//	printk(KERN_INFO "xmit - %d\n", skb->len);
+
 	if (tunnel->recursion++) {
 		tunnel->stat.collisions++;
 		goto tx_error;
@@ -736,7 +796,7 @@
 			dst = addr6->s6_addr32[3];
 		}
 #endif
-		else
+		else 
 			goto tx_error;
 	}
 
@@ -765,38 +825,44 @@
 	else
 		mtu = skb->dst ? skb->dst->pmtu : dev->mtu;
 
-	if (skb->protocol == htons(ETH_P_IP)) {
-		if (skb->dst && mtu < skb->dst->pmtu && mtu >= 68)
-			skb->dst->pmtu = mtu;
-
-		df |= (old_iph->frag_off&htons(IP_DF));
-
-		if ((old_iph->frag_off&htons(IP_DF)) &&
-		    mtu < ntohs(old_iph->tot_len)) {
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
-			ip_rt_put(rt);
-			goto tx_error;
+	/*
+	 * If we are not being used as an ethernet bridge, then we want to
+	 * honour fragmentation stuff.
+	 */
+	if (!dev->br_port) {
+		if (skb->protocol == __constant_htons(ETH_P_IP)) {
+			if (skb->dst && mtu < skb->dst->pmtu && mtu >= 68)
+				skb->dst->pmtu = mtu;
+
+			df |= (old_iph->frag_off&__constant_htons(IP_DF));
+
+			if ((old_iph->frag_off&__constant_htons(IP_DF)) &&
+				mtu < ntohs(old_iph->tot_len)) {
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+				ip_rt_put(rt);
+				goto tx_error;
+			}
 		}
-	}
 #ifdef CONFIG_IPV6
-	else if (skb->protocol == htons(ETH_P_IPV6)) {
-		struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
+		else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
+			struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
 
-		if (rt6 && mtu < rt6->u.dst.pmtu && mtu >= IPV6_MIN_MTU) {
-			if ((tunnel->parms.iph.daddr && !MULTICAST(tunnel->parms.iph.daddr)) ||
-			    rt6->rt6i_dst.plen == 128) {
-				rt6->rt6i_flags |= RTF_MODIFIED;
-				skb->dst->pmtu = mtu;
+			if (rt6 && mtu < rt6->u.dst.pmtu && mtu >= IPV6_MIN_MTU) {
+				if ((tunnel->parms.iph.daddr && !MULTICAST(tunnel->parms.iph.daddr)) ||
+					rt6->rt6i_dst.plen == 128) {
+					rt6->rt6i_flags |= RTF_MODIFIED;
+					skb->dst->pmtu = mtu;
+				}
 			}
-		}
 
-		if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) {
-			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
-			ip_rt_put(rt);
-			goto tx_error;
+			if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) {
+				icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
+				ip_rt_put(rt);
+				goto tx_error;
+			}
 		}
-	}
 #endif
+	}
 
 	if (tunnel->err_count > 0) {
 		if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) {
@@ -807,7 +873,10 @@
 			tunnel->err_count = 0;
 	}
 
-	skb->h.raw = skb->nh.raw;
+	/*
+	 * This is only the case for ethernet frames!!
+	 */
+	skb->h.raw = skb->mac.raw;
 
 	max_headroom = ((tdev->hard_header_len+15)&~15)+ gre_hlen;
 
@@ -831,6 +900,7 @@
 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 	dst_release(skb->dst);
 	skb->dst = &rt->u.dst;
+//	printk("%d-%s", skb->len, rt->u.dst.dev->name);
 
 	/*
 	 *	Push down and install the IPIP header.
@@ -839,7 +909,7 @@
 	iph 			=	skb->nh.iph;
 	iph->version		=	4;
 	iph->ihl		=	sizeof(struct iphdr) >> 2;
-	iph->frag_off		=	df;
+	iph->frag_off		=	0;//__constant_htons(IP_DF);
 	iph->protocol		=	IPPROTO_GRE;
 	iph->tos		=	ipgre_ecn_encapsulate(tos, old_iph, skb);
 	iph->daddr		=	rt->rt_dst;
@@ -857,7 +927,11 @@
 	}
 
 	((u16*)(iph+1))[0] = tunnel->parms.o_flags;
-	((u16*)(iph+1))[1] = skb->protocol;
+
+	if (dev->br_port)
+		((u16*)(iph+1))[1] = GRE_P_ETH_BR;
+	else
+		((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);
@@ -1254,6 +1328,63 @@
 	dev_hold(dev);
 	tunnels_wc[0]		= &ipgre_fb_tunnel;
 	return 0;
+}
+
+unsigned short gre_eth_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ethhdr *eth;
+	unsigned char *rawp;
+
+	skb->mac.raw=skb->data;
+
+	/*
+	 * hack - we don't actually want to pull the hard header length, we
+	 * want to pull the ethernet frame header.
+	 */
+	skb_pull(skb, ETH_HLEN);
+	eth= skb->mac.ethernet;
+	
+	if(*eth->h_dest&1)
+	{
+		if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+			skb->pkt_type=PACKET_BROADCAST;
+		else
+			skb->pkt_type=PACKET_MULTICAST;
+	}
+	
+	/*
+	 *	This ALLMULTI check should be redundant by 1.4
+	 *	so don't forget to remove it.
+	 *
+	 *	Seems, you forgot to remove it. All silly devices
+	 *	seems to set IFF_PROMISC.
+	 */
+	 
+	else if(1 /*dev->flags&IFF_PROMISC*/)
+	{
+		if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
+			skb->pkt_type=PACKET_OTHERHOST;
+	}
+	
+	if (ntohs(eth->h_proto) >= 1536)
+		return eth->h_proto;
+		
+	rawp = skb->data;
+	
+	/*
+	 *	This is a magic hack to spot IPX packets. Older Novell breaks
+	 *	the protocol design and runs IPX over 802.3 without an 802.2 LLC
+	 *	layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+	 *	won't work for fault tolerant netware but does for the rest.
+	 */
+	if (*(unsigned short *)rawp == 0xFFFF)
+		return htons(ETH_P_802_3);
+		
+	/*
+	 *	Real 802.2 LLC
+	 */
+	return htons(ETH_P_802_2);
+	
 }


[Index of Archives]     [LARTC Home Page]     [Netfilter]     [Netfilter Development]     [Network Development]     [Bugtraq]     [GCC Help]     [Yosemite News]     [Linux Kernel]     [Fedora Users]
  Powered by Linux