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