Commit 89cf4f4c authored by Patrick McHardy's avatar Patrick McHardy Committed by Greg Kroah-Hartman

ipv6: reassembly: use seperate reassembly queues for conntrack and local delivery

commit 0b5ccb2e upstream.

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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent ee6bfc69
...@@ -354,8 +354,15 @@ static inline int ipv6_prefix_equal(const struct in6_addr *a1, ...@@ -354,8 +354,15 @@ static inline int ipv6_prefix_equal(const struct in6_addr *a1,
struct inet_frag_queue; struct inet_frag_queue;
enum ip6_defrag_users {
IP6_DEFRAG_LOCAL_DELIVER,
IP6_DEFRAG_CONNTRACK_IN,
IP6_DEFRAG_CONNTRACK_OUT,
};
struct ip6_create_arg { struct ip6_create_arg {
__be32 id; __be32 id;
u32 user;
struct in6_addr *src; struct in6_addr *src;
struct in6_addr *dst; struct in6_addr *dst;
}; };
......
...@@ -9,7 +9,7 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6; ...@@ -9,7 +9,7 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6;
extern int nf_ct_frag6_init(void); extern int nf_ct_frag6_init(void);
extern void nf_ct_frag6_cleanup(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, extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
struct net_device *in, struct net_device *in,
struct net_device *out, struct net_device *out,
......
...@@ -187,6 +187,16 @@ static unsigned int ipv6_confirm(unsigned int hooknum, ...@@ -187,6 +187,16 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
return nf_conntrack_confirm(skb); 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, static unsigned int ipv6_defrag(unsigned int hooknum,
struct sk_buff *skb, struct sk_buff *skb,
const struct net_device *in, const struct net_device *in,
...@@ -199,8 +209,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum, ...@@ -199,8 +209,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum,
if (skb->nfct) if (skb->nfct)
return NF_ACCEPT; return NF_ACCEPT;
reasm = nf_ct_frag6_gather(skb); reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb));
/* queued */ /* queued */
if (reasm == NULL) if (reasm == NULL)
return NF_STOLEN; return NF_STOLEN;
......
...@@ -170,13 +170,14 @@ static void nf_ct_frag6_expire(unsigned long data) ...@@ -170,13 +170,14 @@ 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, 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 inet_frag_queue *q;
struct ip6_create_arg arg; struct ip6_create_arg arg;
unsigned int hash; unsigned int hash;
arg.id = id; arg.id = id;
arg.user = user;
arg.src = src; arg.src = src;
arg.dst = dst; arg.dst = dst;
...@@ -561,7 +562,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) ...@@ -561,7 +562,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
return 0; 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 sk_buff *clone;
struct net_device *dev = skb->dev; struct net_device *dev = skb->dev;
...@@ -607,7 +608,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb) ...@@ -607,7 +608,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
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, &hdr->saddr, &hdr->daddr); fq = fq_find(fhdr->identification, user, &hdr->saddr, &hdr->daddr);
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;
......
...@@ -72,6 +72,7 @@ struct frag_queue ...@@ -72,6 +72,7 @@ struct frag_queue
struct inet_frag_queue q; struct inet_frag_queue q;
__be32 id; /* fragment id */ __be32 id; /* fragment id */
u32 user;
struct in6_addr saddr; struct in6_addr saddr;
struct in6_addr daddr; struct in6_addr daddr;
...@@ -141,7 +142,7 @@ int ip6_frag_match(struct inet_frag_queue *q, void *a) ...@@ -141,7 +142,7 @@ int ip6_frag_match(struct inet_frag_queue *q, void *a)
struct ip6_create_arg *arg = a; struct ip6_create_arg *arg = a;
fq = container_of(q, struct frag_queue, q); 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->saddr, arg->src) &&
ipv6_addr_equal(&fq->daddr, arg->dst)); ipv6_addr_equal(&fq->daddr, arg->dst));
} }
...@@ -163,6 +164,7 @@ void ip6_frag_init(struct inet_frag_queue *q, void *a) ...@@ -163,6 +164,7 @@ void ip6_frag_init(struct inet_frag_queue *q, void *a)
struct ip6_create_arg *arg = a; struct ip6_create_arg *arg = a;
fq->id = arg->id; fq->id = arg->id;
fq->user = arg->user;
ipv6_addr_copy(&fq->saddr, arg->src); ipv6_addr_copy(&fq->saddr, arg->src);
ipv6_addr_copy(&fq->daddr, arg->dst); ipv6_addr_copy(&fq->daddr, arg->dst);
} }
...@@ -244,6 +246,7 @@ fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst, ...@@ -244,6 +246,7 @@ fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst,
unsigned int hash; unsigned int hash;
arg.id = id; arg.id = id;
arg.user = IP6_DEFRAG_LOCAL_DELIVER;
arg.src = src; arg.src = src;
arg.dst = dst; arg.dst = dst;
......
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