| From foo@baz Fri Dec 11 11:39:46 EST 2015 |
| From: =?UTF-8?q?Michal=20Kube=C4=8Dek?= <mkubecek@suse.cz> |
| Date: Tue, 24 Nov 2015 15:07:11 +0100 |
| Subject: ipv6: distinguish frag queues by device for multicast and link-local packets |
| |
| From: =?UTF-8?q?Michal=20Kube=C4=8Dek?= <mkubecek@suse.cz> |
| |
| [ 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| include/net/ipv6.h | 1 + |
| net/ipv6/netfilter/nf_conntrack_reasm.c | 5 +++-- |
| net/ipv6/reassembly.c | 10 +++++++--- |
| 3 files changed, 11 insertions(+), 5 deletions(-) |
| |
| --- a/include/net/ipv6.h |
| +++ b/include/net/ipv6.h |
| @@ -478,6 +478,7 @@ struct ip6_create_arg { |
| u32 user; |
| const struct in6_addr *src; |
| const struct in6_addr *dst; |
| + int iif; |
| u8 ecn; |
| }; |
| |
| --- a/net/ipv6/netfilter/nf_conntrack_reasm.c |
| +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c |
| @@ -172,7 +172,7 @@ static void nf_ct_frag6_expire(unsigned |
| /* Creation primitives. */ |
| static inline struct frag_queue *fq_find(struct net *net, __be32 id, |
| u32 user, struct in6_addr *src, |
| - struct in6_addr *dst, u8 ecn) |
| + struct in6_addr *dst, int iif, u8 ecn) |
| { |
| struct inet_frag_queue *q; |
| struct ip6_create_arg arg; |
| @@ -182,6 +182,7 @@ static inline struct frag_queue *fq_find |
| arg.user = user; |
| arg.src = src; |
| arg.dst = dst; |
| + arg.iif = iif; |
| arg.ecn = ecn; |
| |
| read_lock_bh(&nf_frags.lock); |
| @@ -590,7 +591,7 @@ struct sk_buff *nf_ct_frag6_gather(struc |
| local_bh_enable(); |
| |
| fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr, |
| - ip6_frag_ecn(hdr)); |
| + skb->dev ? skb->dev->ifindex : 0, ip6_frag_ecn(hdr)); |
| if (fq == NULL) { |
| pr_debug("Can't find and can't create new queue\n"); |
| goto ret_orig; |
| --- a/net/ipv6/reassembly.c |
| +++ b/net/ipv6/reassembly.c |
| @@ -111,7 +111,10 @@ bool ip6_frag_match(struct inet_frag_que |
| 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->daddr, arg->dst) && |
| + (arg->iif == fq->iif || |
| + !(ipv6_addr_type(arg->dst) & (IPV6_ADDR_MULTICAST | |
| + IPV6_ADDR_LINKLOCAL))); |
| } |
| EXPORT_SYMBOL(ip6_frag_match); |
| |
| @@ -180,7 +183,7 @@ static void ip6_frag_expire(unsigned lon |
| |
| static __inline__ struct frag_queue * |
| fq_find(struct net *net, __be32 id, const struct in6_addr *src, |
| - const struct in6_addr *dst, u8 ecn) |
| + const struct in6_addr *dst, int iif, u8 ecn) |
| { |
| struct inet_frag_queue *q; |
| struct ip6_create_arg arg; |
| @@ -190,6 +193,7 @@ fq_find(struct net *net, __be32 id, cons |
| arg.user = IP6_DEFRAG_LOCAL_DELIVER; |
| arg.src = src; |
| arg.dst = dst; |
| + arg.iif = iif; |
| arg.ecn = ecn; |
| |
| read_lock(&ip6_frags.lock); |
| @@ -558,7 +562,7 @@ static int ipv6_frag_rcv(struct sk_buff |
| IPSTATS_MIB_REASMFAILS, evicted); |
| |
| fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr, |
| - ip6_frag_ecn(hdr)); |
| + skb->dev ? skb->dev->ifindex : 0, ip6_frag_ecn(hdr)); |
| if (fq != NULL) { |
| int ret; |
| |