mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
ipsec-next-2024-11-15
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEH7ZpcWbFyOOp6OJbrB3Eaf9PW7cFAmc3A/gACgkQrB3Eaf9P W7fNew//XCIhIvFYaQcP2x84T4EYB679NkGlwMATxXgn40+sp7muSwVweynEWNIu FltfBAwYD/MxD7g519abVPMWXs/iYI5duw3vvqnxmkOoebWLLocg2VoqFIdVXlQw /hj+1X/oNT4OKcaQAw/FAGRuYvkc90YB/rRG51RwAIR0tyBjRwfUsozMM8QX/zQI I0cLCgGAf/kylQre+dhvUkMhXaLogMF5v0qzPxhyMBD02JaUpe6+5cdHQcmKOhqa ksTpySYnIKIHZrLizeFGDZpinaDIph20vGaDvDXpqTYFuwvCQsZczJy02dF4otf2 2dZz6+2La+ZM+WsGIqpALqKCNhr8fOcQxCRH3eGLPBwoXXt5CFAMgJKob8hKuonW FgJaYMBZOjYbgGah8WbEe/YsWq4y3uRs48pFtY+T5cn7AskNxIvUoLNjSS83Hlqu PJbveiKsZygig966Q/zUFATYnvj3zEgjVEcSbK6LRyBXL79Njr8l+PZ0Zoz76tc4 bF1Xv0x+lRYmwa9rvOFaeqrP/GTe0xvlitFzuCN7HnXiN8URKnnDY2odkXYzo+Z7 MBbP8wR/CaoiAvdMw74116nAIFOW95LPtvdGJTvlS9jAOt1P7dWQ3/mFKEpItndv cJjWzI7HKl0+85FcCDw+tmsDWWGbALUyPw96i8UgUcDGyqVKUgA= =Ioo8 -----END PGP SIGNATURE----- Merge tag 'ipsec-next-2024-11-15' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next Steffen Klassert says: ==================== ipsec-next-11-15 1) Add support for RFC 9611 per cpu xfrm state handling. 2) Add inbound and outbound xfrm state caches to speed up state lookups. 3) Convert xfrm to dscp_t. From Guillaume Nault. 4) Fix error handling in build_aevent. From Everest K.C. 5) Replace strncpy with strscpy_pad in copy_to_user_auth. From Daniel Yang. 6) Fix an uninitialized symbol during acquire state insertion. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
296a681def
@ -43,6 +43,7 @@ struct netns_xfrm {
|
||||
struct hlist_head __rcu *state_bysrc;
|
||||
struct hlist_head __rcu *state_byspi;
|
||||
struct hlist_head __rcu *state_byseq;
|
||||
struct hlist_head __percpu *state_cache_input;
|
||||
unsigned int state_hmask;
|
||||
unsigned int state_num;
|
||||
struct work_struct state_hash_work;
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <net/sock.h>
|
||||
#include <net/dst.h>
|
||||
#include <net/inet_dscp.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/route.h>
|
||||
#include <net/ipv6.h>
|
||||
@ -184,10 +185,13 @@ struct xfrm_state {
|
||||
};
|
||||
struct hlist_node byspi;
|
||||
struct hlist_node byseq;
|
||||
struct hlist_node state_cache;
|
||||
struct hlist_node state_cache_input;
|
||||
|
||||
refcount_t refcnt;
|
||||
spinlock_t lock;
|
||||
|
||||
u32 pcpu_num;
|
||||
struct xfrm_id id;
|
||||
struct xfrm_selector sel;
|
||||
struct xfrm_mark mark;
|
||||
@ -351,7 +355,7 @@ void xfrm_if_unregister_cb(void);
|
||||
|
||||
struct xfrm_dst_lookup_params {
|
||||
struct net *net;
|
||||
int tos;
|
||||
dscp_t dscp;
|
||||
int oif;
|
||||
xfrm_address_t *saddr;
|
||||
xfrm_address_t *daddr;
|
||||
@ -536,6 +540,7 @@ struct xfrm_policy_queue {
|
||||
* @xp_net: network namespace the policy lives in
|
||||
* @bydst: hlist node for SPD hash table or rbtree list
|
||||
* @byidx: hlist node for index hash table
|
||||
* @state_cache_list: hlist head for policy cached xfrm states
|
||||
* @lock: serialize changes to policy structure members
|
||||
* @refcnt: reference count, freed once it reaches 0
|
||||
* @pos: kernel internal tie-breaker to determine age of policy
|
||||
@ -566,6 +571,8 @@ struct xfrm_policy {
|
||||
struct hlist_node bydst;
|
||||
struct hlist_node byidx;
|
||||
|
||||
struct hlist_head state_cache_list;
|
||||
|
||||
/* This lock only affects elements except for entry. */
|
||||
rwlock_t lock;
|
||||
refcount_t refcnt;
|
||||
@ -1645,6 +1652,10 @@ int xfrm_state_update(struct xfrm_state *x);
|
||||
struct xfrm_state *xfrm_state_lookup(struct net *net, u32 mark,
|
||||
const xfrm_address_t *daddr, __be32 spi,
|
||||
u8 proto, unsigned short family);
|
||||
struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark,
|
||||
const xfrm_address_t *daddr,
|
||||
__be32 spi, u8 proto,
|
||||
unsigned short family);
|
||||
struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark,
|
||||
const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
@ -1684,7 +1695,7 @@ struct xfrmk_spdinfo {
|
||||
u32 spdhmcnt;
|
||||
};
|
||||
|
||||
struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
|
||||
struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq, u32 pcpu_num);
|
||||
int xfrm_state_delete(struct xfrm_state *x);
|
||||
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);
|
||||
@ -1796,7 +1807,7 @@ int verify_spi_info(u8 proto, u32 min, u32 max, struct netlink_ext_ack *extack);
|
||||
int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi,
|
||||
struct netlink_ext_ack *extack);
|
||||
struct xfrm_state *xfrm_find_acq(struct net *net, const struct xfrm_mark *mark,
|
||||
u8 mode, u32 reqid, u32 if_id, u8 proto,
|
||||
u8 mode, u32 reqid, u32 if_id, u32 pcpu_num, u8 proto,
|
||||
const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr, int create,
|
||||
unsigned short family);
|
||||
|
@ -322,6 +322,7 @@ enum xfrm_attr_type_t {
|
||||
XFRMA_MTIMER_THRESH, /* __u32 in seconds for input SA */
|
||||
XFRMA_SA_DIR, /* __u8 */
|
||||
XFRMA_NAT_KEEPALIVE_INTERVAL, /* __u32 in seconds for NAT keepalive */
|
||||
XFRMA_SA_PCPU, /* __u32 */
|
||||
__XFRMA_MAX
|
||||
|
||||
#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */
|
||||
@ -437,6 +438,7 @@ struct xfrm_userpolicy_info {
|
||||
#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */
|
||||
/* Automatically expand selector to include matching ICMP payloads. */
|
||||
#define XFRM_POLICY_ICMP 2
|
||||
#define XFRM_POLICY_CPU_ACQUIRE 4
|
||||
__u8 share;
|
||||
};
|
||||
|
||||
|
@ -53,9 +53,9 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
|
||||
if (sp->len == XFRM_MAX_DEPTH)
|
||||
goto out_reset;
|
||||
|
||||
x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
|
||||
(xfrm_address_t *)&ip_hdr(skb)->daddr,
|
||||
spi, IPPROTO_ESP, AF_INET);
|
||||
x = xfrm_input_state_lookup(dev_net(skb->dev), skb->mark,
|
||||
(xfrm_address_t *)&ip_hdr(skb)->daddr,
|
||||
spi, IPPROTO_ESP, AF_INET);
|
||||
|
||||
if (unlikely(x && x->dir && x->dir != XFRM_SA_DIR_IN)) {
|
||||
/* non-offload path will record the error and audit log */
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/inetdevice.h>
|
||||
#include <net/dst.h>
|
||||
#include <net/xfrm.h>
|
||||
#include <net/inet_dscp.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/l3mdev.h>
|
||||
|
||||
@ -24,7 +25,7 @@ static struct dst_entry *__xfrm4_dst_lookup(struct flowi4 *fl4,
|
||||
|
||||
memset(fl4, 0, sizeof(*fl4));
|
||||
fl4->daddr = params->daddr->a4;
|
||||
fl4->flowi4_tos = params->tos;
|
||||
fl4->flowi4_tos = inet_dscp_to_dsfield(params->dscp);
|
||||
fl4->flowi4_l3mdev = l3mdev_master_ifindex_by_index(params->net,
|
||||
params->oif);
|
||||
fl4->flowi4_mark = params->mark;
|
||||
|
@ -80,9 +80,9 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
|
||||
if (sp->len == XFRM_MAX_DEPTH)
|
||||
goto out_reset;
|
||||
|
||||
x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
|
||||
(xfrm_address_t *)&ipv6_hdr(skb)->daddr,
|
||||
spi, IPPROTO_ESP, AF_INET6);
|
||||
x = xfrm_input_state_lookup(dev_net(skb->dev), skb->mark,
|
||||
(xfrm_address_t *)&ipv6_hdr(skb)->daddr,
|
||||
spi, IPPROTO_ESP, AF_INET6);
|
||||
|
||||
if (unlikely(x && x->dir && x->dir != XFRM_SA_DIR_IN)) {
|
||||
/* non-offload path will record the error and audit log */
|
||||
|
@ -1354,7 +1354,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_
|
||||
}
|
||||
|
||||
if (hdr->sadb_msg_seq) {
|
||||
x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq);
|
||||
x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq, UINT_MAX);
|
||||
if (x && !xfrm_addr_equal(&x->id.daddr, xdaddr, family)) {
|
||||
xfrm_state_put(x);
|
||||
x = NULL;
|
||||
@ -1362,7 +1362,8 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_
|
||||
}
|
||||
|
||||
if (!x)
|
||||
x = xfrm_find_acq(net, &dummy_mark, mode, reqid, 0, proto, xdaddr, xsaddr, 1, family);
|
||||
x = xfrm_find_acq(net, &dummy_mark, mode, reqid, 0, UINT_MAX,
|
||||
proto, xdaddr, xsaddr, 1, family);
|
||||
|
||||
if (x == NULL)
|
||||
return -ENOENT;
|
||||
@ -1417,7 +1418,7 @@ static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, const struct sadb
|
||||
if (hdr->sadb_msg_seq == 0 || hdr->sadb_msg_errno == 0)
|
||||
return 0;
|
||||
|
||||
x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq);
|
||||
x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq, UINT_MAX);
|
||||
if (x == NULL)
|
||||
return 0;
|
||||
|
||||
|
@ -132,6 +132,7 @@ static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
|
||||
[XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
|
||||
[XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT),
|
||||
[XFRMA_NAT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
|
||||
[XFRMA_SA_PCPU] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb,
|
||||
@ -282,9 +283,10 @@ static int xfrm_xlate64_attr(struct sk_buff *dst, const struct nlattr *src)
|
||||
case XFRMA_MTIMER_THRESH:
|
||||
case XFRMA_SA_DIR:
|
||||
case XFRMA_NAT_KEEPALIVE_INTERVAL:
|
||||
case XFRMA_SA_PCPU:
|
||||
return xfrm_nla_cpy(dst, src, nla_len(src));
|
||||
default:
|
||||
BUILD_BUG_ON(XFRMA_MAX != XFRMA_NAT_KEEPALIVE_INTERVAL);
|
||||
BUILD_BUG_ON(XFRMA_MAX != XFRMA_SA_PCPU);
|
||||
pr_warn_once("unsupported nla_type %d\n", src->nla_type);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@ -439,7 +441,7 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
|
||||
int err;
|
||||
|
||||
if (type > XFRMA_MAX) {
|
||||
BUILD_BUG_ON(XFRMA_MAX != XFRMA_NAT_KEEPALIVE_INTERVAL);
|
||||
BUILD_BUG_ON(XFRMA_MAX != XFRMA_SA_PCPU);
|
||||
NL_SET_ERR_MSG(extack, "Bad attribute");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -572,7 +572,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
||||
goto drop;
|
||||
}
|
||||
|
||||
x = xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family);
|
||||
x = xfrm_input_state_lookup(net, mark, daddr, spi, nexthdr, family);
|
||||
if (x == NULL) {
|
||||
secpath_reset(skb);
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
|
||||
|
@ -289,7 +289,7 @@ struct dst_entry *__xfrm_dst_lookup(int family,
|
||||
EXPORT_SYMBOL(__xfrm_dst_lookup);
|
||||
|
||||
static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x,
|
||||
int tos, int oif,
|
||||
dscp_t dscp, int oif,
|
||||
xfrm_address_t *prev_saddr,
|
||||
xfrm_address_t *prev_daddr,
|
||||
int family, u32 mark)
|
||||
@ -312,7 +312,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x,
|
||||
params.net = net;
|
||||
params.saddr = saddr;
|
||||
params.daddr = daddr;
|
||||
params.tos = tos;
|
||||
params.dscp = dscp;
|
||||
params.oif = oif;
|
||||
params.mark = mark;
|
||||
params.ipproto = x->id.proto;
|
||||
@ -434,6 +434,7 @@ struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp)
|
||||
if (policy) {
|
||||
write_pnet(&policy->xp_net, net);
|
||||
INIT_LIST_HEAD(&policy->walk.all);
|
||||
INIT_HLIST_HEAD(&policy->state_cache_list);
|
||||
INIT_HLIST_NODE(&policy->bydst);
|
||||
INIT_HLIST_NODE(&policy->byidx);
|
||||
rwlock_init(&policy->lock);
|
||||
@ -475,6 +476,9 @@ EXPORT_SYMBOL(xfrm_policy_destroy);
|
||||
|
||||
static void xfrm_policy_kill(struct xfrm_policy *policy)
|
||||
{
|
||||
struct net *net = xp_net(policy);
|
||||
struct xfrm_state *x;
|
||||
|
||||
xfrm_dev_policy_delete(policy);
|
||||
|
||||
write_lock_bh(&policy->lock);
|
||||
@ -490,6 +494,13 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)
|
||||
if (del_timer(&policy->timer))
|
||||
xfrm_pol_put(policy);
|
||||
|
||||
/* XXX: Flush state cache */
|
||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
||||
hlist_for_each_entry_rcu(x, &policy->state_cache_list, state_cache) {
|
||||
hlist_del_init_rcu(&x->state_cache);
|
||||
}
|
||||
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||
|
||||
xfrm_pol_put(policy);
|
||||
}
|
||||
|
||||
@ -2576,10 +2587,10 @@ xfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, const struct flowi *fl,
|
||||
|
||||
}
|
||||
|
||||
static int xfrm_get_tos(const struct flowi *fl, int family)
|
||||
static dscp_t xfrm_get_dscp(const struct flowi *fl, int family)
|
||||
{
|
||||
if (family == AF_INET)
|
||||
return fl->u.ip4.flowi4_tos & INET_DSCP_MASK;
|
||||
return inet_dsfield_to_dscp(fl->u.ip4.flowi4_tos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2667,13 +2678,13 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
|
||||
int header_len = 0;
|
||||
int nfheader_len = 0;
|
||||
int trailer_len = 0;
|
||||
int tos;
|
||||
int family = policy->selector.family;
|
||||
xfrm_address_t saddr, daddr;
|
||||
dscp_t dscp;
|
||||
|
||||
xfrm_flowi_addr_get(fl, &saddr, &daddr, family);
|
||||
|
||||
tos = xfrm_get_tos(fl, family);
|
||||
dscp = xfrm_get_dscp(fl, family);
|
||||
|
||||
dst_hold(dst);
|
||||
|
||||
@ -2721,8 +2732,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
|
||||
family = xfrm[i]->props.family;
|
||||
|
||||
oif = fl->flowi_oif ? : fl->flowi_l3mdev;
|
||||
dst = xfrm_dst_lookup(xfrm[i], tos, oif,
|
||||
&saddr, &daddr, family, mark);
|
||||
dst = xfrm_dst_lookup(xfrm[i], dscp, oif, &saddr,
|
||||
&daddr, family, mark);
|
||||
err = PTR_ERR(dst);
|
||||
if (IS_ERR(dst))
|
||||
goto put_states;
|
||||
@ -3275,6 +3286,7 @@ no_transform:
|
||||
dst_release(dst);
|
||||
dst = dst_orig;
|
||||
}
|
||||
|
||||
ok:
|
||||
xfrm_pols_put(pols, drop_pols);
|
||||
if (dst && dst->xfrm &&
|
||||
|
@ -665,6 +665,7 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
|
||||
refcount_set(&x->refcnt, 1);
|
||||
atomic_set(&x->tunnel_users, 0);
|
||||
INIT_LIST_HEAD(&x->km.all);
|
||||
INIT_HLIST_NODE(&x->state_cache);
|
||||
INIT_HLIST_NODE(&x->bydst);
|
||||
INIT_HLIST_NODE(&x->bysrc);
|
||||
INIT_HLIST_NODE(&x->byspi);
|
||||
@ -679,6 +680,7 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
|
||||
x->lft.hard_packet_limit = XFRM_INF;
|
||||
x->replay_maxage = 0;
|
||||
x->replay_maxdiff = 0;
|
||||
x->pcpu_num = UINT_MAX;
|
||||
spin_lock_init(&x->lock);
|
||||
}
|
||||
return x;
|
||||
@ -743,12 +745,18 @@ int __xfrm_state_delete(struct xfrm_state *x)
|
||||
|
||||
if (x->km.state != XFRM_STATE_DEAD) {
|
||||
x->km.state = XFRM_STATE_DEAD;
|
||||
|
||||
spin_lock(&net->xfrm.xfrm_state_lock);
|
||||
list_del(&x->km.all);
|
||||
hlist_del_rcu(&x->bydst);
|
||||
hlist_del_rcu(&x->bysrc);
|
||||
if (x->km.seq)
|
||||
hlist_del_rcu(&x->byseq);
|
||||
if (!hlist_unhashed(&x->state_cache))
|
||||
hlist_del_rcu(&x->state_cache);
|
||||
if (!hlist_unhashed(&x->state_cache_input))
|
||||
hlist_del_rcu(&x->state_cache_input);
|
||||
|
||||
if (x->id.spi)
|
||||
hlist_del_rcu(&x->byspi);
|
||||
net->xfrm.state_num--;
|
||||
@ -1101,6 +1109,52 @@ static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark,
|
||||
const xfrm_address_t *daddr,
|
||||
__be32 spi, u8 proto,
|
||||
unsigned short family)
|
||||
{
|
||||
struct hlist_head *state_cache_input;
|
||||
struct xfrm_state *x = NULL;
|
||||
int cpu = get_cpu();
|
||||
|
||||
state_cache_input = per_cpu_ptr(net->xfrm.state_cache_input, cpu);
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(x, state_cache_input, state_cache_input) {
|
||||
if (x->props.family != family ||
|
||||
x->id.spi != spi ||
|
||||
x->id.proto != proto ||
|
||||
!xfrm_addr_equal(&x->id.daddr, daddr, family))
|
||||
continue;
|
||||
|
||||
if ((mark & x->mark.m) != x->mark.v)
|
||||
continue;
|
||||
if (!xfrm_state_hold_rcu(x))
|
||||
continue;
|
||||
goto out;
|
||||
}
|
||||
|
||||
x = __xfrm_state_lookup(net, mark, daddr, spi, proto, family);
|
||||
|
||||
if (x && x->km.state == XFRM_STATE_VALID) {
|
||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
||||
if (hlist_unhashed(&x->state_cache_input)) {
|
||||
hlist_add_head_rcu(&x->state_cache_input, state_cache_input);
|
||||
} else {
|
||||
hlist_del_rcu(&x->state_cache_input);
|
||||
hlist_add_head_rcu(&x->state_cache_input, state_cache_input);
|
||||
}
|
||||
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||
}
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
put_cpu();
|
||||
return x;
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_input_state_lookup);
|
||||
|
||||
static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, u32 mark,
|
||||
const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
@ -1155,6 +1209,12 @@ static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x,
|
||||
struct xfrm_state **best, int *acq_in_progress,
|
||||
int *error)
|
||||
{
|
||||
/* We need the cpu id just as a lookup key,
|
||||
* we don't require it to be stable.
|
||||
*/
|
||||
unsigned int pcpu_id = get_cpu();
|
||||
put_cpu();
|
||||
|
||||
/* Resolution logic:
|
||||
* 1. There is a valid state with matching selector. Done.
|
||||
* 2. Valid state with inappropriate selector. Skip.
|
||||
@ -1174,13 +1234,18 @@ static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x,
|
||||
&fl->u.__fl_common))
|
||||
return;
|
||||
|
||||
if (x->pcpu_num != UINT_MAX && x->pcpu_num != pcpu_id)
|
||||
return;
|
||||
|
||||
if (!*best ||
|
||||
((*best)->pcpu_num == UINT_MAX && x->pcpu_num == pcpu_id) ||
|
||||
(*best)->km.dying > x->km.dying ||
|
||||
((*best)->km.dying == x->km.dying &&
|
||||
(*best)->curlft.add_time < x->curlft.add_time))
|
||||
*best = x;
|
||||
} else if (x->km.state == XFRM_STATE_ACQ) {
|
||||
*acq_in_progress = 1;
|
||||
if (!*best || x->pcpu_num == pcpu_id)
|
||||
*acq_in_progress = 1;
|
||||
} else if (x->km.state == XFRM_STATE_ERROR ||
|
||||
x->km.state == XFRM_STATE_EXPIRED) {
|
||||
if ((!x->sel.family ||
|
||||
@ -1209,12 +1274,60 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
|
||||
unsigned short encap_family = tmpl->encap_family;
|
||||
unsigned int sequence;
|
||||
struct km_event c;
|
||||
unsigned int pcpu_id;
|
||||
bool cached = false;
|
||||
|
||||
/* We need the cpu id just as a lookup key,
|
||||
* we don't require it to be stable.
|
||||
*/
|
||||
pcpu_id = get_cpu();
|
||||
put_cpu();
|
||||
|
||||
to_put = NULL;
|
||||
|
||||
sequence = read_seqcount_begin(&net->xfrm.xfrm_state_hash_generation);
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(x, &pol->state_cache_list, state_cache) {
|
||||
if (x->props.family == encap_family &&
|
||||
x->props.reqid == tmpl->reqid &&
|
||||
(mark & x->mark.m) == x->mark.v &&
|
||||
x->if_id == if_id &&
|
||||
!(x->props.flags & XFRM_STATE_WILDRECV) &&
|
||||
xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
|
||||
tmpl->mode == x->props.mode &&
|
||||
tmpl->id.proto == x->id.proto &&
|
||||
(tmpl->id.spi == x->id.spi || !tmpl->id.spi))
|
||||
xfrm_state_look_at(pol, x, fl, encap_family,
|
||||
&best, &acquire_in_progress, &error);
|
||||
}
|
||||
|
||||
if (best)
|
||||
goto cached;
|
||||
|
||||
hlist_for_each_entry_rcu(x, &pol->state_cache_list, state_cache) {
|
||||
if (x->props.family == encap_family &&
|
||||
x->props.reqid == tmpl->reqid &&
|
||||
(mark & x->mark.m) == x->mark.v &&
|
||||
x->if_id == if_id &&
|
||||
!(x->props.flags & XFRM_STATE_WILDRECV) &&
|
||||
xfrm_addr_equal(&x->id.daddr, daddr, encap_family) &&
|
||||
tmpl->mode == x->props.mode &&
|
||||
tmpl->id.proto == x->id.proto &&
|
||||
(tmpl->id.spi == x->id.spi || !tmpl->id.spi))
|
||||
xfrm_state_look_at(pol, x, fl, family,
|
||||
&best, &acquire_in_progress, &error);
|
||||
}
|
||||
|
||||
cached:
|
||||
cached = true;
|
||||
if (best)
|
||||
goto found;
|
||||
else if (error)
|
||||
best = NULL;
|
||||
else if (acquire_in_progress) /* XXX: acquire_in_progress should not happen */
|
||||
WARN_ON(1);
|
||||
|
||||
h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);
|
||||
hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h, bydst) {
|
||||
#ifdef CONFIG_XFRM_OFFLOAD
|
||||
@ -1282,7 +1395,10 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
|
||||
}
|
||||
|
||||
found:
|
||||
x = best;
|
||||
if (!(pol->flags & XFRM_POLICY_CPU_ACQUIRE) ||
|
||||
(best && (best->pcpu_num == pcpu_id)))
|
||||
x = best;
|
||||
|
||||
if (!x && !error && !acquire_in_progress) {
|
||||
if (tmpl->id.spi &&
|
||||
(x0 = __xfrm_state_lookup_all(net, mark, daddr,
|
||||
@ -1314,6 +1430,8 @@ found:
|
||||
xfrm_init_tempstate(x, fl, tmpl, daddr, saddr, family);
|
||||
memcpy(&x->mark, &pol->mark, sizeof(x->mark));
|
||||
x->if_id = if_id;
|
||||
if ((pol->flags & XFRM_POLICY_CPU_ACQUIRE) && best)
|
||||
x->pcpu_num = pcpu_id;
|
||||
|
||||
error = security_xfrm_state_alloc_acquire(x, pol->security, fl->flowi_secid);
|
||||
if (error) {
|
||||
@ -1352,6 +1470,7 @@ found:
|
||||
x->km.state = XFRM_STATE_ACQ;
|
||||
x->dir = XFRM_SA_DIR_OUT;
|
||||
list_add(&x->km.all, &net->xfrm.state_all);
|
||||
h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);
|
||||
XFRM_STATE_INSERT(bydst, &x->bydst,
|
||||
net->xfrm.state_bydst + h,
|
||||
x->xso.type);
|
||||
@ -1359,6 +1478,7 @@ found:
|
||||
XFRM_STATE_INSERT(bysrc, &x->bysrc,
|
||||
net->xfrm.state_bysrc + h,
|
||||
x->xso.type);
|
||||
INIT_HLIST_NODE(&x->state_cache);
|
||||
if (x->id.spi) {
|
||||
h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
|
||||
XFRM_STATE_INSERT(byspi, &x->byspi,
|
||||
@ -1392,6 +1512,11 @@ found:
|
||||
x = NULL;
|
||||
error = -ESRCH;
|
||||
}
|
||||
|
||||
/* Use the already installed 'fallback' while the CPU-specific
|
||||
* SA acquire is handled*/
|
||||
if (best)
|
||||
x = best;
|
||||
}
|
||||
out:
|
||||
if (x) {
|
||||
@ -1402,6 +1527,15 @@ out:
|
||||
} else {
|
||||
*err = acquire_in_progress ? -EAGAIN : error;
|
||||
}
|
||||
|
||||
if (x && x->km.state == XFRM_STATE_VALID && !cached &&
|
||||
(!(pol->flags & XFRM_POLICY_CPU_ACQUIRE) || x->pcpu_num == pcpu_id)) {
|
||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
||||
if (hlist_unhashed(&x->state_cache))
|
||||
hlist_add_head_rcu(&x->state_cache, &pol->state_cache_list);
|
||||
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
if (to_put)
|
||||
xfrm_state_put(to_put);
|
||||
@ -1524,12 +1658,14 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
|
||||
unsigned int h;
|
||||
u32 mark = xnew->mark.v & xnew->mark.m;
|
||||
u32 if_id = xnew->if_id;
|
||||
u32 cpu_id = xnew->pcpu_num;
|
||||
|
||||
h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
|
||||
hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {
|
||||
if (x->props.family == family &&
|
||||
x->props.reqid == reqid &&
|
||||
x->if_id == if_id &&
|
||||
x->pcpu_num == cpu_id &&
|
||||
(mark & x->mark.m) == x->mark.v &&
|
||||
xfrm_addr_equal(&x->id.daddr, &xnew->id.daddr, family) &&
|
||||
xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, family))
|
||||
@ -1552,7 +1688,7 @@ EXPORT_SYMBOL(xfrm_state_insert);
|
||||
static struct xfrm_state *__find_acq_core(struct net *net,
|
||||
const struct xfrm_mark *m,
|
||||
unsigned short family, u8 mode,
|
||||
u32 reqid, u32 if_id, u8 proto,
|
||||
u32 reqid, u32 if_id, u32 pcpu_num, u8 proto,
|
||||
const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
int create)
|
||||
@ -1569,6 +1705,7 @@ static struct xfrm_state *__find_acq_core(struct net *net,
|
||||
x->id.spi != 0 ||
|
||||
x->id.proto != proto ||
|
||||
(mark & x->mark.m) != x->mark.v ||
|
||||
x->pcpu_num != pcpu_num ||
|
||||
!xfrm_addr_equal(&x->id.daddr, daddr, family) ||
|
||||
!xfrm_addr_equal(&x->props.saddr, saddr, family))
|
||||
continue;
|
||||
@ -1602,6 +1739,7 @@ static struct xfrm_state *__find_acq_core(struct net *net,
|
||||
break;
|
||||
}
|
||||
|
||||
x->pcpu_num = pcpu_num;
|
||||
x->km.state = XFRM_STATE_ACQ;
|
||||
x->id.proto = proto;
|
||||
x->props.family = family;
|
||||
@ -1630,7 +1768,7 @@ static struct xfrm_state *__find_acq_core(struct net *net,
|
||||
return x;
|
||||
}
|
||||
|
||||
static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
|
||||
static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq, u32 pcpu_num);
|
||||
|
||||
int xfrm_state_add(struct xfrm_state *x)
|
||||
{
|
||||
@ -1656,7 +1794,7 @@ int xfrm_state_add(struct xfrm_state *x)
|
||||
}
|
||||
|
||||
if (use_spi && x->km.seq) {
|
||||
x1 = __xfrm_find_acq_byseq(net, mark, x->km.seq);
|
||||
x1 = __xfrm_find_acq_byseq(net, mark, x->km.seq, x->pcpu_num);
|
||||
if (x1 && ((x1->id.proto != x->id.proto) ||
|
||||
!xfrm_addr_equal(&x1->id.daddr, &x->id.daddr, family))) {
|
||||
to_put = x1;
|
||||
@ -1666,7 +1804,7 @@ int xfrm_state_add(struct xfrm_state *x)
|
||||
|
||||
if (use_spi && !x1)
|
||||
x1 = __find_acq_core(net, &x->mark, family, x->props.mode,
|
||||
x->props.reqid, x->if_id, x->id.proto,
|
||||
x->props.reqid, x->if_id, x->pcpu_num, x->id.proto,
|
||||
&x->id.daddr, &x->props.saddr, 0);
|
||||
|
||||
__xfrm_state_bump_genids(x);
|
||||
@ -1791,6 +1929,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
|
||||
x->props.flags = orig->props.flags;
|
||||
x->props.extra_flags = orig->props.extra_flags;
|
||||
|
||||
x->pcpu_num = orig->pcpu_num;
|
||||
x->if_id = orig->if_id;
|
||||
x->tfcpad = orig->tfcpad;
|
||||
x->replay_maxdiff = orig->replay_maxdiff;
|
||||
@ -2066,13 +2205,14 @@ EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
|
||||
|
||||
struct xfrm_state *
|
||||
xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid,
|
||||
u32 if_id, u8 proto, const xfrm_address_t *daddr,
|
||||
u32 if_id, u32 pcpu_num, u8 proto, const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr, int create, unsigned short family)
|
||||
{
|
||||
struct xfrm_state *x;
|
||||
|
||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
||||
x = __find_acq_core(net, mark, family, mode, reqid, if_id, proto, daddr, saddr, create);
|
||||
x = __find_acq_core(net, mark, family, mode, reqid, if_id, pcpu_num,
|
||||
proto, daddr, saddr, create);
|
||||
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||
|
||||
return x;
|
||||
@ -2207,7 +2347,7 @@ xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n,
|
||||
|
||||
/* Silly enough, but I'm lazy to build resolution list */
|
||||
|
||||
static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq)
|
||||
static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq, u32 pcpu_num)
|
||||
{
|
||||
unsigned int h = xfrm_seq_hash(net, seq);
|
||||
struct xfrm_state *x;
|
||||
@ -2215,6 +2355,7 @@ static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 s
|
||||
hlist_for_each_entry_rcu(x, net->xfrm.state_byseq + h, byseq) {
|
||||
if (x->km.seq == seq &&
|
||||
(mark & x->mark.m) == x->mark.v &&
|
||||
x->pcpu_num == pcpu_num &&
|
||||
x->km.state == XFRM_STATE_ACQ) {
|
||||
xfrm_state_hold(x);
|
||||
return x;
|
||||
@ -2224,12 +2365,12 @@ static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 s
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq)
|
||||
struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq, u32 pcpu_num)
|
||||
{
|
||||
struct xfrm_state *x;
|
||||
|
||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
||||
x = __xfrm_find_acq_byseq(net, mark, seq);
|
||||
x = __xfrm_find_acq_byseq(net, mark, seq, pcpu_num);
|
||||
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||
return x;
|
||||
}
|
||||
@ -2988,6 +3129,11 @@ int __net_init xfrm_state_init(struct net *net)
|
||||
net->xfrm.state_byseq = xfrm_hash_alloc(sz);
|
||||
if (!net->xfrm.state_byseq)
|
||||
goto out_byseq;
|
||||
|
||||
net->xfrm.state_cache_input = alloc_percpu(struct hlist_head);
|
||||
if (!net->xfrm.state_cache_input)
|
||||
goto out_state_cache_input;
|
||||
|
||||
net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
|
||||
|
||||
net->xfrm.state_num = 0;
|
||||
@ -2997,6 +3143,8 @@ int __net_init xfrm_state_init(struct net *net)
|
||||
&net->xfrm.xfrm_state_lock);
|
||||
return 0;
|
||||
|
||||
out_state_cache_input:
|
||||
xfrm_hash_free(net->xfrm.state_byseq, sz);
|
||||
out_byseq:
|
||||
xfrm_hash_free(net->xfrm.state_byspi, sz);
|
||||
out_byspi:
|
||||
@ -3026,6 +3174,7 @@ void xfrm_state_fini(struct net *net)
|
||||
xfrm_hash_free(net->xfrm.state_bysrc, sz);
|
||||
WARN_ON(!hlist_empty(net->xfrm.state_bydst));
|
||||
xfrm_hash_free(net->xfrm.state_bydst, sz);
|
||||
free_percpu(net->xfrm.state_cache_input);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
|
@ -460,6 +460,12 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
|
||||
}
|
||||
}
|
||||
|
||||
if (!sa_dir && attrs[XFRMA_SA_PCPU]) {
|
||||
NL_SET_ERR_MSG(extack, "SA_PCPU only supported with SA_DIR");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@ -839,6 +845,12 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
|
||||
x->nat_keepalive_interval =
|
||||
nla_get_u32(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL]);
|
||||
|
||||
if (attrs[XFRMA_SA_PCPU]) {
|
||||
x->pcpu_num = nla_get_u32(attrs[XFRMA_SA_PCPU]);
|
||||
if (x->pcpu_num >= num_possible_cpus())
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV], extack);
|
||||
if (err)
|
||||
goto error;
|
||||
@ -1087,7 +1099,7 @@ static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb)
|
||||
if (!nla)
|
||||
return -EMSGSIZE;
|
||||
algo = nla_data(nla);
|
||||
strncpy(algo->alg_name, auth->alg_name, sizeof(algo->alg_name));
|
||||
strscpy_pad(algo->alg_name, auth->alg_name, sizeof(algo->alg_name));
|
||||
|
||||
if (redact_secret && auth->alg_key_len)
|
||||
memset(algo->alg_key, 0, (auth->alg_key_len + 7) / 8);
|
||||
@ -1294,6 +1306,11 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
if (x->pcpu_num != UINT_MAX) {
|
||||
ret = nla_put_u32(skb, XFRMA_SA_PCPU, x->pcpu_num);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
if (x->dir)
|
||||
ret = nla_put_u8(skb, XFRMA_SA_DIR, x->dir);
|
||||
|
||||
@ -1698,6 +1715,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
u32 mark;
|
||||
struct xfrm_mark m;
|
||||
u32 if_id = 0;
|
||||
u32 pcpu_num = UINT_MAX;
|
||||
|
||||
p = nlmsg_data(nlh);
|
||||
err = verify_spi_info(p->info.id.proto, p->min, p->max, extack);
|
||||
@ -1714,8 +1732,16 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
if (attrs[XFRMA_IF_ID])
|
||||
if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
|
||||
|
||||
if (attrs[XFRMA_SA_PCPU]) {
|
||||
pcpu_num = nla_get_u32(attrs[XFRMA_SA_PCPU]);
|
||||
if (pcpu_num >= num_possible_cpus()) {
|
||||
err = -EINVAL;
|
||||
goto out_noput;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->info.seq) {
|
||||
x = xfrm_find_acq_byseq(net, mark, p->info.seq);
|
||||
x = xfrm_find_acq_byseq(net, mark, p->info.seq, pcpu_num);
|
||||
if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) {
|
||||
xfrm_state_put(x);
|
||||
x = NULL;
|
||||
@ -1724,7 +1750,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
|
||||
if (!x)
|
||||
x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid,
|
||||
if_id, p->info.id.proto, daddr,
|
||||
if_id, pcpu_num, p->info.id.proto, daddr,
|
||||
&p->info.saddr, 1,
|
||||
family);
|
||||
err = -ENOENT;
|
||||
@ -2524,7 +2550,8 @@ static inline unsigned int xfrm_aevent_msgsize(struct xfrm_state *x)
|
||||
+ nla_total_size(sizeof(struct xfrm_mark))
|
||||
+ nla_total_size(4) /* XFRM_AE_RTHR */
|
||||
+ nla_total_size(4) /* XFRM_AE_ETHR */
|
||||
+ nla_total_size(sizeof(x->dir)); /* XFRMA_SA_DIR */
|
||||
+ nla_total_size(sizeof(x->dir)) /* XFRMA_SA_DIR */
|
||||
+ nla_total_size(4); /* XFRMA_SA_PCPU */
|
||||
}
|
||||
|
||||
static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
|
||||
@ -2580,6 +2607,11 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct
|
||||
err = xfrm_if_id_put(skb, x->if_id);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
if (x->pcpu_num != UINT_MAX) {
|
||||
err = nla_put_u32(skb, XFRMA_SA_PCPU, x->pcpu_num);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
if (x->dir) {
|
||||
err = nla_put_u8(skb, XFRMA_SA_DIR, x->dir);
|
||||
@ -2850,6 +2882,13 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
|
||||
xfrm_mark_get(attrs, &mark);
|
||||
|
||||
if (attrs[XFRMA_SA_PCPU]) {
|
||||
x->pcpu_num = nla_get_u32(attrs[XFRMA_SA_PCPU]);
|
||||
err = -EINVAL;
|
||||
if (x->pcpu_num >= num_possible_cpus())
|
||||
goto free_state;
|
||||
}
|
||||
|
||||
err = verify_newpolicy_info(&ua->policy, extack);
|
||||
if (err)
|
||||
goto free_state;
|
||||
@ -3180,6 +3219,7 @@ const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
|
||||
[XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
|
||||
[XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT),
|
||||
[XFRMA_NAT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
|
||||
[XFRMA_SA_PCPU] = { .type = NLA_U32 },
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(xfrma_policy);
|
||||
|
||||
@ -3243,6 +3283,20 @@ static int xfrm_reject_unused_attr(int type, struct nlattr **attrs,
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs[XFRMA_SA_PCPU]) {
|
||||
switch (type) {
|
||||
case XFRM_MSG_NEWSA:
|
||||
case XFRM_MSG_UPDSA:
|
||||
case XFRM_MSG_ALLOCSPI:
|
||||
case XFRM_MSG_ACQUIRE:
|
||||
|
||||
break;
|
||||
default:
|
||||
NL_SET_ERR_MSG(extack, "Invalid attribute SA_PCPU");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3346,7 +3400,8 @@ static inline unsigned int xfrm_expire_msgsize(void)
|
||||
{
|
||||
return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) +
|
||||
nla_total_size(sizeof(struct xfrm_mark)) +
|
||||
nla_total_size(sizeof_field(struct xfrm_state, dir));
|
||||
nla_total_size(sizeof_field(struct xfrm_state, dir)) +
|
||||
nla_total_size(4); /* XFRMA_SA_PCPU */
|
||||
}
|
||||
|
||||
static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
|
||||
@ -3372,6 +3427,11 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct
|
||||
err = xfrm_if_id_put(skb, x->if_id);
|
||||
if (err)
|
||||
return err;
|
||||
if (x->pcpu_num != UINT_MAX) {
|
||||
err = nla_put_u32(skb, XFRMA_SA_PCPU, x->pcpu_num);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (x->dir) {
|
||||
err = nla_put_u8(skb, XFRMA_SA_DIR, x->dir);
|
||||
@ -3479,6 +3539,8 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state *x)
|
||||
}
|
||||
if (x->if_id)
|
||||
l += nla_total_size(sizeof(x->if_id));
|
||||
if (x->pcpu_num)
|
||||
l += nla_total_size(sizeof(x->pcpu_num));
|
||||
|
||||
/* Must count x->lastused as it may become non-zero behind our back. */
|
||||
l += nla_total_size_64bit(sizeof(u64));
|
||||
@ -3585,6 +3647,7 @@ static inline unsigned int xfrm_acquire_msgsize(struct xfrm_state *x,
|
||||
+ nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
|
||||
+ nla_total_size(sizeof(struct xfrm_mark))
|
||||
+ nla_total_size(xfrm_user_sec_ctx_size(x->security))
|
||||
+ nla_total_size(4) /* XFRMA_SA_PCPU */
|
||||
+ userpolicy_type_attrsize();
|
||||
}
|
||||
|
||||
@ -3621,6 +3684,8 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x,
|
||||
err = xfrm_if_id_put(skb, xp->if_id);
|
||||
if (!err && xp->xdo.dev)
|
||||
err = copy_user_offload(&xp->xdo, skb);
|
||||
if (!err && x->pcpu_num != UINT_MAX)
|
||||
err = nla_put_u32(skb, XFRMA_SA_PCPU, x->pcpu_num);
|
||||
if (err) {
|
||||
nlmsg_cancel(skb, nlh);
|
||||
return err;
|
||||
|
Loading…
Reference in New Issue
Block a user