summaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2015-11-18 17:32:39 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2015-11-23 11:54:44 -0500
commit029f7f3b8701cc7aca8bdb31f0c7edd6a479e357 (patch)
tree51e195fc33eca0200bb40f30fb8a84e4f8d3d655 /net/ipv6
parenta18fd970ce99eee5105a511621d7064812b8cc8c (diff)
netfilter: ipv6: nf_defrag: avoid/free clone operations
commit 6aafeef03b9d9ecf ("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: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c105
-rw-r--r--net/ipv6/netfilter/nf_defrag_ipv6_hooks.c6
2 files changed, 40 insertions, 71 deletions
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index d5efeb87350e..1a86a08adbe5 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -56,7 +56,6 @@ struct nf_ct_frag6_skb_cb
56{ 56{
57 struct inet6_skb_parm h; 57 struct inet6_skb_parm h;
58 int offset; 58 int offset;
59 struct sk_buff *orig;
60}; 59};
61 60
62#define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb *)((skb)->cb)) 61#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 return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr); 169 return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr);
171} 170}
172 171
173static void nf_skb_free(struct sk_buff *skb)
174{
175 if (NFCT_FRAG6_CB(skb)->orig)
176 kfree_skb(NFCT_FRAG6_CB(skb)->orig);
177}
178
179static void nf_ct_frag6_expire(unsigned long data) 172static void nf_ct_frag6_expire(unsigned long data)
180{ 173{
181 struct frag_queue *fq; 174 struct frag_queue *fq;
@@ -376,9 +369,9 @@ err:
376 * the last and the first frames arrived and all the bits are here. 369 * the last and the first frames arrived and all the bits are here.
377 */ 370 */
378static struct sk_buff * 371static struct sk_buff *
379nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) 372nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_device *dev)
380{ 373{
381 struct sk_buff *fp, *op, *head = fq->q.fragments; 374 struct sk_buff *fp, *head = fq->q.fragments;
382 int payload_len; 375 int payload_len;
383 u8 ecn; 376 u8 ecn;
384 377
@@ -429,10 +422,38 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
429 clone->csum = 0; 422 clone->csum = 0;
430 clone->ip_summed = head->ip_summed; 423 clone->ip_summed = head->ip_summed;
431 424
432 NFCT_FRAG6_CB(clone)->orig = NULL;
433 add_frag_mem_limit(fq->q.net, clone->truesize); 425 add_frag_mem_limit(fq->q.net, clone->truesize);
434 } 426 }
435 427
428 /* morph head into last received skb: prev.
429 *
430 * This allows callers of ipv6 conntrack defrag to continue
431 * to use the last skb(frag) passed into the reasm engine.
432 * The last skb frag 'silently' turns into the full reassembled skb.
433 *
434 * Since prev is also part of q->fragments we have to clone it first.
435 */
436 if (head != prev) {
437 struct sk_buff *iter;
438
439 fp = skb_clone(prev, GFP_ATOMIC);
440 if (!fp)
441 goto out_oom;
442
443 fp->next = prev->next;
444 skb_queue_walk(head, iter) {
445 if (iter->next != prev)
446 continue;
447 iter->next = fp;
448 break;
449 }
450
451 skb_morph(prev, head);
452 prev->next = head->next;
453 consume_skb(head);
454 head = prev;
455 }
456
436 /* We have to remove fragment header from datagram and to relocate 457 /* We have to remove fragment header from datagram and to relocate
437 * header in order to calculate ICV correctly. */ 458 * header in order to calculate ICV correctly. */
438 skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0]; 459 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 fq->q.fragments = NULL; 494 fq->q.fragments = NULL;
474 fq->q.fragments_tail = NULL; 495 fq->q.fragments_tail = NULL;
475 496
476 /* all original skbs are linked into the NFCT_FRAG6_CB(head).orig */
477 fp = skb_shinfo(head)->frag_list;
478 if (fp && NFCT_FRAG6_CB(fp)->orig == NULL)
479 /* at above code, head skb is divided into two skbs. */
480 fp = fp->next;
481
482 op = NFCT_FRAG6_CB(head)->orig;
483 for (; fp; fp = fp->next) {
484 struct sk_buff *orig = NFCT_FRAG6_CB(fp)->orig;
485
486 op->next = orig;
487 op = orig;
488 NFCT_FRAG6_CB(fp)->orig = NULL;
489 }
490
491 return head; 497 return head;
492 498
493out_oversize: 499out_oversize:
@@ -565,7 +571,6 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
565 571
566struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user) 572struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
567{ 573{
568 struct sk_buff *clone;
569 struct net_device *dev = skb->dev; 574 struct net_device *dev = skb->dev;
570 struct frag_hdr *fhdr; 575 struct frag_hdr *fhdr;
571 struct frag_queue *fq; 576 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 if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0) 588 if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0)
584 return skb; 589 return skb;
585 590
586 clone = skb_clone(skb, GFP_ATOMIC); 591 if (!pskb_may_pull(skb, fhoff + sizeof(*fhdr)))
587 if (clone == NULL) {
588 pr_debug("Can't clone skb\n");
589 return skb; 592 return skb;
590 }
591 593
592 NFCT_FRAG6_CB(clone)->orig = skb; 594 skb_set_transport_header(skb, fhoff);
593 595 hdr = ipv6_hdr(skb);
594 if (!pskb_may_pull(clone, fhoff + sizeof(*fhdr))) { 596 fhdr = (struct frag_hdr *)skb_transport_header(skb);
595 pr_debug("message is too short.\n");
596 goto ret_orig;
597 }
598
599 skb_set_transport_header(clone, fhoff);
600 hdr = ipv6_hdr(clone);
601 fhdr = (struct frag_hdr *)skb_transport_header(clone);
602 597
603 fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr, 598 fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr,
604 ip6_frag_ecn(hdr)); 599 ip6_frag_ecn(hdr));
605 if (fq == NULL) { 600 if (fq == NULL)
606 pr_debug("Can't find and can't create new queue\n"); 601 return skb;
607 goto ret_orig;
608 }
609 602
610 spin_lock_bh(&fq->q.lock); 603 spin_lock_bh(&fq->q.lock);
611 604
612 if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) { 605 if (nf_ct_frag6_queue(fq, skb, fhdr, nhoff) < 0) {
613 spin_unlock_bh(&fq->q.lock); 606 spin_unlock_bh(&fq->q.lock);
614 pr_debug("Can't insert skb to queue\n"); 607 pr_debug("Can't insert skb to queue\n");
615 inet_frag_put(&fq->q, &nf_frags); 608 inet_frag_put(&fq->q, &nf_frags);
616 goto ret_orig; 609 return skb;
617 } 610 }
618 611
619 if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && 612 if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
620 fq->q.meat == fq->q.len) { 613 fq->q.meat == fq->q.len) {
621 ret_skb = nf_ct_frag6_reasm(fq, dev); 614 ret_skb = nf_ct_frag6_reasm(fq, skb, dev);
622 if (ret_skb == NULL) 615 if (ret_skb == NULL)
623 pr_debug("Can't reassemble fragmented packets\n"); 616 pr_debug("Can't reassemble fragmented packets\n");
624 } 617 }
@@ -626,26 +619,9 @@ struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 use
626 619
627 inet_frag_put(&fq->q, &nf_frags); 620 inet_frag_put(&fq->q, &nf_frags);
628 return ret_skb; 621 return ret_skb;
629
630ret_orig:
631 kfree_skb(clone);
632 return skb;
633} 622}
634EXPORT_SYMBOL_GPL(nf_ct_frag6_gather); 623EXPORT_SYMBOL_GPL(nf_ct_frag6_gather);
635 624
636void nf_ct_frag6_consume_orig(struct sk_buff *skb)
637{
638 struct sk_buff *s, *s2;
639
640 for (s = NFCT_FRAG6_CB(skb)->orig; s;) {
641 s2 = s->next;
642 s->next = NULL;
643 consume_skb(s);
644 s = s2;
645 }
646}
647EXPORT_SYMBOL_GPL(nf_ct_frag6_consume_orig);
648
649static int nf_ct_net_init(struct net *net) 625static int nf_ct_net_init(struct net *net)
650{ 626{
651 int res; 627 int res;
@@ -680,7 +656,6 @@ int nf_ct_frag6_init(void)
680 nf_frags.hashfn = nf_hashfn; 656 nf_frags.hashfn = nf_hashfn;
681 nf_frags.constructor = ip6_frag_init; 657 nf_frags.constructor = ip6_frag_init;
682 nf_frags.destructor = NULL; 658 nf_frags.destructor = NULL;
683 nf_frags.skb_free = nf_skb_free;
684 nf_frags.qsize = sizeof(struct frag_queue); 659 nf_frags.qsize = sizeof(struct frag_queue);
685 nf_frags.match = ip6_frag_match; 660 nf_frags.match = ip6_frag_match;
686 nf_frags.frag_expire = nf_ct_frag6_expire; 661 nf_frags.frag_expire = nf_ct_frag6_expire;
diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
index 4fdbed5ebfb6..fb96b1018884 100644
--- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
+++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
@@ -69,12 +69,6 @@ static unsigned int ipv6_defrag(void *priv,
69 if (reasm == NULL) 69 if (reasm == NULL)
70 return NF_STOLEN; 70 return NF_STOLEN;
71 71
72 /* error occurred or not fragmented */
73 if (reasm == skb)
74 return NF_ACCEPT;
75
76 nf_ct_frag6_consume_orig(reasm);
77
78 NF_HOOK_THRESH(NFPROTO_IPV6, state->hook, state->net, state->sk, reasm, 72 NF_HOOK_THRESH(NFPROTO_IPV6, state->hook, state->net, state->sk, reasm,
79 state->in, state->out, 73 state->in, state->out,
80 state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1); 74 state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1);