1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * IPV6 GSO/GRO offload support
4*4882a593Smuzhiyun * Linux INET6 implementation
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * TCPv6 GSO/GRO support
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun #include <linux/indirect_call_wrapper.h>
9*4882a593Smuzhiyun #include <linux/skbuff.h>
10*4882a593Smuzhiyun #include <net/protocol.h>
11*4882a593Smuzhiyun #include <net/tcp.h>
12*4882a593Smuzhiyun #include <net/ip6_checksum.h>
13*4882a593Smuzhiyun #include "ip6_offload.h"
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun INDIRECT_CALLABLE_SCOPE
tcp6_gro_receive(struct list_head * head,struct sk_buff * skb)16*4882a593Smuzhiyun struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
17*4882a593Smuzhiyun {
18*4882a593Smuzhiyun /* Don't bother verifying checksum if we're going to flush anyway. */
19*4882a593Smuzhiyun if (!NAPI_GRO_CB(skb)->flush &&
20*4882a593Smuzhiyun skb_gro_checksum_validate(skb, IPPROTO_TCP,
21*4882a593Smuzhiyun ip6_gro_compute_pseudo)) {
22*4882a593Smuzhiyun NAPI_GRO_CB(skb)->flush = 1;
23*4882a593Smuzhiyun return NULL;
24*4882a593Smuzhiyun }
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun return tcp_gro_receive(head, skb);
27*4882a593Smuzhiyun }
28*4882a593Smuzhiyun
tcp6_gro_complete(struct sk_buff * skb,int thoff)29*4882a593Smuzhiyun INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun const struct ipv6hdr *iph = ipv6_hdr(skb);
32*4882a593Smuzhiyun struct tcphdr *th = tcp_hdr(skb);
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr,
35*4882a593Smuzhiyun &iph->daddr, 0);
36*4882a593Smuzhiyun skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun return tcp_gro_complete(skb);
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun
tcp6_gso_segment(struct sk_buff * skb,netdev_features_t features)41*4882a593Smuzhiyun static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
42*4882a593Smuzhiyun netdev_features_t features)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun struct tcphdr *th;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun if (!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6))
47*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun if (!pskb_may_pull(skb, sizeof(*th)))
50*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
53*4882a593Smuzhiyun const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
54*4882a593Smuzhiyun struct tcphdr *th = tcp_hdr(skb);
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun /* Set up pseudo header, usually expect stack to have done
57*4882a593Smuzhiyun * this.
58*4882a593Smuzhiyun */
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun th->check = 0;
61*4882a593Smuzhiyun skb->ip_summed = CHECKSUM_PARTIAL;
62*4882a593Smuzhiyun __tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun return tcp_gso_segment(skb, features);
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun static const struct net_offload tcpv6_offload = {
68*4882a593Smuzhiyun .callbacks = {
69*4882a593Smuzhiyun .gso_segment = tcp6_gso_segment,
70*4882a593Smuzhiyun .gro_receive = tcp6_gro_receive,
71*4882a593Smuzhiyun .gro_complete = tcp6_gro_complete,
72*4882a593Smuzhiyun },
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun
tcpv6_offload_init(void)75*4882a593Smuzhiyun int __init tcpv6_offload_init(void)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
78*4882a593Smuzhiyun }
79