Commit 55989a06 authored by Michal Kubeček's avatar Michal Kubeček Committed by Ben Hutchings

ipv6: distinguish frag queues by device for multicast and link-local packets

[ Upstream commit 264640fc ]

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: default avatarMichal Kubecek <mkubecek@suse.cz>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
[bwh: Backported to 3.2: adjust context]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 80996afb
...@@ -382,6 +382,7 @@ struct ip6_create_arg { ...@@ -382,6 +382,7 @@ struct ip6_create_arg {
u32 user; u32 user;
const struct in6_addr *src; const struct in6_addr *src;
const struct in6_addr *dst; const struct in6_addr *dst;
int iif;
}; };
void ip6_frag_init(struct inet_frag_queue *q, void *a); void ip6_frag_init(struct inet_frag_queue *q, void *a);
......
...@@ -162,7 +162,7 @@ static void nf_ct_frag6_expire(unsigned long data) ...@@ -162,7 +162,7 @@ static void nf_ct_frag6_expire(unsigned long data)
/* Creation primitives. */ /* Creation primitives. */
static __inline__ struct nf_ct_frag6_queue * 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 inet_frag_queue *q;
struct ip6_create_arg arg; struct ip6_create_arg arg;
...@@ -172,6 +172,7 @@ fq_find(__be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst) ...@@ -172,6 +172,7 @@ fq_find(__be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst)
arg.user = user; arg.user = user;
arg.src = src; arg.src = src;
arg.dst = dst; arg.dst = dst;
arg.iif = iif;
read_lock_bh(&nf_frags.lock); read_lock_bh(&nf_frags.lock);
hash = inet6_hash_frag(id, src, dst, nf_frags.rnd); 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) ...@@ -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) if (atomic_read(&nf_init_frags.mem) > nf_init_frags.high_thresh)
nf_ct_frag6_evictor(); 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) { if (fq == NULL) {
pr_debug("Can't find and can't create new queue\n"); pr_debug("Can't find and can't create new queue\n");
goto ret_orig; goto ret_orig;
......
...@@ -145,7 +145,10 @@ int ip6_frag_match(struct inet_frag_queue *q, void *a) ...@@ -145,7 +145,10 @@ int ip6_frag_match(struct inet_frag_queue *q, void *a)
fq = container_of(q, struct frag_queue, q); fq = container_of(q, struct frag_queue, q);
return (fq->id == arg->id && fq->user == arg->user && return (fq->id == arg->id && fq->user == arg->user &&
ipv6_addr_equal(&fq->saddr, arg->src) && ipv6_addr_equal(&fq->saddr, arg->src) &&
ipv6_addr_equal(&fq->daddr, arg->dst)); 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); EXPORT_SYMBOL(ip6_frag_match);
...@@ -228,7 +231,8 @@ static void ip6_frag_expire(unsigned long data) ...@@ -228,7 +231,8 @@ static void ip6_frag_expire(unsigned long data)
} }
static __inline__ struct frag_queue * 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 inet_frag_queue *q;
struct ip6_create_arg arg; struct ip6_create_arg arg;
...@@ -238,6 +242,7 @@ fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6 ...@@ -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.user = IP6_DEFRAG_LOCAL_DELIVER;
arg.src = src; arg.src = src;
arg.dst = dst; arg.dst = dst;
arg.iif = iif;
read_lock(&ip6_frags.lock); read_lock(&ip6_frags.lock);
hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd); hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd);
...@@ -583,7 +588,8 @@ static int ipv6_frag_rcv(struct sk_buff *skb) ...@@ -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) if (atomic_read(&net->ipv6.frags.mem) > net->ipv6.frags.high_thresh)
ip6_evictor(net, ip6_dst_idev(skb_dst(skb))); 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) { if (fq != NULL) {
int ret; int ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment