18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IPV6 GSO/GRO offload support 48c2ecf20Sopenharmony_ci * Linux INET6 implementation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * TCPv6 GSO/GRO support 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/indirect_call_wrapper.h> 98c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 108c2ecf20Sopenharmony_ci#include <net/protocol.h> 118c2ecf20Sopenharmony_ci#include <net/tcp.h> 128c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 138c2ecf20Sopenharmony_ci#include "ip6_offload.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE 168c2ecf20Sopenharmony_cistruct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci /* Don't bother verifying checksum if we're going to flush anyway. */ 198c2ecf20Sopenharmony_ci if (!NAPI_GRO_CB(skb)->flush && 208c2ecf20Sopenharmony_ci skb_gro_checksum_validate(skb, IPPROTO_TCP, 218c2ecf20Sopenharmony_ci ip6_gro_compute_pseudo)) { 228c2ecf20Sopenharmony_ci NAPI_GRO_CB(skb)->flush = 1; 238c2ecf20Sopenharmony_ci return NULL; 248c2ecf20Sopenharmony_ci } 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci return tcp_gro_receive(head, skb); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci const struct ipv6hdr *iph = ipv6_hdr(skb); 328c2ecf20Sopenharmony_ci struct tcphdr *th = tcp_hdr(skb); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr, 358c2ecf20Sopenharmony_ci &iph->daddr, 0); 368c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return tcp_gro_complete(skb); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic struct sk_buff *tcp6_gso_segment(struct sk_buff *skb, 428c2ecf20Sopenharmony_ci netdev_features_t features) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct tcphdr *th; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)) 478c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*th))) 508c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { 538c2ecf20Sopenharmony_ci const struct ipv6hdr *ipv6h = ipv6_hdr(skb); 548c2ecf20Sopenharmony_ci struct tcphdr *th = tcp_hdr(skb); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* Set up pseudo header, usually expect stack to have done 578c2ecf20Sopenharmony_ci * this. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci th->check = 0; 618c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_PARTIAL; 628c2ecf20Sopenharmony_ci __tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr); 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return tcp_gso_segment(skb, features); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_cistatic const struct net_offload tcpv6_offload = { 688c2ecf20Sopenharmony_ci .callbacks = { 698c2ecf20Sopenharmony_ci .gso_segment = tcp6_gso_segment, 708c2ecf20Sopenharmony_ci .gro_receive = tcp6_gro_receive, 718c2ecf20Sopenharmony_ci .gro_complete = tcp6_gro_complete, 728c2ecf20Sopenharmony_ci }, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ciint __init tcpv6_offload_init(void) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP); 788c2ecf20Sopenharmony_ci} 79