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 26f6e27114SSteffen Klassert int validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features) 27f6e27114SSteffen Klassert { 28f6e27114SSteffen Klassert int err; 29f6e27114SSteffen Klassert struct xfrm_state *x; 30f6e27114SSteffen Klassert struct xfrm_offload *xo = xfrm_offload(skb); 31f6e27114SSteffen Klassert 32f6e27114SSteffen Klassert if (skb_is_gso(skb)) 33f6e27114SSteffen Klassert return 0; 34f6e27114SSteffen Klassert 35f6e27114SSteffen Klassert if (xo) { 36f6e27114SSteffen Klassert x = skb->sp->xvec[skb->sp->len - 1]; 37f6e27114SSteffen Klassert if (xo->flags & XFRM_GRO || x->xso.flags & XFRM_OFFLOAD_INBOUND) 38f6e27114SSteffen Klassert return 0; 39f6e27114SSteffen Klassert 40f6e27114SSteffen Klassert x->outer_mode->xmit(x, skb); 41f6e27114SSteffen Klassert 42f6e27114SSteffen Klassert err = x->type_offload->xmit(x, skb, features); 43f6e27114SSteffen Klassert if (err) { 44f6e27114SSteffen Klassert XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR); 45f6e27114SSteffen Klassert return err; 46f6e27114SSteffen Klassert } 47f6e27114SSteffen Klassert 48f6e27114SSteffen Klassert skb_push(skb, skb->data - skb_mac_header(skb)); 49f6e27114SSteffen Klassert } 50f6e27114SSteffen Klassert 51f6e27114SSteffen Klassert return 0; 52f6e27114SSteffen Klassert } 53f6e27114SSteffen Klassert EXPORT_SYMBOL_GPL(validate_xmit_xfrm); 54f6e27114SSteffen Klassert 55d77e38e6SSteffen Klassert int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, 56d77e38e6SSteffen Klassert struct xfrm_user_offload *xuo) 57d77e38e6SSteffen Klassert { 58d77e38e6SSteffen Klassert int err; 59d77e38e6SSteffen Klassert struct dst_entry *dst; 60d77e38e6SSteffen Klassert struct net_device *dev; 61d77e38e6SSteffen Klassert struct xfrm_state_offload *xso = &x->xso; 62d77e38e6SSteffen Klassert xfrm_address_t *saddr; 63d77e38e6SSteffen Klassert xfrm_address_t *daddr; 64d77e38e6SSteffen Klassert 65d77e38e6SSteffen Klassert if (!x->type_offload) 66d77e38e6SSteffen Klassert return 0; 67d77e38e6SSteffen Klassert 68d77e38e6SSteffen Klassert /* We don't yet support UDP encapsulation, TFC padding and ESN. */ 69d77e38e6SSteffen Klassert if (x->encap || x->tfcpad || (x->props.flags & XFRM_STATE_ESN)) 70d77e38e6SSteffen Klassert return 0; 71d77e38e6SSteffen Klassert 72d77e38e6SSteffen Klassert dev = dev_get_by_index(net, xuo->ifindex); 73d77e38e6SSteffen Klassert if (!dev) { 74d77e38e6SSteffen Klassert if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) { 75d77e38e6SSteffen Klassert saddr = &x->props.saddr; 76d77e38e6SSteffen Klassert daddr = &x->id.daddr; 77d77e38e6SSteffen Klassert } else { 78d77e38e6SSteffen Klassert saddr = &x->id.daddr; 79d77e38e6SSteffen Klassert daddr = &x->props.saddr; 80d77e38e6SSteffen Klassert } 81d77e38e6SSteffen Klassert 82d77e38e6SSteffen Klassert dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr, x->props.family); 83d77e38e6SSteffen Klassert if (IS_ERR(dst)) 84d77e38e6SSteffen Klassert return 0; 85d77e38e6SSteffen Klassert 86d77e38e6SSteffen Klassert dev = dst->dev; 87d77e38e6SSteffen Klassert 88d77e38e6SSteffen Klassert dev_hold(dev); 89d77e38e6SSteffen Klassert dst_release(dst); 90d77e38e6SSteffen Klassert } 91d77e38e6SSteffen Klassert 92d77e38e6SSteffen Klassert if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_state_add) { 93d77e38e6SSteffen Klassert dev_put(dev); 94d77e38e6SSteffen Klassert return 0; 95d77e38e6SSteffen Klassert } 96d77e38e6SSteffen Klassert 97d77e38e6SSteffen Klassert xso->dev = dev; 98d77e38e6SSteffen Klassert xso->num_exthdrs = 1; 99d77e38e6SSteffen Klassert xso->flags = xuo->flags; 100d77e38e6SSteffen Klassert 101d77e38e6SSteffen Klassert err = dev->xfrmdev_ops->xdo_dev_state_add(x); 102d77e38e6SSteffen Klassert if (err) { 103d77e38e6SSteffen Klassert dev_put(dev); 104d77e38e6SSteffen Klassert return err; 105d77e38e6SSteffen Klassert } 106d77e38e6SSteffen Klassert 107d77e38e6SSteffen Klassert return 0; 108d77e38e6SSteffen Klassert } 109d77e38e6SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_dev_state_add); 110d77e38e6SSteffen Klassert 111d77e38e6SSteffen Klassert bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) 112d77e38e6SSteffen Klassert { 113d77e38e6SSteffen Klassert int mtu; 114d77e38e6SSteffen Klassert struct dst_entry *dst = skb_dst(skb); 115d77e38e6SSteffen Klassert struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 116d77e38e6SSteffen Klassert struct net_device *dev = x->xso.dev; 117d77e38e6SSteffen Klassert 118d77e38e6SSteffen Klassert if (!x->type_offload || x->encap) 119d77e38e6SSteffen Klassert return false; 120d77e38e6SSteffen Klassert 121d77e38e6SSteffen Klassert if ((x->xso.offload_handle && (dev == dst->path->dev)) && 122d77e38e6SSteffen Klassert !dst->child->xfrm && x->type->get_mtu) { 123d77e38e6SSteffen Klassert mtu = x->type->get_mtu(x, xdst->child_mtu_cached); 124d77e38e6SSteffen Klassert 125d77e38e6SSteffen Klassert if (skb->len <= mtu) 126d77e38e6SSteffen Klassert goto ok; 127d77e38e6SSteffen Klassert 128d77e38e6SSteffen Klassert if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu)) 129d77e38e6SSteffen Klassert goto ok; 130d77e38e6SSteffen Klassert } 131d77e38e6SSteffen Klassert 132d77e38e6SSteffen Klassert return false; 133d77e38e6SSteffen Klassert 134d77e38e6SSteffen Klassert ok: 135d77e38e6SSteffen Klassert if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_offload_ok) 136d77e38e6SSteffen Klassert return x->xso.dev->xfrmdev_ops->xdo_dev_offload_ok(skb, x); 137d77e38e6SSteffen Klassert 138d77e38e6SSteffen Klassert return true; 139d77e38e6SSteffen Klassert } 140d77e38e6SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_dev_offload_ok); 141b81f884aSHangbin Liu #endif 142d77e38e6SSteffen Klassert 143d77e38e6SSteffen Klassert int xfrm_dev_register(struct net_device *dev) 144d77e38e6SSteffen Klassert { 145d77e38e6SSteffen Klassert if ((dev->features & NETIF_F_HW_ESP) && !dev->xfrmdev_ops) 146d77e38e6SSteffen Klassert return NOTIFY_BAD; 147d77e38e6SSteffen Klassert if ((dev->features & NETIF_F_HW_ESP_TX_CSUM) && 148d77e38e6SSteffen Klassert !(dev->features & NETIF_F_HW_ESP)) 149d77e38e6SSteffen Klassert return NOTIFY_BAD; 150d77e38e6SSteffen Klassert 151d77e38e6SSteffen Klassert return NOTIFY_DONE; 152d77e38e6SSteffen Klassert } 153d77e38e6SSteffen Klassert 154d77e38e6SSteffen Klassert static int xfrm_dev_unregister(struct net_device *dev) 155d77e38e6SSteffen Klassert { 156d77e38e6SSteffen Klassert return NOTIFY_DONE; 157d77e38e6SSteffen Klassert } 158d77e38e6SSteffen Klassert 159d77e38e6SSteffen Klassert static int xfrm_dev_feat_change(struct net_device *dev) 160d77e38e6SSteffen Klassert { 161d77e38e6SSteffen Klassert if ((dev->features & NETIF_F_HW_ESP) && !dev->xfrmdev_ops) 162d77e38e6SSteffen Klassert return NOTIFY_BAD; 163d77e38e6SSteffen Klassert else if (!(dev->features & NETIF_F_HW_ESP)) 164d77e38e6SSteffen Klassert dev->xfrmdev_ops = NULL; 165d77e38e6SSteffen Klassert 166d77e38e6SSteffen Klassert if ((dev->features & NETIF_F_HW_ESP_TX_CSUM) && 167d77e38e6SSteffen Klassert !(dev->features & NETIF_F_HW_ESP)) 168d77e38e6SSteffen Klassert return NOTIFY_BAD; 169d77e38e6SSteffen Klassert 170d77e38e6SSteffen Klassert return NOTIFY_DONE; 171d77e38e6SSteffen Klassert } 172d77e38e6SSteffen Klassert 173d77e38e6SSteffen Klassert static int xfrm_dev_down(struct net_device *dev) 174d77e38e6SSteffen Klassert { 1752c1497bbSIlan Tayari if (dev->features & NETIF_F_HW_ESP) 176d77e38e6SSteffen Klassert xfrm_dev_state_flush(dev_net(dev), dev, true); 177d77e38e6SSteffen Klassert 178d77e38e6SSteffen Klassert xfrm_garbage_collect(dev_net(dev)); 179d77e38e6SSteffen Klassert 180d77e38e6SSteffen Klassert return NOTIFY_DONE; 181d77e38e6SSteffen Klassert } 182d77e38e6SSteffen Klassert 18321f42cc9SSteffen Klassert static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) 18421f42cc9SSteffen Klassert { 18521f42cc9SSteffen Klassert struct net_device *dev = netdev_notifier_info_to_dev(ptr); 18621f42cc9SSteffen Klassert 18721f42cc9SSteffen Klassert switch (event) { 188d77e38e6SSteffen Klassert case NETDEV_REGISTER: 189d77e38e6SSteffen Klassert return xfrm_dev_register(dev); 190d77e38e6SSteffen Klassert 191d77e38e6SSteffen Klassert case NETDEV_UNREGISTER: 192d77e38e6SSteffen Klassert return xfrm_dev_unregister(dev); 193d77e38e6SSteffen Klassert 194d77e38e6SSteffen Klassert case NETDEV_FEAT_CHANGE: 195d77e38e6SSteffen Klassert return xfrm_dev_feat_change(dev); 196d77e38e6SSteffen Klassert 19721f42cc9SSteffen Klassert case NETDEV_DOWN: 198d77e38e6SSteffen Klassert return xfrm_dev_down(dev); 19921f42cc9SSteffen Klassert } 20021f42cc9SSteffen Klassert return NOTIFY_DONE; 20121f42cc9SSteffen Klassert } 20221f42cc9SSteffen Klassert 20321f42cc9SSteffen Klassert static struct notifier_block xfrm_dev_notifier = { 20421f42cc9SSteffen Klassert .notifier_call = xfrm_dev_event, 20521f42cc9SSteffen Klassert }; 20621f42cc9SSteffen Klassert 20721f42cc9SSteffen Klassert void __net_init xfrm_dev_init(void) 20821f42cc9SSteffen Klassert { 20921f42cc9SSteffen Klassert register_netdevice_notifier(&xfrm_dev_notifier); 21021f42cc9SSteffen Klassert } 211