xfrm: Add an IPsec hardware offloading API
This patch adds all the bits that are needed to do IPsec hardware offload for IPsec states and ESP packets. We add xfrmdev_ops to the net_device. xfrmdev_ops has function pointers that are needed to manage the xfrm states in the hardware and to do a per packet offloading decision. Joint work with: Ilan Tayari <ilant@mellanox.com> Guy Shapiro <guysh@mellanox.com> Yossi Kuperman <yossiku@mellanox.com> Signed-off-by: Guy Shapiro <guysh@mellanox.com> Signed-off-by: Ilan Tayari <ilant@mellanox.com> Signed-off-by: Yossi Kuperman <yossiku@mellanox.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
This commit is contained in:
@@ -22,13 +22,149 @@
|
||||
#include <net/xfrm.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
|
||||
struct xfrm_user_offload *xuo)
|
||||
{
|
||||
int err;
|
||||
struct dst_entry *dst;
|
||||
struct net_device *dev;
|
||||
struct xfrm_state_offload *xso = &x->xso;
|
||||
xfrm_address_t *saddr;
|
||||
xfrm_address_t *daddr;
|
||||
|
||||
if (!x->type_offload)
|
||||
return 0;
|
||||
|
||||
/* We don't yet support UDP encapsulation, TFC padding and ESN. */
|
||||
if (x->encap || x->tfcpad || (x->props.flags & XFRM_STATE_ESN))
|
||||
return 0;
|
||||
|
||||
dev = dev_get_by_index(net, xuo->ifindex);
|
||||
if (!dev) {
|
||||
if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) {
|
||||
saddr = &x->props.saddr;
|
||||
daddr = &x->id.daddr;
|
||||
} else {
|
||||
saddr = &x->id.daddr;
|
||||
daddr = &x->props.saddr;
|
||||
}
|
||||
|
||||
dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr, x->props.family);
|
||||
if (IS_ERR(dst))
|
||||
return 0;
|
||||
|
||||
dev = dst->dev;
|
||||
|
||||
dev_hold(dev);
|
||||
dst_release(dst);
|
||||
}
|
||||
|
||||
if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_state_add) {
|
||||
dev_put(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
xso->dev = dev;
|
||||
xso->num_exthdrs = 1;
|
||||
xso->flags = xuo->flags;
|
||||
|
||||
err = dev->xfrmdev_ops->xdo_dev_state_add(x);
|
||||
if (err) {
|
||||
dev_put(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_dev_state_add);
|
||||
|
||||
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
|
||||
{
|
||||
int mtu;
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
|
||||
struct net_device *dev = x->xso.dev;
|
||||
|
||||
if (!x->type_offload || x->encap)
|
||||
return false;
|
||||
|
||||
if ((x->xso.offload_handle && (dev == dst->path->dev)) &&
|
||||
!dst->child->xfrm && x->type->get_mtu) {
|
||||
mtu = x->type->get_mtu(x, xdst->child_mtu_cached);
|
||||
|
||||
if (skb->len <= mtu)
|
||||
goto ok;
|
||||
|
||||
if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu))
|
||||
goto ok;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
ok:
|
||||
if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_offload_ok)
|
||||
return x->xso.dev->xfrmdev_ops->xdo_dev_offload_ok(skb, x);
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_dev_offload_ok);
|
||||
|
||||
int xfrm_dev_register(struct net_device *dev)
|
||||
{
|
||||
if ((dev->features & NETIF_F_HW_ESP) && !dev->xfrmdev_ops)
|
||||
return NOTIFY_BAD;
|
||||
if ((dev->features & NETIF_F_HW_ESP_TX_CSUM) &&
|
||||
!(dev->features & NETIF_F_HW_ESP))
|
||||
return NOTIFY_BAD;
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int xfrm_dev_unregister(struct net_device *dev)
|
||||
{
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int xfrm_dev_feat_change(struct net_device *dev)
|
||||
{
|
||||
if ((dev->features & NETIF_F_HW_ESP) && !dev->xfrmdev_ops)
|
||||
return NOTIFY_BAD;
|
||||
else if (!(dev->features & NETIF_F_HW_ESP))
|
||||
dev->xfrmdev_ops = NULL;
|
||||
|
||||
if ((dev->features & NETIF_F_HW_ESP_TX_CSUM) &&
|
||||
!(dev->features & NETIF_F_HW_ESP))
|
||||
return NOTIFY_BAD;
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int xfrm_dev_down(struct net_device *dev)
|
||||
{
|
||||
if (dev->hw_features & NETIF_F_HW_ESP)
|
||||
xfrm_dev_state_flush(dev_net(dev), dev, true);
|
||||
|
||||
xfrm_garbage_collect(dev_net(dev));
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_REGISTER:
|
||||
return xfrm_dev_register(dev);
|
||||
|
||||
case NETDEV_UNREGISTER:
|
||||
return xfrm_dev_unregister(dev);
|
||||
|
||||
case NETDEV_FEAT_CHANGE:
|
||||
return xfrm_dev_feat_change(dev);
|
||||
|
||||
case NETDEV_DOWN:
|
||||
xfrm_garbage_collect(dev_net(dev));
|
||||
return xfrm_dev_down(dev);
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
Reference in New Issue
Block a user