Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / net / ipv4 / ip_gre.c
index 4a227f5..0f9b3a3 100644 (file)
@@ -10,7 +10,7 @@
  *
  */
 
-#include <linux/config.h>
+#include <linux/capability.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/sched.h>
@@ -28,6 +28,7 @@
 #include <linux/inetdevice.h>
 #include <linux/igmp.h>
 #include <linux/netfilter_ipv4.h>
+#include <linux/if_ether.h>
 
 #include <net/sock.h>
 #include <net/ip.h>
@@ -36,6 +37,7 @@
 #include <net/ipip.h>
 #include <net/arp.h>
 #include <net/checksum.h>
+#include <net/dsfield.h>
 #include <net/inet_ecn.h>
 #include <net/xfrm.h>
 
@@ -151,7 +153,7 @@ static struct ip_tunnel *tunnels[4][HASH_SIZE];
 #define tunnels_l      (tunnels[1])
 #define tunnels_wc     (tunnels[0])
 
-static rwlock_t ipgre_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(ipgre_lock);
 
 /* Given src, dst and key, find appropriate for input tunnel. */
 
@@ -186,7 +188,7 @@ static struct ip_tunnel * ipgre_tunnel_lookup(u32 remote, u32 local, u32 key)
        }
 
        if (ipgre_fb_tunnel_dev->flags&IFF_UP)
-               return ipgre_fb_tunnel_dev->priv;
+               return netdev_priv(ipgre_fb_tunnel_dev);
        return NULL;
 }
 
@@ -276,7 +278,7 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int
          return NULL;
 
        dev->init = ipgre_tunnel_init;
-       nt = dev->priv;
+       nt = netdev_priv(dev);
        nt->parms = *parms;
 
        if (register_netdevice(dev) < 0) {
@@ -284,12 +286,8 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int
                goto failed;
        }
 
-       nt = dev->priv;
-       nt->parms = *parms;
-
        dev_hold(dev);
        ipgre_tunnel_link(nt);
-       /* Do not decrement MOD_USE_COUNT here. */
        return nt;
 
 failed:
@@ -298,12 +296,12 @@ failed:
 
 static void ipgre_tunnel_uninit(struct net_device *dev)
 {
-       ipgre_tunnel_unlink((struct ip_tunnel*)dev->priv);
+       ipgre_tunnel_unlink(netdev_priv(dev));
        dev_put(dev);
 }
 
 
-void ipgre_err(struct sk_buff *skb, u32 info)
+static void ipgre_err(struct sk_buff *skb, u32 info)
 {
 #ifndef I_WISH_WORLD_WERE_PERFECT
 
@@ -510,14 +508,14 @@ out:
 
        /* change mtu on this route */
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
-               if (rel_info > dst_pmtu(skb2->dst)) {
+               if (rel_info > dst_mtu(skb2->dst)) {
                        kfree_skb(skb2);
                        return;
                }
                skb2->dst->ops->update_pmtu(skb2->dst, rel_info);
                rel_info = htonl(rel_info);
        } else if (type == ICMP_TIME_EXCEEDED) {
-               struct ip_tunnel *t = (struct ip_tunnel*)skb2->dev->priv;
+               struct ip_tunnel *t = netdev_priv(skb2->dev);
                if (t->parms.iph.ttl) {
                        rel_type = ICMP_DEST_UNREACH;
                        rel_code = ICMP_HOST_UNREACH;
@@ -533,11 +531,9 @@ static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
 {
        if (INET_ECN_is_ce(iph->tos)) {
                if (skb->protocol == htons(ETH_P_IP)) {
-                       if (INET_ECN_is_not_ce(skb->nh.iph->tos))
-                               IP_ECN_set_ce(skb->nh.iph);
+                       IP_ECN_set_ce(skb->nh.iph);
                } else if (skb->protocol == htons(ETH_P_IPV6)) {
-                       if (INET_ECN_is_not_ce(ip6_get_dsfield(skb->nh.ipv6h)))
-                               IP6_ECN_set_ce(skb->nh.ipv6h);
+                       IP6_ECN_set_ce(skb->nh.ipv6h);
                }
        }
 }
@@ -549,11 +545,11 @@ ipgre_ecn_encapsulate(u8 tos, struct iphdr *old_iph, struct sk_buff *skb)
        if (skb->protocol == htons(ETH_P_IP))
                inner = old_iph->tos;
        else if (skb->protocol == htons(ETH_P_IPV6))
-               inner = ip6_get_dsfield((struct ipv6hdr*)old_iph);
+               inner = ipv6_get_dsfield((struct ipv6hdr *)old_iph);
        return INET_ECN_encapsulate(tos, inner);
 }
 
-int ipgre_rcv(struct sk_buff *skb)
+static int ipgre_rcv(struct sk_buff *skb)
 {
        struct iphdr *iph;
        u8     *h;
@@ -579,15 +575,16 @@ int ipgre_rcv(struct sk_buff *skb)
                        goto drop_nolock;
 
                if (flags&GRE_CSUM) {
-                       if (skb->ip_summed == CHECKSUM_HW) {
+                       switch (skb->ip_summed) {
+                       case CHECKSUM_HW:
                                csum = (u16)csum_fold(skb->csum);
-                               if (csum)
-                                       skb->ip_summed = CHECKSUM_NONE;
-                       }
-                       if (skb->ip_summed == CHECKSUM_NONE) {
-                               skb->csum = skb_checksum(skb, 0, skb->len, 0);
+                               if (!csum)
+                                       break;
+                               /* fall through */
+                       case CHECKSUM_NONE:
+                               skb->csum = 0;
+                               csum = __skb_checksum_complete(skb);
                                skb->ip_summed = CHECKSUM_HW;
-                               csum = (u16)csum_fold(skb->csum);
                        }
                        offset += 4;
                }
@@ -605,13 +602,21 @@ int ipgre_rcv(struct sk_buff *skb)
        if ((tunnel = ipgre_tunnel_lookup(iph->saddr, iph->daddr, key)) != NULL) {
                secpath_reset(skb);
 
+               skb->protocol = *(u16*)(h + 2);
+               /* WCCP version 1 and 2 protocol decoding.
+                * - Change protocol to IP
+                * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
+                */
+               if (flags == 0 &&
+                   skb->protocol == __constant_htons(ETH_P_WCCP)) {
+                       skb->protocol = __constant_htons(ETH_P_IP);
+                       if ((*(h + offset) & 0xF0) != 0x40) 
+                               offset += 4;
+               }
+
                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_postpull_rcsum(skb, skb->h.raw, offset);
                skb->pkt_type = PACKET_HOST;
 #ifdef CONFIG_NET_IPGRE_BROADCAST
                if (MULTICAST(iph->daddr)) {
@@ -643,19 +648,13 @@ int ipgre_rcv(struct sk_buff *skb)
                skb->dev = tunnel->dev;
                dst_release(skb->dst);
                skb->dst = NULL;
-#ifdef CONFIG_NETFILTER
-               nf_conntrack_put(skb->nfct);
-               skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-               skb->nf_debug = 0;
-#endif
-#endif
+               nf_reset(skb);
                ipgre_ecn_decapsulate(iph, skb);
                netif_rx(skb);
                read_unlock(&ipgre_lock);
                return(0);
        }
-       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
+       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 
 drop:
        read_unlock(&ipgre_lock);
@@ -666,7 +665,7 @@ drop_nolock:
 
 static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
+       struct ip_tunnel *tunnel = netdev_priv(dev);
        struct net_device_stats *stats = &tunnel->stat;
        struct iphdr  *old_iph = skb->nh.iph;
        struct iphdr  *tiph;
@@ -762,9 +761,9 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 
        df = tiph->frag_off;
        if (df)
-               mtu = dst_pmtu(&rt->u.dst) - tunnel->hlen;
+               mtu = dst_mtu(&rt->u.dst) - tunnel->hlen;
        else
-               mtu = skb->dst ? dst_pmtu(skb->dst) : dev->mtu;
+               mtu = skb->dst ? dst_mtu(skb->dst) : dev->mtu;
 
        if (skb->dst)
                skb->dst->ops->update_pmtu(skb->dst, mtu);
@@ -783,7 +782,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        else if (skb->protocol == htons(ETH_P_IPV6)) {
                struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
 
-               if (rt6 && mtu < dst_pmtu(skb->dst) && mtu >= IPV6_MIN_MTU) {
+               if (rt6 && mtu < dst_mtu(skb->dst) && mtu >= IPV6_MIN_MTU) {
                        if ((tunnel->parms.iph.daddr && !MULTICAST(tunnel->parms.iph.daddr)) ||
                            rt6->rt6i_dst.plen == 128) {
                                rt6->rt6i_flags |= RTF_MODIFIED;
@@ -829,6 +828,8 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        skb->h.raw = skb->nh.raw;
        skb->nh.raw = skb_push(skb, gre_hlen);
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+       IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
+                             IPSKB_REROUTED);
        dst_release(skb->dst);
        skb->dst = &rt->u.dst;
 
@@ -877,13 +878,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                }
        }
 
-#ifdef CONFIG_NETFILTER
-       nf_conntrack_put(skb->nfct);
-       skb->nfct = NULL;
-#ifdef CONFIG_NETFILTER_DEBUG
-       skb->nf_debug = 0;
-#endif
-#endif
+       nf_reset(skb);
 
        IPTUNNEL_XMIT();
        tunnel->recursion--;
@@ -917,7 +912,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                        t = ipgre_tunnel_locate(&p, 0);
                }
                if (t == NULL)
-                       t = (struct ip_tunnel*)dev->priv;
+                       t = netdev_priv(dev);
                memcpy(&p, &t->parms, sizeof(p));
                if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
                        err = -EFAULT;
@@ -957,7 +952,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                        } else {
                                unsigned nflags=0;
 
-                               t = (struct ip_tunnel*)dev->priv;
+                               t = netdev_priv(dev);
 
                                if (MULTICAST(p.iph.daddr))
                                        nflags = IFF_BROADCAST;
@@ -1006,7 +1001,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                        if ((t = ipgre_tunnel_locate(&p, 0)) == NULL)
                                goto done;
                        err = -EPERM;
-                       if (t == ipgre_fb_tunnel_dev->priv)
+                       if (t == netdev_priv(ipgre_fb_tunnel_dev))
                                goto done;
                        dev = t->dev;
                }
@@ -1023,12 +1018,12 @@ done:
 
 static struct net_device_stats *ipgre_tunnel_get_stats(struct net_device *dev)
 {
-       return &(((struct ip_tunnel*)dev->priv)->stat);
+       return &(((struct ip_tunnel*)netdev_priv(dev))->stat);
 }
 
 static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 {
-       struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
+       struct ip_tunnel *tunnel = netdev_priv(dev);
        if (new_mtu < 68 || new_mtu > 0xFFF8 - tunnel->hlen)
                return -EINVAL;
        dev->mtu = new_mtu;
@@ -1068,7 +1063,7 @@ static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 static int ipgre_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
                        void *daddr, void *saddr, unsigned len)
 {
-       struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
+       struct ip_tunnel *t = netdev_priv(dev);
        struct iphdr *iph = (struct iphdr *)skb_push(skb, t->hlen);
        u16 *p = (u16*)(iph+1);
 
@@ -1095,7 +1090,7 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev, unsigned sh
 
 static int ipgre_open(struct net_device *dev)
 {
-       struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
+       struct ip_tunnel *t = netdev_priv(dev);
 
        if (MULTICAST(t->parms.iph.daddr)) {
                struct flowi fl = { .oif = t->parms.link,
@@ -1109,17 +1104,17 @@ static int ipgre_open(struct net_device *dev)
                        return -EADDRNOTAVAIL;
                dev = rt->u.dst.dev;
                ip_rt_put(rt);
-               if (__in_dev_get(dev) == NULL)
+               if (__in_dev_get_rtnl(dev) == NULL)
                        return -EADDRNOTAVAIL;
                t->mlink = dev->ifindex;
-               ip_mc_inc_group(__in_dev_get(dev), t->parms.iph.daddr);
+               ip_mc_inc_group(__in_dev_get_rtnl(dev), t->parms.iph.daddr);
        }
        return 0;
 }
 
 static int ipgre_close(struct net_device *dev)
 {
-       struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
+       struct ip_tunnel *t = netdev_priv(dev);
        if (MULTICAST(t->parms.iph.daddr) && t->mlink) {
                struct in_device *in_dev = inetdev_by_index(t->mlink);
                if (in_dev) {
@@ -1144,7 +1139,7 @@ static void ipgre_tunnel_setup(struct net_device *dev)
 
        dev->type               = ARPHRD_IPGRE;
        dev->hard_header_len    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
-       dev->mtu                = 1500 - sizeof(struct iphdr) - 4;
+       dev->mtu                = ETH_DATA_LEN - sizeof(struct iphdr) - 4;
        dev->flags              = IFF_NOARP;
        dev->iflink             = 0;
        dev->addr_len           = 4;
@@ -1156,10 +1151,10 @@ static int ipgre_tunnel_init(struct net_device *dev)
        struct ip_tunnel *tunnel;
        struct iphdr *iph;
        int hlen = LL_MAX_HEADER;
-       int mtu = 1500;
+       int mtu = ETH_DATA_LEN;
        int addend = sizeof(struct iphdr) + 4;
 
-       tunnel = (struct ip_tunnel*)dev->priv;
+       tunnel = netdev_priv(dev);
        iph = &tunnel->parms.iph;
 
        tunnel->dev = dev;
@@ -1221,9 +1216,9 @@ static int ipgre_tunnel_init(struct net_device *dev)
        return 0;
 }
 
-int __init ipgre_fb_tunnel_init(struct net_device *dev)
+static int __init ipgre_fb_tunnel_init(struct net_device *dev)
 {
-       struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
+       struct ip_tunnel *tunnel = netdev_priv(dev);
        struct iphdr *iph = &tunnel->parms.iph;
 
        tunnel->dev = dev;
@@ -1240,7 +1235,7 @@ int __init ipgre_fb_tunnel_init(struct net_device *dev)
 }
 
 
-static struct inet_protocol ipgre_protocol = {
+static struct net_protocol ipgre_protocol = {
        .handler        =       ipgre_rcv,
        .err_handler    =       ipgre_err,
 };
@@ -1252,7 +1247,7 @@ static struct inet_protocol ipgre_protocol = {
 
 static int __init ipgre_init(void)
 {
-       int err = -EINVAL;
+       int err;
 
        printk(KERN_INFO "GRE over IPv4 tunneling driver\n");
 
@@ -1265,27 +1260,44 @@ static int __init ipgre_init(void)
                                           ipgre_tunnel_setup);
        if (!ipgre_fb_tunnel_dev) {
                err = -ENOMEM;
-               goto fail;
+               goto err1;
        }
 
        ipgre_fb_tunnel_dev->init = ipgre_fb_tunnel_init;
 
        if ((err = register_netdev(ipgre_fb_tunnel_dev)))
-               goto fail;
+               goto err2;
 out:
        return err;
-fail:
-       inet_del_protocol(&ipgre_protocol, IPPROTO_GRE);
+err2:
        free_netdev(ipgre_fb_tunnel_dev);
+err1:
+       inet_del_protocol(&ipgre_protocol, IPPROTO_GRE);
        goto out;
 }
 
-void ipgre_fini(void)
+static void __exit ipgre_destroy_tunnels(void)
+{
+       int prio;
+
+       for (prio = 0; prio < 4; prio++) {
+               int h;
+               for (h = 0; h < HASH_SIZE; h++) {
+                       struct ip_tunnel *t;
+                       while ((t = tunnels[prio][h]) != NULL)
+                               unregister_netdevice(t->dev);
+               }
+       }
+}
+
+static void __exit ipgre_fini(void)
 {
        if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0)
                printk(KERN_INFO "ipgre close: can't remove protocol\n");
 
-       unregister_netdev(ipgre_fb_tunnel_dev);
+       rtnl_lock();
+       ipgre_destroy_tunnels();
+       rtnl_unlock();
 }
 
 module_init(ipgre_init);