Commit 029f7f3b authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso

netfilter: ipv6: nf_defrag: avoid/free clone operations

commit 6aafeef0
("netfilter: push reasm skb through instead of original frag skbs")
changed ipv6 defrag to not use the original skbs anymore.

So rather than keeping the original skbs around just to discard them
afterwards just use the original skbs directly for the fraglist of
the newly assembled skb and remove the extra clone/free operations.

The skb that completes the fragment queue is morphed into a the
reassembled one instead, just like ipv4 defrag.

openvswitch doesn't need any additional skb_morph magic anymore to deal
with this situation so just remove that.

A followup patch can then also remove the NF_HOOK (re)invocation in
the ipv6 netfilter defrag hook.

Cc: Joe Stringer <joestringer@nicira.com>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent a18fd970
...@@ -6,7 +6,6 @@ void nf_defrag_ipv6_enable(void); ...@@ -6,7 +6,6 @@ void nf_defrag_ipv6_enable(void);
int nf_ct_frag6_init(void); int nf_ct_frag6_init(void);
void nf_ct_frag6_cleanup(void); void nf_ct_frag6_cleanup(void);
struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user); struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user);
void nf_ct_frag6_consume_orig(struct sk_buff *skb);
struct inet_frags_ctl; struct inet_frags_ctl;
......
...@@ -56,7 +56,6 @@ struct nf_ct_frag6_skb_cb ...@@ -56,7 +56,6 @@ struct nf_ct_frag6_skb_cb
{ {
struct inet6_skb_parm h; struct inet6_skb_parm h;
int offset; int offset;
struct sk_buff *orig;
}; };
#define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb *)((skb)->cb)) #define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb *)((skb)->cb))
...@@ -170,12 +169,6 @@ static unsigned int nf_hashfn(const struct inet_frag_queue *q) ...@@ -170,12 +169,6 @@ static unsigned int nf_hashfn(const struct inet_frag_queue *q)
return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr); return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr);
} }
static void nf_skb_free(struct sk_buff *skb)
{
if (NFCT_FRAG6_CB(skb)->orig)
kfree_skb(NFCT_FRAG6_CB(skb)->orig);
}
static void nf_ct_frag6_expire(unsigned long data) static void nf_ct_frag6_expire(unsigned long data)
{ {
struct frag_queue *fq; struct frag_queue *fq;
...@@ -376,9 +369,9 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, ...@@ -376,9 +369,9 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
* the last and the first frames arrived and all the bits are here. * the last and the first frames arrived and all the bits are here.
*/ */
static struct sk_buff * static struct sk_buff *
nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_device *dev)
{ {
struct sk_buff *fp, *op, *head = fq->q.fragments; struct sk_buff *fp, *head = fq->q.fragments;
int payload_len; int payload_len;
u8 ecn; u8 ecn;
...@@ -429,10 +422,38 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) ...@@ -429,10 +422,38 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
clone->csum = 0; clone->csum = 0;
clone->ip_summed = head->ip_summed; clone->ip_summed = head->ip_summed;
NFCT_FRAG6_CB(clone)->orig = NULL;
add_frag_mem_limit(fq->q.net, clone->truesize); add_frag_mem_limit(fq->q.net, clone->truesize);
} }
/* morph head into last received skb: prev.
*
* This allows callers of ipv6 conntrack defrag to continue
* to use the last skb(frag) passed into the reasm engine.
* The last skb frag 'silently' turns into the full reassembled skb.
*
* Since prev is also part of q->fragments we have to clone it first.
*/
if (head != prev) {
struct sk_buff *iter;
fp = skb_clone(prev, GFP_ATOMIC);
if (!fp)
goto out_oom;
fp->next = prev->next;
skb_queue_walk(head, iter) {
if (iter->next != prev)
continue;
iter->next = fp;
break;
}
skb_morph(prev, head);
prev->next = head->next;
consume_skb(head);
head = prev;
}
/* We have to remove fragment header from datagram and to relocate /* We have to remove fragment header from datagram and to relocate
* header in order to calculate ICV correctly. */ * header in order to calculate ICV correctly. */
skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0]; skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0];
...@@ -473,21 +494,6 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) ...@@ -473,21 +494,6 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
fq->q.fragments = NULL; fq->q.fragments = NULL;
fq->q.fragments_tail = NULL; fq->q.fragments_tail = NULL;
/* all original skbs are linked into the NFCT_FRAG6_CB(head).orig */
fp = skb_shinfo(head)->frag_list;
if (fp && NFCT_FRAG6_CB(fp)->orig == NULL)
/* at above code, head skb is divided into two skbs. */
fp = fp->next;
op = NFCT_FRAG6_CB(head)->orig;
for (; fp; fp = fp->next) {
struct sk_buff *orig = NFCT_FRAG6_CB(fp)->orig;
op->next = orig;
op = orig;
NFCT_FRAG6_CB(fp)->orig = NULL;
}
return head; return head;
out_oversize: out_oversize:
...@@ -565,7 +571,6 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) ...@@ -565,7 +571,6 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user) struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
{ {
struct sk_buff *clone;
struct net_device *dev = skb->dev; struct net_device *dev = skb->dev;
struct frag_hdr *fhdr; struct frag_hdr *fhdr;
struct frag_queue *fq; struct frag_queue *fq;
...@@ -583,42 +588,30 @@ struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 use ...@@ -583,42 +588,30 @@ struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 use
if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0) if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0)
return skb; return skb;
clone = skb_clone(skb, GFP_ATOMIC); if (!pskb_may_pull(skb, fhoff + sizeof(*fhdr)))
if (clone == NULL) {
pr_debug("Can't clone skb\n");
return skb; return skb;
}
NFCT_FRAG6_CB(clone)->orig = skb;
if (!pskb_may_pull(clone, fhoff + sizeof(*fhdr))) { skb_set_transport_header(skb, fhoff);
pr_debug("message is too short.\n"); hdr = ipv6_hdr(skb);
goto ret_orig; fhdr = (struct frag_hdr *)skb_transport_header(skb);
}
skb_set_transport_header(clone, fhoff);
hdr = ipv6_hdr(clone);
fhdr = (struct frag_hdr *)skb_transport_header(clone);
fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr, fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr,
ip6_frag_ecn(hdr)); ip6_frag_ecn(hdr));
if (fq == NULL) { if (fq == NULL)
pr_debug("Can't find and can't create new queue\n"); return skb;
goto ret_orig;
}
spin_lock_bh(&fq->q.lock); spin_lock_bh(&fq->q.lock);
if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) { if (nf_ct_frag6_queue(fq, skb, fhdr, nhoff) < 0) {
spin_unlock_bh(&fq->q.lock); spin_unlock_bh(&fq->q.lock);
pr_debug("Can't insert skb to queue\n"); pr_debug("Can't insert skb to queue\n");
inet_frag_put(&fq->q, &nf_frags); inet_frag_put(&fq->q, &nf_frags);
goto ret_orig; return skb;
} }
if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
fq->q.meat == fq->q.len) { fq->q.meat == fq->q.len) {
ret_skb = nf_ct_frag6_reasm(fq, dev); ret_skb = nf_ct_frag6_reasm(fq, skb, dev);
if (ret_skb == NULL) if (ret_skb == NULL)
pr_debug("Can't reassemble fragmented packets\n"); pr_debug("Can't reassemble fragmented packets\n");
} }
...@@ -626,26 +619,9 @@ struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 use ...@@ -626,26 +619,9 @@ struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 use
inet_frag_put(&fq->q, &nf_frags); inet_frag_put(&fq->q, &nf_frags);
return ret_skb; return ret_skb;
ret_orig:
kfree_skb(clone);
return skb;
} }
EXPORT_SYMBOL_GPL(nf_ct_frag6_gather); EXPORT_SYMBOL_GPL(nf_ct_frag6_gather);
void nf_ct_frag6_consume_orig(struct sk_buff *skb)
{
struct sk_buff *s, *s2;
for (s = NFCT_FRAG6_CB(skb)->orig; s;) {
s2 = s->next;
s->next = NULL;
consume_skb(s);
s = s2;
}
}
EXPORT_SYMBOL_GPL(nf_ct_frag6_consume_orig);
static int nf_ct_net_init(struct net *net) static int nf_ct_net_init(struct net *net)
{ {
int res; int res;
...@@ -680,7 +656,6 @@ int nf_ct_frag6_init(void) ...@@ -680,7 +656,6 @@ int nf_ct_frag6_init(void)
nf_frags.hashfn = nf_hashfn; nf_frags.hashfn = nf_hashfn;
nf_frags.constructor = ip6_frag_init; nf_frags.constructor = ip6_frag_init;
nf_frags.destructor = NULL; nf_frags.destructor = NULL;
nf_frags.skb_free = nf_skb_free;
nf_frags.qsize = sizeof(struct frag_queue); nf_frags.qsize = sizeof(struct frag_queue);
nf_frags.match = ip6_frag_match; nf_frags.match = ip6_frag_match;
nf_frags.frag_expire = nf_ct_frag6_expire; nf_frags.frag_expire = nf_ct_frag6_expire;
......
...@@ -69,12 +69,6 @@ static unsigned int ipv6_defrag(void *priv, ...@@ -69,12 +69,6 @@ static unsigned int ipv6_defrag(void *priv,
if (reasm == NULL) if (reasm == NULL)
return NF_STOLEN; return NF_STOLEN;
/* error occurred or not fragmented */
if (reasm == skb)
return NF_ACCEPT;
nf_ct_frag6_consume_orig(reasm);
NF_HOOK_THRESH(NFPROTO_IPV6, state->hook, state->net, state->sk, reasm, NF_HOOK_THRESH(NFPROTO_IPV6, state->hook, state->net, state->sk, reasm,
state->in, state->out, state->in, state->out,
state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1); state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
......
...@@ -321,21 +321,7 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key, ...@@ -321,21 +321,7 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
if (!reasm) if (!reasm)
return -EINPROGRESS; return -EINPROGRESS;
if (skb == reasm) {
kfree_skb(skb);
return -EINVAL;
}
/* Don't free 'skb' even though it is one of the original
* fragments, as we're going to morph it into the head.
*/
skb_get(skb);
nf_ct_frag6_consume_orig(reasm);
key->ip.proto = ipv6_hdr(reasm)->nexthdr; key->ip.proto = ipv6_hdr(reasm)->nexthdr;
skb_morph(skb, reasm);
skb->next = reasm->next;
consume_skb(reasm);
ovs_cb.mru = IP6CB(skb)->frag_max_size; ovs_cb.mru = IP6CB(skb)->frag_max_size;
#endif #endif
} else { } else {
......
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