Remove obsolete #include <linux/config.h>
[cascardo/linux.git] / net / ipv6 / reassembly.c
index 5d316cb..4e299c6 100644 (file)
@@ -28,7 +28,6 @@
  *     YOSHIFUJI,H. @USAGI     Always remove fragment header to
  *                             calculate ICV correctly.
  */
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/string.h>
@@ -121,6 +120,10 @@ static __inline__ void fq_unlink(struct frag_queue *fq)
        write_unlock(&ip6_frag_lock);
 }
 
+/*
+ * callers should be careful not to use the hash value outside the ipfrag_lock
+ * as doing so could race with ipfrag_hash_rnd being recalculated.
+ */
 static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr,
                               struct in6_addr *daddr)
 {
@@ -203,7 +206,7 @@ static inline void frag_free_queue(struct frag_queue *fq, int *work)
 
 static inline struct frag_queue *frag_alloc_queue(void)
 {
-       struct frag_queue *fq = kmalloc(sizeof(struct frag_queue), GFP_ATOMIC);
+       struct frag_queue *fq = kzalloc(sizeof(struct frag_queue), GFP_ATOMIC);
 
        if(!fq)
                return NULL;
@@ -288,6 +291,7 @@ static void ip6_evictor(void)
 static void ip6_frag_expire(unsigned long data)
 {
        struct frag_queue *fq = (struct frag_queue *) data;
+       struct net_device *dev;
 
        spin_lock(&fq->lock);
 
@@ -299,22 +303,22 @@ static void ip6_frag_expire(unsigned long data)
        IP6_INC_STATS_BH(IPSTATS_MIB_REASMTIMEOUT);
        IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
 
-       /* Send error only if the first segment arrived. */
-       if (fq->last_in&FIRST_IN && fq->fragments) {
-               struct net_device *dev = dev_get_by_index(fq->iif);
+       /* Don't send error if the first segment did not arrive. */
+       if (!(fq->last_in&FIRST_IN) || !fq->fragments)
+               goto out;
 
-               /*
-                  But use as source device on which LAST ARRIVED
-                  segment was received. And do not use fq->dev
-                  pointer directly, device might already disappeared.
-                */
-               if (dev) {
-                       fq->fragments->dev = dev;
-                       icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
-                                   dev);
-                       dev_put(dev);
-               }
-       }
+       dev = dev_get_by_index(fq->iif);
+       if (!dev)
+               goto out;
+
+       /*
+          But use as source device on which LAST ARRIVED
+          segment was received. And do not use fq->dev
+          pointer directly, device might already disappeared.
+        */
+       fq->fragments->dev = dev;
+       icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0, dev);
+       dev_put(dev);
 out:
        spin_unlock(&fq->lock);
        fq_put(fq, NULL);
@@ -323,15 +327,16 @@ out:
 /* Creation primitives. */
 
 
-static struct frag_queue *ip6_frag_intern(unsigned int hash,
-                                         struct frag_queue *fq_in)
+static struct frag_queue *ip6_frag_intern(struct frag_queue *fq_in)
 {
        struct frag_queue *fq;
+       unsigned int hash;
 #ifdef CONFIG_SMP
        struct hlist_node *n;
 #endif
 
        write_lock(&ip6_frag_lock);
+       hash = ip6qhashfn(fq_in->id, &fq_in->saddr, &fq_in->daddr);
 #ifdef CONFIG_SMP
        hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) {
                if (fq->id == fq_in->id && 
@@ -361,15 +366,13 @@ static struct frag_queue *ip6_frag_intern(unsigned int hash,
 
 
 static struct frag_queue *
-ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst)
+ip6_frag_create(u32 id, struct in6_addr *src, struct in6_addr *dst)
 {
        struct frag_queue *fq;
 
        if ((fq = frag_alloc_queue()) == NULL)
                goto oom;
 
-       memset(fq, 0, sizeof(struct frag_queue));
-
        fq->id = id;
        ipv6_addr_copy(&fq->saddr, src);
        ipv6_addr_copy(&fq->daddr, dst);
@@ -380,7 +383,7 @@ ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr
        spin_lock_init(&fq->lock);
        atomic_set(&fq->refcnt, 1);
 
-       return ip6_frag_intern(hash, fq);
+       return ip6_frag_intern(fq);
 
 oom:
        IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
@@ -392,9 +395,10 @@ fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst)
 {
        struct frag_queue *fq;
        struct hlist_node *n;
-       unsigned int hash = ip6qhashfn(id, src, dst);
+       unsigned int hash;
 
        read_lock(&ip6_frag_lock);
+       hash = ip6qhashfn(id, src, dst);
        hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) {
                if (fq->id == id && 
                    ipv6_addr_equal(src, &fq->saddr) &&
@@ -406,7 +410,7 @@ fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst)
        }
        read_unlock(&ip6_frag_lock);
 
-       return ip6_frag_create(hash, id, src, dst);
+       return ip6_frag_create(id, src, dst);
 }
 
 
@@ -581,7 +585,6 @@ err:
  *     the last and the first frames arrived and all the bits are here.
  */
 static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
-                         unsigned int *nhoffp,
                          struct net_device *dev)
 {
        struct sk_buff *fp, *head = fq->fragments;
@@ -654,6 +657,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
        head->dev = dev;
        skb_set_timestamp(head, &fq->stamp);
        head->nh.ipv6h->payload_len = htons(payload_len);
+       IP6CB(head)->nhoff = nhoff;
 
        *skb_in = head;
 
@@ -663,7 +667,6 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
 
        IP6_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
        fq->fragments = NULL;
-       *nhoffp = nhoff;
        return 1;
 
 out_oversize:
@@ -678,7 +681,7 @@ out_fail:
        return -1;
 }
 
-static int ipv6_frag_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
+static int ipv6_frag_rcv(struct sk_buff **skbp)
 {
        struct sk_buff *skb = *skbp; 
        struct net_device *dev = skb->dev;
@@ -710,7 +713,7 @@ static int ipv6_frag_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
                skb->h.raw += sizeof(struct frag_hdr);
                IP6_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
 
-               *nhoffp = (u8*)fhdr - skb->nh.raw;
+               IP6CB(skb)->nhoff = (u8*)fhdr - skb->nh.raw;
                return 1;
        }
 
@@ -722,11 +725,11 @@ static int ipv6_frag_rcv(struct sk_buff **skbp, unsigned int *nhoffp)
 
                spin_lock(&fq->lock);
 
-               ip6_frag_queue(fq, skb, fhdr, *nhoffp);
+               ip6_frag_queue(fq, skb, fhdr, IP6CB(skb)->nhoff);
 
                if (fq->last_in == (FIRST_IN|LAST_IN) &&
                    fq->meat == fq->len)
-                       ret = ip6_frag_reasm(fq, skbp, nhoffp, dev);
+                       ret = ip6_frag_reasm(fq, skbp, dev);
 
                spin_unlock(&fq->lock);
                fq_put(fq, NULL);