xref: /openbmc/linux/net/ipv6/tcpv6_offload.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28663e02aSVlad Yasevich /*
38663e02aSVlad Yasevich  *	IPV6 GSO/GRO offload support
48663e02aSVlad Yasevich  *	Linux INET6 implementation
58663e02aSVlad Yasevich  *
68663e02aSVlad Yasevich  *      TCPv6 GSO/GRO support
78663e02aSVlad Yasevich  */
8028e0a47SPaolo Abeni #include <linux/indirect_call_wrapper.h>
98663e02aSVlad Yasevich #include <linux/skbuff.h>
104721031cSEric Dumazet #include <net/gro.h>
118663e02aSVlad Yasevich #include <net/protocol.h>
128663e02aSVlad Yasevich #include <net/tcp.h>
138663e02aSVlad Yasevich #include <net/ip6_checksum.h>
148663e02aSVlad Yasevich #include "ip6_offload.h"
158663e02aSVlad Yasevich 
16028e0a47SPaolo Abeni INDIRECT_CALLABLE_SCOPE
tcp6_gro_receive(struct list_head * head,struct sk_buff * skb)17028e0a47SPaolo Abeni struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
188663e02aSVlad Yasevich {
19cc5c00bbSHerbert Xu 	/* Don't bother verifying checksum if we're going to flush anyway. */
20149d0774STom Herbert 	if (!NAPI_GRO_CB(skb)->flush &&
21149d0774STom Herbert 	    skb_gro_checksum_validate(skb, IPPROTO_TCP,
22149d0774STom Herbert 				      ip6_gro_compute_pseudo)) {
238663e02aSVlad Yasevich 		NAPI_GRO_CB(skb)->flush = 1;
248663e02aSVlad Yasevich 		return NULL;
258663e02aSVlad Yasevich 	}
268663e02aSVlad Yasevich 
278663e02aSVlad Yasevich 	return tcp_gro_receive(head, skb);
288663e02aSVlad Yasevich }
298663e02aSVlad Yasevich 
tcp6_gro_complete(struct sk_buff * skb,int thoff)30028e0a47SPaolo Abeni INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
318663e02aSVlad Yasevich {
328663e02aSVlad Yasevich 	const struct ipv6hdr *iph = ipv6_hdr(skb);
338663e02aSVlad Yasevich 	struct tcphdr *th = tcp_hdr(skb);
348663e02aSVlad Yasevich 
35299603e8SJerry Chu 	th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr,
36299603e8SJerry Chu 				  &iph->daddr, 0);
37c3caf119SJerry Chu 	skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
388663e02aSVlad Yasevich 
39*b1f2abcfSParav Pandit 	tcp_gro_complete(skb);
40*b1f2abcfSParav Pandit 	return 0;
418663e02aSVlad Yasevich }
428663e02aSVlad Yasevich 
tcp6_gso_segment(struct sk_buff * skb,netdev_features_t features)4374abc20cSEric Dumazet static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
44d020f8f7STom Herbert 					netdev_features_t features)
45d020f8f7STom Herbert {
46d020f8f7STom Herbert 	struct tcphdr *th;
47d020f8f7STom Herbert 
48121d57afSWillem de Bruijn 	if (!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6))
49121d57afSWillem de Bruijn 		return ERR_PTR(-EINVAL);
50121d57afSWillem de Bruijn 
51d020f8f7STom Herbert 	if (!pskb_may_pull(skb, sizeof(*th)))
52d020f8f7STom Herbert 		return ERR_PTR(-EINVAL);
53d020f8f7STom Herbert 
54d020f8f7STom Herbert 	if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
55d020f8f7STom Herbert 		const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
56d020f8f7STom Herbert 		struct tcphdr *th = tcp_hdr(skb);
57d020f8f7STom Herbert 
58d020f8f7STom Herbert 		/* Set up pseudo header, usually expect stack to have done
59d020f8f7STom Herbert 		 * this.
60d020f8f7STom Herbert 		 */
61d020f8f7STom Herbert 
62d020f8f7STom Herbert 		th->check = 0;
63d020f8f7STom Herbert 		skb->ip_summed = CHECKSUM_PARTIAL;
64d020f8f7STom Herbert 		__tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
65d020f8f7STom Herbert 	}
66d020f8f7STom Herbert 
67d020f8f7STom Herbert 	return tcp_gso_segment(skb, features);
68d020f8f7STom Herbert }
698663e02aSVlad Yasevich static const struct net_offload tcpv6_offload = {
70f191a1d1SVlad Yasevich 	.callbacks = {
71d020f8f7STom Herbert 		.gso_segment	=	tcp6_gso_segment,
728663e02aSVlad Yasevich 		.gro_receive	=	tcp6_gro_receive,
738663e02aSVlad Yasevich 		.gro_complete	=	tcp6_gro_complete,
74f191a1d1SVlad Yasevich 	},
758663e02aSVlad Yasevich };
768663e02aSVlad Yasevich 
tcpv6_offload_init(void)778663e02aSVlad Yasevich int __init tcpv6_offload_init(void)
788663e02aSVlad Yasevich {
798663e02aSVlad Yasevich 	return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
808663e02aSVlad Yasevich }
81