0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/indirect_call_wrapper.h>
0009 #include <linux/skbuff.h>
0010 #include <net/gro.h>
0011 #include <net/protocol.h>
0012 #include <net/tcp.h>
0013 #include <net/ip6_checksum.h>
0014 #include "ip6_offload.h"
0015
0016 INDIRECT_CALLABLE_SCOPE
0017 struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
0018 {
0019
0020 if (!NAPI_GRO_CB(skb)->flush &&
0021 skb_gro_checksum_validate(skb, IPPROTO_TCP,
0022 ip6_gro_compute_pseudo)) {
0023 NAPI_GRO_CB(skb)->flush = 1;
0024 return NULL;
0025 }
0026
0027 return tcp_gro_receive(head, skb);
0028 }
0029
0030 INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
0031 {
0032 const struct ipv6hdr *iph = ipv6_hdr(skb);
0033 struct tcphdr *th = tcp_hdr(skb);
0034
0035 th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr,
0036 &iph->daddr, 0);
0037 skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
0038
0039 return tcp_gro_complete(skb);
0040 }
0041
0042 static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
0043 netdev_features_t features)
0044 {
0045 struct tcphdr *th;
0046
0047 if (!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6))
0048 return ERR_PTR(-EINVAL);
0049
0050 if (!pskb_may_pull(skb, sizeof(*th)))
0051 return ERR_PTR(-EINVAL);
0052
0053 if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
0054 const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
0055 struct tcphdr *th = tcp_hdr(skb);
0056
0057
0058
0059
0060
0061 th->check = 0;
0062 skb->ip_summed = CHECKSUM_PARTIAL;
0063 __tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
0064 }
0065
0066 return tcp_gso_segment(skb, features);
0067 }
0068 static const struct net_offload tcpv6_offload = {
0069 .callbacks = {
0070 .gso_segment = tcp6_gso_segment,
0071 .gro_receive = tcp6_gro_receive,
0072 .gro_complete = tcp6_gro_complete,
0073 },
0074 };
0075
0076 int __init tcpv6_offload_init(void)
0077 {
0078 return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
0079 }