Merge branch 'upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/ralf/upstrea...
[sfrench/cifs-2.6.git] / net / ipv6 / netfilter / nf_conntrack_reasm.c
index 2ebe515d914ee576e82e3ce38a46f428f0034a66..24c0d03095bf8620894d4d2748b9953bc66f90a7 100644 (file)
@@ -39,6 +39,7 @@
 #include <net/rawv6.h>
 #include <net/ndisc.h>
 #include <net/addrconf.h>
+#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
 #include <linux/sysctl.h>
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv6.h>
@@ -70,14 +71,37 @@ struct nf_ct_frag6_queue
        __u16                   nhoffset;
 };
 
-struct inet_frags_ctl nf_frags_ctl __read_mostly = {
-       .high_thresh     = 256 * 1024,
-       .low_thresh      = 192 * 1024,
-       .timeout         = IPV6_FRAG_TIMEOUT,
-       .secret_interval = 10 * 60 * HZ,
-};
-
 static struct inet_frags nf_frags;
+static struct netns_frags nf_init_frags;
+
+#ifdef CONFIG_SYSCTL
+struct ctl_table nf_ct_ipv6_sysctl_table[] = {
+       {
+               .procname       = "nf_conntrack_frag6_timeout",
+               .data           = &nf_init_frags.timeout,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = &proc_dointvec_jiffies,
+       },
+       {
+               .ctl_name       = NET_NF_CONNTRACK_FRAG6_LOW_THRESH,
+               .procname       = "nf_conntrack_frag6_low_thresh",
+               .data           = &nf_init_frags.low_thresh,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = &proc_dointvec,
+       },
+       {
+               .ctl_name       = NET_NF_CONNTRACK_FRAG6_HIGH_THRESH,
+               .procname       = "nf_conntrack_frag6_high_thresh",
+               .data           = &nf_init_frags.high_thresh,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = &proc_dointvec,
+       },
+       { .ctl_name = 0 }
+};
+#endif
 
 static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr,
                               struct in6_addr *daddr)
@@ -106,92 +130,35 @@ static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr,
        return c & (INETFRAGS_HASHSZ - 1);
 }
 
-static void nf_ct_frag6_secret_rebuild(unsigned long dummy)
+static unsigned int nf_hashfn(struct inet_frag_queue *q)
 {
-       unsigned long now = jiffies;
-       int i;
+       struct nf_ct_frag6_queue *nq;
 
-       write_lock(&nf_frags.lock);
-       get_random_bytes(&nf_frags.rnd, sizeof(u32));
-       for (i = 0; i < INETFRAGS_HASHSZ; i++) {
-               struct nf_ct_frag6_queue *q;
-               struct hlist_node *p, *n;
-
-               hlist_for_each_entry_safe(q, p, n, &nf_frags.hash[i], q.list) {
-                       unsigned int hval = ip6qhashfn(q->id,
-                                                      &q->saddr,
-                                                      &q->daddr);
-                       if (hval != i) {
-                               hlist_del(&q->q.list);
-                               /* Relink to new hash chain. */
-                               hlist_add_head(&q->q.list,
-                                              &nf_frags.hash[hval]);
-                       }
-               }
-       }
-       write_unlock(&nf_frags.lock);
-
-       mod_timer(&nf_frags.secret_timer, now + nf_frags_ctl.secret_interval);
+       nq = container_of(q, struct nf_ct_frag6_queue, q);
+       return ip6qhashfn(nq->id, &nq->saddr, &nq->daddr);
 }
 
-/* Memory Tracking Functions. */
-static inline void frag_kfree_skb(struct sk_buff *skb, unsigned int *work)
+static void nf_skb_free(struct sk_buff *skb)
 {
-       if (work)
-               *work -= skb->truesize;
-       atomic_sub(skb->truesize, &nf_frags.mem);
        if (NFCT_FRAG6_CB(skb)->orig)
                kfree_skb(NFCT_FRAG6_CB(skb)->orig);
-
-       kfree_skb(skb);
 }
 
-static inline void frag_free_queue(struct nf_ct_frag6_queue *fq,
-                                  unsigned int *work)
+/* Memory Tracking Functions. */
+static inline void frag_kfree_skb(struct sk_buff *skb, unsigned int *work)
 {
        if (work)
-               *work -= sizeof(struct nf_ct_frag6_queue);
-       atomic_sub(sizeof(struct nf_ct_frag6_queue), &nf_frags.mem);
-       kfree(fq);
-}
-
-static inline struct nf_ct_frag6_queue *frag_alloc_queue(void)
-{
-       struct nf_ct_frag6_queue *fq = kmalloc(sizeof(struct nf_ct_frag6_queue), GFP_ATOMIC);
-
-       if (!fq)
-               return NULL;
-       atomic_add(sizeof(struct nf_ct_frag6_queue), &nf_frags.mem);
-       return fq;
+               *work -= skb->truesize;
+       atomic_sub(skb->truesize, &nf_init_frags.mem);
+       nf_skb_free(skb);
+       kfree_skb(skb);
 }
 
 /* Destruction primitives. */
 
-/* Complete destruction of fq. */
-static void nf_ct_frag6_destroy(struct nf_ct_frag6_queue *fq,
-                               unsigned int *work)
-{
-       struct sk_buff *fp;
-
-       BUG_TRAP(fq->q.last_in&COMPLETE);
-       BUG_TRAP(del_timer(&fq->q.timer) == 0);
-
-       /* Release all fragment data. */
-       fp = fq->q.fragments;
-       while (fp) {
-               struct sk_buff *xp = fp->next;
-
-               frag_kfree_skb(fp, work);
-               fp = xp;
-       }
-
-       frag_free_queue(fq, work);
-}
-
-static __inline__ void fq_put(struct nf_ct_frag6_queue *fq, unsigned int *work)
+static __inline__ void fq_put(struct nf_ct_frag6_queue *fq)
 {
-       if (atomic_dec_and_test(&fq->q.refcnt))
-               nf_ct_frag6_destroy(fq, work);
+       inet_frag_put(&fq->q, &nf_frags);
 }
 
 /* Kill fq entry. It is not destroyed immediately,
@@ -204,39 +171,17 @@ static __inline__ void fq_kill(struct nf_ct_frag6_queue *fq)
 
 static void nf_ct_frag6_evictor(void)
 {
-       struct nf_ct_frag6_queue *fq;
-       struct list_head *tmp;
-       unsigned int work;
-
-       work = atomic_read(&nf_frags.mem);
-       if (work <= nf_frags_ctl.low_thresh)
-               return;
-
-       work -= nf_frags_ctl.low_thresh;
-       while (work > 0) {
-               read_lock(&nf_frags.lock);
-               if (list_empty(&nf_frags.lru_list)) {
-                       read_unlock(&nf_frags.lock);
-                       return;
-               }
-               tmp = nf_frags.lru_list.next;
-               BUG_ON(tmp == NULL);
-               fq = list_entry(tmp, struct nf_ct_frag6_queue, q.lru_list);
-               atomic_inc(&fq->q.refcnt);
-               read_unlock(&nf_frags.lock);
-
-               spin_lock(&fq->q.lock);
-               if (!(fq->q.last_in&COMPLETE))
-                       fq_kill(fq);
-               spin_unlock(&fq->q.lock);
-
-               fq_put(fq, &work);
-       }
+       local_bh_disable();
+       inet_frag_evictor(&nf_init_frags, &nf_frags);
+       local_bh_enable();
 }
 
 static void nf_ct_frag6_expire(unsigned long data)
 {
-       struct nf_ct_frag6_queue *fq = (struct nf_ct_frag6_queue *) data;
+       struct nf_ct_frag6_queue *fq;
+
+       fq = container_of((struct inet_frag_queue *)data,
+                       struct nf_ct_frag6_queue, q);
 
        spin_lock(&fq->q.lock);
 
@@ -247,96 +192,34 @@ static void nf_ct_frag6_expire(unsigned long data)
 
 out:
        spin_unlock(&fq->q.lock);
-       fq_put(fq, NULL);
+       fq_put(fq);
 }
 
 /* Creation primitives. */
 
-static struct nf_ct_frag6_queue *nf_ct_frag6_intern(unsigned int hash,
-                                         struct nf_ct_frag6_queue *fq_in)
+static __inline__ struct nf_ct_frag6_queue *
+fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst)
 {
-       struct nf_ct_frag6_queue *fq;
-#ifdef CONFIG_SMP
-       struct hlist_node *n;
-#endif
-
-       write_lock(&nf_frags.lock);
-#ifdef CONFIG_SMP
-       hlist_for_each_entry(fq, n, &nf_frags.hash[hash], q.list) {
-               if (fq->id == fq_in->id &&
-                   ipv6_addr_equal(&fq_in->saddr, &fq->saddr) &&
-                   ipv6_addr_equal(&fq_in->daddr, &fq->daddr)) {
-                       atomic_inc(&fq->q.refcnt);
-                       write_unlock(&nf_frags.lock);
-                       fq_in->q.last_in |= COMPLETE;
-                       fq_put(fq_in, NULL);
-                       return fq;
-               }
-       }
-#endif
-       fq = fq_in;
-
-       if (!mod_timer(&fq->q.timer, jiffies + nf_frags_ctl.timeout))
-               atomic_inc(&fq->q.refcnt);
-
-       atomic_inc(&fq->q.refcnt);
-       hlist_add_head(&fq->q.list, &nf_frags.hash[hash]);
-       INIT_LIST_HEAD(&fq->q.lru_list);
-       list_add_tail(&fq->q.lru_list, &nf_frags.lru_list);
-       nf_frags.nqueues++;
-       write_unlock(&nf_frags.lock);
-       return fq;
-}
+       struct inet_frag_queue *q;
+       struct ip6_create_arg arg;
+       unsigned int hash;
 
+       arg.id = id;
+       arg.src = src;
+       arg.dst = dst;
+       hash = ip6qhashfn(id, src, dst);
 
-static struct nf_ct_frag6_queue *
-nf_ct_frag6_create(unsigned int hash, __be32 id, struct in6_addr *src,                            struct in6_addr *dst)
-{
-       struct nf_ct_frag6_queue *fq;
-
-       if ((fq = frag_alloc_queue()) == NULL) {
-               pr_debug("Can't alloc new queue\n");
+       q = inet_frag_find(&nf_init_frags, &nf_frags, &arg, hash);
+       if (q == NULL)
                goto oom;
-       }
 
-       memset(fq, 0, sizeof(struct nf_ct_frag6_queue));
-
-       fq->id = id;
-       ipv6_addr_copy(&fq->saddr, src);
-       ipv6_addr_copy(&fq->daddr, dst);
-
-       setup_timer(&fq->q.timer, nf_ct_frag6_expire, (unsigned long)fq);
-       spin_lock_init(&fq->q.lock);
-       atomic_set(&fq->q.refcnt, 1);
-
-       return nf_ct_frag6_intern(hash, fq);
+       return container_of(q, struct nf_ct_frag6_queue, q);
 
 oom:
+       pr_debug("Can't alloc new queue\n");
        return NULL;
 }
 
-static __inline__ struct nf_ct_frag6_queue *
-fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst)
-{
-       struct nf_ct_frag6_queue *fq;
-       struct hlist_node *n;
-       unsigned int hash = ip6qhashfn(id, src, dst);
-
-       read_lock(&nf_frags.lock);
-       hlist_for_each_entry(fq, n, &nf_frags.hash[hash], q.list) {
-               if (fq->id == id &&
-                   ipv6_addr_equal(src, &fq->saddr) &&
-                   ipv6_addr_equal(dst, &fq->daddr)) {
-                       atomic_inc(&fq->q.refcnt);
-                       read_unlock(&nf_frags.lock);
-                       return fq;
-               }
-       }
-       read_unlock(&nf_frags.lock);
-
-       return nf_ct_frag6_create(hash, id, src, dst);
-}
-
 
 static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
                             struct frag_hdr *fhdr, int nhoff)
@@ -495,7 +378,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
        skb->dev = NULL;
        fq->q.stamp = skb->tstamp;
        fq->q.meat += skb->len;
-       atomic_add(skb->truesize, &nf_frags.mem);
+       atomic_add(skb->truesize, &nf_init_frags.mem);
 
        /* The first fragment.
         * nhoffset is obtained from the first fragment, of course.
@@ -505,7 +388,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
                fq->q.last_in |= FIRST_IN;
        }
        write_lock(&nf_frags.lock);
-       list_move_tail(&fq->q.lru_list, &nf_frags.lru_list);
+       list_move_tail(&fq->q.lru_list, &nf_init_frags.lru_list);
        write_unlock(&nf_frags.lock);
        return 0;
 
@@ -572,7 +455,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
                clone->ip_summed = head->ip_summed;
 
                NFCT_FRAG6_CB(clone)->orig = NULL;
-               atomic_add(clone->truesize, &nf_frags.mem);
+               atomic_add(clone->truesize, &nf_init_frags.mem);
        }
 
        /* We have to remove fragment header from datagram and to relocate
@@ -586,7 +469,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
        skb_shinfo(head)->frag_list = head->next;
        skb_reset_transport_header(head);
        skb_push(head, head->data - skb_network_header(head));
-       atomic_sub(head->truesize, &nf_frags.mem);
+       atomic_sub(head->truesize, &nf_init_frags.mem);
 
        for (fp=head->next; fp; fp = fp->next) {
                head->data_len += fp->len;
@@ -596,7 +479,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
                else if (head->ip_summed == CHECKSUM_COMPLETE)
                        head->csum = csum_add(head->csum, fp->csum);
                head->truesize += fp->truesize;
-               atomic_sub(fp->truesize, &nf_frags.mem);
+               atomic_sub(fp->truesize, &nf_init_frags.mem);
        }
 
        head->next = NULL;
@@ -746,7 +629,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
                goto ret_orig;
        }
 
-       if (atomic_read(&nf_frags.mem) > nf_frags_ctl.high_thresh)
+       if (atomic_read(&nf_init_frags.mem) > nf_init_frags.high_thresh)
                nf_ct_frag6_evictor();
 
        fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr);
@@ -760,7 +643,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
        if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) {
                spin_unlock(&fq->q.lock);
                pr_debug("Can't insert skb to queue\n");
-               fq_put(fq, NULL);
+               fq_put(fq);
                goto ret_orig;
        }
 
@@ -771,7 +654,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
        }
        spin_unlock(&fq->q.lock);
 
-       fq_put(fq, NULL);
+       fq_put(fq);
        return ret_skb;
 
 ret_orig:
@@ -800,28 +683,20 @@ void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
        nf_conntrack_put_reasm(skb);
 }
 
-int nf_ct_frag6_kfree_frags(struct sk_buff *skb)
-{
-       struct sk_buff *s, *s2;
-
-       for (s = NFCT_FRAG6_CB(skb)->orig; s; s = s2) {
-
-               s2 = s->next;
-               kfree_skb(s);
-       }
-
-       kfree_skb(skb);
-
-       return 0;
-}
-
 int nf_ct_frag6_init(void)
 {
-       setup_timer(&nf_frags.secret_timer, nf_ct_frag6_secret_rebuild, 0);
-       nf_frags.secret_timer.expires = jiffies + nf_frags_ctl.secret_interval;
-       add_timer(&nf_frags.secret_timer);
-
-       nf_frags.ctl = &nf_frags_ctl;
+       nf_frags.hashfn = nf_hashfn;
+       nf_frags.constructor = ip6_frag_init;
+       nf_frags.destructor = NULL;
+       nf_frags.skb_free = nf_skb_free;
+       nf_frags.qsize = sizeof(struct nf_ct_frag6_queue);
+       nf_frags.match = ip6_frag_match;
+       nf_frags.frag_expire = nf_ct_frag6_expire;
+       nf_frags.secret_interval = 10 * 60 * HZ;
+       nf_init_frags.timeout = IPV6_FRAG_TIMEOUT;
+       nf_init_frags.high_thresh = 256 * 1024;
+       nf_init_frags.low_thresh = 192 * 1024;
+       inet_frags_init_net(&nf_init_frags);
        inet_frags_init(&nf_frags);
 
        return 0;
@@ -831,7 +706,6 @@ void nf_ct_frag6_cleanup(void)
 {
        inet_frags_fini(&nf_frags);
 
-       del_timer(&nf_frags.secret_timer);
-       nf_frags_ctl.low_thresh = 0;
+       nf_init_frags.low_thresh = 0;
        nf_ct_frag6_evictor();
 }