ipv6: reassembly: use seperate reassembly queues for conntrack and local delivery
Patrick McHardy [Tue, 15 Dec 2009 15:59:18 +0000 (16:59 +0100)]
Currently the same reassembly queue might be used for packets reassembled
by conntrack in different positions in the stack (PREROUTING/LOCAL_OUT),
as well as local delivery. This can cause "packet jumps" when the fragment
completing a reassembled packet is queued from a different position in the
stack than the previous ones.

Add a "user" identifier to the reassembly queue key to seperate the queues
of each caller, similar to what we do for IPv4.

Signed-off-by: Patrick McHardy <kaber@trash.net>

include/net/ipv6.h
include/net/netfilter/ipv6/nf_conntrack_ipv6.h
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/reassembly.c

index 92db861..d691603 100644 (file)
@@ -350,8 +350,15 @@ static inline int ipv6_prefix_equal(const struct in6_addr *a1,
 
 struct inet_frag_queue;
 
+enum ip6_defrag_users {
+       IP6_DEFRAG_LOCAL_DELIVER,
+       IP6_DEFRAG_CONNTRACK_IN,
+       IP6_DEFRAG_CONNTRACK_OUT,
+};
+
 struct ip6_create_arg {
        __be32 id;
+       u32 user;
        struct in6_addr *src;
        struct in6_addr *dst;
 };
index abc55ad..1ee717e 100644 (file)
@@ -9,7 +9,7 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6;
 
 extern int nf_ct_frag6_init(void);
 extern void nf_ct_frag6_cleanup(void);
-extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb);
+extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user);
 extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
                               struct net_device *in,
                               struct net_device *out,
index 5f2ec20..c0a82fe 100644 (file)
@@ -187,6 +187,16 @@ out:
        return nf_conntrack_confirm(skb);
 }
 
+static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum,
+                                               struct sk_buff *skb)
+{
+       if (hooknum == NF_INET_PRE_ROUTING)
+               return IP6_DEFRAG_CONNTRACK_IN;
+       else
+               return IP6_DEFRAG_CONNTRACK_OUT;
+
+}
+
 static unsigned int ipv6_defrag(unsigned int hooknum,
                                struct sk_buff *skb,
                                const struct net_device *in,
@@ -199,8 +209,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum,
        if (skb->nfct)
                return NF_ACCEPT;
 
-       reasm = nf_ct_frag6_gather(skb);
-
+       reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb));
        /* queued */
        if (reasm == NULL)
                return NF_STOLEN;
index e0b9424..312c20a 100644 (file)
@@ -168,13 +168,14 @@ out:
 /* Creation primitives. */
 
 static __inline__ struct nf_ct_frag6_queue *
-fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst)
+fq_find(__be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst)
 {
        struct inet_frag_queue *q;
        struct ip6_create_arg arg;
        unsigned int hash;
 
        arg.id = id;
+       arg.user = user;
        arg.src = src;
        arg.dst = dst;
 
@@ -559,7 +560,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
        return 0;
 }
 
-struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
+struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user)
 {
        struct sk_buff *clone;
        struct net_device *dev = skb->dev;
@@ -605,7 +606,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
        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);
+       fq = fq_find(fhdr->identification, user, &hdr->saddr, &hdr->daddr);
        if (fq == NULL) {
                pr_debug("Can't find and can't create new queue\n");
                goto ret_orig;
index 4d98549..3b3a956 100644 (file)
@@ -72,6 +72,7 @@ struct frag_queue
        struct inet_frag_queue  q;
 
        __be32                  id;             /* fragment id          */
+       u32                     user;
        struct in6_addr         saddr;
        struct in6_addr         daddr;
 
@@ -141,7 +142,7 @@ int ip6_frag_match(struct inet_frag_queue *q, void *a)
        struct ip6_create_arg *arg = a;
 
        fq = container_of(q, struct frag_queue, q);
-       return (fq->id == arg->id &&
+       return (fq->id == arg->id && fq->user == arg->user &&
                        ipv6_addr_equal(&fq->saddr, arg->src) &&
                        ipv6_addr_equal(&fq->daddr, arg->dst));
 }
@@ -163,6 +164,7 @@ void ip6_frag_init(struct inet_frag_queue *q, void *a)
        struct ip6_create_arg *arg = a;
 
        fq->id = arg->id;
+       fq->user = arg->user;
        ipv6_addr_copy(&fq->saddr, arg->src);
        ipv6_addr_copy(&fq->daddr, arg->dst);
 }
@@ -243,6 +245,7 @@ fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst,
        unsigned int hash;
 
        arg.id = id;
+       arg.user = IP6_DEFRAG_LOCAL_DELIVER;
        arg.src = src;
        arg.dst = dst;