inet: frags: use rhashtables for reassembly units
[sfrench/cifs-2.6.git] / net / ipv6 / reassembly.c
index f0071b113a92fcff15ac57610170c12b17cb59ba..3fc853e4492abb109062d662296c0b470763042a 100644 (file)
@@ -79,52 +79,13 @@ static struct inet_frags ip6_frags;
 static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
                          struct net_device *dev);
 
-/*
- * 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 inet6_hash_frag(__be32 id, const struct in6_addr *saddr,
-                                   const struct in6_addr *daddr)
-{
-       net_get_random_once(&ip6_frags.rnd, sizeof(ip6_frags.rnd));
-       return jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr),
-                           (__force u32)id, ip6_frags.rnd);
-}
-
-static unsigned int ip6_hashfn(const struct inet_frag_queue *q)
-{
-       const struct frag_queue *fq;
-
-       fq = container_of(q, struct frag_queue, q);
-       return inet6_hash_frag(fq->id, &fq->saddr, &fq->daddr);
-}
-
-bool ip6_frag_match(const struct inet_frag_queue *q, const void *a)
-{
-       const struct frag_queue *fq;
-       const struct ip6_create_arg *arg = a;
-
-       fq = container_of(q, struct frag_queue, q);
-       return  fq->id == arg->id &&
-               fq->user == arg->user &&
-               ipv6_addr_equal(&fq->saddr, arg->src) &&
-               ipv6_addr_equal(&fq->daddr, arg->dst) &&
-               (arg->iif == fq->iif ||
-                !(ipv6_addr_type(arg->dst) & (IPV6_ADDR_MULTICAST |
-                                              IPV6_ADDR_LINKLOCAL)));
-}
-EXPORT_SYMBOL(ip6_frag_match);
-
 void ip6_frag_init(struct inet_frag_queue *q, const void *a)
 {
        struct frag_queue *fq = container_of(q, struct frag_queue, q);
-       const struct ip6_create_arg *arg = a;
+       const struct frag_v6_compare_key *key = a;
 
-       fq->id = arg->id;
-       fq->user = arg->user;
-       fq->saddr = *arg->src;
-       fq->daddr = *arg->dst;
-       fq->ecn = arg->ecn;
+       q->key.v6 = *key;
+       fq->ecn = 0;
 }
 EXPORT_SYMBOL(ip6_frag_init);
 
@@ -182,23 +143,22 @@ static void ip6_frag_expire(struct timer_list *t)
 }
 
 static struct frag_queue *
-fq_find(struct net *net, __be32 id, const struct in6_addr *src,
-       const struct in6_addr *dst, int iif, u8 ecn)
+fq_find(struct net *net, __be32 id, const struct ipv6hdr *hdr, int iif)
 {
+       struct frag_v6_compare_key key = {
+               .id = id,
+               .saddr = hdr->saddr,
+               .daddr = hdr->daddr,
+               .user = IP6_DEFRAG_LOCAL_DELIVER,
+               .iif = iif,
+       };
        struct inet_frag_queue *q;
-       struct ip6_create_arg arg;
-       unsigned int hash;
-
-       arg.id = id;
-       arg.user = IP6_DEFRAG_LOCAL_DELIVER;
-       arg.src = src;
-       arg.dst = dst;
-       arg.iif = iif;
-       arg.ecn = ecn;
 
-       hash = inet6_hash_frag(id, src, dst);
+       if (!(ipv6_addr_type(&hdr->daddr) & (IPV6_ADDR_MULTICAST |
+                                           IPV6_ADDR_LINKLOCAL)))
+               key.iif = 0;
 
-       q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash);
+       q = inet_frag_find(&net->ipv6.frags, &key);
        if (IS_ERR_OR_NULL(q)) {
                inet_frag_maybe_warn_overflow(q, pr_fmt());
                return NULL;
@@ -530,6 +490,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
        struct frag_queue *fq;
        const struct ipv6hdr *hdr = ipv6_hdr(skb);
        struct net *net = dev_net(skb_dst(skb)->dev);
+       int iif;
 
        if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED)
                goto fail_hdr;
@@ -558,13 +519,14 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
                return 1;
        }
 
-       fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr,
-                    skb->dev ? skb->dev->ifindex : 0, ip6_frag_ecn(hdr));
+       iif = skb->dev ? skb->dev->ifindex : 0;
+       fq = fq_find(net, fhdr->identification, hdr, iif);
        if (fq) {
                int ret;
 
                spin_lock(&fq->q.lock);
 
+               fq->iif = iif;
                ret = ip6_frag_queue(fq, skb, fhdr, IP6CB(skb)->nhoff);
 
                spin_unlock(&fq->q.lock);
@@ -738,17 +700,47 @@ static struct pernet_operations ip6_frags_ops = {
        .exit = ipv6_frags_exit_net,
 };
 
+static u32 ip6_key_hashfn(const void *data, u32 len, u32 seed)
+{
+       return jhash2(data,
+                     sizeof(struct frag_v6_compare_key) / sizeof(u32), seed);
+}
+
+static u32 ip6_obj_hashfn(const void *data, u32 len, u32 seed)
+{
+       const struct inet_frag_queue *fq = data;
+
+       return jhash2((const u32 *)&fq->key.v6,
+                     sizeof(struct frag_v6_compare_key) / sizeof(u32), seed);
+}
+
+static int ip6_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr)
+{
+       const struct frag_v6_compare_key *key = arg->key;
+       const struct inet_frag_queue *fq = ptr;
+
+       return !!memcmp(&fq->key, key, sizeof(*key));
+}
+
+const struct rhashtable_params ip6_rhash_params = {
+       .head_offset            = offsetof(struct inet_frag_queue, node),
+       .hashfn                 = ip6_key_hashfn,
+       .obj_hashfn             = ip6_obj_hashfn,
+       .obj_cmpfn              = ip6_obj_cmpfn,
+       .automatic_shrinking    = true,
+};
+EXPORT_SYMBOL(ip6_rhash_params);
+
 int __init ipv6_frag_init(void)
 {
        int ret;
 
-       ip6_frags.hashfn = ip6_hashfn;
        ip6_frags.constructor = ip6_frag_init;
        ip6_frags.destructor = NULL;
        ip6_frags.qsize = sizeof(struct frag_queue);
-       ip6_frags.match = ip6_frag_match;
        ip6_frags.frag_expire = ip6_frag_expire;
        ip6_frags.frags_cache_name = ip6_frag_cache_name;
+       ip6_frags.rhash_params = ip6_rhash_params;
        ret = inet_frags_init(&ip6_frags);
        if (ret)
                goto out;