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: Patrick McHardy <kaber@trash.net>
This commit is contained in:
@@ -350,8 +350,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;
|
|||||||
|
|
||||||
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 @@ out:
|
|||||||
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,
|
|||||||
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;
|
||||||
|
@@ -168,13 +168,14 @@ out:
|
|||||||
/* 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;
|
||||||
|
|
||||||
@@ -559,7 +560,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;
|
||||||
@@ -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)
|
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
|
|||||||
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)
|
|||||||
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)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@@ -243,6 +245,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;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user