aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/protocol.h3
-rw-r--r--net/ipv6/af_inet6.c107
2 files changed, 105 insertions, 5 deletions
diff --git a/include/net/protocol.h b/include/net/protocol.h
index cb2965aa1b62..ffa5b8b1f1df 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -59,6 +59,9 @@ struct inet6_protocol
59 int (*gso_send_check)(struct sk_buff *skb); 59 int (*gso_send_check)(struct sk_buff *skb);
60 struct sk_buff *(*gso_segment)(struct sk_buff *skb, 60 struct sk_buff *(*gso_segment)(struct sk_buff *skb,
61 int features); 61 int features);
62 struct sk_buff **(*gro_receive)(struct sk_buff **head,
63 struct sk_buff *skb);
64 int (*gro_complete)(struct sk_buff *skb);
62 65
63 unsigned int flags; /* INET6_PROTO_xxx */ 66 unsigned int flags; /* INET6_PROTO_xxx */
64}; 67};
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 437b750b98fd..94f74f5b0cbf 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -672,8 +672,7 @@ int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)
672 672
673EXPORT_SYMBOL_GPL(ipv6_opt_accepted); 673EXPORT_SYMBOL_GPL(ipv6_opt_accepted);
674 674
675static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb, 675static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
676 int proto)
677{ 676{
678 struct inet6_protocol *ops = NULL; 677 struct inet6_protocol *ops = NULL;
679 678
@@ -704,7 +703,7 @@ static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb,
704 __skb_pull(skb, len); 703 __skb_pull(skb, len);
705 } 704 }
706 705
707 return ops; 706 return proto;
708} 707}
709 708
710static int ipv6_gso_send_check(struct sk_buff *skb) 709static int ipv6_gso_send_check(struct sk_buff *skb)
@@ -721,7 +720,9 @@ static int ipv6_gso_send_check(struct sk_buff *skb)
721 err = -EPROTONOSUPPORT; 720 err = -EPROTONOSUPPORT;
722 721
723 rcu_read_lock(); 722 rcu_read_lock();
724 ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); 723 ops = rcu_dereference(inet6_protos[
724 ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
725
725 if (likely(ops && ops->gso_send_check)) { 726 if (likely(ops && ops->gso_send_check)) {
726 skb_reset_transport_header(skb); 727 skb_reset_transport_header(skb);
727 err = ops->gso_send_check(skb); 728 err = ops->gso_send_check(skb);
@@ -757,7 +758,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
757 segs = ERR_PTR(-EPROTONOSUPPORT); 758 segs = ERR_PTR(-EPROTONOSUPPORT);
758 759
759 rcu_read_lock(); 760 rcu_read_lock();
760 ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); 761 ops = rcu_dereference(inet6_protos[
762 ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
763
761 if (likely(ops && ops->gso_segment)) { 764 if (likely(ops && ops->gso_segment)) {
762 skb_reset_transport_header(skb); 765 skb_reset_transport_header(skb);
763 segs = ops->gso_segment(skb, features); 766 segs = ops->gso_segment(skb, features);
@@ -777,11 +780,105 @@ out:
777 return segs; 780 return segs;
778} 781}
779 782
783struct ipv6_gro_cb {
784 struct napi_gro_cb napi;
785 int proto;
786};
787
788#define IPV6_GRO_CB(skb) ((struct ipv6_gro_cb *)(skb)->cb)
789
790static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
791 struct sk_buff *skb)
792{
793 struct inet6_protocol *ops;
794 struct sk_buff **pp = NULL;
795 struct sk_buff *p;
796 struct ipv6hdr *iph;
797 unsigned int nlen;
798 int flush = 1;
799 int proto;
800
801 if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
802 goto out;
803
804 iph = ipv6_hdr(skb);
805 __skb_pull(skb, sizeof(*iph));
806
807 flush += ntohs(iph->payload_len) != skb->len;
808
809 rcu_read_lock();
810 proto = ipv6_gso_pull_exthdrs(skb, iph->nexthdr);
811 IPV6_GRO_CB(skb)->proto = proto;
812 ops = rcu_dereference(inet6_protos[proto]);
813 if (!ops || !ops->gro_receive)
814 goto out_unlock;
815
816 flush--;
817 skb_reset_transport_header(skb);
818 nlen = skb_network_header_len(skb);
819
820 for (p = *head; p; p = p->next) {
821 struct ipv6hdr *iph2;
822
823 if (!NAPI_GRO_CB(p)->same_flow)
824 continue;
825
826 iph2 = ipv6_hdr(p);
827
828 /* All fields must match except length. */
829 if (nlen != skb_network_header_len(p) ||
830 memcmp(iph, iph2, offsetof(struct ipv6hdr, payload_len)) ||
831 memcmp(&iph->nexthdr, &iph2->nexthdr,
832 nlen - offsetof(struct ipv6hdr, nexthdr))) {
833 NAPI_GRO_CB(p)->same_flow = 0;
834 continue;
835 }
836
837 NAPI_GRO_CB(p)->flush |= flush;
838 }
839
840 NAPI_GRO_CB(skb)->flush |= flush;
841
842 pp = ops->gro_receive(head, skb);
843
844out_unlock:
845 rcu_read_unlock();
846
847out:
848 NAPI_GRO_CB(skb)->flush |= flush;
849
850 return pp;
851}
852
853static int ipv6_gro_complete(struct sk_buff *skb)
854{
855 struct inet6_protocol *ops;
856 struct ipv6hdr *iph = ipv6_hdr(skb);
857 int err = -ENOSYS;
858
859 iph->payload_len = htons(skb->len - skb_network_offset(skb) -
860 sizeof(*iph));
861
862 rcu_read_lock();
863 ops = rcu_dereference(inet6_protos[IPV6_GRO_CB(skb)->proto]);
864 if (WARN_ON(!ops || !ops->gro_complete))
865 goto out_unlock;
866
867 err = ops->gro_complete(skb);
868
869out_unlock:
870 rcu_read_unlock();
871
872 return err;
873}
874
780static struct packet_type ipv6_packet_type = { 875static struct packet_type ipv6_packet_type = {
781 .type = __constant_htons(ETH_P_IPV6), 876 .type = __constant_htons(ETH_P_IPV6),
782 .func = ipv6_rcv, 877 .func = ipv6_rcv,
783 .gso_send_check = ipv6_gso_send_check, 878 .gso_send_check = ipv6_gso_send_check,
784 .gso_segment = ipv6_gso_segment, 879 .gso_segment = ipv6_gso_segment,
880 .gro_receive = ipv6_gro_receive,
881 .gro_complete = ipv6_gro_complete,
785}; 882};
786 883
787static int __init ipv6_packet_init(void) 884static int __init ipv6_packet_init(void)