xfrm: add an interface to offload policy
Extend netlink interface to add and delete XFRM policy from the device. This functionality is a first step to implement packet IPsec offload solution. Signed-off-by: Raed Salem <raeds@nvidia.com> Signed-off-by: Leon Romanovsky <leonro@nvidia.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
This commit is contained in:
parent
62f6eca5de
commit
919e43fad5
5 changed files with 201 additions and 1 deletions
|
@ -1040,6 +1040,9 @@ struct xfrmdev_ops {
|
||||||
bool (*xdo_dev_offload_ok) (struct sk_buff *skb,
|
bool (*xdo_dev_offload_ok) (struct sk_buff *skb,
|
||||||
struct xfrm_state *x);
|
struct xfrm_state *x);
|
||||||
void (*xdo_dev_state_advance_esn) (struct xfrm_state *x);
|
void (*xdo_dev_state_advance_esn) (struct xfrm_state *x);
|
||||||
|
int (*xdo_dev_policy_add) (struct xfrm_policy *x);
|
||||||
|
void (*xdo_dev_policy_delete) (struct xfrm_policy *x);
|
||||||
|
void (*xdo_dev_policy_free) (struct xfrm_policy *x);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,7 @@ struct xfrm_state_walk {
|
||||||
enum {
|
enum {
|
||||||
XFRM_DEV_OFFLOAD_IN = 1,
|
XFRM_DEV_OFFLOAD_IN = 1,
|
||||||
XFRM_DEV_OFFLOAD_OUT,
|
XFRM_DEV_OFFLOAD_OUT,
|
||||||
|
XFRM_DEV_OFFLOAD_FWD,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -541,6 +542,8 @@ struct xfrm_policy {
|
||||||
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
|
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
|
||||||
struct hlist_node bydst_inexact_list;
|
struct hlist_node bydst_inexact_list;
|
||||||
struct rcu_head rcu;
|
struct rcu_head rcu;
|
||||||
|
|
||||||
|
struct xfrm_dev_offload xdo;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct net *xp_net(const struct xfrm_policy *xp)
|
static inline struct net *xp_net(const struct xfrm_policy *xp)
|
||||||
|
@ -1585,6 +1588,8 @@ struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
|
||||||
int xfrm_state_delete(struct xfrm_state *x);
|
int xfrm_state_delete(struct xfrm_state *x);
|
||||||
int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
|
int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
|
||||||
int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
|
int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
|
||||||
|
int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
|
||||||
|
bool task_valid);
|
||||||
void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
|
void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
|
||||||
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
|
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
|
||||||
u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
|
u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
|
||||||
|
@ -1899,6 +1904,9 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
|
||||||
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
|
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
|
||||||
struct xfrm_user_offload *xuo,
|
struct xfrm_user_offload *xuo,
|
||||||
struct netlink_ext_ack *extack);
|
struct netlink_ext_ack *extack);
|
||||||
|
int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
|
||||||
|
struct xfrm_user_offload *xuo, u8 dir,
|
||||||
|
struct netlink_ext_ack *extack);
|
||||||
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x);
|
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x);
|
||||||
|
|
||||||
static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x)
|
static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x)
|
||||||
|
@ -1947,6 +1955,28 @@ static inline void xfrm_dev_state_free(struct xfrm_state *x)
|
||||||
netdev_put(dev, &xso->dev_tracker);
|
netdev_put(dev, &xso->dev_tracker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void xfrm_dev_policy_delete(struct xfrm_policy *x)
|
||||||
|
{
|
||||||
|
struct xfrm_dev_offload *xdo = &x->xdo;
|
||||||
|
struct net_device *dev = xdo->dev;
|
||||||
|
|
||||||
|
if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_policy_delete)
|
||||||
|
dev->xfrmdev_ops->xdo_dev_policy_delete(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void xfrm_dev_policy_free(struct xfrm_policy *x)
|
||||||
|
{
|
||||||
|
struct xfrm_dev_offload *xdo = &x->xdo;
|
||||||
|
struct net_device *dev = xdo->dev;
|
||||||
|
|
||||||
|
if (dev && dev->xfrmdev_ops) {
|
||||||
|
if (dev->xfrmdev_ops->xdo_dev_policy_free)
|
||||||
|
dev->xfrmdev_ops->xdo_dev_policy_free(x);
|
||||||
|
xdo->dev = NULL;
|
||||||
|
netdev_put(dev, &xdo->dev_tracker);
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
static inline void xfrm_dev_resume(struct sk_buff *skb)
|
static inline void xfrm_dev_resume(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
@ -1974,6 +2004,21 @@ static inline void xfrm_dev_state_free(struct xfrm_state *x)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
|
||||||
|
struct xfrm_user_offload *xuo, u8 dir,
|
||||||
|
struct netlink_ext_ack *extack)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void xfrm_dev_policy_delete(struct xfrm_policy *x)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void xfrm_dev_policy_free(struct xfrm_policy *x)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
|
static inline bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -325,6 +325,69 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(xfrm_dev_state_add);
|
EXPORT_SYMBOL_GPL(xfrm_dev_state_add);
|
||||||
|
|
||||||
|
int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
|
||||||
|
struct xfrm_user_offload *xuo, u8 dir,
|
||||||
|
struct netlink_ext_ack *extack)
|
||||||
|
{
|
||||||
|
struct xfrm_dev_offload *xdo = &xp->xdo;
|
||||||
|
struct net_device *dev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!xuo->flags || xuo->flags & ~XFRM_OFFLOAD_PACKET) {
|
||||||
|
/* We support only packet offload mode and it means
|
||||||
|
* that user must set XFRM_OFFLOAD_PACKET bit.
|
||||||
|
*/
|
||||||
|
NL_SET_ERR_MSG(extack, "Unrecognized flags in offload request");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = dev_get_by_index(net, xuo->ifindex);
|
||||||
|
if (!dev)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_policy_add) {
|
||||||
|
xdo->dev = NULL;
|
||||||
|
dev_put(dev);
|
||||||
|
NL_SET_ERR_MSG(extack, "Policy offload is not supported");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
xdo->dev = dev;
|
||||||
|
netdev_tracker_alloc(dev, &xdo->dev_tracker, GFP_ATOMIC);
|
||||||
|
xdo->real_dev = dev;
|
||||||
|
xdo->type = XFRM_DEV_OFFLOAD_PACKET;
|
||||||
|
switch (dir) {
|
||||||
|
case XFRM_POLICY_IN:
|
||||||
|
xdo->dir = XFRM_DEV_OFFLOAD_IN;
|
||||||
|
break;
|
||||||
|
case XFRM_POLICY_OUT:
|
||||||
|
xdo->dir = XFRM_DEV_OFFLOAD_OUT;
|
||||||
|
break;
|
||||||
|
case XFRM_POLICY_FWD:
|
||||||
|
xdo->dir = XFRM_DEV_OFFLOAD_FWD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
xdo->dev = NULL;
|
||||||
|
dev_put(dev);
|
||||||
|
NL_SET_ERR_MSG(extack, "Unrecognized oflload direction");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dev->xfrmdev_ops->xdo_dev_policy_add(xp);
|
||||||
|
if (err) {
|
||||||
|
xdo->dev = NULL;
|
||||||
|
xdo->real_dev = NULL;
|
||||||
|
xdo->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
|
||||||
|
xdo->dir = 0;
|
||||||
|
netdev_put(dev, &xdo->dev_tracker);
|
||||||
|
NL_SET_ERR_MSG(extack, "Device failed to offload this policy");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(xfrm_dev_policy_add);
|
||||||
|
|
||||||
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
|
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
|
||||||
{
|
{
|
||||||
int mtu;
|
int mtu;
|
||||||
|
@ -427,8 +490,10 @@ static int xfrm_api_check(struct net_device *dev)
|
||||||
|
|
||||||
static int xfrm_dev_down(struct net_device *dev)
|
static int xfrm_dev_down(struct net_device *dev)
|
||||||
{
|
{
|
||||||
if (dev->features & NETIF_F_HW_ESP)
|
if (dev->features & NETIF_F_HW_ESP) {
|
||||||
xfrm_dev_state_flush(dev_net(dev), dev, true);
|
xfrm_dev_state_flush(dev_net(dev), dev, true);
|
||||||
|
xfrm_dev_policy_flush(dev_net(dev), dev, true);
|
||||||
|
}
|
||||||
|
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -425,6 +425,7 @@ void xfrm_policy_destroy(struct xfrm_policy *policy)
|
||||||
if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer))
|
if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer))
|
||||||
BUG();
|
BUG();
|
||||||
|
|
||||||
|
xfrm_dev_policy_free(policy);
|
||||||
call_rcu(&policy->rcu, xfrm_policy_destroy_rcu);
|
call_rcu(&policy->rcu, xfrm_policy_destroy_rcu);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(xfrm_policy_destroy);
|
EXPORT_SYMBOL(xfrm_policy_destroy);
|
||||||
|
@ -1769,12 +1770,41 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int xfrm_dev_policy_flush_secctx_check(struct net *net,
|
||||||
|
struct net_device *dev,
|
||||||
|
bool task_valid)
|
||||||
|
{
|
||||||
|
struct xfrm_policy *pol;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) {
|
||||||
|
if (pol->walk.dead ||
|
||||||
|
xfrm_policy_id2dir(pol->index) >= XFRM_POLICY_MAX ||
|
||||||
|
pol->xdo.dev != dev)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
err = security_xfrm_policy_delete(pol->security);
|
||||||
|
if (err) {
|
||||||
|
xfrm_audit_policy_delete(pol, 0, task_valid);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
static inline int
|
static inline int
|
||||||
xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
|
xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int xfrm_dev_policy_flush_secctx_check(struct net *net,
|
||||||
|
struct net_device *dev,
|
||||||
|
bool task_valid)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
|
int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
|
||||||
|
@ -1814,6 +1844,44 @@ out:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(xfrm_policy_flush);
|
EXPORT_SYMBOL(xfrm_policy_flush);
|
||||||
|
|
||||||
|
int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
|
||||||
|
bool task_valid)
|
||||||
|
{
|
||||||
|
int dir, err = 0, cnt = 0;
|
||||||
|
struct xfrm_policy *pol;
|
||||||
|
|
||||||
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
||||||
|
|
||||||
|
err = xfrm_dev_policy_flush_secctx_check(net, dev, task_valid);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
again:
|
||||||
|
list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) {
|
||||||
|
dir = xfrm_policy_id2dir(pol->index);
|
||||||
|
if (pol->walk.dead ||
|
||||||
|
dir >= XFRM_POLICY_MAX ||
|
||||||
|
pol->xdo.dev != dev)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
__xfrm_policy_unlink(pol, dir);
|
||||||
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
||||||
|
cnt++;
|
||||||
|
xfrm_audit_policy_delete(pol, 1, task_valid);
|
||||||
|
xfrm_policy_kill(pol);
|
||||||
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
if (cnt)
|
||||||
|
__xfrm_policy_inexact_flush(net);
|
||||||
|
else
|
||||||
|
err = -ESRCH;
|
||||||
|
out:
|
||||||
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(xfrm_dev_policy_flush);
|
||||||
|
|
||||||
int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
|
int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
|
||||||
int (*func)(struct xfrm_policy *, int, int, void*),
|
int (*func)(struct xfrm_policy *, int, int, void*),
|
||||||
void *data)
|
void *data)
|
||||||
|
@ -2245,6 +2313,7 @@ int xfrm_policy_delete(struct xfrm_policy *pol, int dir)
|
||||||
pol = __xfrm_policy_unlink(pol, dir);
|
pol = __xfrm_policy_unlink(pol, dir);
|
||||||
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
||||||
if (pol) {
|
if (pol) {
|
||||||
|
xfrm_dev_policy_delete(pol);
|
||||||
xfrm_policy_kill(pol);
|
xfrm_policy_kill(pol);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1892,6 +1892,15 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net,
|
||||||
if (attrs[XFRMA_IF_ID])
|
if (attrs[XFRMA_IF_ID])
|
||||||
xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
|
xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
|
||||||
|
|
||||||
|
/* configure the hardware if offload is requested */
|
||||||
|
if (attrs[XFRMA_OFFLOAD_DEV]) {
|
||||||
|
err = xfrm_dev_policy_add(net, xp,
|
||||||
|
nla_data(attrs[XFRMA_OFFLOAD_DEV]),
|
||||||
|
p->dir, extack);
|
||||||
|
if (err)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
return xp;
|
return xp;
|
||||||
error:
|
error:
|
||||||
*errp = err;
|
*errp = err;
|
||||||
|
@ -1931,6 +1940,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||||
xfrm_audit_policy_add(xp, err ? 0 : 1, true);
|
xfrm_audit_policy_add(xp, err ? 0 : 1, true);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
|
xfrm_dev_policy_delete(xp);
|
||||||
security_xfrm_policy_free(xp->security);
|
security_xfrm_policy_free(xp->security);
|
||||||
kfree(xp);
|
kfree(xp);
|
||||||
return err;
|
return err;
|
||||||
|
@ -2043,6 +2053,8 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr
|
||||||
err = xfrm_mark_put(skb, &xp->mark);
|
err = xfrm_mark_put(skb, &xp->mark);
|
||||||
if (!err)
|
if (!err)
|
||||||
err = xfrm_if_id_put(skb, xp->if_id);
|
err = xfrm_if_id_put(skb, xp->if_id);
|
||||||
|
if (!err && xp->xdo.dev)
|
||||||
|
err = copy_user_offload(&xp->xdo, skb);
|
||||||
if (err) {
|
if (err) {
|
||||||
nlmsg_cancel(skb, nlh);
|
nlmsg_cancel(skb, nlh);
|
||||||
return err;
|
return err;
|
||||||
|
@ -3381,6 +3393,8 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x,
|
||||||
err = xfrm_mark_put(skb, &xp->mark);
|
err = xfrm_mark_put(skb, &xp->mark);
|
||||||
if (!err)
|
if (!err)
|
||||||
err = xfrm_if_id_put(skb, xp->if_id);
|
err = xfrm_if_id_put(skb, xp->if_id);
|
||||||
|
if (!err && xp->xdo.dev)
|
||||||
|
err = copy_user_offload(&xp->xdo, skb);
|
||||||
if (err) {
|
if (err) {
|
||||||
nlmsg_cancel(skb, nlh);
|
nlmsg_cancel(skb, nlh);
|
||||||
return err;
|
return err;
|
||||||
|
@ -3499,6 +3513,8 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp,
|
||||||
err = xfrm_mark_put(skb, &xp->mark);
|
err = xfrm_mark_put(skb, &xp->mark);
|
||||||
if (!err)
|
if (!err)
|
||||||
err = xfrm_if_id_put(skb, xp->if_id);
|
err = xfrm_if_id_put(skb, xp->if_id);
|
||||||
|
if (!err && xp->xdo.dev)
|
||||||
|
err = copy_user_offload(&xp->xdo, skb);
|
||||||
if (err) {
|
if (err) {
|
||||||
nlmsg_cancel(skb, nlh);
|
nlmsg_cancel(skb, nlh);
|
||||||
return err;
|
return err;
|
||||||
|
@ -3582,6 +3598,8 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e
|
||||||
err = xfrm_mark_put(skb, &xp->mark);
|
err = xfrm_mark_put(skb, &xp->mark);
|
||||||
if (!err)
|
if (!err)
|
||||||
err = xfrm_if_id_put(skb, xp->if_id);
|
err = xfrm_if_id_put(skb, xp->if_id);
|
||||||
|
if (!err && xp->xdo.dev)
|
||||||
|
err = copy_user_offload(&xp->xdo, skb);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_free_skb;
|
goto out_free_skb;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue