linux/net/ipv6/tcpv6_offload.c
<<
>>
Prefs
   1/*
   2 *      IPV6 GSO/GRO offload support
   3 *      Linux INET6 implementation
   4 *
   5 *      This program is free software; you can redistribute it and/or
   6 *      modify it under the terms of the GNU General Public License
   7 *      as published by the Free Software Foundation; either version
   8 *      2 of the License, or (at your option) any later version.
   9 *
  10 *      TCPv6 GSO/GRO support
  11 */
  12#include <linux/skbuff.h>
  13#include <net/protocol.h>
  14#include <net/tcp.h>
  15#include <net/ip6_checksum.h>
  16#include "ip6_offload.h"
  17
  18static int tcp_v6_gso_send_check(struct sk_buff *skb)
  19{
  20        const struct ipv6hdr *ipv6h;
  21        struct tcphdr *th;
  22
  23        if (!pskb_may_pull(skb, sizeof(*th)))
  24                return -EINVAL;
  25
  26        ipv6h = ipv6_hdr(skb);
  27        th = tcp_hdr(skb);
  28
  29        th->check = 0;
  30        skb->ip_summed = CHECKSUM_PARTIAL;
  31        __tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
  32        return 0;
  33}
  34
  35static struct sk_buff **tcp6_gro_receive(struct sk_buff **head,
  36                                         struct sk_buff *skb)
  37{
  38        const struct ipv6hdr *iph = skb_gro_network_header(skb);
  39        __wsum wsum;
  40        __sum16 sum;
  41
  42        switch (skb->ip_summed) {
  43        case CHECKSUM_COMPLETE:
  44                if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
  45                                  skb->csum)) {
  46                        skb->ip_summed = CHECKSUM_UNNECESSARY;
  47                        break;
  48                }
  49flush:
  50                NAPI_GRO_CB(skb)->flush = 1;
  51                return NULL;
  52
  53        case CHECKSUM_NONE:
  54                wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr,
  55                                                    skb_gro_len(skb),
  56                                                    IPPROTO_TCP, 0));
  57                sum = csum_fold(skb_checksum(skb,
  58                                             skb_gro_offset(skb),
  59                                             skb_gro_len(skb),
  60                                             wsum));
  61                if (sum)
  62                        goto flush;
  63
  64                skb->ip_summed = CHECKSUM_UNNECESSARY;
  65                break;
  66        }
  67
  68        return tcp_gro_receive(head, skb);
  69}
  70
  71static int tcp6_gro_complete(struct sk_buff *skb)
  72{
  73        const struct ipv6hdr *iph = ipv6_hdr(skb);
  74        struct tcphdr *th = tcp_hdr(skb);
  75
  76        th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
  77                                  &iph->saddr, &iph->daddr, 0);
  78        skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
  79
  80        return tcp_gro_complete(skb);
  81}
  82
  83static const struct net_offload tcpv6_offload = {
  84        .callbacks = {
  85                .gso_send_check =       tcp_v6_gso_send_check,
  86                .gso_segment    =       tcp_tso_segment,
  87                .gro_receive    =       tcp6_gro_receive,
  88                .gro_complete   =       tcp6_gro_complete,
  89        },
  90};
  91
  92int __init tcpv6_offload_init(void)
  93{
  94        return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
  95}
  96