121f42cc9SSteffen Klassert /* 221f42cc9SSteffen Klassert * xfrm_device.c - IPsec device offloading code. 321f42cc9SSteffen Klassert * 421f42cc9SSteffen Klassert * Copyright (c) 2015 secunet Security Networks AG 521f42cc9SSteffen Klassert * 621f42cc9SSteffen Klassert * Author: 721f42cc9SSteffen Klassert * Steffen Klassert <steffen.klassert@secunet.com> 821f42cc9SSteffen Klassert * 921f42cc9SSteffen Klassert * This program is free software; you can redistribute it and/or 1021f42cc9SSteffen Klassert * modify it under the terms of the GNU General Public License 1121f42cc9SSteffen Klassert * as published by the Free Software Foundation; either version 1221f42cc9SSteffen Klassert * 2 of the License, or (at your option) any later version. 1321f42cc9SSteffen Klassert */ 1421f42cc9SSteffen Klassert 1521f42cc9SSteffen Klassert #include <linux/errno.h> 1621f42cc9SSteffen Klassert #include <linux/module.h> 1721f42cc9SSteffen Klassert #include <linux/netdevice.h> 1821f42cc9SSteffen Klassert #include <linux/skbuff.h> 1921f42cc9SSteffen Klassert #include <linux/slab.h> 2021f42cc9SSteffen Klassert #include <linux/spinlock.h> 2121f42cc9SSteffen Klassert #include <net/dst.h> 2221f42cc9SSteffen Klassert #include <net/xfrm.h> 2321f42cc9SSteffen Klassert #include <linux/notifier.h> 2421f42cc9SSteffen Klassert 25b81f884aSHangbin Liu #ifdef CONFIG_XFRM_OFFLOAD 263dca3f38SSteffen Klassert struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features) 27f6e27114SSteffen Klassert { 28f6e27114SSteffen Klassert int err; 293dca3f38SSteffen Klassert __u32 seq; 30f6e27114SSteffen Klassert struct xfrm_state *x; 313dca3f38SSteffen Klassert struct sk_buff *skb2; 323dca3f38SSteffen Klassert netdev_features_t esp_features = features; 33f6e27114SSteffen Klassert struct xfrm_offload *xo = xfrm_offload(skb); 34f6e27114SSteffen Klassert 353dca3f38SSteffen Klassert if (!xo) 363dca3f38SSteffen Klassert return skb; 37f6e27114SSteffen Klassert 383dca3f38SSteffen Klassert if (!(features & NETIF_F_HW_ESP)) 393dca3f38SSteffen Klassert esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK); 403dca3f38SSteffen Klassert 41f6e27114SSteffen Klassert x = skb->sp->xvec[skb->sp->len - 1]; 42f6e27114SSteffen Klassert if (xo->flags & XFRM_GRO || x->xso.flags & XFRM_OFFLOAD_INBOUND) 433dca3f38SSteffen Klassert return skb; 44f6e27114SSteffen Klassert 453dca3f38SSteffen Klassert if (skb_is_gso(skb)) { 463dca3f38SSteffen Klassert struct net_device *dev = skb->dev; 473dca3f38SSteffen Klassert 483dca3f38SSteffen Klassert if (unlikely(!x->xso.offload_handle || (x->xso.dev != dev))) { 493dca3f38SSteffen Klassert struct sk_buff *segs; 503dca3f38SSteffen Klassert 513dca3f38SSteffen Klassert /* Packet got rerouted, fixup features and segment it. */ 523dca3f38SSteffen Klassert esp_features = esp_features & ~(NETIF_F_HW_ESP 533dca3f38SSteffen Klassert | NETIF_F_GSO_ESP); 543dca3f38SSteffen Klassert 553dca3f38SSteffen Klassert segs = skb_gso_segment(skb, esp_features); 563dca3f38SSteffen Klassert if (IS_ERR(segs)) { 573dca3f38SSteffen Klassert XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR); 583dca3f38SSteffen Klassert kfree_skb(skb); 593dca3f38SSteffen Klassert return NULL; 603dca3f38SSteffen Klassert } else { 613dca3f38SSteffen Klassert consume_skb(skb); 623dca3f38SSteffen Klassert skb = segs; 633dca3f38SSteffen Klassert } 643dca3f38SSteffen Klassert } else { 653dca3f38SSteffen Klassert return skb; 663dca3f38SSteffen Klassert } 673dca3f38SSteffen Klassert } 683dca3f38SSteffen Klassert 693dca3f38SSteffen Klassert if (!skb->next) { 70f6e27114SSteffen Klassert x->outer_mode->xmit(x, skb); 71f6e27114SSteffen Klassert 723dca3f38SSteffen Klassert err = x->type_offload->xmit(x, skb, esp_features); 73f6e27114SSteffen Klassert if (err) { 74f6e27114SSteffen Klassert XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR); 753dca3f38SSteffen Klassert kfree_skb(skb); 763dca3f38SSteffen Klassert return NULL; 77f6e27114SSteffen Klassert } 78f6e27114SSteffen Klassert 79f6e27114SSteffen Klassert skb_push(skb, skb->data - skb_mac_header(skb)); 803dca3f38SSteffen Klassert 813dca3f38SSteffen Klassert return skb; 82f6e27114SSteffen Klassert } 83f6e27114SSteffen Klassert 843dca3f38SSteffen Klassert skb2 = skb; 853dca3f38SSteffen Klassert seq = xo->seq.low; 863dca3f38SSteffen Klassert 873dca3f38SSteffen Klassert do { 883dca3f38SSteffen Klassert struct sk_buff *nskb = skb2->next; 893dca3f38SSteffen Klassert 903dca3f38SSteffen Klassert xo = xfrm_offload(skb2); 913dca3f38SSteffen Klassert xo->flags |= XFRM_GSO_SEGMENT; 923dca3f38SSteffen Klassert xo->seq.low = seq; 933dca3f38SSteffen Klassert xo->seq.hi = xfrm_replay_seqhi(x, seq); 943dca3f38SSteffen Klassert 953dca3f38SSteffen Klassert if(!(features & NETIF_F_HW_ESP)) 963dca3f38SSteffen Klassert xo->flags |= CRYPTO_FALLBACK; 973dca3f38SSteffen Klassert 983dca3f38SSteffen Klassert x->outer_mode->xmit(x, skb2); 993dca3f38SSteffen Klassert 1003dca3f38SSteffen Klassert err = x->type_offload->xmit(x, skb2, esp_features); 1013dca3f38SSteffen Klassert if (err) { 1023dca3f38SSteffen Klassert XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR); 1033dca3f38SSteffen Klassert skb2->next = nskb; 1043dca3f38SSteffen Klassert kfree_skb_list(skb2); 1053dca3f38SSteffen Klassert return NULL; 1063dca3f38SSteffen Klassert } 1073dca3f38SSteffen Klassert 1083dca3f38SSteffen Klassert if (!skb_is_gso(skb2)) 1093dca3f38SSteffen Klassert seq++; 1103dca3f38SSteffen Klassert else 1113dca3f38SSteffen Klassert seq += skb_shinfo(skb2)->gso_segs; 1123dca3f38SSteffen Klassert 1133dca3f38SSteffen Klassert skb_push(skb2, skb2->data - skb_mac_header(skb2)); 1143dca3f38SSteffen Klassert 1153dca3f38SSteffen Klassert skb2 = nskb; 1163dca3f38SSteffen Klassert } while (skb2); 1173dca3f38SSteffen Klassert 1183dca3f38SSteffen Klassert return skb; 119f6e27114SSteffen Klassert } 120f6e27114SSteffen Klassert EXPORT_SYMBOL_GPL(validate_xmit_xfrm); 121f6e27114SSteffen Klassert 122d77e38e6SSteffen Klassert int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, 123d77e38e6SSteffen Klassert struct xfrm_user_offload *xuo) 124d77e38e6SSteffen Klassert { 125d77e38e6SSteffen Klassert int err; 126d77e38e6SSteffen Klassert struct dst_entry *dst; 127d77e38e6SSteffen Klassert struct net_device *dev; 128d77e38e6SSteffen Klassert struct xfrm_state_offload *xso = &x->xso; 129d77e38e6SSteffen Klassert xfrm_address_t *saddr; 130d77e38e6SSteffen Klassert xfrm_address_t *daddr; 131d77e38e6SSteffen Klassert 132d77e38e6SSteffen Klassert if (!x->type_offload) 133ffdb5211SIlan Tayari return -EINVAL; 134d77e38e6SSteffen Klassert 135d77e38e6SSteffen Klassert /* We don't yet support UDP encapsulation, TFC padding and ESN. */ 136d77e38e6SSteffen Klassert if (x->encap || x->tfcpad || (x->props.flags & XFRM_STATE_ESN)) 13743024b9cSYossef Efraim return -EINVAL; 138d77e38e6SSteffen Klassert 139d77e38e6SSteffen Klassert dev = dev_get_by_index(net, xuo->ifindex); 140d77e38e6SSteffen Klassert if (!dev) { 141d77e38e6SSteffen Klassert if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) { 142d77e38e6SSteffen Klassert saddr = &x->props.saddr; 143d77e38e6SSteffen Klassert daddr = &x->id.daddr; 144d77e38e6SSteffen Klassert } else { 145d77e38e6SSteffen Klassert saddr = &x->id.daddr; 146d77e38e6SSteffen Klassert daddr = &x->props.saddr; 147d77e38e6SSteffen Klassert } 148d77e38e6SSteffen Klassert 149077fbac4SLorenzo Colitti dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr, 150077fbac4SLorenzo Colitti x->props.family, x->props.output_mark); 151d77e38e6SSteffen Klassert if (IS_ERR(dst)) 152d77e38e6SSteffen Klassert return 0; 153d77e38e6SSteffen Klassert 154d77e38e6SSteffen Klassert dev = dst->dev; 155d77e38e6SSteffen Klassert 156d77e38e6SSteffen Klassert dev_hold(dev); 157d77e38e6SSteffen Klassert dst_release(dst); 158d77e38e6SSteffen Klassert } 159d77e38e6SSteffen Klassert 160d77e38e6SSteffen Klassert if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_state_add) { 16167a63387SSteffen Klassert xso->dev = NULL; 162d77e38e6SSteffen Klassert dev_put(dev); 163d77e38e6SSteffen Klassert return 0; 164d77e38e6SSteffen Klassert } 165d77e38e6SSteffen Klassert 166d77e38e6SSteffen Klassert xso->dev = dev; 167d77e38e6SSteffen Klassert xso->num_exthdrs = 1; 168d77e38e6SSteffen Klassert xso->flags = xuo->flags; 169d77e38e6SSteffen Klassert 170d77e38e6SSteffen Klassert err = dev->xfrmdev_ops->xdo_dev_state_add(x); 171d77e38e6SSteffen Klassert if (err) { 172d77e38e6SSteffen Klassert dev_put(dev); 173d77e38e6SSteffen Klassert return err; 174d77e38e6SSteffen Klassert } 175d77e38e6SSteffen Klassert 176d77e38e6SSteffen Klassert return 0; 177d77e38e6SSteffen Klassert } 178d77e38e6SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_dev_state_add); 179d77e38e6SSteffen Klassert 180d77e38e6SSteffen Klassert bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) 181d77e38e6SSteffen Klassert { 182d77e38e6SSteffen Klassert int mtu; 183d77e38e6SSteffen Klassert struct dst_entry *dst = skb_dst(skb); 184d77e38e6SSteffen Klassert struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 185d77e38e6SSteffen Klassert struct net_device *dev = x->xso.dev; 186d77e38e6SSteffen Klassert 187d77e38e6SSteffen Klassert if (!x->type_offload || x->encap) 188d77e38e6SSteffen Klassert return false; 189d77e38e6SSteffen Klassert 1900f6c480fSDavid Miller if ((x->xso.offload_handle && (dev == xfrm_dst_path(dst)->dev)) && 191b6ca8bd5SDavid Miller !xdst->child->xfrm && x->type->get_mtu) { 192d77e38e6SSteffen Klassert mtu = x->type->get_mtu(x, xdst->child_mtu_cached); 193d77e38e6SSteffen Klassert 194d77e38e6SSteffen Klassert if (skb->len <= mtu) 195d77e38e6SSteffen Klassert goto ok; 196d77e38e6SSteffen Klassert 197d77e38e6SSteffen Klassert if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu)) 198d77e38e6SSteffen Klassert goto ok; 199d77e38e6SSteffen Klassert } 200d77e38e6SSteffen Klassert 201d77e38e6SSteffen Klassert return false; 202d77e38e6SSteffen Klassert 203d77e38e6SSteffen Klassert ok: 204d77e38e6SSteffen Klassert if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_offload_ok) 205d77e38e6SSteffen Klassert return x->xso.dev->xfrmdev_ops->xdo_dev_offload_ok(skb, x); 206d77e38e6SSteffen Klassert 207d77e38e6SSteffen Klassert return true; 208d77e38e6SSteffen Klassert } 209d77e38e6SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_dev_offload_ok); 210b81f884aSHangbin Liu #endif 211d77e38e6SSteffen Klassert 21224d472e4SWei Yongjun static int xfrm_dev_register(struct net_device *dev) 213d77e38e6SSteffen Klassert { 214d77e38e6SSteffen Klassert if ((dev->features & NETIF_F_HW_ESP) && !dev->xfrmdev_ops) 215d77e38e6SSteffen Klassert return NOTIFY_BAD; 216d77e38e6SSteffen Klassert if ((dev->features & NETIF_F_HW_ESP_TX_CSUM) && 217d77e38e6SSteffen Klassert !(dev->features & NETIF_F_HW_ESP)) 218d77e38e6SSteffen Klassert return NOTIFY_BAD; 219d77e38e6SSteffen Klassert 220d77e38e6SSteffen Klassert return NOTIFY_DONE; 221d77e38e6SSteffen Klassert } 222d77e38e6SSteffen Klassert 223d77e38e6SSteffen Klassert static int xfrm_dev_unregister(struct net_device *dev) 224d77e38e6SSteffen Klassert { 225ec30d78cSFlorian Westphal xfrm_policy_cache_flush(); 226d77e38e6SSteffen Klassert return NOTIFY_DONE; 227d77e38e6SSteffen Klassert } 228d77e38e6SSteffen Klassert 229d77e38e6SSteffen Klassert static int xfrm_dev_feat_change(struct net_device *dev) 230d77e38e6SSteffen Klassert { 231d77e38e6SSteffen Klassert if ((dev->features & NETIF_F_HW_ESP) && !dev->xfrmdev_ops) 232d77e38e6SSteffen Klassert return NOTIFY_BAD; 233d77e38e6SSteffen Klassert else if (!(dev->features & NETIF_F_HW_ESP)) 234d77e38e6SSteffen Klassert dev->xfrmdev_ops = NULL; 235d77e38e6SSteffen Klassert 236d77e38e6SSteffen Klassert if ((dev->features & NETIF_F_HW_ESP_TX_CSUM) && 237d77e38e6SSteffen Klassert !(dev->features & NETIF_F_HW_ESP)) 238d77e38e6SSteffen Klassert return NOTIFY_BAD; 239d77e38e6SSteffen Klassert 240d77e38e6SSteffen Klassert return NOTIFY_DONE; 241d77e38e6SSteffen Klassert } 242d77e38e6SSteffen Klassert 243d77e38e6SSteffen Klassert static int xfrm_dev_down(struct net_device *dev) 244d77e38e6SSteffen Klassert { 2452c1497bbSIlan Tayari if (dev->features & NETIF_F_HW_ESP) 246d77e38e6SSteffen Klassert xfrm_dev_state_flush(dev_net(dev), dev, true); 247d77e38e6SSteffen Klassert 248ec30d78cSFlorian Westphal xfrm_policy_cache_flush(); 249d77e38e6SSteffen Klassert return NOTIFY_DONE; 250d77e38e6SSteffen Klassert } 251d77e38e6SSteffen Klassert 25221f42cc9SSteffen Klassert static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) 25321f42cc9SSteffen Klassert { 25421f42cc9SSteffen Klassert struct net_device *dev = netdev_notifier_info_to_dev(ptr); 25521f42cc9SSteffen Klassert 25621f42cc9SSteffen Klassert switch (event) { 257d77e38e6SSteffen Klassert case NETDEV_REGISTER: 258d77e38e6SSteffen Klassert return xfrm_dev_register(dev); 259d77e38e6SSteffen Klassert 260d77e38e6SSteffen Klassert case NETDEV_UNREGISTER: 261d77e38e6SSteffen Klassert return xfrm_dev_unregister(dev); 262d77e38e6SSteffen Klassert 263d77e38e6SSteffen Klassert case NETDEV_FEAT_CHANGE: 264d77e38e6SSteffen Klassert return xfrm_dev_feat_change(dev); 265d77e38e6SSteffen Klassert 26621f42cc9SSteffen Klassert case NETDEV_DOWN: 267d77e38e6SSteffen Klassert return xfrm_dev_down(dev); 26821f42cc9SSteffen Klassert } 26921f42cc9SSteffen Klassert return NOTIFY_DONE; 27021f42cc9SSteffen Klassert } 27121f42cc9SSteffen Klassert 27221f42cc9SSteffen Klassert static struct notifier_block xfrm_dev_notifier = { 27321f42cc9SSteffen Klassert .notifier_call = xfrm_dev_event, 27421f42cc9SSteffen Klassert }; 27521f42cc9SSteffen Klassert 27621f42cc9SSteffen Klassert void __net_init xfrm_dev_init(void) 27721f42cc9SSteffen Klassert { 27821f42cc9SSteffen Klassert register_netdevice_notifier(&xfrm_dev_notifier); 27921f42cc9SSteffen Klassert } 280