17785bba2SSteffen Klassert /* 27785bba2SSteffen Klassert * IPV4 GSO/GRO offload support 37785bba2SSteffen Klassert * Linux INET implementation 47785bba2SSteffen Klassert * 57785bba2SSteffen Klassert * Copyright (C) 2016 secunet Security Networks AG 67785bba2SSteffen Klassert * Author: Steffen Klassert <steffen.klassert@secunet.com> 77785bba2SSteffen Klassert * 87785bba2SSteffen Klassert * This program is free software; you can redistribute it and/or modify it 97785bba2SSteffen Klassert * under the terms and conditions of the GNU General Public License, 107785bba2SSteffen Klassert * version 2, as published by the Free Software Foundation. 117785bba2SSteffen Klassert * 127785bba2SSteffen Klassert * ESP GRO support 137785bba2SSteffen Klassert */ 147785bba2SSteffen Klassert 157785bba2SSteffen Klassert #include <linux/skbuff.h> 167785bba2SSteffen Klassert #include <linux/init.h> 177785bba2SSteffen Klassert #include <net/protocol.h> 187785bba2SSteffen Klassert #include <crypto/aead.h> 197785bba2SSteffen Klassert #include <crypto/authenc.h> 207785bba2SSteffen Klassert #include <linux/err.h> 217785bba2SSteffen Klassert #include <linux/module.h> 227785bba2SSteffen Klassert #include <net/ip.h> 237785bba2SSteffen Klassert #include <net/xfrm.h> 247785bba2SSteffen Klassert #include <net/esp.h> 257785bba2SSteffen Klassert #include <linux/scatterlist.h> 267785bba2SSteffen Klassert #include <linux/kernel.h> 277785bba2SSteffen Klassert #include <linux/slab.h> 287785bba2SSteffen Klassert #include <linux/spinlock.h> 297785bba2SSteffen Klassert #include <net/udp.h> 307785bba2SSteffen Klassert 31d4546c25SDavid Miller static struct sk_buff *esp4_gro_receive(struct list_head *head, 327785bba2SSteffen Klassert struct sk_buff *skb) 337785bba2SSteffen Klassert { 347785bba2SSteffen Klassert int offset = skb_gro_offset(skb); 357785bba2SSteffen Klassert struct xfrm_offload *xo; 367785bba2SSteffen Klassert struct xfrm_state *x; 377785bba2SSteffen Klassert __be32 seq; 387785bba2SSteffen Klassert __be32 spi; 397785bba2SSteffen Klassert int err; 407785bba2SSteffen Klassert 41374d1b5aSSteffen Klassert if (!pskb_pull(skb, offset)) 42374d1b5aSSteffen Klassert return NULL; 437785bba2SSteffen Klassert 447785bba2SSteffen Klassert if ((err = xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq)) != 0) 457785bba2SSteffen Klassert goto out; 467785bba2SSteffen Klassert 47bcd1f8a4SSteffen Klassert xo = xfrm_offload(skb); 48bcd1f8a4SSteffen Klassert if (!xo || !(xo->flags & CRYPTO_DONE)) { 490ca64da1SFlorian Westphal struct sec_path *sp = secpath_set(skb); 500ca64da1SFlorian Westphal 510ca64da1SFlorian Westphal if (!sp) 527785bba2SSteffen Klassert goto out; 537785bba2SSteffen Klassert 540ca64da1SFlorian Westphal if (sp->len == XFRM_MAX_DEPTH) 55*6ed69184SMyungho Jung goto out_reset; 567785bba2SSteffen Klassert 577785bba2SSteffen Klassert x = xfrm_state_lookup(dev_net(skb->dev), skb->mark, 587785bba2SSteffen Klassert (xfrm_address_t *)&ip_hdr(skb)->daddr, 597785bba2SSteffen Klassert spi, IPPROTO_ESP, AF_INET); 607785bba2SSteffen Klassert if (!x) 61*6ed69184SMyungho Jung goto out_reset; 627785bba2SSteffen Klassert 630ca64da1SFlorian Westphal sp->xvec[sp->len++] = x; 640ca64da1SFlorian Westphal sp->olen++; 657785bba2SSteffen Klassert 667785bba2SSteffen Klassert xo = xfrm_offload(skb); 677785bba2SSteffen Klassert if (!xo) { 687785bba2SSteffen Klassert xfrm_state_put(x); 69*6ed69184SMyungho Jung goto out_reset; 707785bba2SSteffen Klassert } 71bcd1f8a4SSteffen Klassert } 72bcd1f8a4SSteffen Klassert 737785bba2SSteffen Klassert xo->flags |= XFRM_GRO; 747785bba2SSteffen Klassert 757785bba2SSteffen Klassert XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; 767785bba2SSteffen Klassert XFRM_SPI_SKB_CB(skb)->family = AF_INET; 777785bba2SSteffen Klassert XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); 787785bba2SSteffen Klassert XFRM_SPI_SKB_CB(skb)->seq = seq; 797785bba2SSteffen Klassert 807785bba2SSteffen Klassert /* We don't need to handle errors from xfrm_input, it does all 817785bba2SSteffen Klassert * the error handling and frees the resources on error. */ 827785bba2SSteffen Klassert xfrm_input(skb, IPPROTO_ESP, spi, -2); 837785bba2SSteffen Klassert 847785bba2SSteffen Klassert return ERR_PTR(-EINPROGRESS); 85*6ed69184SMyungho Jung out_reset: 86*6ed69184SMyungho Jung secpath_reset(skb); 877785bba2SSteffen Klassert out: 887785bba2SSteffen Klassert skb_push(skb, offset); 897785bba2SSteffen Klassert NAPI_GRO_CB(skb)->same_flow = 0; 907785bba2SSteffen Klassert NAPI_GRO_CB(skb)->flush = 1; 917785bba2SSteffen Klassert 927785bba2SSteffen Klassert return NULL; 937785bba2SSteffen Klassert } 947785bba2SSteffen Klassert 957862b405SSteffen Klassert static void esp4_gso_encap(struct xfrm_state *x, struct sk_buff *skb) 967862b405SSteffen Klassert { 977862b405SSteffen Klassert struct ip_esp_hdr *esph; 987862b405SSteffen Klassert struct iphdr *iph = ip_hdr(skb); 997862b405SSteffen Klassert struct xfrm_offload *xo = xfrm_offload(skb); 1007862b405SSteffen Klassert int proto = iph->protocol; 1017862b405SSteffen Klassert 1027862b405SSteffen Klassert skb_push(skb, -skb_network_offset(skb)); 1037862b405SSteffen Klassert esph = ip_esp_hdr(skb); 1047862b405SSteffen Klassert *skb_mac_header(skb) = IPPROTO_ESP; 1057862b405SSteffen Klassert 1067862b405SSteffen Klassert esph->spi = x->id.spi; 1077862b405SSteffen Klassert esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); 1087862b405SSteffen Klassert 1097862b405SSteffen Klassert xo->proto = proto; 1107862b405SSteffen Klassert } 1117862b405SSteffen Klassert 1127862b405SSteffen Klassert static struct sk_buff *esp4_gso_segment(struct sk_buff *skb, 1137862b405SSteffen Klassert netdev_features_t features) 1147862b405SSteffen Klassert { 1157862b405SSteffen Klassert struct xfrm_state *x; 1167862b405SSteffen Klassert struct ip_esp_hdr *esph; 1177862b405SSteffen Klassert struct crypto_aead *aead; 1187862b405SSteffen Klassert netdev_features_t esp_features = features; 1197862b405SSteffen Klassert struct xfrm_offload *xo = xfrm_offload(skb); 1202294be0fSFlorian Westphal struct sec_path *sp; 1217862b405SSteffen Klassert 1227862b405SSteffen Klassert if (!xo) 1233dca3f38SSteffen Klassert return ERR_PTR(-EINVAL); 1247862b405SSteffen Klassert 125121d57afSWillem de Bruijn if (!(skb_shinfo(skb)->gso_type & SKB_GSO_ESP)) 1265ca11440SDavid S. Miller return ERR_PTR(-EINVAL); 1277862b405SSteffen Klassert 1282294be0fSFlorian Westphal sp = skb_sec_path(skb); 1292294be0fSFlorian Westphal x = sp->xvec[sp->len - 1]; 1307862b405SSteffen Klassert aead = x->data; 1317862b405SSteffen Klassert esph = ip_esp_hdr(skb); 1327862b405SSteffen Klassert 1337862b405SSteffen Klassert if (esph->spi != x->id.spi) 1343dca3f38SSteffen Klassert return ERR_PTR(-EINVAL); 1357862b405SSteffen Klassert 1367862b405SSteffen Klassert if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead))) 1373dca3f38SSteffen Klassert return ERR_PTR(-EINVAL); 1387862b405SSteffen Klassert 1397862b405SSteffen Klassert __skb_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)); 1407862b405SSteffen Klassert 1417862b405SSteffen Klassert skb->encap_hdr_csum = 1; 1427862b405SSteffen Klassert 143fcb662deSShannon Nelson if (!(features & NETIF_F_HW_ESP) || x->xso.dev != skb->dev) 1447862b405SSteffen Klassert esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK); 1455211fcfbSShannon Nelson else if (!(features & NETIF_F_HW_ESP_TX_CSUM)) 1465211fcfbSShannon Nelson esp_features = features & ~NETIF_F_CSUM_MASK; 1477862b405SSteffen Klassert 1487862b405SSteffen Klassert xo->flags |= XFRM_GSO_SEGMENT; 1497862b405SSteffen Klassert 1503dca3f38SSteffen Klassert return x->outer_mode->gso_segment(x, skb, esp_features); 1517862b405SSteffen Klassert } 1527862b405SSteffen Klassert 153fca11ebdSSteffen Klassert static int esp_input_tail(struct xfrm_state *x, struct sk_buff *skb) 154fca11ebdSSteffen Klassert { 155fca11ebdSSteffen Klassert struct crypto_aead *aead = x->data; 156ec9567a9SIlan Tayari struct xfrm_offload *xo = xfrm_offload(skb); 157fca11ebdSSteffen Klassert 158fca11ebdSSteffen Klassert if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead))) 159fca11ebdSSteffen Klassert return -EINVAL; 160fca11ebdSSteffen Klassert 161ec9567a9SIlan Tayari if (!(xo->flags & CRYPTO_DONE)) 162fca11ebdSSteffen Klassert skb->ip_summed = CHECKSUM_NONE; 163fca11ebdSSteffen Klassert 164fca11ebdSSteffen Klassert return esp_input_done2(skb, 0); 165fca11ebdSSteffen Klassert } 166fca11ebdSSteffen Klassert 167fca11ebdSSteffen Klassert static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_t features) 168fca11ebdSSteffen Klassert { 169fca11ebdSSteffen Klassert int err; 170fca11ebdSSteffen Klassert int alen; 171fca11ebdSSteffen Klassert int blksize; 172fca11ebdSSteffen Klassert struct xfrm_offload *xo; 173fca11ebdSSteffen Klassert struct ip_esp_hdr *esph; 174fca11ebdSSteffen Klassert struct crypto_aead *aead; 175fca11ebdSSteffen Klassert struct esp_info esp; 176fca11ebdSSteffen Klassert bool hw_offload = true; 1773dca3f38SSteffen Klassert __u32 seq; 178fca11ebdSSteffen Klassert 179fca11ebdSSteffen Klassert esp.inplace = true; 180fca11ebdSSteffen Klassert 181fca11ebdSSteffen Klassert xo = xfrm_offload(skb); 182fca11ebdSSteffen Klassert 183fca11ebdSSteffen Klassert if (!xo) 184fca11ebdSSteffen Klassert return -EINVAL; 185fca11ebdSSteffen Klassert 186fcb662deSShannon Nelson if (!(features & NETIF_F_HW_ESP) || x->xso.dev != skb->dev) { 187fca11ebdSSteffen Klassert xo->flags |= CRYPTO_FALLBACK; 188fca11ebdSSteffen Klassert hw_offload = false; 189fca11ebdSSteffen Klassert } 190fca11ebdSSteffen Klassert 191fca11ebdSSteffen Klassert esp.proto = xo->proto; 192fca11ebdSSteffen Klassert 193fca11ebdSSteffen Klassert /* skb is pure payload to encrypt */ 194fca11ebdSSteffen Klassert 195fca11ebdSSteffen Klassert aead = x->data; 196fca11ebdSSteffen Klassert alen = crypto_aead_authsize(aead); 197fca11ebdSSteffen Klassert 198fca11ebdSSteffen Klassert esp.tfclen = 0; 199fca11ebdSSteffen Klassert /* XXX: Add support for tfc padding here. */ 200fca11ebdSSteffen Klassert 201fca11ebdSSteffen Klassert blksize = ALIGN(crypto_aead_blocksize(aead), 4); 202fca11ebdSSteffen Klassert esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize); 203fca11ebdSSteffen Klassert esp.plen = esp.clen - skb->len - esp.tfclen; 204fca11ebdSSteffen Klassert esp.tailen = esp.tfclen + esp.plen + alen; 205fca11ebdSSteffen Klassert 206fca11ebdSSteffen Klassert esp.esph = ip_esp_hdr(skb); 207fca11ebdSSteffen Klassert 208fca11ebdSSteffen Klassert 209fca11ebdSSteffen Klassert if (!hw_offload || (hw_offload && !skb_is_gso(skb))) { 210fca11ebdSSteffen Klassert esp.nfrags = esp_output_head(x, skb, &esp); 211fca11ebdSSteffen Klassert if (esp.nfrags < 0) 212fca11ebdSSteffen Klassert return esp.nfrags; 213fca11ebdSSteffen Klassert } 214fca11ebdSSteffen Klassert 2153dca3f38SSteffen Klassert seq = xo->seq.low; 2163dca3f38SSteffen Klassert 217fca11ebdSSteffen Klassert esph = esp.esph; 218fca11ebdSSteffen Klassert esph->spi = x->id.spi; 219fca11ebdSSteffen Klassert 220fca11ebdSSteffen Klassert skb_push(skb, -skb_network_offset(skb)); 221fca11ebdSSteffen Klassert 222fca11ebdSSteffen Klassert if (xo->flags & XFRM_GSO_SEGMENT) { 2233dca3f38SSteffen Klassert esph->seq_no = htonl(seq); 2243dca3f38SSteffen Klassert 2253dca3f38SSteffen Klassert if (!skb_is_gso(skb)) 2263dca3f38SSteffen Klassert xo->seq.low++; 2273dca3f38SSteffen Klassert else 2283dca3f38SSteffen Klassert xo->seq.low += skb_shinfo(skb)->gso_segs; 2293dca3f38SSteffen Klassert } 2303dca3f38SSteffen Klassert 2313dca3f38SSteffen Klassert esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32)); 2323dca3f38SSteffen Klassert 233fca11ebdSSteffen Klassert ip_hdr(skb)->tot_len = htons(skb->len); 234fca11ebdSSteffen Klassert ip_send_check(ip_hdr(skb)); 235fca11ebdSSteffen Klassert 236fca11ebdSSteffen Klassert if (hw_offload) 237fca11ebdSSteffen Klassert return 0; 238fca11ebdSSteffen Klassert 239fca11ebdSSteffen Klassert err = esp_output_tail(x, skb, &esp); 2404ff0308fSSteffen Klassert if (err) 241fca11ebdSSteffen Klassert return err; 242fca11ebdSSteffen Klassert 243fca11ebdSSteffen Klassert secpath_reset(skb); 244fca11ebdSSteffen Klassert 245fca11ebdSSteffen Klassert return 0; 246fca11ebdSSteffen Klassert } 247fca11ebdSSteffen Klassert 2487785bba2SSteffen Klassert static const struct net_offload esp4_offload = { 2497785bba2SSteffen Klassert .callbacks = { 2507785bba2SSteffen Klassert .gro_receive = esp4_gro_receive, 2517862b405SSteffen Klassert .gso_segment = esp4_gso_segment, 2527785bba2SSteffen Klassert }, 2537785bba2SSteffen Klassert }; 2547785bba2SSteffen Klassert 255fca11ebdSSteffen Klassert static const struct xfrm_type_offload esp_type_offload = { 256fca11ebdSSteffen Klassert .description = "ESP4 OFFLOAD", 257fca11ebdSSteffen Klassert .owner = THIS_MODULE, 258fca11ebdSSteffen Klassert .proto = IPPROTO_ESP, 259fca11ebdSSteffen Klassert .input_tail = esp_input_tail, 260fca11ebdSSteffen Klassert .xmit = esp_xmit, 2617862b405SSteffen Klassert .encap = esp4_gso_encap, 262fca11ebdSSteffen Klassert }; 263fca11ebdSSteffen Klassert 2647785bba2SSteffen Klassert static int __init esp4_offload_init(void) 2657785bba2SSteffen Klassert { 266fca11ebdSSteffen Klassert if (xfrm_register_type_offload(&esp_type_offload, AF_INET) < 0) { 267fca11ebdSSteffen Klassert pr_info("%s: can't add xfrm type offload\n", __func__); 268fca11ebdSSteffen Klassert return -EAGAIN; 269fca11ebdSSteffen Klassert } 270fca11ebdSSteffen Klassert 2717785bba2SSteffen Klassert return inet_add_offload(&esp4_offload, IPPROTO_ESP); 2727785bba2SSteffen Klassert } 2737785bba2SSteffen Klassert 2747785bba2SSteffen Klassert static void __exit esp4_offload_exit(void) 2757785bba2SSteffen Klassert { 276fca11ebdSSteffen Klassert if (xfrm_unregister_type_offload(&esp_type_offload, AF_INET) < 0) 277fca11ebdSSteffen Klassert pr_info("%s: can't remove xfrm type offload\n", __func__); 278fca11ebdSSteffen Klassert 2797785bba2SSteffen Klassert inet_del_offload(&esp4_offload, IPPROTO_ESP); 2807785bba2SSteffen Klassert } 2817785bba2SSteffen Klassert 2827785bba2SSteffen Klassert module_init(esp4_offload_init); 2837785bba2SSteffen Klassert module_exit(esp4_offload_exit); 2847785bba2SSteffen Klassert MODULE_LICENSE("GPL"); 2857785bba2SSteffen Klassert MODULE_AUTHOR("Steffen Klassert <steffen.klassert@secunet.com>"); 286ffdb5211SIlan Tayari MODULE_ALIAS_XFRM_OFFLOAD_TYPE(AF_INET, XFRM_PROTO_ESP); 287