linux/net/ipv6/esp6_offload.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * IPV6 GSO/GRO offload support
   4 * Linux INET implementation
   5 *
   6 * Copyright (C) 2016 secunet Security Networks AG
   7 * Author: Steffen Klassert <steffen.klassert@secunet.com>
   8 *
   9 * ESP GRO support
  10 */
  11
  12#include <linux/skbuff.h>
  13#include <linux/init.h>
  14#include <net/protocol.h>
  15#include <crypto/aead.h>
  16#include <crypto/authenc.h>
  17#include <linux/err.h>
  18#include <linux/module.h>
  19#include <net/ip.h>
  20#include <net/xfrm.h>
  21#include <net/esp.h>
  22#include <linux/scatterlist.h>
  23#include <linux/kernel.h>
  24#include <linux/slab.h>
  25#include <linux/spinlock.h>
  26#include <net/ip6_route.h>
  27#include <net/ipv6.h>
  28#include <linux/icmpv6.h>
  29
  30static __u16 esp6_nexthdr_esp_offset(struct ipv6hdr *ipv6_hdr, int nhlen)
  31{
  32        int off = sizeof(struct ipv6hdr);
  33        struct ipv6_opt_hdr *exthdr;
  34
  35        if (likely(ipv6_hdr->nexthdr == NEXTHDR_ESP))
  36                return offsetof(struct ipv6hdr, nexthdr);
  37
  38        while (off < nhlen) {
  39                exthdr = (void *)ipv6_hdr + off;
  40                if (exthdr->nexthdr == NEXTHDR_ESP)
  41                        return off;
  42
  43                off += ipv6_optlen(exthdr);
  44        }
  45
  46        return 0;
  47}
  48
  49static struct sk_buff *esp6_gro_receive(struct list_head *head,
  50                                        struct sk_buff *skb)
  51{
  52        int offset = skb_gro_offset(skb);
  53        struct xfrm_offload *xo;
  54        struct xfrm_state *x;
  55        __be32 seq;
  56        __be32 spi;
  57        int nhoff;
  58        int err;
  59
  60        if (!pskb_pull(skb, offset))
  61                return NULL;
  62
  63        if ((err = xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq)) != 0)
  64                goto out;
  65
  66        xo = xfrm_offload(skb);
  67        if (!xo || !(xo->flags & CRYPTO_DONE)) {
  68                struct sec_path *sp = secpath_set(skb);
  69
  70                if (!sp)
  71                        goto out;
  72
  73                if (sp->len == XFRM_MAX_DEPTH)
  74                        goto out_reset;
  75
  76                x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
  77                                      (xfrm_address_t *)&ipv6_hdr(skb)->daddr,
  78                                      spi, IPPROTO_ESP, AF_INET6);
  79                if (!x)
  80                        goto out_reset;
  81
  82                skb->mark = xfrm_smark_get(skb->mark, x);
  83
  84                sp->xvec[sp->len++] = x;
  85                sp->olen++;
  86
  87                xo = xfrm_offload(skb);
  88                if (!xo)
  89                        goto out_reset;
  90        }
  91
  92        xo->flags |= XFRM_GRO;
  93
  94        nhoff = esp6_nexthdr_esp_offset(ipv6_hdr(skb), offset);
  95        if (!nhoff)
  96                goto out;
  97
  98        IP6CB(skb)->nhoff = nhoff;
  99        XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
 100        XFRM_SPI_SKB_CB(skb)->family = AF_INET6;
 101        XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr);
 102        XFRM_SPI_SKB_CB(skb)->seq = seq;
 103
 104        /* We don't need to handle errors from xfrm_input, it does all
 105         * the error handling and frees the resources on error. */
 106        xfrm_input(skb, IPPROTO_ESP, spi, -2);
 107
 108        return ERR_PTR(-EINPROGRESS);
 109out_reset:
 110        secpath_reset(skb);
 111out:
 112        skb_push(skb, offset);
 113        NAPI_GRO_CB(skb)->same_flow = 0;
 114        NAPI_GRO_CB(skb)->flush = 1;
 115
 116        return NULL;
 117}
 118
 119static void esp6_gso_encap(struct xfrm_state *x, struct sk_buff *skb)
 120{
 121        struct ip_esp_hdr *esph;
 122        struct ipv6hdr *iph = ipv6_hdr(skb);
 123        struct xfrm_offload *xo = xfrm_offload(skb);
 124        u8 proto = iph->nexthdr;
 125
 126        skb_push(skb, -skb_network_offset(skb));
 127
 128        if (x->outer_mode.encap == XFRM_MODE_TRANSPORT) {
 129                __be16 frag;
 130
 131                ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &proto, &frag);
 132        }
 133
 134        esph = ip_esp_hdr(skb);
 135        *skb_mac_header(skb) = IPPROTO_ESP;
 136
 137        esph->spi = x->id.spi;
 138        esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
 139
 140        xo->proto = proto;
 141}
 142
 143static struct sk_buff *xfrm6_tunnel_gso_segment(struct xfrm_state *x,
 144                                                struct sk_buff *skb,
 145                                                netdev_features_t features)
 146{
 147        __skb_push(skb, skb->mac_len);
 148        return skb_mac_gso_segment(skb, features);
 149}
 150
 151static struct sk_buff *xfrm6_transport_gso_segment(struct xfrm_state *x,
 152                                                   struct sk_buff *skb,
 153                                                   netdev_features_t features)
 154{
 155        const struct net_offload *ops;
 156        struct sk_buff *segs = ERR_PTR(-EINVAL);
 157        struct xfrm_offload *xo = xfrm_offload(skb);
 158
 159        skb->transport_header += x->props.header_len;
 160        ops = rcu_dereference(inet6_offloads[xo->proto]);
 161        if (likely(ops && ops->callbacks.gso_segment))
 162                segs = ops->callbacks.gso_segment(skb, features);
 163
 164        return segs;
 165}
 166
 167static struct sk_buff *xfrm6_beet_gso_segment(struct xfrm_state *x,
 168                                              struct sk_buff *skb,
 169                                              netdev_features_t features)
 170{
 171        struct xfrm_offload *xo = xfrm_offload(skb);
 172        struct sk_buff *segs = ERR_PTR(-EINVAL);
 173        const struct net_offload *ops;
 174        u8 proto = xo->proto;
 175
 176        skb->transport_header += x->props.header_len;
 177
 178        if (x->sel.family != AF_INET6) {
 179                skb->transport_header -=
 180                        (sizeof(struct ipv6hdr) - sizeof(struct iphdr));
 181
 182                if (proto == IPPROTO_BEETPH) {
 183                        struct ip_beet_phdr *ph =
 184                                (struct ip_beet_phdr *)skb->data;
 185
 186                        skb->transport_header += ph->hdrlen * 8;
 187                        proto = ph->nexthdr;
 188                } else {
 189                        skb->transport_header -= IPV4_BEET_PHMAXLEN;
 190                }
 191
 192                if (proto == IPPROTO_TCP)
 193                        skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
 194        } else {
 195                __be16 frag;
 196
 197                skb->transport_header +=
 198                        ipv6_skip_exthdr(skb, 0, &proto, &frag);
 199        }
 200
 201        __skb_pull(skb, skb_transport_offset(skb));
 202        ops = rcu_dereference(inet6_offloads[proto]);
 203        if (likely(ops && ops->callbacks.gso_segment))
 204                segs = ops->callbacks.gso_segment(skb, features);
 205
 206        return segs;
 207}
 208
 209static struct sk_buff *xfrm6_outer_mode_gso_segment(struct xfrm_state *x,
 210                                                    struct sk_buff *skb,
 211                                                    netdev_features_t features)
 212{
 213        switch (x->outer_mode.encap) {
 214        case XFRM_MODE_TUNNEL:
 215                return xfrm6_tunnel_gso_segment(x, skb, features);
 216        case XFRM_MODE_TRANSPORT:
 217                return xfrm6_transport_gso_segment(x, skb, features);
 218        case XFRM_MODE_BEET:
 219                return xfrm6_beet_gso_segment(x, skb, features);
 220        }
 221
 222        return ERR_PTR(-EOPNOTSUPP);
 223}
 224
 225static struct sk_buff *esp6_gso_segment(struct sk_buff *skb,
 226                                        netdev_features_t features)
 227{
 228        struct xfrm_state *x;
 229        struct ip_esp_hdr *esph;
 230        struct crypto_aead *aead;
 231        netdev_features_t esp_features = features;
 232        struct xfrm_offload *xo = xfrm_offload(skb);
 233        struct sec_path *sp;
 234
 235        if (!xo)
 236                return ERR_PTR(-EINVAL);
 237
 238        if (!(skb_shinfo(skb)->gso_type & SKB_GSO_ESP))
 239                return ERR_PTR(-EINVAL);
 240
 241        sp = skb_sec_path(skb);
 242        x = sp->xvec[sp->len - 1];
 243        aead = x->data;
 244        esph = ip_esp_hdr(skb);
 245
 246        if (esph->spi != x->id.spi)
 247                return ERR_PTR(-EINVAL);
 248
 249        if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)))
 250                return ERR_PTR(-EINVAL);
 251
 252        __skb_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead));
 253
 254        skb->encap_hdr_csum = 1;
 255
 256        if (!(features & NETIF_F_HW_ESP) || x->xso.dev != skb->dev)
 257                esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK |
 258                                            NETIF_F_SCTP_CRC);
 259        else if (!(features & NETIF_F_HW_ESP_TX_CSUM))
 260                esp_features = features & ~(NETIF_F_CSUM_MASK |
 261                                            NETIF_F_SCTP_CRC);
 262
 263        xo->flags |= XFRM_GSO_SEGMENT;
 264
 265        return xfrm6_outer_mode_gso_segment(x, skb, esp_features);
 266}
 267
 268static int esp6_input_tail(struct xfrm_state *x, struct sk_buff *skb)
 269{
 270        struct crypto_aead *aead = x->data;
 271        struct xfrm_offload *xo = xfrm_offload(skb);
 272
 273        if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead)))
 274                return -EINVAL;
 275
 276        if (!(xo->flags & CRYPTO_DONE))
 277                skb->ip_summed = CHECKSUM_NONE;
 278
 279        return esp6_input_done2(skb, 0);
 280}
 281
 282static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb,  netdev_features_t features)
 283{
 284        int len;
 285        int err;
 286        int alen;
 287        int blksize;
 288        struct xfrm_offload *xo;
 289        struct crypto_aead *aead;
 290        struct esp_info esp;
 291        bool hw_offload = true;
 292        __u32 seq;
 293
 294        esp.inplace = true;
 295
 296        xo = xfrm_offload(skb);
 297
 298        if (!xo)
 299                return -EINVAL;
 300
 301        if (!(features & NETIF_F_HW_ESP) || x->xso.dev != skb->dev) {
 302                xo->flags |= CRYPTO_FALLBACK;
 303                hw_offload = false;
 304        }
 305
 306        esp.proto = xo->proto;
 307
 308        /* skb is pure payload to encrypt */
 309
 310        aead = x->data;
 311        alen = crypto_aead_authsize(aead);
 312
 313        esp.tfclen = 0;
 314        /* XXX: Add support for tfc padding here. */
 315
 316        blksize = ALIGN(crypto_aead_blocksize(aead), 4);
 317        esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize);
 318        esp.plen = esp.clen - skb->len - esp.tfclen;
 319        esp.tailen = esp.tfclen + esp.plen + alen;
 320
 321        if (!hw_offload || !skb_is_gso(skb)) {
 322                esp.nfrags = esp6_output_head(x, skb, &esp);
 323                if (esp.nfrags < 0)
 324                        return esp.nfrags;
 325        }
 326
 327        seq = xo->seq.low;
 328
 329        esp.esph = ip_esp_hdr(skb);
 330        esp.esph->spi = x->id.spi;
 331
 332        skb_push(skb, -skb_network_offset(skb));
 333
 334        if (xo->flags & XFRM_GSO_SEGMENT) {
 335                esp.esph->seq_no = htonl(seq);
 336
 337                if (!skb_is_gso(skb))
 338                        xo->seq.low++;
 339                else
 340                        xo->seq.low += skb_shinfo(skb)->gso_segs;
 341        }
 342
 343        esp.seqno = cpu_to_be64(xo->seq.low + ((u64)xo->seq.hi << 32));
 344
 345        len = skb->len - sizeof(struct ipv6hdr);
 346        if (len > IPV6_MAXPLEN)
 347                len = 0;
 348
 349        ipv6_hdr(skb)->payload_len = htons(len);
 350
 351        if (hw_offload) {
 352                if (!skb_ext_add(skb, SKB_EXT_SEC_PATH))
 353                        return -ENOMEM;
 354
 355                xo = xfrm_offload(skb);
 356                if (!xo)
 357                        return -EINVAL;
 358
 359                xo->flags |= XFRM_XMIT;
 360                return 0;
 361        }
 362
 363        err = esp6_output_tail(x, skb, &esp);
 364        if (err)
 365                return err;
 366
 367        secpath_reset(skb);
 368
 369        return 0;
 370}
 371
 372static const struct net_offload esp6_offload = {
 373        .callbacks = {
 374                .gro_receive = esp6_gro_receive,
 375                .gso_segment = esp6_gso_segment,
 376        },
 377};
 378
 379static const struct xfrm_type_offload esp6_type_offload = {
 380        .owner          = THIS_MODULE,
 381        .proto          = IPPROTO_ESP,
 382        .input_tail     = esp6_input_tail,
 383        .xmit           = esp6_xmit,
 384        .encap          = esp6_gso_encap,
 385};
 386
 387static int __init esp6_offload_init(void)
 388{
 389        if (xfrm_register_type_offload(&esp6_type_offload, AF_INET6) < 0) {
 390                pr_info("%s: can't add xfrm type offload\n", __func__);
 391                return -EAGAIN;
 392        }
 393
 394        return inet6_add_offload(&esp6_offload, IPPROTO_ESP);
 395}
 396
 397static void __exit esp6_offload_exit(void)
 398{
 399        xfrm_unregister_type_offload(&esp6_type_offload, AF_INET6);
 400        inet6_del_offload(&esp6_offload, IPPROTO_ESP);
 401}
 402
 403module_init(esp6_offload_init);
 404module_exit(esp6_offload_exit);
 405MODULE_LICENSE("GPL");
 406MODULE_AUTHOR("Steffen Klassert <steffen.klassert@secunet.com>");
 407MODULE_ALIAS_XFRM_OFFLOAD_TYPE(AF_INET6, XFRM_PROTO_ESP);
 408MODULE_DESCRIPTION("IPV6 GSO/GRO offload support");
 409