Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b5ccb2e authored by Patrick McHardy's avatar Patrick McHardy
Browse files

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



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>
parent 9abfe315
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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;
};
+1 −1
Original line number Diff line number Diff line
@@ -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,
+11 −2
Original line number Diff line number Diff line
@@ -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;
+4 −3
Original line number Diff line number Diff line
@@ -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;
+4 −1
Original line number Diff line number Diff line
@@ -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;