]> git.hungrycats.org Git - linux/commitdiff
ipv6: distinguish frag queues by device for multicast and link-local packets
authorMichal Kubeček <mkubecek@suse.cz>
Tue, 24 Nov 2015 14:07:11 +0000 (15:07 +0100)
committerBen Hutchings <ben@decadent.org.uk>
Wed, 30 Dec 2015 02:26:02 +0000 (02:26 +0000)
[ Upstream commit 264640fc2c5f4f913db5c73fa3eb1ead2c45e9d7 ]

If a fragmented multicast packet is received on an ethernet device which
has an active macvlan on top of it, each fragment is duplicated and
received both on the underlying device and the macvlan. If some
fragments for macvlan are processed before the whole packet for the
underlying device is reassembled, the "overlapping fragments" test in
ip6_frag_queue() discards the whole fragment queue.

To resolve this, add device ifindex to the search key and require it to
match reassembling multicast packets and packets to link-local
addresses.

Note: similar patch has been already submitted by Yoshifuji Hideaki in

  http://patchwork.ozlabs.org/patch/220979/

but got lost and forgotten for some reason.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
Signed-off-by: David S. Miller <davem@davemloft.net>
[bwh: Backported to 3.2: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
include/net/ipv6.h
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/reassembly.c

index ab2e6d724cf8f2ab88bb15526b54f740fc3e2642..77763c69e0a651326d44f8c9c2c98d918ea56a56 100644 (file)
@@ -382,6 +382,7 @@ struct ip6_create_arg {
        u32 user;
        const struct in6_addr *src;
        const struct in6_addr *dst;
+       int iif;
 };
 
 void ip6_frag_init(struct inet_frag_queue *q, void *a);
index 52e2f65ff3a309df6df9c584c0ad7e1a87e76529..2252b87a198a12dab4185dd6229e01f906d53ca0 100644 (file)
@@ -162,7 +162,7 @@ out:
 /* Creation primitives. */
 
 static __inline__ struct nf_ct_frag6_queue *
-fq_find(__be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst)
+fq_find(__be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst, int iif)
 {
        struct inet_frag_queue *q;
        struct ip6_create_arg arg;
@@ -172,6 +172,7 @@ fq_find(__be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst)
        arg.user = user;
        arg.src = src;
        arg.dst = dst;
+       arg.iif = iif;
 
        read_lock_bh(&nf_frags.lock);
        hash = inet6_hash_frag(id, src, dst, nf_frags.rnd);
@@ -558,7 +559,8 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user)
        if (atomic_read(&nf_init_frags.mem) > nf_init_frags.high_thresh)
                nf_ct_frag6_evictor();
 
-       fq = fq_find(fhdr->identification, user, &hdr->saddr, &hdr->daddr);
+       fq = fq_find(fhdr->identification, user, &hdr->saddr, &hdr->daddr,
+                    skb->dev ? skb->dev->ifindex : 0);
        if (fq == NULL) {
                pr_debug("Can't find and can't create new queue\n");
                goto ret_orig;
index eba5deb79fe3b1845ad1c46d72b9273b1c531dc6..5378faad7d2a40d4de4621b41ff2135805b5dadb 100644 (file)
@@ -144,8 +144,11 @@ int ip6_frag_match(struct inet_frag_queue *q, void *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));
+               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);
 
@@ -228,7 +231,8 @@ out:
 }
 
 static __inline__ struct frag_queue *
-fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6_addr *dst)
+fq_find(struct net *net, __be32 id, const struct in6_addr *src,
+       const struct in6_addr *dst, int iif)
 {
        struct inet_frag_queue *q;
        struct ip6_create_arg arg;
@@ -238,6 +242,7 @@ fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6
        arg.user = IP6_DEFRAG_LOCAL_DELIVER;
        arg.src = src;
        arg.dst = dst;
+       arg.iif = iif;
 
        read_lock(&ip6_frags.lock);
        hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd);
@@ -583,7 +588,8 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
        if (atomic_read(&net->ipv6.frags.mem) > net->ipv6.frags.high_thresh)
                ip6_evictor(net, ip6_dst_idev(skb_dst(skb)));
 
-       fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr);
+       fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr,
+                    skb->dev ? skb->dev->ifindex : 0);
        if (fq != NULL) {
                int ret;