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); + }