Skip to content

Commit 919e43f

Browse files
rleonklassert
authored andcommitted
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 <[email protected]> Signed-off-by: Leon Romanovsky <[email protected]> Signed-off-by: Steffen Klassert <[email protected]>
1 parent 62f6eca commit 919e43f

File tree

5 files changed

+201
-1
lines changed

5 files changed

+201
-1
lines changed

include/linux/netdevice.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,9 @@ struct xfrmdev_ops {
10401040
bool (*xdo_dev_offload_ok) (struct sk_buff *skb,
10411041
struct xfrm_state *x);
10421042
void (*xdo_dev_state_advance_esn) (struct xfrm_state *x);
1043+
int (*xdo_dev_policy_add) (struct xfrm_policy *x);
1044+
void (*xdo_dev_policy_delete) (struct xfrm_policy *x);
1045+
void (*xdo_dev_policy_free) (struct xfrm_policy *x);
10431046
};
10441047
#endif
10451048

include/net/xfrm.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ struct xfrm_state_walk {
129129
enum {
130130
XFRM_DEV_OFFLOAD_IN = 1,
131131
XFRM_DEV_OFFLOAD_OUT,
132+
XFRM_DEV_OFFLOAD_FWD,
132133
};
133134

134135
enum {
@@ -541,6 +542,8 @@ struct xfrm_policy {
541542
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
542543
struct hlist_node bydst_inexact_list;
543544
struct rcu_head rcu;
545+
546+
struct xfrm_dev_offload xdo;
544547
};
545548

546549
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);
15851588
int xfrm_state_delete(struct xfrm_state *x);
15861589
int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
15871590
int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
1591+
int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
1592+
bool task_valid);
15881593
void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
15891594
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
15901595
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
18991904
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
19001905
struct xfrm_user_offload *xuo,
19011906
struct netlink_ext_ack *extack);
1907+
int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
1908+
struct xfrm_user_offload *xuo, u8 dir,
1909+
struct netlink_ext_ack *extack);
19021910
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x);
19031911

19041912
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)
19471955
netdev_put(dev, &xso->dev_tracker);
19481956
}
19491957
}
1958+
1959+
static inline void xfrm_dev_policy_delete(struct xfrm_policy *x)
1960+
{
1961+
struct xfrm_dev_offload *xdo = &x->xdo;
1962+
struct net_device *dev = xdo->dev;
1963+
1964+
if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_policy_delete)
1965+
dev->xfrmdev_ops->xdo_dev_policy_delete(x);
1966+
}
1967+
1968+
static inline void xfrm_dev_policy_free(struct xfrm_policy *x)
1969+
{
1970+
struct xfrm_dev_offload *xdo = &x->xdo;
1971+
struct net_device *dev = xdo->dev;
1972+
1973+
if (dev && dev->xfrmdev_ops) {
1974+
if (dev->xfrmdev_ops->xdo_dev_policy_free)
1975+
dev->xfrmdev_ops->xdo_dev_policy_free(x);
1976+
xdo->dev = NULL;
1977+
netdev_put(dev, &xdo->dev_tracker);
1978+
}
1979+
}
19501980
#else
19511981
static inline void xfrm_dev_resume(struct sk_buff *skb)
19521982
{
@@ -1974,6 +2004,21 @@ static inline void xfrm_dev_state_free(struct xfrm_state *x)
19742004
{
19752005
}
19762006

2007+
static inline int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
2008+
struct xfrm_user_offload *xuo, u8 dir,
2009+
struct netlink_ext_ack *extack)
2010+
{
2011+
return 0;
2012+
}
2013+
2014+
static inline void xfrm_dev_policy_delete(struct xfrm_policy *x)
2015+
{
2016+
}
2017+
2018+
static inline void xfrm_dev_policy_free(struct xfrm_policy *x)
2019+
{
2020+
}
2021+
19772022
static inline bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
19782023
{
19792024
return false;

net/xfrm/xfrm_device.c

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,69 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
325325
}
326326
EXPORT_SYMBOL_GPL(xfrm_dev_state_add);
327327

328+
int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
329+
struct xfrm_user_offload *xuo, u8 dir,
330+
struct netlink_ext_ack *extack)
331+
{
332+
struct xfrm_dev_offload *xdo = &xp->xdo;
333+
struct net_device *dev;
334+
int err;
335+
336+
if (!xuo->flags || xuo->flags & ~XFRM_OFFLOAD_PACKET) {
337+
/* We support only packet offload mode and it means
338+
* that user must set XFRM_OFFLOAD_PACKET bit.
339+
*/
340+
NL_SET_ERR_MSG(extack, "Unrecognized flags in offload request");
341+
return -EINVAL;
342+
}
343+
344+
dev = dev_get_by_index(net, xuo->ifindex);
345+
if (!dev)
346+
return -EINVAL;
347+
348+
if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_policy_add) {
349+
xdo->dev = NULL;
350+
dev_put(dev);
351+
NL_SET_ERR_MSG(extack, "Policy offload is not supported");
352+
return -EINVAL;
353+
}
354+
355+
xdo->dev = dev;
356+
netdev_tracker_alloc(dev, &xdo->dev_tracker, GFP_ATOMIC);
357+
xdo->real_dev = dev;
358+
xdo->type = XFRM_DEV_OFFLOAD_PACKET;
359+
switch (dir) {
360+
case XFRM_POLICY_IN:
361+
xdo->dir = XFRM_DEV_OFFLOAD_IN;
362+
break;
363+
case XFRM_POLICY_OUT:
364+
xdo->dir = XFRM_DEV_OFFLOAD_OUT;
365+
break;
366+
case XFRM_POLICY_FWD:
367+
xdo->dir = XFRM_DEV_OFFLOAD_FWD;
368+
break;
369+
default:
370+
xdo->dev = NULL;
371+
dev_put(dev);
372+
NL_SET_ERR_MSG(extack, "Unrecognized oflload direction");
373+
return -EINVAL;
374+
}
375+
376+
err = dev->xfrmdev_ops->xdo_dev_policy_add(xp);
377+
if (err) {
378+
xdo->dev = NULL;
379+
xdo->real_dev = NULL;
380+
xdo->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
381+
xdo->dir = 0;
382+
netdev_put(dev, &xdo->dev_tracker);
383+
NL_SET_ERR_MSG(extack, "Device failed to offload this policy");
384+
return err;
385+
}
386+
387+
return 0;
388+
}
389+
EXPORT_SYMBOL_GPL(xfrm_dev_policy_add);
390+
328391
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
329392
{
330393
int mtu;
@@ -427,8 +490,10 @@ static int xfrm_api_check(struct net_device *dev)
427490

428491
static int xfrm_dev_down(struct net_device *dev)
429492
{
430-
if (dev->features & NETIF_F_HW_ESP)
493+
if (dev->features & NETIF_F_HW_ESP) {
431494
xfrm_dev_state_flush(dev_net(dev), dev, true);
495+
xfrm_dev_policy_flush(dev_net(dev), dev, true);
496+
}
432497

433498
return NOTIFY_DONE;
434499
}

net/xfrm/xfrm_policy.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ void xfrm_policy_destroy(struct xfrm_policy *policy)
425425
if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer))
426426
BUG();
427427

428+
xfrm_dev_policy_free(policy);
428429
call_rcu(&policy->rcu, xfrm_policy_destroy_rcu);
429430
}
430431
EXPORT_SYMBOL(xfrm_policy_destroy);
@@ -1769,12 +1770,41 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
17691770
}
17701771
return err;
17711772
}
1773+
1774+
static inline int xfrm_dev_policy_flush_secctx_check(struct net *net,
1775+
struct net_device *dev,
1776+
bool task_valid)
1777+
{
1778+
struct xfrm_policy *pol;
1779+
int err = 0;
1780+
1781+
list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) {
1782+
if (pol->walk.dead ||
1783+
xfrm_policy_id2dir(pol->index) >= XFRM_POLICY_MAX ||
1784+
pol->xdo.dev != dev)
1785+
continue;
1786+
1787+
err = security_xfrm_policy_delete(pol->security);
1788+
if (err) {
1789+
xfrm_audit_policy_delete(pol, 0, task_valid);
1790+
return err;
1791+
}
1792+
}
1793+
return err;
1794+
}
17721795
#else
17731796
static inline int
17741797
xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
17751798
{
17761799
return 0;
17771800
}
1801+
1802+
static inline int xfrm_dev_policy_flush_secctx_check(struct net *net,
1803+
struct net_device *dev,
1804+
bool task_valid)
1805+
{
1806+
return 0;
1807+
}
17781808
#endif
17791809

17801810
int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
@@ -1814,6 +1844,44 @@ int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
18141844
}
18151845
EXPORT_SYMBOL(xfrm_policy_flush);
18161846

1847+
int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
1848+
bool task_valid)
1849+
{
1850+
int dir, err = 0, cnt = 0;
1851+
struct xfrm_policy *pol;
1852+
1853+
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
1854+
1855+
err = xfrm_dev_policy_flush_secctx_check(net, dev, task_valid);
1856+
if (err)
1857+
goto out;
1858+
1859+
again:
1860+
list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) {
1861+
dir = xfrm_policy_id2dir(pol->index);
1862+
if (pol->walk.dead ||
1863+
dir >= XFRM_POLICY_MAX ||
1864+
pol->xdo.dev != dev)
1865+
continue;
1866+
1867+
__xfrm_policy_unlink(pol, dir);
1868+
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
1869+
cnt++;
1870+
xfrm_audit_policy_delete(pol, 1, task_valid);
1871+
xfrm_policy_kill(pol);
1872+
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
1873+
goto again;
1874+
}
1875+
if (cnt)
1876+
__xfrm_policy_inexact_flush(net);
1877+
else
1878+
err = -ESRCH;
1879+
out:
1880+
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
1881+
return err;
1882+
}
1883+
EXPORT_SYMBOL(xfrm_dev_policy_flush);
1884+
18171885
int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
18181886
int (*func)(struct xfrm_policy *, int, int, void*),
18191887
void *data)
@@ -2245,6 +2313,7 @@ int xfrm_policy_delete(struct xfrm_policy *pol, int dir)
22452313
pol = __xfrm_policy_unlink(pol, dir);
22462314
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
22472315
if (pol) {
2316+
xfrm_dev_policy_delete(pol);
22482317
xfrm_policy_kill(pol);
22492318
return 0;
22502319
}

net/xfrm/xfrm_user.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,6 +1892,15 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net,
18921892
if (attrs[XFRMA_IF_ID])
18931893
xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
18941894

1895+
/* configure the hardware if offload is requested */
1896+
if (attrs[XFRMA_OFFLOAD_DEV]) {
1897+
err = xfrm_dev_policy_add(net, xp,
1898+
nla_data(attrs[XFRMA_OFFLOAD_DEV]),
1899+
p->dir, extack);
1900+
if (err)
1901+
goto error;
1902+
}
1903+
18951904
return xp;
18961905
error:
18971906
*errp = err;
@@ -1931,6 +1940,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
19311940
xfrm_audit_policy_add(xp, err ? 0 : 1, true);
19321941

19331942
if (err) {
1943+
xfrm_dev_policy_delete(xp);
19341944
security_xfrm_policy_free(xp->security);
19351945
kfree(xp);
19361946
return err;
@@ -2043,6 +2053,8 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr
20432053
err = xfrm_mark_put(skb, &xp->mark);
20442054
if (!err)
20452055
err = xfrm_if_id_put(skb, xp->if_id);
2056+
if (!err && xp->xdo.dev)
2057+
err = copy_user_offload(&xp->xdo, skb);
20462058
if (err) {
20472059
nlmsg_cancel(skb, nlh);
20482060
return err;
@@ -3381,6 +3393,8 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x,
33813393
err = xfrm_mark_put(skb, &xp->mark);
33823394
if (!err)
33833395
err = xfrm_if_id_put(skb, xp->if_id);
3396+
if (!err && xp->xdo.dev)
3397+
err = copy_user_offload(&xp->xdo, skb);
33843398
if (err) {
33853399
nlmsg_cancel(skb, nlh);
33863400
return err;
@@ -3499,6 +3513,8 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp,
34993513
err = xfrm_mark_put(skb, &xp->mark);
35003514
if (!err)
35013515
err = xfrm_if_id_put(skb, xp->if_id);
3516+
if (!err && xp->xdo.dev)
3517+
err = copy_user_offload(&xp->xdo, skb);
35023518
if (err) {
35033519
nlmsg_cancel(skb, nlh);
35043520
return err;
@@ -3582,6 +3598,8 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e
35823598
err = xfrm_mark_put(skb, &xp->mark);
35833599
if (!err)
35843600
err = xfrm_if_id_put(skb, xp->if_id);
3601+
if (!err && xp->xdo.dev)
3602+
err = copy_user_offload(&xp->xdo, skb);
35853603
if (err)
35863604
goto out_free_skb;
35873605

0 commit comments

Comments
 (0)