mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 13:14:07 +08:00
2874c5fd28
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license as published by the free software foundation either version 2 of the license or at your option any later version extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 3029 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
79 lines
1.9 KiB
C
79 lines
1.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* IPV6 GSO/GRO offload support
|
|
* Linux INET6 implementation
|
|
*
|
|
* TCPv6 GSO/GRO support
|
|
*/
|
|
#include <linux/indirect_call_wrapper.h>
|
|
#include <linux/skbuff.h>
|
|
#include <net/protocol.h>
|
|
#include <net/tcp.h>
|
|
#include <net/ip6_checksum.h>
|
|
#include "ip6_offload.h"
|
|
|
|
INDIRECT_CALLABLE_SCOPE
|
|
struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
|
|
{
|
|
/* Don't bother verifying checksum if we're going to flush anyway. */
|
|
if (!NAPI_GRO_CB(skb)->flush &&
|
|
skb_gro_checksum_validate(skb, IPPROTO_TCP,
|
|
ip6_gro_compute_pseudo)) {
|
|
NAPI_GRO_CB(skb)->flush = 1;
|
|
return NULL;
|
|
}
|
|
|
|
return tcp_gro_receive(head, skb);
|
|
}
|
|
|
|
INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
|
|
{
|
|
const struct ipv6hdr *iph = ipv6_hdr(skb);
|
|
struct tcphdr *th = tcp_hdr(skb);
|
|
|
|
th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr,
|
|
&iph->daddr, 0);
|
|
skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
|
|
|
|
return tcp_gro_complete(skb);
|
|
}
|
|
|
|
static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
|
|
netdev_features_t features)
|
|
{
|
|
struct tcphdr *th;
|
|
|
|
if (!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
if (!pskb_may_pull(skb, sizeof(*th)))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
|
|
const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
|
|
struct tcphdr *th = tcp_hdr(skb);
|
|
|
|
/* Set up pseudo header, usually expect stack to have done
|
|
* this.
|
|
*/
|
|
|
|
th->check = 0;
|
|
skb->ip_summed = CHECKSUM_PARTIAL;
|
|
__tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
|
|
}
|
|
|
|
return tcp_gso_segment(skb, features);
|
|
}
|
|
static const struct net_offload tcpv6_offload = {
|
|
.callbacks = {
|
|
.gso_segment = tcp6_gso_segment,
|
|
.gro_receive = tcp6_gro_receive,
|
|
.gro_complete = tcp6_gro_complete,
|
|
},
|
|
};
|
|
|
|
int __init tcpv6_offload_init(void)
|
|
{
|
|
return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
|
|
}
|