linux/net/ipv4/gre_offload.c
<<
>>
Prefs
   1/*
   2 *      IPV4 GSO/GRO offload support
   3 *      Linux INET 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 *      GRE GSO support
  11 */
  12
  13#include <linux/skbuff.h>
  14#include <net/protocol.h>
  15#include <net/gre.h>
  16
  17static int gre_gso_send_check(struct sk_buff *skb)
  18{
  19        if (!skb->encapsulation)
  20                return -EINVAL;
  21        return 0;
  22}
  23
  24static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
  25                                       netdev_features_t features)
  26{
  27        struct sk_buff *segs = ERR_PTR(-EINVAL);
  28        netdev_features_t enc_features;
  29        int ghl = GRE_HEADER_SECTION;
  30        struct gre_base_hdr *greh;
  31        int mac_len = skb->mac_len;
  32        __be16 protocol = skb->protocol;
  33        int tnl_hlen;
  34        bool csum;
  35
  36        if (unlikely(skb_shinfo(skb)->gso_type &
  37                                ~(SKB_GSO_TCPV4 |
  38                                  SKB_GSO_TCPV6 |
  39                                  SKB_GSO_UDP |
  40                                  SKB_GSO_DODGY |
  41                                  SKB_GSO_TCP_ECN |
  42                                  SKB_GSO_GRE)))
  43                goto out;
  44
  45        if (unlikely(!pskb_may_pull(skb, sizeof(*greh))))
  46                goto out;
  47
  48        greh = (struct gre_base_hdr *)skb_transport_header(skb);
  49
  50        if (greh->flags & GRE_KEY)
  51                ghl += GRE_HEADER_SECTION;
  52        if (greh->flags & GRE_SEQ)
  53                ghl += GRE_HEADER_SECTION;
  54        if (greh->flags & GRE_CSUM) {
  55                ghl += GRE_HEADER_SECTION;
  56                csum = true;
  57        } else
  58                csum = false;
  59
  60        /* setup inner skb. */
  61        skb->protocol = greh->protocol;
  62        skb->encapsulation = 0;
  63
  64        if (unlikely(!pskb_may_pull(skb, ghl)))
  65                goto out;
  66
  67        __skb_pull(skb, ghl);
  68        skb_reset_mac_header(skb);
  69        skb_set_network_header(skb, skb_inner_network_offset(skb));
  70        skb->mac_len = skb_inner_network_offset(skb);
  71
  72        /* segment inner packet. */
  73        enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
  74        segs = skb_mac_gso_segment(skb, enc_features);
  75        if (!segs || IS_ERR(segs))
  76                goto out;
  77
  78        skb = segs;
  79        tnl_hlen = skb_tnl_header_len(skb);
  80        do {
  81                __skb_push(skb, ghl);
  82                if (csum) {
  83                        __be32 *pcsum;
  84
  85                        if (skb_has_shared_frag(skb)) {
  86                                int err;
  87
  88                                err = __skb_linearize(skb);
  89                                if (err) {
  90                                        kfree_skb_list(segs);
  91                                        segs = ERR_PTR(err);
  92                                        goto out;
  93                                }
  94                        }
  95
  96                        greh = (struct gre_base_hdr *)(skb->data);
  97                        pcsum = (__be32 *)(greh + 1);
  98                        *pcsum = 0;
  99                        *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0));
 100                }
 101                __skb_push(skb, tnl_hlen - ghl);
 102
 103                skb_reset_inner_headers(skb);
 104                skb->encapsulation = 1;
 105
 106                skb_reset_mac_header(skb);
 107                skb_set_network_header(skb, mac_len);
 108                skb->mac_len = mac_len;
 109                skb->protocol = protocol;
 110        } while ((skb = skb->next));
 111out:
 112        return segs;
 113}
 114
 115static const struct net_offload gre_offload = {
 116        .callbacks = {
 117                .gso_send_check = gre_gso_send_check,
 118                .gso_segment = gre_gso_segment,
 119        },
 120};
 121
 122int __init gre_offload_init(void)
 123{
 124        return inet_add_offload(&gre_offload, IPPROTO_GRE);
 125}
 126
 127void __exit gre_offload_exit(void)
 128{
 129        inet_del_offload(&gre_offload, IPPROTO_GRE);
 130}
 131